Add support for running o32 and n32 binaries on mips64.

This commit is contained in:
Todd C. Miller
2022-05-24 13:43:51 -06:00
parent bc6a0e1a16
commit d6be44db00
2 changed files with 123 additions and 16 deletions

View File

@@ -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, &regs->u) == -1)
debug_return_bool(false);
# else
struct iovec iov;
iov.iov_base = &regs->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 = &regs->u.compat;
iov.iov_len = sizeof(regs->u.compat);
} else
#endif
{
iov.iov_base = &regs->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, &regs->u) == -1)
debug_return_bool(false);
# else
struct iovec iov;
iov.iov_base = &regs->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),

View File

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