From bc6a0e1a16baeaff6a50d3f6a5d9d5eb6a8537f3 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 24 May 2022 13:43:50 -0600 Subject: [PATCH] Enable ptrace support for MIPS but only for log_subcmds. It is not possible to change the syscall return value on MIPS so we cannot support full intercept mode. Another complication on MIPS is that if a system call is invoked via syscall(__NR_###), v0 holds __NR_O32_Linux and the real syscall is in the first arg (a0) and other args are shifted by one. --- src/exec_ptrace.c | 31 ++++++++------ src/exec_ptrace.h | 102 ++++++++++++++++++++++++++++++++++++++++++---- src/sudo_exec.h | 3 +- 3 files changed, 114 insertions(+), 22 deletions(-) diff --git a/src/exec_ptrace.c b/src/exec_ptrace.c index d3d524dfb..28d4d3367 100644 --- a/src/exec_ptrace.c +++ b/src/exec_ptrace.c @@ -109,9 +109,9 @@ static inline void set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg1(regs->u.compat) = addr; + compat_reg_set_arg1(regs->u.compat, addr); } else { - reg_arg1(regs->u.native) = addr; + reg_set_arg1(regs->u.native, addr); } } @@ -129,9 +129,9 @@ static inline void set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg2(regs->u.compat) = addr; + compat_reg_set_arg2(regs->u.compat, addr); } else { - reg_arg2(regs->u.native) = addr; + reg_set_arg2(regs->u.native, addr); } } @@ -150,9 +150,9 @@ static inline void set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg3(regs->u.compat) = addr; + compat_reg_set_arg3(regs->u.compat, addr); } else { - reg_arg3(regs->u.native) = addr; + reg_set_arg3(regs->u.native, addr); } } @@ -170,9 +170,9 @@ static inline void set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) { if (regs->compat) { - compat_reg_arg4(regs->u.compat) = addr; + compat_reg_set_arg4(regs->u.compat, addr); } else { - reg_arg4(regs->u.native) = addr; + reg_set_arg4(regs->u.native, addr); } } # endif /* notyet */ @@ -212,7 +212,7 @@ get_sc_arg1(struct sudo_ptrace_regs *regs) static inline void set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg1(regs->u.native) = addr; + reg_set_arg1(regs->u.native, addr); } static inline unsigned long @@ -224,7 +224,7 @@ get_sc_arg2(struct sudo_ptrace_regs *regs) static inline void set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg2(regs->u.native) = addr; + reg_set_arg2(regs->u.native, addr); } static inline unsigned long @@ -237,7 +237,7 @@ get_sc_arg3(struct sudo_ptrace_regs *regs) static inline void set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg3(regs->u.native) = addr; + reg_set_arg3(regs->u.native, addr); } static inline unsigned long @@ -249,7 +249,7 @@ get_sc_arg4(struct sudo_ptrace_regs *regs) static inline void set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) { - reg_arg4(regs->u.native) = addr; + reg_set_arg4(regs->u.native, addr); } # endif /* notyet */ # endif /* SECCOMP_AUDIT_ARCH_COMPAT */ @@ -1280,10 +1280,15 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept) bool exec_ptrace_intercept_supported(void) { +# ifdef __mips__ + /* MIPS doesn't support changing the syscall return value. */ + return false; +# else if (seccomp_trap_supported == -1) - seccomp_trap_supporetd = have_seccomp_action("trap"); + seccomp_trap_supported = have_seccomp_action("trap"); return seccomp_trap_supported == true; +# endif } bool diff --git a/src/exec_ptrace.h b/src/exec_ptrace.h index bdc8a45ad..933120989 100644 --- a/src/exec_ptrace.h +++ b/src/exec_ptrace.h @@ -37,6 +37,10 @@ # define __X32_SYSCALL_BIT 0x40000000 #endif +#if defined(__mips__) && !defined(__NR_O32_Linux) +# define __NR_O32_Linux 4000 +#endif + /* Align address to a (compat) word boundary. */ #define WORDALIGN(_a, _r) \ (((_a) + ((long)(_r).wordsize - 1L)) & ~((long)(_r).wordsize - 1L)) @@ -141,20 +145,60 @@ # else # error "Unsupported MIPS ABI" # endif -/* Untested/incomplete. +/* * If called via syscall(__NR_###), v0 holds __NR_O32_Linux and the real - * syscall the first arg (a0) and other args are shifted by one. - * We don't currently support this. + * syscall is in the first arg (a0). The actual args are shifted by one. * MIPS does not support setting the syscall return value via ptrace. */ # define sudo_pt_regs struct pt_regs -# define reg_syscall(x) (x).regs[2] /* v0 */ +# define reg_syscall(_r) ({ \ + __u64 _nr; \ + if ((_r).regs[2] == __NR_O32_Linux) \ + _nr = (_r).regs[4]; /* a0 */ \ + else \ + _nr = (_r).regs[2]; /* v0 */ \ + _nr; \ +}) # define reg_retval(x) (x).regs[2] /* v0 */ # define reg_sp(x) (x).regs[29] /* sp */ -# define reg_arg1(x) (x).regs[4] /* a0 */ -# define reg_arg2(x) (x).regs[5] /* a1 */ -# define reg_arg3(x) (x).regs[6] /* a2 */ -# define reg_arg4(x) (x).regs[7] /* a3 */ +# define reg_arg1(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[5] : (x).regs[4]) +# define reg_set_arg1(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[5] = _v; /* a1 */ \ + else \ + (_r).regs[4] = _v; /* a0 */ \ +} while (0) +# define reg_arg2(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[6] : (x).regs[5]) +# define reg_set_arg2(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[6] = _v; /* a2 */ \ + else \ + (_r).regs[5] = _v; /* a1 */ \ +} while (0) +# define reg_arg3(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[7] : (x).regs[6]) +# define reg_set_arg3(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[7] = _v; /* a3 */ \ + else \ + (_r).regs[6] = _v; /* a2 */ \ +} while (0) +# define reg_arg4(x) \ + ((x).regs[2] == __NR_O32_Linux ? (x).regs[8] : (x).regs[7]) +# define reg_set_arg4(_r, _v) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[8] = _v; /* a4 */ \ + else \ + (_r).regs[7] = _v; /* a3 */ \ +} while (0) +# define reg_set_syscall(_r, _nr) do { \ + if ((_r).regs[2] == __NR_O32_Linux) \ + (_r).regs[4] = _nr; /* a0 */ \ + else \ + (_r).regs[2] = _nr; /* v0 */ \ +} while (0) #elif defined(__powerpc__) # if defined(__powerpc64__) # if BYTE_ORDER == LITTLE_ENDIAN @@ -333,6 +377,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; diff --git a/src/sudo_exec.h b/src/sudo_exec.h index 77c2c9aac..f37901b42 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -92,10 +92,11 @@ union sudo_token_un { /* * Use ptrace-based intercept (using seccomp) on Linux if possible. + * On MIPS we can't change the syscall return and only support log_subcmds. */ #if defined(_PATH_SUDO_INTERCEPT) && defined(__linux__) # if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER -# if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__powerpc__) || (defined(__riscv) && __riscv_xlen == 64) || defined(__s390__) +# if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__mips__) || defined(__powerpc__) || (defined(__riscv) && __riscv_xlen == 64) || defined(__s390__) # ifndef HAVE_PTRACE_INTERCEPT # define HAVE_PTRACE_INTERCEPT 1 # endif /* HAVE_PTRACE_INTERCEPT */