From 106c4cb4b1dd814fc29c56269dc964d03dadde15 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 7 Dec 2011 13:18:29 -0500 Subject: libsandbox: add x32 ABI support We can trace x32 when the host is x86_64 or x32, but x32 cannot trace x86_64 due to limitations in the kernel interface -- all pointers get truncated to 32bits. We'll have to add external ptrace helpers in the future to make this work, but for now, we'll just let x86_64 code run unchecked :(. URL: https://bugs.gentoo.org/394179 Signed-off-by: Mike Frysinger --- configure.ac | 2 +- libsandbox/libsandbox.h | 1 + libsandbox/trace.c | 21 +++++++++++--- libsandbox/trace/linux/i386.c | 10 +++++++ libsandbox/trace/linux/x86_64.c | 48 +++++++++++++++++++++++++++++-- libsandbox/wrapper-funcs/__wrapper_exec.c | 4 +-- tests/script-0 | 4 +-- 7 files changed, 78 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 26f6822..3bf3a8c 100644 --- a/configure.ac +++ b/configure.ac @@ -46,7 +46,7 @@ AC_ARG_ENABLE([schizo], SB_SCHIZO_SETTINGS="no" if test "x$enable_schizo" = "xyes" ; then case $host_alias in - x86_64*linux*) SB_SCHIZO_SETTINGS="x86_64:-m64 x86:-m32";; + x86_64*linux*) SB_SCHIZO_SETTINGS="x86_64:-m64 x86:-m32 x32:-mx32";; esac fi if test "$SB_SCHIZO_SETTINGS" != "no" ; then diff --git a/libsandbox/libsandbox.h b/libsandbox/libsandbox.h index 38e983d..76dd8c8 100644 --- a/libsandbox/libsandbox.h +++ b/libsandbox/libsandbox.h @@ -61,6 +61,7 @@ extern pid_t trace_pid; extern void sb_lock(void); extern void sb_unlock(void); +bool trace_possible(const char *filename, char *const argv[], const void *data); void trace_main(const char *filename, char *const argv[]); /* glibc modified realpath() function */ diff --git a/libsandbox/trace.c b/libsandbox/trace.c index f2071e0..ea769fd 100644 --- a/libsandbox/trace.c +++ b/libsandbox/trace.c @@ -11,6 +11,7 @@ static long _do_ptrace(enum __ptrace_request request, const char *srequest, void *addr, void *data); #define do_ptrace(request, addr, data) _do_ptrace(request, #request, addr, data) +#define _trace_possible(data) true #ifdef DEBUG # define SBDEBUG 1 @@ -485,6 +486,16 @@ void trace_main(const char *filename, char *const argv[]) #else +#undef _trace_possible +#define _trace_possible(data) false + +void trace_main(const char *filename, char *const argv[]) +{ + /* trace_possible() triggers a warning for us */ +} + +#endif + static char *flatten_args(char *const argv[]) { char *ret; @@ -512,11 +523,13 @@ static char *flatten_args(char *const argv[]) return ret; } -void trace_main(const char *filename, char *const argv[]) +bool trace_possible(const char *filename, char *const argv[], const void *data) { + if (_trace_possible(data)) + return true; + char *args = flatten_args(argv); - sb_eqawarn("Static ELF: %s: %s\n", filename, args); + sb_eqawarn("Unable to trace static ELF: %s: %s\n", filename, args); free(args); + return false; } - -#endif diff --git a/libsandbox/trace/linux/i386.c b/libsandbox/trace/linux/i386.c index f214026..d7b9eaa 100644 --- a/libsandbox/trace/linux/i386.c +++ b/libsandbox/trace/linux/i386.c @@ -1,3 +1,13 @@ +#undef _trace_possible +#define _trace_possible _trace_possible +static bool _trace_possible(const void *data) +{ + /* i386 can only trace i386 :( */ + const Elf64_Ehdr *ehdr = data; + return (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_machine == EM_386); +} + #define trace_reg_sysnum orig_eax #define trace_reg_ret eax diff --git a/libsandbox/trace/linux/x86_64.c b/libsandbox/trace/linux/x86_64.c index 9b7e4ea..5bd1361 100644 --- a/libsandbox/trace/linux/x86_64.c +++ b/libsandbox/trace/linux/x86_64.c @@ -1,3 +1,6 @@ +#undef _trace_possible +#define _trace_possible _trace_possible + #ifdef SB_SCHIZO static const struct syscall_entry syscall_table_32[] = { @@ -12,20 +15,51 @@ static const struct syscall_entry syscall_table_64[] = { #undef S { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, }; +static const struct syscall_entry syscall_table_x32[] = { +#define S(s) { SB_SYS_x32_##s, SB_NR_##s, #s }, +#include "trace_syscalls_x32.h" +#undef S + { SB_NR_UNDEF, SB_NR_UNDEF, NULL }, +}; static bool pers_is_32(trace_regs *regs) { switch (regs->cs) { case 0x23: return true; case 0x33: return false; - default: sb_ebort("unknown x86_64 personality"); + default: sb_ebort("unknown x86_64 (CS) personality"); + } +} + +static bool pers_is_x32(trace_regs *regs) +{ + switch (regs->ds) { + case 0x2b: return true; + case 0x00: return false; + default: sb_ebort("unknown x86_64 (DS) personality"); } } static const struct syscall_entry *trace_check_personality(void *vregs) { trace_regs *regs = vregs; - return pers_is_32(regs) ? syscall_table_32 : syscall_table_64; + if (pers_is_32(regs)) + return syscall_table_32; + else if (pers_is_x32(regs)) + return syscall_table_x32; + else + return syscall_table_64; +} + +static bool _trace_possible(const void *data) +{ + /* x86_64 can trace anything, but x32 can't trace x86_64 */ +#if defined(__x86_64__) && defined(__ILP32__) + const Elf64_Ehdr *ehdr = data; + return (ehdr->e_ident[EI_CLASS] == ELFCLASS32); +#else + return true; +#endif } #else @@ -35,6 +69,13 @@ static bool pers_is_32(trace_regs *regs) return false; } +static bool _trace_possible(const void *data) +{ + const Elf64_Ehdr *ehdr = data; + return (ehdr->e_ident[EI_CLASS] == ELFCLASS64) && + (ehdr->e_machine == EM_X86_64); +} + #endif #define trace_reg_sysnum orig_rax @@ -56,6 +97,7 @@ static unsigned long trace_arg(void *vregs, int num) { trace_regs *regs = vregs; if (pers_is_32(regs)) + /* 32bit x86 */ switch (num) { case 1: return regs->rbx; case 2: return regs->rcx; @@ -82,7 +124,7 @@ static void trace_dump_regs(void *vregs) { trace_regs *regs = vregs; sb_printf("{ "); -#define D(r) sb_printf(#r":%lu ", regs->r) +#define D(r) sb_printf(#r":%"PRIu64" ", regs->r) D(rax); D(rdi); D(rsi); diff --git a/libsandbox/wrapper-funcs/__wrapper_exec.c b/libsandbox/wrapper-funcs/__wrapper_exec.c index c3536c3..0ffc08a 100644 --- a/libsandbox/wrapper-funcs/__wrapper_exec.c +++ b/libsandbox/wrapper-funcs/__wrapper_exec.c @@ -33,7 +33,7 @@ static void sb_check_exec(const char *filename, char *const argv[]) return; if (stat(filename, &st)) goto out_fd; - if (st.st_size < EI_NIDENT) + if (st.st_size < sizeof(Elf64_Ehdr)) goto out_fd; elf = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (elf == MAP_FAILED) @@ -65,7 +65,7 @@ static void sb_check_exec(const char *filename, char *const argv[]) else PARSE_ELF(64); - do_trace = true; + do_trace = trace_possible(filename, argv, elf); /* Now that we're done with stuff, clean up before forking */ done: diff --git a/tests/script-0 b/tests/script-0 index 7e6bde6..b25032f 100755 --- a/tests/script-0 +++ b/tests/script-0 @@ -1,6 +1,6 @@ #!/bin/sh # shell scripts only get properly wrapped if our native shell is the # same abi as the compiled libsandbox #259244 -sh=$(scanelf -BF'%M#F' /bin/sh) -sb=$(scanelf -BF'%M#F' "${abs_top_builddir}"/libsandbox/.libs/libsandbox.so) +sh=$(scanelf -BF'%M %a#F' /bin/sh) +sb=$(scanelf -BF'%M %a#F' "${abs_top_builddir}"/libsandbox/.libs/libsandbox.so) [ "${sh}" = "${sb}" ] && exit 0 || exit 77 -- cgit v1.2.3