aboutsummaryrefslogtreecommitdiffstats
path: root/executor/executor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'executor/executor.cc')
-rw-r--r--executor/executor.cc149
1 files changed, 105 insertions, 44 deletions
diff --git a/executor/executor.cc b/executor/executor.cc
index 4d9ee9f26..52275b185 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -34,8 +34,8 @@
#include "syscalls.h"
-#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long)
-#define KCOV_INIT_TABLE _IOR('c', 2, unsigned long)
+#define KCOV_INIT_TRACE _IOR('c', 1, unsigned long long)
+#define KCOV_INIT_TABLE _IOR('c', 2, unsigned long long)
#define KCOV_ENABLE _IO('c', 100)
#define KCOV_DISABLE _IO('c', 101)
@@ -67,12 +67,19 @@ const int kRetryStatus = 69;
// so good enough for our purposes.
const uint64_t default_value = -1;
+enum sandbox_type {
+ sandbox_none,
+ sandbox_setuid,
+ sandbox_namespace,
+};
+
bool flag_debug;
bool flag_cover;
bool flag_threaded;
bool flag_collide;
bool flag_deduplicate;
-bool flag_drop_privs;
+bool flag_sandbox_privs;
+sandbox_type flag_sandbox;
__attribute__((aligned(64 << 10))) char input_data[kMaxInput];
__attribute__((aligned(64 << 10))) char output_data[kMaxOutput];
@@ -94,7 +101,7 @@ struct thread_t {
bool created;
int id;
pthread_t th;
- uintptr_t* cover_data;
+ uint64_t* cover_data;
uint64_t* copyout_pos;
int ready;
int done;
@@ -103,10 +110,10 @@ struct thread_t {
int call_index;
int call_num;
int num_args;
- uint64_t args[kMaxArgs];
+ uintptr_t args[kMaxArgs];
uint64_t res;
uint64_t reserrno;
- uintptr_t cover_size;
+ uint64_t cover_size;
int cover_fd;
};
@@ -117,7 +124,11 @@ __attribute__((noreturn)) void fail(const char* msg, ...);
__attribute__((noreturn)) void error(const char* msg, ...);
__attribute__((noreturn)) void exitf(const char* msg, ...);
void debug(const char* msg, ...);
-int sandbox(void* arg);
+int sandbox_proc(void* arg);
+int do_sandbox_none();
+int do_sandbox_setuid();
+int do_sandbox_namespace();
+void sandbox_common();
void loop();
void execute_one();
uint64_t read_input(uint64_t** input_posp, bool peek = false);
@@ -137,8 +148,8 @@ uint64_t current_time_ms();
void cover_open();
void cover_enable(thread_t* th);
void cover_reset(thread_t* th);
-uintptr_t cover_read(thread_t* th);
-uintptr_t cover_dedup(thread_t* th, uintptr_t n);
+uint64_t cover_read(thread_t* th);
+uint64_t cover_dedup(thread_t* th, uint64_t n);
int main(int argc, char** argv)
{
@@ -165,7 +176,11 @@ int main(int argc, char** argv)
flag_threaded = flags & (1 << 2);
flag_collide = flags & (1 << 3);
flag_deduplicate = flags & (1 << 4);
- flag_drop_privs = flags & (1 << 5);
+ flag_sandbox = sandbox_none;
+ if (flags & (1 << 5))
+ flag_sandbox = sandbox_setuid;
+ else if (flags & (1 << 6))
+ flag_sandbox = sandbox_namespace;
if (!flag_threaded)
flag_collide = false;
@@ -181,17 +196,18 @@ int main(int argc, char** argv)
syscall(SYS_rt_sigaction, 0x21, &sa, NULL, 8);
int pid = -1;
- if (flag_drop_privs) {
- real_uid = getuid();
- real_gid = getgid();
- pid = clone(sandbox, &sandbox_stack[sizeof(sandbox_stack) - 8],
- CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL);
- } else {
- pid = fork();
- if (pid == 0) {
- loop();
- exit(1);
- }
+ switch (flag_sandbox) {
+ case sandbox_none:
+ pid = do_sandbox_none();
+ break;
+ case sandbox_setuid:
+ pid = do_sandbox_setuid();
+ break;
+ case sandbox_namespace:
+ pid = do_sandbox_namespace();
+ break;
+ default:
+ fail("unknown sandbox type");
}
if (pid < 0)
fail("clone failed");
@@ -282,7 +298,46 @@ void loop()
}
}
-int sandbox(void* arg)
+int do_sandbox_none()
+{
+ int pid = fork();
+ if (pid)
+ return pid;
+ loop();
+ exit(1);
+}
+
+int do_sandbox_setuid()
+{
+ int pid = fork();
+ if (pid)
+ return pid;
+
+ sandbox_common();
+
+ const int nobody = 65534;
+ if (setgroups(0, NULL))
+ fail("failed to setgroups");
+ // glibc versions do not we want -- they force all threads to setuid.
+ // We want to preserve the thread above as root.
+ if (syscall(SYS_setresgid, nobody, nobody, nobody))
+ fail("failed to setresgid");
+ if (syscall(SYS_setresuid, nobody, nobody, nobody))
+ fail("failed to setresuid");
+
+ loop();
+ exit(1);
+}
+
+int do_sandbox_namespace()
+{
+ real_uid = getuid();
+ real_gid = getgid();
+ return clone(sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 8],
+ CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL);
+}
+
+void sandbox_common()
{
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
setpgrp();
@@ -299,8 +354,14 @@ int sandbox(void* arg)
setrlimit(RLIMIT_CORE, &rlim);
// CLONE_NEWIPC/CLONE_IO cause EINVAL on some systems, so we do them separately of clone.
+ unshare(CLONE_NEWNS);
unshare(CLONE_NEWIPC);
unshare(CLONE_IO);
+}
+
+int sandbox_proc(void* arg)
+{
+ sandbox_common();
// /proc/self/setgroups is not present on some systems, ignore error.
write_file("/proc/self/setgroups", "deny");
@@ -540,7 +601,7 @@ void handle_completion(thread_t* th)
write_output(th->cover_size);
// Truncate PCs to uint32_t assuming that they fit into 32-bits.
// True for x86_64 and arm64 without KASLR.
- for (uint32_t i = 0; i < th->cover_size; i++)
+ for (uint64_t i = 0; i < th->cover_size; i++)
write_output((uint32_t)th->cover_data[i + 1]);
completed++;
__atomic_store_n((uint32_t*)&output_data[0], completed, __ATOMIC_RELEASE);
@@ -651,9 +712,9 @@ void execute_call(thread_t* th)
int fd = open("/dev/fuse", O_RDWR);
if (fd != -1) {
char buf[256];
- sprintf(buf, "fd=%d,user_id=%lu,group_id=%lu,rootmode=0%o", fd, uid, gid, (unsigned)mode & ~3u);
+ sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u);
if (maxread != 0)
- sprintf(buf + strlen(buf), ",max_read=%lu", maxread);
+ sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
if (mode & 1)
strcat(buf, ",default_permissions");
if (mode & 2)
@@ -679,11 +740,11 @@ void execute_call(thread_t* th)
if (fd != -1) {
if (syscall(SYS_mknodat, AT_FDCWD, blkdev, S_IFBLK, makedev(7, 199)) == 0) {
char buf[256];
- sprintf(buf, "fd=%d,user_id=%lu,group_id=%lu,rootmode=0%o", fd, uid, gid, (unsigned)mode & ~3u);
+ sprintf(buf, "fd=%d,user_id=%ld,group_id=%ld,rootmode=0%o", fd, (long)uid, (long)gid, (unsigned)mode & ~3u);
if (maxread != 0)
- sprintf(buf + strlen(buf), ",max_read=%lu", maxread);
+ sprintf(buf + strlen(buf), ",max_read=%ld", (long)maxread);
if (blksize != 0)
- sprintf(buf + strlen(buf), ",blksize=%lu", blksize);
+ sprintf(buf + strlen(buf), ",blksize=%ld", (long)blksize);
if (mode & 1)
strcat(buf, ",default_permissions");
if (mode & 2)
@@ -717,8 +778,8 @@ void cover_open()
if (th->cover_fd == -1)
fail("open of /sys/kernel/debug/kcov failed");
if (ioctl(th->cover_fd, KCOV_INIT_TRACE, kCoverSize))
- fail("cover enable write failed");
- th->cover_data = (uintptr_t*)mmap(NULL, kCoverSize * sizeof(th->cover_data[0]), PROT_READ | PROT_WRITE, MAP_SHARED, th->cover_fd, 0);
+ fail("cover init write failed");
+ th->cover_data = (uint64_t*)mmap(NULL, kCoverSize * sizeof(th->cover_data[0]), PROT_READ | PROT_WRITE, MAP_SHARED, th->cover_fd, 0);
if ((void*)th->cover_data == MAP_FAILED)
fail("cover mmap failed");
}
@@ -741,11 +802,11 @@ void cover_reset(thread_t* th)
__atomic_store_n(&th->cover_data[0], 0, __ATOMIC_RELAXED);
}
-uintptr_t cover_read(thread_t* th)
+uint64_t cover_read(thread_t* th)
{
if (!flag_cover)
return 0;
- uintptr_t n = __atomic_load_n(&th->cover_data[0], __ATOMIC_RELAXED);
+ uint64_t n = __atomic_load_n(&th->cover_data[0], __ATOMIC_RELAXED);
debug("#%d: read cover = %d\n", th->id, n);
if (n >= kCoverSize)
fail("#%d: too much cover %d", th->id, n);
@@ -756,14 +817,14 @@ uintptr_t cover_read(thread_t* th)
return n;
}
-uintptr_t cover_dedup(thread_t* th, uintptr_t n)
+uint64_t cover_dedup(thread_t* th, uint64_t n)
{
- uintptr_t* cover_data = th->cover_data + 1;
+ uint64_t* cover_data = th->cover_data + 1;
std::sort(cover_data, cover_data + n);
- uintptr_t w = 0;
- uintptr_t last = 0;
- for (uintptr_t i = 0; i < n; i++) {
- uintptr_t pc = cover_data[i];
+ uint64_t w = 0;
+ uint64_t last = 0;
+ for (uint64_t i = 0; i < n; i++) {
+ uint64_t pc = cover_data[i];
if (pc == last)
continue;
cover_data[w++] = last = pc;
@@ -902,7 +963,7 @@ retry:
// But full sandboxing is expensive, so let's ignore this error for now.
exitf("opendir(%s) failed due to NOFILE, exiting");
}
- fail("opendir(%s) failed", dir);
+ exitf("opendir(%s) failed", dir);
}
while (dirent* ep = readdir(dp)) {
if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
@@ -911,7 +972,7 @@ retry:
snprintf(filename, sizeof(filename), "%s/%s", dir, ep->d_name);
struct stat st;
if (lstat(filename, &st))
- fail("lstat(%s) failed", filename);
+ exitf("lstat(%s) failed", filename);
if (S_ISDIR(st.st_mode)) {
remove_dir(filename);
continue;
@@ -925,10 +986,10 @@ retry:
break;
}
if (errno != EBUSY || i > 100)
- fail("unlink(%s) failed", filename);
+ exitf("unlink(%s) failed", filename);
debug("umount(%s)\n", filename);
if (umount2(filename, MNT_DETACH))
- fail("umount(%s) failed", filename);
+ exitf("umount(%s) failed", filename);
}
}
closedir(dp);
@@ -944,7 +1005,7 @@ retry:
if (errno == EBUSY) {
debug("umount(%s)\n", dir);
if (umount2(dir, MNT_DETACH))
- fail("umount(%s) failed", dir);
+ exitf("umount(%s) failed", dir);
continue;
}
if (errno == ENOTEMPTY) {
@@ -954,7 +1015,7 @@ retry:
}
}
}
- fail("rmdir(%s) failed", dir);
+ exitf("rmdir(%s) failed", dir);
}
}