diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c index 28d4d3367..6315a2dc9 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -51,6 +51,13 @@ # include "exec_intercept.h" # include "exec_ptrace.h" +/* We need to take care when ptracing 32-bit binaries on 64-bit kernels. */ +# ifdef __LP64__ +# define COMPAT_FLAG 0x01 +# else +# define COMPAT_FLAG 0x00 +# endif + static int seccomp_trap_supported = -1; /* Register getters and setters. */ @@ -264,13 +271,19 @@ set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) static bool ptrace_getregs(int pid, struct sudo_ptrace_regs *regs, bool compat) { - struct iovec iov; debug_decl(ptrace_getregs, SUDO_DEBUG_EXEC); +# ifdef __mips__ + /* PTRACE_GETREGSET has bugs with the MIPS o32 ABI at least. */ + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s->u) == -1) + debug_return_bool(false); +# else + struct iovec iov; iov.iov_base = ®s->u; iov.iov_len = sizeof(regs->u); if (ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) debug_return_bool(false); +# endif /* __mips__ */ /* Machine-dependent parameters to support compat binaries. */ if (compat) { @@ -291,21 +304,19 @@ ptrace_getregs(int pid, struct sudo_ptrace_regs *regs, bool compat) static bool ptrace_setregs(int pid, struct sudo_ptrace_regs *regs) { - struct iovec iov; debug_decl(ptrace_setregs, SUDO_DEBUG_EXEC); -# ifdef SECCOMP_AUDIT_ARCH_COMPAT - if (regs->compat) { - iov.iov_base = ®s->u.compat; - iov.iov_len = sizeof(regs->u.compat); - } else -#endif - { - iov.iov_base = ®s->u.native; - iov.iov_len = sizeof(regs->u.native); - } +# ifdef __mips__ + /* PTRACE_SETREGSET has bugs with the MIPS o32 ABI at least. */ + if (ptrace(PTRACE_SETREGS, pid, NULL, ®s->u) == -1) + debug_return_bool(false); +# else + struct iovec iov; + iov.iov_base = ®s->u; + iov.iov_len = sizeof(regs->u); if (ptrace(PTRACE_SETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) debug_return_bool(false); +# endif /* __mips__ */ debug_return_bool(true); } @@ -775,16 +786,27 @@ set_exec_filter(void) struct sock_filter exec_filter[] = { /* Load architecture value (AUDIT_ARCH_*) into the accumulator. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), +# ifdef SECCOMP_AUDIT_ARCH_COMPAT2 + /* Match on the compat2 architecture or jump to the compat check. */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH_COMPAT2, 0, 4), + /* Load syscall number into the accumulator. */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), + /* Jump to trace for compat2 execve(2)/execveat(2), else allow. */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT2_execve, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT2_execveat, 0, 13), + /* Trace execve(2)/execveat(2) syscalls (w/ compat flag) */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | COMPAT_FLAG), +# endif /* SECCOMP_AUDIT_ARCH_COMPAT2 */ # ifdef SECCOMP_AUDIT_ARCH_COMPAT /* Match on the compat architecture or jump to the native arch check. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH_COMPAT, 0, 4), /* Load syscall number into the accumulator. */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), - /* Jump to trace for compat execve(2)/execveat(2), else try native. */ + /* Jump to trace for compat execve(2)/execveat(2), else allow. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT_execve, 1, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, COMPAT_execveat, 0, 8), /* Trace execve(2)/execveat(2) syscalls (w/ compat flag) */ - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | 0x1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE | COMPAT_FLAG), # endif /* SECCOMP_AUDIT_ARCH_COMPAT */ /* Jump to the end unless the architecture matches. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_AUDIT_ARCH, 0, 6), diff --git a/src/exec_ptrace.h b/src/exec_ptrace.h index 933120989..7f26283a8 100644 --- a/src/exec_ptrace.h +++ b/src/exec_ptrace.h @@ -37,8 +37,13 @@ # define __X32_SYSCALL_BIT 0x40000000 #endif -#if defined(__mips__) && !defined(__NR_O32_Linux) -# define __NR_O32_Linux 4000 +#ifdef __mips__ +# ifndef __NR_O32_Linux +# define __NR_O32_Linux 4000 +# endif +# ifndef __NR_N32_Linux +# define __NR_N32_Linux 6000 +# endif #endif /* Align address to a (compat) word boundary. */ @@ -185,6 +190,7 @@ else \ (_r).regs[6] = _v; /* a2 */ \ } while (0) +/* XXX - reg_arg4 probably wrong for syscall() type calls on 032. */ # define reg_arg4(x) \ ((x).regs[2] == __NR_O32_Linux ? (x).regs[8] : (x).regs[7]) # define reg_set_arg4(_r, _v) do { \ @@ -318,6 +324,43 @@ struct arm_pt_regs { # define compat_reg_arg3(x) (x).uregs[2] /* r2 */ # define compat_reg_arg4(x) (x).uregs[3] /* r3 */ # define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) +#elif defined(__mips__) +# if _MIPS_SIM == _MIPS_SIM_ABI64 +/* MIPS o32/n32 binary compatibility on a mips64 system. */ +# if BYTE_ORDER == LITTLE_ENDIAN +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPSEL +# define SECCOMP_AUDIT_ARCH_COMPAT2 AUDIT_ARCH_MIPSEL64N32 +# else +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPS +# define SECCOMP_AUDIT_ARCH_COMPAT2 AUDIT_ARCH_MIPS64N32 +# endif +# define COMPAT_execve __NR_O32_Linux + 11 +# define COMPAT_execveat __NR_O32_Linux + 356 +# define COMPAT2_execve __NR_N32_Linux + 57 +# define COMPAT2_execveat __NR_N32_Linux + 320 +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# if BYTE_ORDER == LITTLE_ENDIAN +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPSEL +# else +# define SECCOMP_AUDIT_ARCH_COMPAT AUDIT_ARCH_MIPS +# endif +# define COMPAT_execve __NR_O32_Linux + 11 +# define COMPAT_execveat __NR_O32_Linux + 356 +# endif /* _MIPS_SIM_ABI64 */ +/* MIPS ABIs use a common ptrace interface. */ +# define compat_sudo_pt_regs struct pt_regs +# define compat_reg_syscall(x) reg_syscall(x) +# define compat_reg_retval(x) reg_retval(x) +# define compat_reg_sp(x) reg_sp(x) +# define compat_reg_arg1(x) reg_arg1(x) +# define compat_reg_set_arg1(_r, _v) reg_set_arg1(_r, _v) +# define compat_reg_arg2(x) reg_arg2(x) +# define compat_reg_set_arg2(_r, _v) reg_set_arg2(_r, _v) +# define compat_reg_arg3(x) reg_arg3(x) +# define compat_reg_set_arg3(_r, _v) reg_set_arg3(_r, _v) +# define compat_reg_arg4(x) reg_arg4(x) +# define compat_reg_set_arg4(_r, _v) reg_set_arg4(_r, _v) +# define compat_reg_set_syscall(_r, _nr) reg_set_syscall(_r, _nr) #elif defined(__powerpc64__) struct ppc_pt_regs { unsigned int gpr[32]; @@ -419,6 +462,48 @@ struct ppc_pt_regs { } while (0) #endif +/* Set the syscall arguments the "normal" way by default. */ +#ifndef reg_set_arg1 +# define reg_set_arg1(_r, _v) do { \ + reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg1 +# define compat_reg_set_arg1(_r, _v) do { \ + compat_reg_arg1(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg2 +# define reg_set_arg2(_r, _v) do { \ + reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg2 +# define compat_reg_set_arg2(_r, _v) do { \ + compat_reg_arg2(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg3 +# define reg_set_arg3(_r, _v) do { \ + reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg3 +# define compat_reg_set_arg3(_r, _v) do { \ + compat_reg_arg3(_r) = (_v); \ +} while (0) +#endif +#ifndef reg_set_arg4 +# define reg_set_arg4(_r, _v) do { \ + reg_arg4(_r) = (_v); \ +} while (0) +#endif +#ifndef compat_reg_set_arg4 +# define compat_reg_set_arg4(_r, _v) do { \ + compat_reg_arg4(_r) = (_v); \ +} while (0) +#endif + struct sudo_ptrace_regs { union { sudo_pt_regs native;