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.
This commit is contained in:
Todd C. Miller
2022-05-24 13:43:50 -06:00
parent f053f174bc
commit bc6a0e1a16
3 changed files with 114 additions and 22 deletions

View File

@@ -109,9 +109,9 @@ static inline void
set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr)
{ {
if (regs->compat) { if (regs->compat) {
compat_reg_arg1(regs->u.compat) = addr; compat_reg_set_arg1(regs->u.compat, addr);
} else { } 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) set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr)
{ {
if (regs->compat) { if (regs->compat) {
compat_reg_arg2(regs->u.compat) = addr; compat_reg_set_arg2(regs->u.compat, addr);
} else { } 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) set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr)
{ {
if (regs->compat) { if (regs->compat) {
compat_reg_arg3(regs->u.compat) = addr; compat_reg_set_arg3(regs->u.compat, addr);
} else { } 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) set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr)
{ {
if (regs->compat) { if (regs->compat) {
compat_reg_arg4(regs->u.compat) = addr; compat_reg_set_arg4(regs->u.compat, addr);
} else { } else {
reg_arg4(regs->u.native) = addr; reg_set_arg4(regs->u.native, addr);
} }
} }
# endif /* notyet */ # endif /* notyet */
@@ -212,7 +212,7 @@ get_sc_arg1(struct sudo_ptrace_regs *regs)
static inline void static inline void
set_sc_arg1(struct sudo_ptrace_regs *regs, unsigned long addr) 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 static inline unsigned long
@@ -224,7 +224,7 @@ get_sc_arg2(struct sudo_ptrace_regs *regs)
static inline void static inline void
set_sc_arg2(struct sudo_ptrace_regs *regs, unsigned long addr) 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 static inline unsigned long
@@ -237,7 +237,7 @@ get_sc_arg3(struct sudo_ptrace_regs *regs)
static inline void static inline void
set_sc_arg3(struct sudo_ptrace_regs *regs, unsigned long addr) 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 static inline unsigned long
@@ -249,7 +249,7 @@ get_sc_arg4(struct sudo_ptrace_regs *regs)
static inline void static inline void
set_sc_arg4(struct sudo_ptrace_regs *regs, unsigned long addr) 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 /* notyet */
# endif /* SECCOMP_AUDIT_ARCH_COMPAT */ # endif /* SECCOMP_AUDIT_ARCH_COMPAT */
@@ -1280,10 +1280,15 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept)
bool bool
exec_ptrace_intercept_supported(void) exec_ptrace_intercept_supported(void)
{ {
# ifdef __mips__
/* MIPS doesn't support changing the syscall return value. */
return false;
# else
if (seccomp_trap_supported == -1) if (seccomp_trap_supported == -1)
seccomp_trap_supporetd = have_seccomp_action("trap"); seccomp_trap_supported = have_seccomp_action("trap");
return seccomp_trap_supported == true; return seccomp_trap_supported == true;
# endif
} }
bool bool

View File

@@ -37,6 +37,10 @@
# define __X32_SYSCALL_BIT 0x40000000 # define __X32_SYSCALL_BIT 0x40000000
#endif #endif
#if defined(__mips__) && !defined(__NR_O32_Linux)
# define __NR_O32_Linux 4000
#endif
/* Align address to a (compat) word boundary. */ /* Align address to a (compat) word boundary. */
#define WORDALIGN(_a, _r) \ #define WORDALIGN(_a, _r) \
(((_a) + ((long)(_r).wordsize - 1L)) & ~((long)(_r).wordsize - 1L)) (((_a) + ((long)(_r).wordsize - 1L)) & ~((long)(_r).wordsize - 1L))
@@ -141,20 +145,60 @@
# else # else
# error "Unsupported MIPS ABI" # error "Unsupported MIPS ABI"
# endif # endif
/* Untested/incomplete. /*
* If called via syscall(__NR_###), v0 holds __NR_O32_Linux and the real * 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. * syscall is in the first arg (a0). The actual args are shifted by one.
* We don't currently support this.
* MIPS does not support setting the syscall return value via ptrace. * MIPS does not support setting the syscall return value via ptrace.
*/ */
# define sudo_pt_regs struct pt_regs # 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_retval(x) (x).regs[2] /* v0 */
# define reg_sp(x) (x).regs[29] /* sp */ # define reg_sp(x) (x).regs[29] /* sp */
# define reg_arg1(x) (x).regs[4] /* a0 */ # define reg_arg1(x) \
# define reg_arg2(x) (x).regs[5] /* a1 */ ((x).regs[2] == __NR_O32_Linux ? (x).regs[5] : (x).regs[4])
# define reg_arg3(x) (x).regs[6] /* a2 */ # define reg_set_arg1(_r, _v) do { \
# define reg_arg4(x) (x).regs[7] /* a3 */ 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__) #elif defined(__powerpc__)
# if defined(__powerpc64__) # if defined(__powerpc64__)
# if BYTE_ORDER == LITTLE_ENDIAN # if BYTE_ORDER == LITTLE_ENDIAN
@@ -333,6 +377,48 @@ struct ppc_pt_regs {
} while (0) } while (0)
#endif #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 { struct sudo_ptrace_regs {
union { union {
sudo_pt_regs native; sudo_pt_regs native;

View File

@@ -92,10 +92,11 @@ union sudo_token_un {
/* /*
* Use ptrace-based intercept (using seccomp) on Linux if possible. * 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(_PATH_SUDO_INTERCEPT) && defined(__linux__)
# if defined(HAVE_DECL_SECCOMP_SET_MODE_FILTER) && HAVE_DECL_SECCOMP_SET_MODE_FILTER # 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 # ifndef HAVE_PTRACE_INTERCEPT
# define HAVE_PTRACE_INTERCEPT 1 # define HAVE_PTRACE_INTERCEPT 1
# endif /* HAVE_PTRACE_INTERCEPT */ # endif /* HAVE_PTRACE_INTERCEPT */