trace: check asprintf() return value
[project/procd.git] / trace / trace.c
index 5189cfb..27cf108 100644 (file)
@@ -25,6 +25,7 @@
 #include <errno.h>
 #include <string.h>
 #include <syslog.h>
+#include <limits.h>
 
 #ifndef PTRACE_EVENT_STOP
 /* PTRACE_EVENT_STOP is defined in linux/ptrace.h, but this header
@@ -32,6 +33,7 @@
 #define PTRACE_EVENT_STOP 128
 #endif
 
+#include <libubox/ulog.h>
 #include <libubox/uloop.h>
 #include <libubox/blobmsg.h>
 #include <libubox/blobmsg_json.h>
@@ -65,46 +67,30 @@ enum mode {
        SECCOMP_TRACE,
 } mode = UTRACE;
 
-#define PROC_NAME(mode) (mode == UTRACE ? "utrace" : "seccomp-trace")
-
-#define INFO(fmt, ...) do { \
-       fprintf(stderr, "%s: "fmt, PROC_NAME(mode), ## __VA_ARGS__); \
-} while (0)
-
-#define ERROR(fmt, ...) do { \
-       syslog(LOG_ERR, "%s: "fmt, PROC_NAME(mode), ## __VA_ARGS__); \
-       fprintf(stderr, "%s: "fmt, PROC_NAME(mode), ## __VA_ARGS__);  \
-} while (0)
-
-#define LOGERR(fmt, ...) do { \
-       syslog(LOG_ERR, "%s: "fmt, PROC_NAME(mode), ## __VA_ARGS__); \
-} while (0)
-
 struct tracee {
        struct uloop_process proc;
        int in_syscall;
 };
 
 static struct tracee tracer;
-static int *syscall_count;
+static int syscall_count[SYSCALL_COUNT];
 static int violation_count;
 static struct blob_buf b;
-static int syscall_max;
 static int debug;
 char *json = NULL;
 int ptrace_restart;
 
-static int max_syscall = ARRAY_SIZE(syscall_names);
-
 static void set_syscall(const char *name, int val)
 {
        int i;
 
-       for (i = 0; i < max_syscall; i++)
-               if (syscall_names[i] && !strcmp(syscall_names[i], name)) {
+       for (i = 0; i < SYSCALL_COUNT; i++) {
+               int sc = syscall_index_to_number(i);
+               if (syscall_name(sc) && !strcmp(syscall_name(sc), name)) {
                        syscall_count[i] = val;
                        return;
                }
+       }
 }
 
 struct syscall {
@@ -130,29 +116,29 @@ static void print_syscalls(int policy, const char *json)
                set_syscall("exit", 1);
        }
 
-       struct syscall sorted[ARRAY_SIZE(syscall_names)];
+       struct syscall sorted[SYSCALL_COUNT];
 
-       for (i = 0; i < ARRAY_SIZE(syscall_names); i++) {
-               sorted[i].syscall = i;
+       for (i = 0; i < SYSCALL_COUNT; i++) {
+               sorted[i].syscall = syscall_index_to_number(i);
                sorted[i].count = syscall_count[i];
        }
 
-       qsort(sorted, ARRAY_SIZE(syscall_names), sizeof(sorted[0]), cmp_count);
+       qsort(sorted, SYSCALL_COUNT, sizeof(sorted[0]), cmp_count);
 
        blob_buf_init(&b, 0);
        c = blobmsg_open_array(&b, "whitelist");
 
-       for (i = 0; i < ARRAY_SIZE(syscall_names); i++) {
+       for (i = 0; i < SYSCALL_COUNT; i++) {
                int sc = sorted[i].syscall;
                if (!sorted[i].count)
                        break;
-               if (syscall_names[sc]) {
+               if (syscall_name(sc)) {
                        if (debug)
                                printf("syscall %d (%s) was called %d times\n",
-                                       sc, syscall_names[sc], sorted[i].count);
-                       blobmsg_add_string(&b, NULL, syscall_names[sc]);
+                                      sc, syscall_name(sc), sorted[i].count);
+                       blobmsg_add_string(&b, NULL, syscall_name(sc));
                } else {
-                       ERROR("no name found for syscall(%d)\n", sc);
+                       ULOG_ERR("no name found for syscall(%d)\n", sc);
                }
        }
        blobmsg_close_array(&b, c);
@@ -162,9 +148,9 @@ static void print_syscalls(int policy, const char *json)
                if (fp) {
                        fprintf(fp, "%s", blobmsg_format_json_indent(b.head, true, 0));
                        fclose(fp);
-                       INFO("saving syscall trace to %s\n", json);
+                       ULOG_INFO("saving syscall trace to %s\n", json);
                } else {
-                       ERROR("failed to open %s\n", json);
+                       ULOG_ERR("failed to open %s\n", json);
                }
        } else {
                printf("%s\n",
@@ -187,13 +173,14 @@ static void report_seccomp_vialation(pid_t pid, unsigned syscall)
 
        if (violation_count < INT_MAX)
                violation_count++;
-       if (syscall < ARRAY_SIZE(syscall_names)) {
-               syscall_count[syscall]++;
-               LOGERR("%s[%u] tried to call non-whitelisted syscall: %s (see %s)\n",
-                      buf, pid,  syscall_names[syscall], json);
+       int i = syscall_index(syscall);
+       if (i >= 0) {
+               syscall_count[i]++;
+               ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %s (see %s)\n",
+                        buf, pid,  syscall_name(syscall), json);
        } else {
-               LOGERR("%s[%u] tried to call non-whitelisted syscall: %d (see %s)\n",
-                      buf, pid,  syscall, json);
+               ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %d (see %s)\n",
+                        buf, pid,  syscall, json);
        }
 }
 
@@ -209,11 +196,11 @@ static void tracer_cb(struct uloop_process *c, int ret)
                if (WSTOPSIG(ret) & 0x80) {
                        if (!tracee->in_syscall) {
                                int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr);
-
-                               if (syscall < syscall_max) {
-                                       syscall_count[syscall]++;
+                               int i = syscall_index(syscall);
+                               if (i >= 0) {
+                                       syscall_count[i]++;
                                        if (debug)
-                                               fprintf(stderr, "%s()\n", syscall_names[syscall]);
+                                               fprintf(stderr, "%s()\n", syscall_name(syscall));
                                } else if (debug) {
                                        fprintf(stderr, "syscal(%d)\n", syscall);
                                }
@@ -262,6 +249,15 @@ static void tracer_cb(struct uloop_process *c, int ret)
        uloop_process_add(c);
 }
 
+static void sigterm_handler(int signum)
+{
+       /* When we receive SIGTERM, we forward it to the tracee. After
+        * the tracee exits, trace_cb() will be called and make us
+        * exit too. */
+       kill(tracer.proc.pid, SIGTERM);
+}
+
+
 int main(int argc, char **argv, char **envp)
 {
        int status, ch, policy = EPERM;
@@ -321,17 +317,21 @@ int main(int argc, char **argv, char **envp)
                case SECCOMP_TRACE:
                        preload = "/lib/libpreload-seccomp.so";
                        newenv = 2;
-                       asprintf(&_envp[1], "SECCOMP_FILE=%s", json ? json : "");
+                       if (asprintf(&_envp[1], "SECCOMP_FILE=%s", json ? json : "") < 0)
+                               ULOG_ERR("failed to allocate SECCOMP_FILE env: %m\n");
+
                        kill(getpid(), SIGSTOP);
                        break;
                }
-               asprintf(&_envp[0], "LD_PRELOAD=%s%s%s", preload,
-                        old_preload ? ":" : "",
-                        old_preload ? old_preload : "");
+               if (asprintf(&_envp[0], "LD_PRELOAD=%s%s%s", preload,
+                            old_preload ? ":" : "",
+                             old_preload ? old_preload : "") < 0)
+                       ULOG_ERR("failed to allocate LD_PRELOAD env: %m\n");
+
                memcpy(&_envp[newenv], envp, envc * sizeof(char *));
 
                ret = execve(_argv[0], _argv, _envp);
-               ERROR("failed to exec %s: %s\n", _argv[0], strerror(errno));
+               ULOG_ERR("failed to exec %s: %m\n", _argv[0]);
 
                free(_argv);
                free(_envp);
@@ -341,14 +341,15 @@ int main(int argc, char **argv, char **envp)
        if (child < 0)
                return -1;
 
-       syscall_max = ARRAY_SIZE(syscall_names);
-       syscall_count = calloc(syscall_max, sizeof(int));
        waitpid(child, &status, WUNTRACED);
        if (!WIFSTOPPED(status)) {
-               ERROR("failed to start %s\n", *argv);
+               ULOG_ERR("failed to start %s\n", *argv);
                return -1;
        }
 
+       /* Initialize uloop to catch all ptrace stops from now on. */
+       uloop_init();
+
        int ptrace_options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE;
        switch (mode) {
        case UTRACE:
@@ -360,13 +361,19 @@ int main(int argc, char **argv, char **envp)
                ptrace_restart = PTRACE_CONT;
                break;
        }
-       ptrace(PTRACE_SEIZE, child, 0, ptrace_options);
-       ptrace(ptrace_restart, child, 0, SIGCONT);
+       if (ptrace(PTRACE_SEIZE, child, 0, ptrace_options) == -1) {
+               ULOG_ERR("PTRACE_SEIZE: %m\n");
+               return -1;
+       }
+       if (ptrace(ptrace_restart, child, 0, SIGCONT) == -1) {
+               ULOG_ERR("ptrace_restart: %m\n");
+               return -1;
+       }
 
-       uloop_init();
        tracer.proc.pid = child;
        tracer.proc.cb = tracer_cb;
        uloop_process_add(&tracer.proc);
+       signal(SIGTERM, sigterm_handler); /* Override uloop's SIGTERM handler */
        uloop_run();
        uloop_done();
 
@@ -375,12 +382,13 @@ int main(int argc, char **argv, char **envp)
        case UTRACE:
                if (!json)
                        if (asprintf(&json, "/tmp/%s.%u.json", basename(*argv), child) < 0)
-                               ERROR("failed to allocate output path: %s\n", strerror(errno));
+                               ULOG_ERR("failed to allocate output path: %m\n");
                break;
        case SECCOMP_TRACE:
                if (!violation_count)
                        return 0;
-               asprintf(&json, "/tmp/%s.%u.violations.json", basename(*argv), child);
+               if (asprintf(&json, "/tmp/%s.%u.violations.json", basename(*argv), child) < 0)
+                       ULOG_ERR("failed to allocate violations output path: %m\n");
                break;
        }
        print_syscalls(policy, json);