From 3a522ba1bc852c3d4660a4fa32e4a94999d09a47 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Sat, 10 Jul 2021 17:19:52 +0100 Subject: [PATCH] Pass the syscall number to capsicum permission-denied signals The syscall number is stored in the same register as the syscall return on amd64 (and possibly other architectures) and so it is impossible to recover in the signal handler after the call has returned. This small tweak delivers it in the `si_value` field of the signal, which is sufficient to catch capability violations and emulate them with a call to a more-privileged process in the signal handler. Approved by: markj (mentor) Reviewed by: kib, bcr (manpages) Differential Revision: https://reviews.freebsd.org/D29185 --- lib/libc/sys/procctl.2 | 10 ++++++++++ share/man/man3/siginfo.3 | 2 ++ sys/amd64/amd64/trap.c | 1 + sys/amd64/cloudabi32/cloudabi32_sysvec.c | 1 + sys/amd64/cloudabi64/cloudabi64_sysvec.c | 1 + sys/amd64/ia32/ia32_syscall.c | 1 + sys/amd64/include/proc.h | 1 + sys/amd64/linux/linux_sysvec.c | 1 + sys/amd64/linux32/linux32_sysvec.c | 1 + sys/arm/arm/syscall.c | 1 + sys/arm/cloudabi32/cloudabi32_sysvec.c | 1 + sys/arm/include/proc.h | 1 + sys/arm64/arm64/elf32_machdep.c | 1 + sys/arm64/arm64/trap.c | 1 + sys/arm64/cloudabi32/cloudabi32_sysvec.c | 1 + sys/arm64/cloudabi64/cloudabi64_sysvec.c | 1 + sys/arm64/include/proc.h | 1 + sys/arm64/linux/linux_sysvec.c | 1 + sys/i386/cloudabi32/cloudabi32_sysvec.c | 1 + sys/i386/i386/trap.c | 1 + sys/i386/include/proc.h | 1 + sys/i386/linux/linux_sysvec.c | 1 + sys/kern/subr_syscall.c | 1 + sys/mips/include/proc.h | 1 + sys/mips/mips/trap.c | 1 + sys/powerpc/include/proc.h | 1 + sys/powerpc/powerpc/trap.c | 1 + sys/riscv/include/proc.h | 1 + sys/riscv/riscv/trap.c | 1 + sys/sys/signal.h | 7 +++++++ 30 files changed, 46 insertions(+) diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2 index 432ed5919a8..ce7a2be5d5e 100644 --- a/lib/libc/sys/procctl.2 +++ b/lib/libc/sys/procctl.2 @@ -454,6 +454,16 @@ and the .Va si_code member is set to .Dv TRAP_CAP . +The system call number is stored in the +.Va si_syscall +field of the +.Fa siginfo +signal handler parameter. +The other system call parameters can be read from the +.Fa ucontext_t +but the system call number is typically stored in the register +that also contains the return value and so is unavailable in the +signal handler. .Pp See .Xr capsicum 4 diff --git a/share/man/man3/siginfo.3 b/share/man/man3/siginfo.3 index acc8785b2f0..7f8a809cdfa 100644 --- a/share/man/man3/siginfo.3 +++ b/share/man/man3/siginfo.3 @@ -85,6 +85,8 @@ timer overrun count .It Vt int Ta Va si_mqd Ta .Tn POSIX message queue ID +.It Vt int Ta Va si_syscall Ta +system-call number for system calls blocked by Capsicum .El .Pp The diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c index 55649687ce5..b08495f3f13 100644 --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -1059,6 +1059,7 @@ cpu_fetch_syscall_args(struct thread *td) sa = &td->td_sa; sa->code = frame->tf_rax; + sa->original_code = sa->code; if (__predict_false(sa->code == SYS_syscall || sa->code == SYS___syscall || diff --git a/sys/amd64/cloudabi32/cloudabi32_sysvec.c b/sys/amd64/cloudabi32/cloudabi32_sysvec.c index 164f87e90e9..26924ed5a98 100644 --- a/sys/amd64/cloudabi32/cloudabi32_sysvec.c +++ b/sys/amd64/cloudabi32/cloudabi32_sysvec.c @@ -101,6 +101,7 @@ cloudabi32_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_rax; + sa->original_code = sa->code; if (sa->code >= CLOUDABI32_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi32_sysent[sa->code]; diff --git a/sys/amd64/cloudabi64/cloudabi64_sysvec.c b/sys/amd64/cloudabi64/cloudabi64_sysvec.c index d3893902b08..c08d912e84d 100644 --- a/sys/amd64/cloudabi64/cloudabi64_sysvec.c +++ b/sys/amd64/cloudabi64/cloudabi64_sysvec.c @@ -98,6 +98,7 @@ cloudabi64_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_rax; + sa->original_code = sa->code; if (sa->code >= CLOUDABI64_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi64_sysent[sa->code]; diff --git a/sys/amd64/ia32/ia32_syscall.c b/sys/amd64/ia32/ia32_syscall.c index 6c9399d1a52..9294ef8ce74 100644 --- a/sys/amd64/ia32/ia32_syscall.c +++ b/sys/amd64/ia32/ia32_syscall.c @@ -150,6 +150,7 @@ ia32_fetch_syscall_args(struct thread *td) params = (caddr_t)frame->tf_rsp + sizeof(u_int32_t); sa->code = frame->tf_rax; + sa->original_code = sa->code; /* * Need to check if this is a 32 bit or 64 bit syscall. diff --git a/sys/amd64/include/proc.h b/sys/amd64/include/proc.h index 59796e729ac..0f8cf50e326 100644 --- a/sys/amd64/include/proc.h +++ b/sys/amd64/include/proc.h @@ -92,6 +92,7 @@ struct mdproc { struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[8]; }; diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c index c5538932b1e..566af6de29e 100644 --- a/sys/amd64/linux/linux_sysvec.c +++ b/sys/amd64/linux/linux_sysvec.c @@ -191,6 +191,7 @@ linux_fetch_syscall_args(struct thread *td) sa->args[4] = frame->tf_r8; sa->args[5] = frame->tf_r9; sa->code = frame->tf_rax; + sa->original_code = sa->code; if (sa->code >= p->p_sysent->sv_size) /* nosys */ diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c index 504d57e418a..7dfd57a74a1 100644 --- a/sys/amd64/linux32/linux32_sysvec.c +++ b/sys/amd64/linux32/linux32_sysvec.c @@ -662,6 +662,7 @@ linux32_fetch_syscall_args(struct thread *td) sa->args[4] = frame->tf_rdi; sa->args[5] = frame->tf_rbp; /* Unconfirmed */ sa->code = frame->tf_rax; + sa->original_code = sa->code; if (sa->code >= p->p_sysent->sv_size) /* nosys */ diff --git a/sys/arm/arm/syscall.c b/sys/arm/arm/syscall.c index a851db6e455..a635de0ec71 100644 --- a/sys/arm/arm/syscall.c +++ b/sys/arm/arm/syscall.c @@ -108,6 +108,7 @@ cpu_fetch_syscall_args(struct thread *td) nap = 4; sa = &td->td_sa; sa->code = td->td_frame->tf_r7; + sa->original_code = sa->code; ap = &td->td_frame->tf_r0; if (sa->code == SYS_syscall) { sa->code = *ap++; diff --git a/sys/arm/cloudabi32/cloudabi32_sysvec.c b/sys/arm/cloudabi32/cloudabi32_sysvec.c index a8c5da47d26..4df57b22e13 100644 --- a/sys/arm/cloudabi32/cloudabi32_sysvec.c +++ b/sys/arm/cloudabi32/cloudabi32_sysvec.c @@ -78,6 +78,7 @@ cloudabi32_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_r12; + sa->original_code = sa->code; if (sa->code >= CLOUDABI32_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi32_sysent[sa->code]; diff --git a/sys/arm/include/proc.h b/sys/arm/include/proc.h index a37ccd8f621..9566c264731 100644 --- a/sys/arm/include/proc.h +++ b/sys/arm/include/proc.h @@ -75,6 +75,7 @@ struct mdproc { */ struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[MAXARGS]; } __aligned(8); diff --git a/sys/arm64/arm64/elf32_machdep.c b/sys/arm64/arm64/elf32_machdep.c index 206413b4517..7cedbffc4d4 100644 --- a/sys/arm64/arm64/elf32_machdep.c +++ b/sys/arm64/arm64/elf32_machdep.c @@ -175,6 +175,7 @@ freebsd32_fetch_syscall_args(struct thread *td) /* r7 is the syscall id */ sa->code = td->td_frame->tf_x[7]; + sa->original_code = sa->code; if (sa->code == SYS_syscall) { sa->code = *ap++; diff --git a/sys/arm64/arm64/trap.c b/sys/arm64/arm64/trap.c index 744b646b31c..2b2d2e433ef 100644 --- a/sys/arm64/arm64/trap.c +++ b/sys/arm64/arm64/trap.c @@ -130,6 +130,7 @@ cpu_fetch_syscall_args(struct thread *td) dst_ap = &sa->args[0]; sa->code = td->td_frame->tf_x[8]; + sa->original_code = sa->code; if (__predict_false(sa->code == SYS_syscall || sa->code == SYS___syscall)) { sa->code = *ap++; diff --git a/sys/arm64/cloudabi32/cloudabi32_sysvec.c b/sys/arm64/cloudabi32/cloudabi32_sysvec.c index 889393560ed..b7be5cc0e5e 100644 --- a/sys/arm64/cloudabi32/cloudabi32_sysvec.c +++ b/sys/arm64/cloudabi32/cloudabi32_sysvec.c @@ -75,6 +75,7 @@ cloudabi32_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_x[0]; + sa->original_code = sa->code; if (sa->code >= CLOUDABI32_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi32_sysent[sa->code]; diff --git a/sys/arm64/cloudabi64/cloudabi64_sysvec.c b/sys/arm64/cloudabi64/cloudabi64_sysvec.c index bdbd828b7b6..624d8669345 100644 --- a/sys/arm64/cloudabi64/cloudabi64_sysvec.c +++ b/sys/arm64/cloudabi64/cloudabi64_sysvec.c @@ -78,6 +78,7 @@ cloudabi64_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_x[8]; + sa->original_code = sa->code; if (sa->code >= CLOUDABI64_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi64_sysent[sa->code]; diff --git a/sys/arm64/include/proc.h b/sys/arm64/include/proc.h index bb933dc9824..3800798d79b 100644 --- a/sys/arm64/include/proc.h +++ b/sys/arm64/include/proc.h @@ -49,6 +49,7 @@ struct mdproc { #define MAXARGS 8 struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[MAXARGS]; }; diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c index e1d3708b70e..7749c1cd7fc 100644 --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -125,6 +125,7 @@ linux_fetch_syscall_args(struct thread *td) sa = &td->td_sa; sa->code = td->td_frame->tf_x[8]; + sa->original_code = sa->code; /* LINUXTODO: generic syscall? */ if (sa->code >= p->p_sysent->sv_size) sa->callp = &p->p_sysent->sv_table[0]; diff --git a/sys/i386/cloudabi32/cloudabi32_sysvec.c b/sys/i386/cloudabi32/cloudabi32_sysvec.c index 4f12d2b6cbc..e0a50f6697a 100644 --- a/sys/i386/cloudabi32/cloudabi32_sysvec.c +++ b/sys/i386/cloudabi32/cloudabi32_sysvec.c @@ -96,6 +96,7 @@ cloudabi32_fetch_syscall_args(struct thread *td) /* Obtain system call number. */ sa->code = frame->tf_eax; + sa->original_code = sa->code; if (sa->code >= CLOUDABI32_SYS_MAXSYSCALL) return (ENOSYS); sa->callp = &cloudabi32_sysent[sa->code]; diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c index 045478149be..07abac23c9d 100644 --- a/sys/i386/i386/trap.c +++ b/sys/i386/i386/trap.c @@ -1052,6 +1052,7 @@ cpu_fetch_syscall_args(struct thread *td) #endif sa->code = frame->tf_eax; + sa->original_code = sa->code; params = (caddr_t)frame->tf_esp + sizeof(uint32_t); /* diff --git a/sys/i386/include/proc.h b/sys/i386/include/proc.h index 2950946ff15..76e1ac61147 100644 --- a/sys/i386/include/proc.h +++ b/sys/i386/include/proc.h @@ -64,6 +64,7 @@ struct mdproc { struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[8]; }; diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index adb70fded6d..a0959d55b58 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -756,6 +756,7 @@ linux_fetch_syscall_args(struct thread *td) sa = &td->td_sa; sa->code = frame->tf_eax; + sa->original_code = sa->code; sa->args[0] = frame->tf_ebx; sa->args[1] = frame->tf_ecx; sa->args[2] = frame->tf_edx; diff --git a/sys/kern/subr_syscall.c b/sys/kern/subr_syscall.c index 85a0814a212..2304e3e7f3f 100644 --- a/sys/kern/subr_syscall.c +++ b/sys/kern/subr_syscall.c @@ -230,6 +230,7 @@ syscallret(struct thread *td) ksi.ksi_signo = SIGTRAP; ksi.ksi_errno = td->td_errno; ksi.ksi_code = TRAP_CAP; + ksi.ksi_info.si_syscall = sa->original_code; trapsignal(td, &ksi); } } diff --git a/sys/mips/include/proc.h b/sys/mips/include/proc.h index 0cb1d433387..29d832a162e 100644 --- a/sys/mips/include/proc.h +++ b/sys/mips/include/proc.h @@ -84,6 +84,7 @@ struct mdproc { #define MAXARGS 8 struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[MAXARGS]; }; diff --git a/sys/mips/mips/trap.c b/sys/mips/mips/trap.c index 96a2de4ee81..9d7a0760637 100644 --- a/sys/mips/mips/trap.c +++ b/sys/mips/mips/trap.c @@ -355,6 +355,7 @@ cpu_fetch_syscall_args(struct thread *td) else locr0->pc += sizeof(int); sa->code = locr0->v0; + sa->original_code = sa->code; switch (sa->code) { case SYS___syscall: diff --git a/sys/powerpc/include/proc.h b/sys/powerpc/include/proc.h index d4df3ccfefe..aac4e66b39f 100644 --- a/sys/powerpc/include/proc.h +++ b/sys/powerpc/include/proc.h @@ -62,6 +62,7 @@ struct mdproc { #define MAXARGS 8 struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[MAXARGS]; }; diff --git a/sys/powerpc/powerpc/trap.c b/sys/powerpc/powerpc/trap.c index 3a1bdf7cde0..ab5189a0f41 100644 --- a/sys/powerpc/powerpc/trap.c +++ b/sys/powerpc/powerpc/trap.c @@ -667,6 +667,7 @@ cpu_fetch_syscall_args(struct thread *td) sa = &td->td_sa; sa->code = frame->fixreg[0]; + sa->original_code = sa->code; params = (caddr_t)(frame->fixreg + FIRSTARG); n = NARGREG; diff --git a/sys/riscv/include/proc.h b/sys/riscv/include/proc.h index 4b5ae9ebe3e..1c6c8d2919b 100644 --- a/sys/riscv/include/proc.h +++ b/sys/riscv/include/proc.h @@ -48,6 +48,7 @@ struct mdproc { #define MAXARGS 8 struct syscall_args { u_int code; + u_int original_code; struct sysent *callp; register_t args[MAXARGS]; }; diff --git a/sys/riscv/riscv/trap.c b/sys/riscv/riscv/trap.c index 07d7f84a94e..8844638c820 100644 --- a/sys/riscv/riscv/trap.c +++ b/sys/riscv/riscv/trap.c @@ -103,6 +103,7 @@ cpu_fetch_syscall_args(struct thread *td) dst_ap = &sa->args[0]; sa->code = td->td_frame->tf_t[0]; + sa->original_code = sa->code; if (__predict_false(sa->code == SYS_syscall || sa->code == SYS___syscall)) { sa->code = *ap++; diff --git a/sys/sys/signal.h b/sys/sys/signal.h index 8b45a521c3e..9dae3ce0474 100644 --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -255,6 +255,12 @@ typedef struct __siginfo { struct { long _band; /* band event for SIGPOLL */ } _poll; /* was this ever used ? */ + struct { + int _syscall; /* Syscall number for signals + * delivered as a result of + * system calls denied by + * Capsicum. */ + } _capsicum; struct { long __spare1__; int __spare2__[7]; @@ -267,6 +273,7 @@ typedef struct __siginfo { #define si_overrun _reason._timer._overrun #define si_mqd _reason._mesgq._mqd #define si_band _reason._poll._band +#define si_syscall _reason._capsicum._syscall #if defined(_WANT_LWPINFO32) || (defined(_KERNEL) && defined(__LP64__)) struct siginfo32 {