aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2009-03-18 07:44:52 -0400
committerMike Frysinger <vapier@gentoo.org>2009-03-18 23:36:08 -0400
commit2641abe6d67ecaa89a1ed7bd6dad93cf63bdd6c0 (patch)
tree3177d5c052bdee022ca6920be595c18fbbf55b97
parentsandbox: add desktop/icon files (diff)
downloadsandbox-2641abe6d67ecaa89a1ed7bd6dad93cf63bdd6c0.tar.gz
sandbox-2641abe6d67ecaa89a1ed7bd6dad93cf63bdd6c0.tar.bz2
sandbox-2641abe6d67ecaa89a1ed7bd6dad93cf63bdd6c0.zip
libsandbox: initial support for tracing of static binaries via ptrace()
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
-rw-r--r--.gitignore1
-rw-r--r--TODO12
-rw-r--r--configure.ac5
-rw-r--r--headers.h12
-rw-r--r--libsandbox/Makefile.am27
-rw-r--r--libsandbox/libsandbox.c31
-rw-r--r--libsandbox/libsandbox.h6
-rw-r--r--libsandbox/memory.c3
-rw-r--r--libsandbox/trace.c386
-rw-r--r--libsandbox/trace/common.c3
-rw-r--r--libsandbox/trace/linux/arch.c17
-rw-r--r--libsandbox/trace/linux/common.c13
-rw-r--r--libsandbox/trace/linux/i386.c24
-rw-r--r--libsandbox/trace/linux/x86_64.c24
-rw-r--r--libsandbox/trace/os.c14
-rw-r--r--libsandbox/wrapper-funcs/__wrapper_exec.c50
-rw-r--r--scripts/Makefile.am3
-rw-r--r--scripts/gen_trace_header.awk28
18 files changed, 592 insertions, 67 deletions
diff --git a/.gitignore b/.gitignore
index 5de0ebb..209185d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,6 +38,7 @@ Makefile.in
/libsandbox/libsandbox.map
/libsandbox/sb_nr.h
/libsandbox/symbols.h
+/libsandbox/trace.h
/src/sandbox
/src/sandbox.sh
diff --git a/TODO b/TODO
index c7f4cec..5103175 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,15 @@
-implement limited support for static ELFs with ptrace ?
+try out tracing on *BSD and Solaris
+
+add tests for tracing static binaries
+
+trace static children of static children
+
+inject errors into trace when violation occurs rather than kill the child
cache results of filesystem checks
+review erealpath vs realpath usage
+
add tests to make sure errno is saved/restored properly
wrappers for execl{,l,p} ... unfortunately, we'll probably have to basically
@@ -10,5 +18,3 @@ reimplement the functions (building up argv[] and then call the execv* ver)
erealpath() might deref symlinks when working with unreadable paths as non-root
even when working on funcs that do not deref funcs themselves ... this isnt a
real big issue though
-
-add a whitelist system for static binaries rather than hardcoding into source
diff --git a/configure.ac b/configure.ac
index 2fc1b77..e96b778 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,9 +71,13 @@ AC_CHECK_HEADERS_ONCE([ \
sys/file.h \
sys/mman.h \
sys/param.h \
+ sys/ptrace.h \
+ sys/reg.h \
sys/stat.h \
+ sys/syscall.h \
sys/time.h \
sys/types.h \
+ sys/user.h \
sys/wait.h \
])
@@ -118,6 +122,7 @@ AC_CHECK_FUNCS_ONCE([ \
openat \
openat64 \
pathconf \
+ ptrace \
realpath \
remove \
renameat \
diff --git a/headers.h b/headers.h
index 823258d..f5d158d 100644
--- a/headers.h
+++ b/headers.h
@@ -93,15 +93,27 @@
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
+#ifdef HAVE_SYS_PTRACE_H
+# include <sys/ptrace.h>
+#endif
+#ifdef HAVE_SYS_REG_H
+# include <sys/reg.h>
+#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
+#ifdef HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
+#ifdef HAVE_SYS_USER_H
+# include <sys/user.h>
+#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
diff --git a/libsandbox/Makefile.am b/libsandbox/Makefile.am
index bdc16ed..03e2d8c 100644
--- a/libsandbox/Makefile.am
+++ b/libsandbox/Makefile.am
@@ -31,32 +31,41 @@ libsandbox_la_SOURCES = \
libsandbox.h \
libsandbox.c \
memory.c \
+ trace.c \
wrappers.h \
wrappers.c \
canonicalize.c
libsandbox.c: libsandbox.map sb_nr.h
+trace.c: trace.h sb_nr.h $(TRACE_FILES)
wrappers.c: symbols.h
+TRACE_FILES = $(wildcard $(srcdir)/trace/*.[ch] $(srcdir)/trace/*/*.[ch])
+
+SCRIPT_DIR = $(top_srcdir)/scripts
+
SYMBOLS_FILE = $(srcdir)/symbols.h.in
SYMBOLS_LIST = $(shell $(SED) -n '/^[^\#]/p' $(SYMBOLS_FILE))
-SYMBOLS_WRAPPERS = $(wildcard $(srcdir)/wrapper-funcs/*.[ch])
-GEN_VERSION_MAP_SCRIPT = $(top_srcdir)/scripts/gen_symbol_version_map.awk
-GEN_HEADER_SCRIPT = $(top_srcdir)/scripts/gen_symbol_header.awk
+SYMBOLS_WRAPPERS = $(wildcard $(srcdir)/wrapper-funcs/*.[ch])
+GEN_VERSION_MAP_SCRIPT = $(SCRIPT_DIR)/gen_symbol_version_map.awk
+GEN_HEADER_SCRIPT = $(SCRIPT_DIR)/gen_symbol_header.awk
+GEN_TRACE_SCRIPT = $(SCRIPT_DIR)/gen_trace_header.awk
+SB_AWK = LC_ALL=C $(AWK) -v SYMBOLS_LIST="$(SYMBOLS_LIST)" -f
libsandbox.map: $(SYMBOLS_FILE) $(GEN_VERSION_MAP_SCRIPT)
- $(READELF) -s $(LIBC_PATH) | \
- LC_ALL=C $(AWK) -v SYMBOLS_LIST="$(SYMBOLS_LIST)" -f $(GEN_VERSION_MAP_SCRIPT) > $@
+ $(READELF) -s $(LIBC_PATH) | $(SB_AWK) $(GEN_VERSION_MAP_SCRIPT) > $@
symbols.h: $(SYMBOLS_FILE) $(SYMBOLS_WRAPPERS) $(GEN_HEADER_SCRIPT)
- $(READELF) -s $(LIBC_PATH) | \
- LC_ALL=C $(AWK) -v SYMBOLS_LIST="$(SYMBOLS_LIST)" -f $(GEN_HEADER_SCRIPT) > $@
+ $(READELF) -s $(LIBC_PATH) | $(SB_AWK) $(GEN_HEADER_SCRIPT) > $@
SB_NR_FILE = $(srcdir)/sb_nr.h.in
sb_nr.h: symbols.h $(SB_NR_FILE)
$(EGREP) -h '^\#define SB_' $^ > $@
-EXTRA_DIST = $(SYMBOLS_FILE) $(SYMBOLS_WRAPPERS) $(SB_NR_FILE)
+trace.h: $(GEN_TRACE_SCRIPT)
+ $(COMPILE) -E -dD $(top_srcdir)/headers.h | $(SB_AWK) $(GEN_TRACE_SCRIPT) > $@
+
+EXTRA_DIST = $(SYMBOLS_FILE) $(SYMBOLS_WRAPPERS) $(SB_NR_FILE) $(TRACE_FILES)
-CLEANFILES = libsandbox.map sb_nr.h symbols.h
+CLEANFILES = libsandbox.map sb_nr.h symbols.h trace.h
DISTCLEANFILES = $(CLEANFILES)
diff --git a/libsandbox/libsandbox.c b/libsandbox/libsandbox.c
index 86ee6be..78267ea 100644
--- a/libsandbox/libsandbox.c
+++ b/libsandbox/libsandbox.c
@@ -64,7 +64,7 @@ typedef struct {
static sbcontext_t sbcontext;
static char *cached_env_vars[MAX_DYN_PREFIXES];
-volatile bool sandbox_on = true;
+bool sandbox_on = true;
static bool sb_init = false;
static char *resolve_path(const char *, int);
@@ -194,7 +194,13 @@ static char *resolve_path(const char *path, int follow_link)
* etc. If that fails (might not exist), we try to get the
* realpath of the parent directory, as that should hopefully
* exist. If all else fails, just go with canonicalize */
- if (NULL == realpath(path, filtered_path)) {
+ char *ret;
+ if (trace_pid)
+ ret = erealpath(path, filtered_path);
+ else
+ ret = realpath(path, filtered_path);
+
+ if (!ret) {
char tmp_str1[SB_PATH_MAX];
snprintf(tmp_str1, SB_PATH_MAX, "%s", path);
@@ -202,7 +208,11 @@ static char *resolve_path(const char *path, int follow_link)
/* If not, then check if we can resolve the
* parent directory */
- if (NULL == realpath(dname, filtered_path)) {
+ if (trace_pid)
+ ret = erealpath(path, filtered_path);
+ else
+ ret = realpath(path, filtered_path);
+ if (!ret) {
/* Fall back to canonicalize */
if (-1 == canonicalize(path, filtered_path)) {
free(filtered_path);
@@ -243,6 +253,17 @@ char *egetcwd(char *buf, size_t size)
struct stat st;
char *tmpbuf, *oldbuf = buf;
+ /* If tracing a child, our cwd may not be the same as the child's */
+ if (trace_pid) {
+ char proc[20];
+ sprintf(proc, "/proc/%i/cwd", trace_pid);
+ ssize_t ret = readlink(proc, buf, size);
+ if (ret == -1)
+ return NULL;
+ buf[ret] = '\0';
+ return buf;
+ }
+
/* Need to disable sandbox, as on non-linux libc's, opendir() is
* used by some getcwd() implementations and resolves to the sandbox
* opendir() wrapper, causing infinit recursion and finially crashes.
@@ -354,7 +375,7 @@ static bool write_logfile(const char *logfile, const char *func, const char *pat
(0 == S_ISREG(log_stat.st_mode))) {
SB_EERROR("SECURITY BREACH", " '%s' %s\n", logfile,
"already exists and is not a regular file!");
- abort();
+ sb_abort();
}
logfd = sb_open(logfile,
@@ -915,7 +936,7 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in
*/
if (dirfd != AT_FDCWD && file[0] != '/') {
size_t at_len = sizeof(at_file_buf) - 1 - 1 - strlen(file);
- sprintf(at_file_buf, "/proc/%i/fd/%i", getpid(), dirfd);
+ sprintf(at_file_buf, "/proc/%i/fd/%i", trace_pid ? : getpid(), dirfd);
ssize_t ret = readlink(at_file_buf, at_file_buf, at_len);
if (ret == -1) {
/* see comments at end of check_syscall() */
diff --git a/libsandbox/libsandbox.h b/libsandbox/libsandbox.h
index b2363a7..21f94d2 100644
--- a/libsandbox/libsandbox.h
+++ b/libsandbox/libsandbox.h
@@ -56,10 +56,14 @@ bool before_syscall_open_int(int, int, const char *, const char *, int);
bool before_syscall_open_char(int, int, const char *, const char *, const char *);
extern char sandbox_lib[SB_PATH_MAX];
-extern volatile bool sandbox_on;
+extern bool sandbox_on;
+extern pid_t trace_pid;
+
+void trace_main(const char *filename, char *const argv[]);
__attribute__((__format__(__printf__, 1, 2))) void sb_eqawarn(const char *format, ...);
void sb_dump_backtrace(void);
+__attribute__((noreturn)) void sb_abort(void);
/* glibc modified realpath() function */
char *erealpath(const char *, char *);
diff --git a/libsandbox/memory.c b/libsandbox/memory.c
index 180e250..05e9691 100644
--- a/libsandbox/memory.c
+++ b/libsandbox/memory.c
@@ -63,6 +63,9 @@ void *realloc(void *ptr, size_t size)
}
old_malloc_size = SB_MALLOC_TO_SIZE(ptr);
+ /* Since mmap() is heavy, don't bother shrinking */
+ if (size <= old_malloc_size)
+ return ptr;
ret = malloc(size);
if (!ret)
return ret;
diff --git a/libsandbox/trace.c b/libsandbox/trace.c
new file mode 100644
index 0000000..e5bebd9
--- /dev/null
+++ b/libsandbox/trace.c
@@ -0,0 +1,386 @@
+/* Limited sandbox support for static binaries
+ *
+ * Copyright 2009 Gentoo Foundation
+ * Licensed under the GPL-2
+ */
+
+#include "headers.h"
+#include "sbutil.h"
+#include "libsandbox.h"
+#include "wrappers.h"
+#include "sb_nr.h"
+
+#include "trace/os.c"
+
+pid_t trace_pid;
+
+#ifndef SB_NO_TRACE
+
+#ifdef DEBUG
+# define SBDEBUG 1
+#else
+# define SBDEBUG 0
+#endif
+#define __SB_DEBUG(fmt, args...) do { if (SBDEBUG) sb_printf(fmt, ## args); } while (0)
+#define _SB_DEBUG(fmt, args...) do { if (SBDEBUG) SB_EWARN("TRACE ", "(pid=%i):%s: " fmt, getpid(), __func__, ## args); } while (0)
+#define SB_DEBUG(fmt, args...) _SB_DEBUG(fmt "\n", ## args)
+
+static volatile bool child_stopped;
+
+static long _do_ptrace(enum __ptrace_request request, const char *srequest, void *addr, void *data)
+{
+ long ret;
+ errno = 0;
+ try_again:
+ ret = ptrace(request, trace_pid, addr, data);
+ if (ret == -1 || (request == PTRACE_PEEKUSER && errno)) {
+ /* Child hasn't gotten to the next marker yet */
+ if (errno == ESRCH) {
+ sched_yield();
+ goto try_again;
+ }
+
+ SB_EERROR("ISE:trace_loop ", "ptrace(%s): %s\n",
+ srequest, strerror(errno));
+ sb_abort();
+ }
+ return ret;
+}
+#define do_ptrace(request, addr, data) _do_ptrace(request, #request, addr, data)
+
+static long do_peekuser(long offset)
+{
+ return do_ptrace(PTRACE_PEEKUSER, (void *)offset, NULL);
+}
+
+static long do_peekdata(long offset)
+{
+ return do_ptrace(PTRACE_PEEKDATA, (void *)offset, NULL);
+}
+
+static char *do_peekstr(unsigned long lptr)
+{
+ size_t len, l;
+ char *ret;
+ long a, i;
+ union {
+ long val;
+ char x[sizeof(long)];
+ } s;
+
+ l = 0;
+ len = 1024;
+ ret = xmalloc(len);
+ while (1) {
+ a = lptr & (sizeof(long) - 1);
+ lptr -= a;
+ s.val = do_peekdata(lptr);
+ for (i = a; i < sizeof(long); ++i) {
+ ret[l++] = s.x[i];
+ if (!s.x[i])
+ return ret;
+ if (l >= len) {
+ len += 1024;
+ ret = xrealloc(ret, len);
+ }
+ }
+ lptr += sizeof(long);
+ }
+}
+
+static void trace_child_signal(int signo, siginfo_t *info, void *context)
+{
+ SB_DEBUG("got sig %i: code:%i status:%i", signo, info->si_code, info->si_status);
+
+ switch (info->si_code) {
+ case CLD_KILLED:
+ exit(128 + info->si_status);
+
+ case CLD_EXITED:
+ __SB_DEBUG(" = %i\n", info->si_status);
+ exit(info->si_status);
+
+ case CLD_TRAPPED:
+ switch (info->si_status) {
+ case SIGSTOP:
+ kill(trace_pid, SIGCONT);
+ case SIGTRAP:
+ child_stopped = true;
+ case SIGCONT:
+ return;
+ }
+ /* fall through */
+ }
+
+ SB_EERROR("ISE:trace_child_signal ", "child signal %i, code %i, status %i\n",
+ signo, info->si_code, info->si_status);
+ sb_abort();
+}
+
+static const struct {
+ const int nr;
+ const char *name;
+} syscalls[] = {
+#define S(s) { SYS_##s, #s },
+#include "trace.h"
+#undef S
+};
+static const char *sysname(int nr)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(syscalls); ++i)
+ if (syscalls[i].nr == nr)
+ return syscalls[i].name;
+ return "unk";
+}
+
+static bool _trace_check_syscall_C(void *vregs, int sb_nr, const char *func, int ibase)
+{
+ char *path = do_peekstr(trace_arg(vregs, ibase));
+ __SB_DEBUG("(\"%s\")", path);
+ bool ret = _SB_SAFE(sb_nr, func, path);
+ free(path);
+ return ret;
+}
+static bool trace_check_syscall_C(void *vregs, int sb_nr, const char *func)
+{
+ return _trace_check_syscall_C(vregs, sb_nr, func, 1);
+}
+
+static bool __trace_check_syscall_DCF(void *vregs, int sb_nr, const char *func, int ibase, int flags)
+{
+ int dirfd = trace_arg(vregs, ibase);
+ char *path = do_peekstr(trace_arg(vregs, ibase + 1));
+ __SB_DEBUG("(%i, \"%s\", %x)", dirfd, path, flags);
+ bool ret = _SB_SAFE_AT(sb_nr, func, dirfd, path, flags);
+ free(path);
+ return ret;
+}
+static bool _trace_check_syscall_DCF(void *vregs, int sb_nr, const char *func, int ibase)
+{
+ int flags = trace_arg(vregs, ibase + 2);
+ return __trace_check_syscall_DCF(vregs, sb_nr, func, ibase, flags);
+}
+static bool trace_check_syscall_DCF(void *vregs, int sb_nr, const char *func)
+{
+ return _trace_check_syscall_DCF(vregs, sb_nr, func, 1);
+}
+
+static bool _trace_check_syscall_DC(void *vregs, int sb_nr, const char *func, int ibase)
+{
+ return __trace_check_syscall_DCF(vregs, sb_nr, func, ibase, 0);
+}
+static bool trace_check_syscall_DC(void *vregs, int sb_nr, const char *func)
+{
+ return _trace_check_syscall_DC(vregs, sb_nr, func, 1);
+}
+
+static bool trace_check_syscall(int nr, void *vregs)
+{
+ struct user_regs_struct *regs = vregs;
+ bool ret = true;
+
+ /* These funcs aren't syscalls and so there are no checks:
+ * - fopen
+ * - lutimes
+ * - mkfifo*
+ * - opendir (uses open())
+ * - _*x*mknod*
+ * - 64bit versions of most funcs
+ * - system / popen / most exec funcs
+ *
+ * Can't use switch() statement here as we auto define missing
+ * SYS_xxx to SB_NR_UNDEF in the build system
+ */
+ if (nr == SB_NR_UNDEF)
+ goto done;
+ else if (nr == SYS_chmod) return trace_check_syscall_C (vregs, SB_NR_CHMOD, "chmod");
+ else if (nr == SYS_chown) return trace_check_syscall_C (vregs, SB_NR_CHOWN, "chown");
+ else if (nr == SYS_creat) return trace_check_syscall_C (vregs, SB_NR_CREAT, "creat");
+ else if (nr == SYS_fchmodat) return trace_check_syscall_DCF(vregs, SB_NR_FCHMODAT, "fchmodat");
+ else if (nr == SYS_fchownat) return trace_check_syscall_DCF(vregs, SB_NR_FCHOWNAT, "fchownat");
+ else if (nr == SYS_futimesat) return trace_check_syscall_DC (vregs, SB_NR_FUTIMESAT, "futimesat");
+ else if (nr == SYS_lchown) return trace_check_syscall_C (vregs, SB_NR_LCHOWN, "lchown");
+ else if (nr == SYS_link) return _trace_check_syscall_C (vregs, SB_NR_LINK, "link", 2);
+ else if (nr == SYS_linkat) return _trace_check_syscall_DCF(vregs, SB_NR_LINKAT, "linkat", 3);
+ else if (nr == SYS_mkdir) return trace_check_syscall_C (vregs, SB_NR_MKDIR, "mkdir");
+ else if (nr == SYS_mkdirat) return trace_check_syscall_DC (vregs, SB_NR_MKDIRAT, "mkdirat");
+ else if (nr == SYS_mknod) return trace_check_syscall_C (vregs, SB_NR_MKNOD, "mknod");
+ else if (nr == SYS_mknodat) return trace_check_syscall_DC (vregs, SB_NR_MKNODAT, "mknodat");
+ else if (nr == SYS_rename) return trace_check_syscall_C (vregs, SB_NR_RENAME, "rename") &&
+ _trace_check_syscall_C (vregs, SB_NR_RENAME, "rename", 2);
+ else if (nr == SYS_renameat) return trace_check_syscall_DC (vregs, SB_NR_RENAMEAT, "renameat") &&
+ _trace_check_syscall_DC (vregs, SB_NR_RENAMEAT, "renameat", 3);
+ else if (nr == SYS_rmdir) return trace_check_syscall_C (vregs, SB_NR_RMDIR, "rmdir");
+ else if (nr == SYS_symlink) return _trace_check_syscall_C (vregs, SB_NR_SYMLINK, "symlink", 2);
+ else if (nr == SYS_symlinkat) return _trace_check_syscall_DC (vregs, SB_NR_SYMLINKAT, "symlinkat", 2);
+ else if (nr == SYS_truncate) return trace_check_syscall_C (vregs, SB_NR_TRUNCATE, "truncate");
+ else if (nr == SYS_truncate64)return trace_check_syscall_C (vregs, SB_NR_TRUNCATE64, "truncate64");
+ else if (nr == SYS_unlink) return trace_check_syscall_C (vregs, SB_NR_UNLINK, "unlink");
+ else if (nr == SYS_unlinkat) return trace_check_syscall_DCF(vregs, SB_NR_UNLINKAT, "unlinkat");
+ else if (nr == SYS_utime) return trace_check_syscall_C (vregs, SB_NR_UTIME, "utime");
+ else if (nr == SYS_utimes) return trace_check_syscall_C (vregs, SB_NR_UTIMES, "utimes");
+ else if (nr == SYS_utimensat) return trace_check_syscall_DCF(vregs, SB_NR_UTIMENSAT, "utimensat");
+
+ else if (nr == SYS_access) {
+ char *path = do_peekstr(trace_arg(regs, 1));
+ int flags = trace_arg(regs, 2);
+ __SB_DEBUG("(\"%s\", %x)", path, flags);
+ ret = _SB_SAFE_ACCESS(SB_NR_ACCESS, "access", path, flags);
+ free(path);
+ return ret;
+
+ } else if (nr == SYS_faccessat) {
+ int dirfd = trace_arg(regs, 1);
+ char *path = do_peekstr(trace_arg(regs, 2));
+ int flags = trace_arg(regs, 3);
+ __SB_DEBUG("(%i, \"%s\", %x)", dirfd, path, flags);
+ ret = _SB_SAFE_ACCESS_AT(SB_NR_FACCESSAT, "faccessat", dirfd, path, flags);
+ free(path);
+ return ret;
+
+ } else if (nr == SYS_open) {
+ char *path = do_peekstr(trace_arg(regs, 1));
+ int flags = trace_arg(regs, 2);
+ __SB_DEBUG("(\"%s\", %x)", path, flags);
+ ret = _SB_SAFE_OPEN_INT(SB_NR_OPEN, "open", path, flags);
+ free(path);
+ return ret;
+
+ } else if (nr == SYS_openat) {
+ int dirfd = trace_arg(regs, 1);
+ char *path = do_peekstr(trace_arg(regs, 2));
+ int flags = trace_arg(regs, 3);
+ __SB_DEBUG("(%i, \"%s\", %x)", dirfd, path, flags);
+ ret = _SB_SAFE_OPEN_INT_AT(SB_NR_OPENAT, "openat", dirfd, path, flags);
+ free(path);
+ return ret;
+ }
+
+ done:
+ __SB_DEBUG("(...)");
+ return ret;
+}
+
+static void trace_loop(void)
+{
+ struct user_regs_struct regs;
+ bool before_syscall;
+ long ret;
+ int nr, exec_state;
+
+ exec_state = 0;
+ before_syscall = true;
+ do {
+ ret = do_ptrace(PTRACE_SYSCALL, NULL, NULL);
+ nr = trace_sysnum();
+ if (!exec_state) {
+ if (!before_syscall || nr != SYS_execve)
+ goto loop_again;
+ ++exec_state;
+ }
+
+ ret = do_ptrace(PTRACE_GETREGS, NULL, &regs);
+ if (before_syscall) {
+ _SB_DEBUG("%s:%i", sysname(nr), nr);
+ if (!trace_check_syscall(nr, &regs)) {
+ do_ptrace(PTRACE_KILL, NULL, NULL);
+ exit(1);
+ }
+ } else {
+ int err;
+ ret = trace_result(&regs, &err);
+
+ __SB_DEBUG(" = %li", ret);
+ if (err) {
+ __SB_DEBUG(" (errno: %i: %s)", err, strerror(err));
+
+ /* If the exec() failed for whatever reason, kill the
+ * child and have the parent resume like normal
+ */
+ if (exec_state == 1) {
+ do_ptrace(PTRACE_KILL, NULL, NULL);
+ trace_pid = 0;
+ return;
+ }
+ }
+ __SB_DEBUG("\n");
+
+ exec_state = 2;
+ }
+
+ loop_again:
+ before_syscall = !before_syscall;
+ } while (1);
+}
+
+void trace_main(const char *filename, char *const argv[])
+{
+ struct sigaction sa, old_sa;
+
+ child_stopped = false;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ sa.sa_sigaction = trace_child_signal;
+ sigaction(SIGCHLD, &sa, &old_sa);
+
+ trace_pid = fork();
+ if (trace_pid == -1) {
+ SB_EERROR("ISE:trace_main ", "vfork() failed: %s\n",
+ strerror(errno));
+ sb_abort();
+ } else if (trace_pid) {
+ SB_DEBUG("parent waiting for child (pid=%i) to signal", trace_pid);
+ while (!child_stopped)
+ sched_yield();
+#ifdef PTRACE_O_TRACESYSGOOD
+ /* Not all kernel versions support this, so ignore return */
+ ptrace(PTRACE_SETOPTIONS, trace_pid, NULL, (void *)PTRACE_O_TRACESYSGOOD);
+#endif
+ trace_loop();
+ return;
+ }
+
+ SB_DEBUG("child setting up ...");
+ sigaction(SIGCHLD, &old_sa, NULL);
+ do_ptrace(PTRACE_TRACEME, NULL, NULL);
+ kill(getpid(), SIGSTOP);
+ /* child returns */
+}
+
+#else
+
+static char *flatten_args(char *const argv[])
+{
+ char *ret;
+ size_t i, len;
+
+ len = 1;
+ for (i = 0; argv[i]; ++i) {
+ len += strlen(argv[i]) + 1;
+ if (strchr(argv[i], ' '))
+ len += 2;
+ }
+
+ ret = xmalloc(len);
+ ret[0] = '\0';
+ for (i = 0; argv[i]; ++i) {
+ if (strchr(argv[i], ' ')) {
+ strcat(ret, "'");
+ strcat(ret, argv[i]);
+ strcat(ret, "'");
+ } else
+ strcat(ret, argv[i]);
+ strcat(ret, " ");
+ }
+
+ return ret;
+}
+
+void trace_main(const char *filename, char *const argv[])
+{
+ char *args = flatten_args(argv);
+ sb_eqawarn("Static ELF: %s: %s\n", filename, args);
+ free(args);
+}
+
+#endif
diff --git a/libsandbox/trace/common.c b/libsandbox/trace/common.c
new file mode 100644
index 0000000..20fcb7e
--- /dev/null
+++ b/libsandbox/trace/common.c
@@ -0,0 +1,3 @@
+static int trace_sysnum(void);
+static long trace_raw_ret(void *vregs);
+static unsigned long trace_arg(void *vregs, int num);
diff --git a/libsandbox/trace/linux/arch.c b/libsandbox/trace/linux/arch.c
new file mode 100644
index 0000000..5897b2f
--- /dev/null
+++ b/libsandbox/trace/linux/arch.c
@@ -0,0 +1,17 @@
+#include "common.c"
+
+/* Linux uses ptrace() */
+#if !defined(HAVE_PTRACE) || !defined(HAVE_SYS_PTRACE_H) || !defined(HAVE_SYS_USER_H)
+# define SB_NO_TRACE_ARCH
+#elif defined(__i386__)
+# include "i386.c"
+#elif defined(__x86_64__)
+# include "x86_64.c"
+#else
+# define SB_NO_TRACE_ARCH
+#endif
+
+#ifdef SB_NO_TRACE_ARCH
+# warning "trace: sorry, no support for your architecture"
+# define SB_NO_TRACE
+#endif
diff --git a/libsandbox/trace/linux/common.c b/libsandbox/trace/linux/common.c
new file mode 100644
index 0000000..287af0a
--- /dev/null
+++ b/libsandbox/trace/linux/common.c
@@ -0,0 +1,13 @@
+static long do_peekuser(long offset);
+
+static int trace_errno(long err)
+{
+ return (err < 0 && err > -4096) ? err * -1 : 0;
+}
+
+static long trace_result(void *vregs, int *error)
+{
+ long sr = trace_raw_ret(vregs);
+ *error = trace_errno(sr);
+ return *error ? -1 : sr;
+}
diff --git a/libsandbox/trace/linux/i386.c b/libsandbox/trace/linux/i386.c
new file mode 100644
index 0000000..83cc094
--- /dev/null
+++ b/libsandbox/trace/linux/i386.c
@@ -0,0 +1,24 @@
+static int trace_sysnum(void)
+{
+ return do_peekuser(4 * ORIG_EAX);
+}
+
+static long trace_raw_ret(void *vregs)
+{
+ struct user_regs_struct *regs = vregs;
+ return regs->eax;
+}
+
+static unsigned long trace_arg(void *vregs, int num)
+{
+ struct user_regs_struct *regs = vregs;
+ switch (num) {
+ case 1: return regs->ebx;
+ case 2: return regs->ecx;
+ case 3: return regs->edx;
+ case 4: return regs->esi;
+ case 5: return regs->edi;
+ case 6: return regs->ebp;
+ default: return -1;
+ }
+}
diff --git a/libsandbox/trace/linux/x86_64.c b/libsandbox/trace/linux/x86_64.c
new file mode 100644
index 0000000..1f40036
--- /dev/null
+++ b/libsandbox/trace/linux/x86_64.c
@@ -0,0 +1,24 @@
+static int trace_sysnum(void)
+{
+ return do_peekuser(8 * ORIG_RAX);
+}
+
+static long trace_raw_ret(void *vregs)
+{
+ struct user_regs_struct *regs = vregs;
+ return regs->rax;
+}
+
+static unsigned long trace_arg(void *vregs, int num)
+{
+ struct user_regs_struct *regs = vregs;
+ switch (num) {
+ case 1: return regs->rdi;
+ case 2: return regs->rsi;
+ case 3: return regs->rdx;
+ case 4: return regs->r10;
+ case 5: return regs->r8;
+ case 6: return regs->r9;
+ default: return -1;
+ }
+}
diff --git a/libsandbox/trace/os.c b/libsandbox/trace/os.c
new file mode 100644
index 0000000..945cc61
--- /dev/null
+++ b/libsandbox/trace/os.c
@@ -0,0 +1,14 @@
+#include "common.c"
+
+#undef SB_NO_TRACE
+#if 0
+#elif defined(__linux__)
+# include "linux/arch.c"
+#else
+# define SB_NO_TRACE_OS
+#endif
+
+#ifdef SB_NO_TRACE_OS
+# warning "trace: sorry, no support for your OS"
+# define SB_NO_TRACE
+#endif
diff --git a/libsandbox/wrapper-funcs/__wrapper_exec.c b/libsandbox/wrapper-funcs/__wrapper_exec.c
index 2052221..399e8ad 100644
--- a/libsandbox/wrapper-funcs/__wrapper_exec.c
+++ b/libsandbox/wrapper-funcs/__wrapper_exec.c
@@ -20,57 +20,13 @@ static WRAPPER_RET_TYPE (*WRAPPER_TRUE_NAME)(WRAPPER_ARGS_PROTO) = NULL;
#ifndef SB_EXEC_COMMON
#define SB_EXEC_COMMON
-static char *flatten_args(char *const argv[])
-{
- char *ret;
- size_t i, len;
-
- len = 1;
- for (i = 0; argv[i]; ++i) {
- len += strlen(argv[i]) + 1;
- if (strchr(argv[i], ' '))
- len += 2;
- }
-
- ret = xmalloc(len);
- ret[0] = '\0';
- for (i = 0; argv[i]; ++i) {
- if (strchr(argv[i], ' ')) {
- strcat(ret, "'");
- strcat(ret, argv[i]);
- strcat(ret, "'");
- } else
- strcat(ret, argv[i]);
- strcat(ret, " ");
- }
-
- return ret;
-}
-
-/* See to see if this an ELF and if so, is it static which we can't wrap */
+/* Check to see if this a static ELF and if so, protect using trace mechanisms */
static void sb_check_exec(const char *filename, char *const argv[])
{
int fd;
unsigned char *elf;
struct stat st;
-#ifdef __linux__
- /* Filter some common safe static things ...
- * Should make a whitelist system for this ...
- */
- if (!strncmp(argv[0], "/lib", 4) && strstr(argv[0], ".so.")) {
- /* Packages often run `ldd /some/binary` which will in
- * turn run `/lib/ld-linux.so.2 --verify /some/binary`
- */
- if (!strcmp(argv[1], "--verify"))
- return;
-
- } else if (argv[1] && !strcmp(argv[1], "prelink") &&
- argv[2] && !strcmp(argv[2], "--version"))
- /* Portage likes to run `prelink --version` */
- return;
-#endif
-
fd = open(filename, O_RDONLY);
if (fd == -1)
return;
@@ -108,9 +64,7 @@ static void sb_check_exec(const char *filename, char *const argv[])
else
PARSE_ELF(64);
- char *args = flatten_args(argv);
- sb_eqawarn("Static ELF: %s: %s\n", filename, args);
- free(args);
+ trace_main(filename, argv);
done:
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index faf1df2..41ee99e 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -1,3 +1,4 @@
EXTRA_DIST = \
gen_symbol_version_map.awk \
- gen_symbol_header.awk
+ gen_symbol_header.awk \
+ gen_trace_header.awk
diff --git a/scripts/gen_trace_header.awk b/scripts/gen_trace_header.awk
new file mode 100644
index 0000000..6b99966
--- /dev/null
+++ b/scripts/gen_trace_header.awk
@@ -0,0 +1,28 @@
+BEGIN {
+ COUNT = split(" " SYMBOLS_LIST, SYMBOLS);
+}
+
+{
+ if ($1 != "#define" || $2 !~ /^SYS_/)
+ next;
+
+ sub(/^SYS_/, "", $2);
+
+ for (i = 1; i <= COUNT; ++i)
+ if (SYMBOLS[i] == $2) {
+ SYMBOLS[i] = "";
+ break;
+ }
+
+ print "S(" $2 ")";
+}
+
+END {
+ for (x in SYMBOLS) {
+ s = SYMBOLS[x];
+ if (s != "") {
+ print "#define SYS_" s " SB_NR_UNDEF";
+ print "S(" s ")";
+ }
+ }
+}