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)
{
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

View File

@@ -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;

View File

@@ -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 */