aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-11-19 17:16:36 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-11-19 17:16:36 +0100
commit4784f4f6f50d7ddf958ddbb717bb2a0cb3e397a9 (patch)
treee3f8006173e967ab1d3d0a1cc8f276a15d59074d /executor
parent93f8cc38aa01d8ebfbfed4f111e7a0a48dc89cac (diff)
add fuse support
Diffstat (limited to 'executor')
-rw-r--r--executor/executor.cc73
-rw-r--r--executor/syscalls.h16
2 files changed, 82 insertions, 7 deletions
diff --git a/executor/executor.cc b/executor/executor.cc
index c0cdf72f1..7f0e82ba9 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -33,7 +33,7 @@ const int kOutPipeFd = 6;
const int kCoverFd = 5;
const int kMaxInput = 1 << 20;
const int kMaxOutput = 16 << 20;
-const int kMaxArgs = 6;
+const int kMaxArgs = 9;
const int kMaxThreads = 16;
const int kMaxCommands = 4 << 10;
@@ -160,6 +160,7 @@ int main()
fail("fork failed");
if (pid == 0) {
setpgid(0, 0);
+ unshare(CLONE_NEWNS);
if (flag_drop_privs) {
// Pre-create one thread with root privileges for execution of special syscalls (e.g. mount).
if (flag_threaded)
@@ -167,9 +168,11 @@ int main()
// TODO: 65534 is meant to be nobody
if (setgroups(0, NULL))
fail("failed to setgroups");
- if (setresgid(65534, 65534, 65534))
+ // 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, 65534, 65534, 65534))
fail("failed to setresgid");
- if (setresuid(65534, 65534, 65534))
+ if (syscall(SYS_setresuid, 65534, 65534, 65534))
fail("failed to setresuid");
}
// Don't need that SIGCANCEL/SIGSETXID glibc stuff.
@@ -336,7 +339,8 @@ thread_t* schedule_call(int n, int call_index, int call_num, uint64_t num_args,
// Figure out whether we need root privs for this call.
bool root = false;
switch (syscalls[call_num].sys_nr) {
- case __NR_syz_dri_open:
+ case __NR_syz_fuse_mount:
+ case __NR_syz_fuseblk_mount:
root = true;
}
// Find a spare thread to execute the call.
@@ -447,16 +451,16 @@ void execute_call(thread_t* th)
}
debug(")\n");
- if (kMaxArgs != 6)
- fail("inconsistent number of arguments");
-
cover_reset(th);
switch (call->sys_nr) {
default: {
+ if (th->num_args > 6)
+ fail("bad number of arguments");
th->res = syscall(call->sys_nr, th->args[0], th->args[1], th->args[2], th->args[3], th->args[4], th->args[5]);
break;
}
case __NR_syz_openpts: {
+ // syz_openpts(fd fd[tty], flags flags[open_flags]) fd[tty]
int ptyno = 0;
if (ioctl(th->args[0], TIOCGPTN, &ptyno) == 0) {
char buf[128];
@@ -468,10 +472,65 @@ void execute_call(thread_t* th)
}
}
case __NR_syz_dri_open: {
+ // syz_dri_open(card_id intptr, flags flags[open_flags]) fd[dri]
char buf[128];
sprintf(buf, "/dev/dri/card%lu", th->args[0]);
th->res = open(buf, th->args[1], 0);
}
+ case __NR_syz_fuse_mount: {
+ // syz_fuse_mount(target filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, flags flags[mount_flags]) fd[fuse]
+ uint64_t target = th->args[0];
+ uint64_t mode = th->args[1];
+ uint64_t uid = th->args[2];
+ uint64_t gid = th->args[3];
+ uint64_t maxread = th->args[4];
+ uint64_t flags = th->args[5];
+
+ 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);
+ if (maxread != 0)
+ sprintf(buf + strlen(buf), ",max_read=%lu", maxread);
+ if (mode & 1)
+ strcat(buf, ",default_permissions");
+ if (mode & 2)
+ strcat(buf, ",allow_other");
+ syscall(SYS_mount, "", target, "fuse", flags, buf);
+ // Ignore errors, maybe fuzzer can do something useful with fd alone.
+ }
+ th->res = fd;
+ }
+ case __NR_syz_fuseblk_mount: {
+ // syz_fuseblk_mount(target filename, blkdev filename, mode flags[fuse_mode], uid uid, gid gid, maxread intptr, blksize intptr, flags flags[mount_flags]) fd[fuse]
+ uint64_t target = th->args[0];
+ uint64_t blkdev = th->args[1];
+ uint64_t mode = th->args[2];
+ uint64_t uid = th->args[3];
+ uint64_t gid = th->args[4];
+ uint64_t maxread = th->args[5];
+ uint64_t blksize = th->args[6];
+ uint64_t flags = th->args[7];
+
+ int fd = open("/dev/fuse", O_RDWR);
+ if (fd != -1) {
+ if (syscall(SYS_mknod, 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);
+ if (maxread != 0)
+ sprintf(buf + strlen(buf), ",max_read=%lu", maxread);
+ if (blksize != 0)
+ sprintf(buf + strlen(buf), ",blksize=%lu", blksize);
+ if (mode & 1)
+ strcat(buf, ",default_permissions");
+ if (mode & 2)
+ strcat(buf, ",allow_other");
+ syscall(SYS_mount, blkdev, target, "fuseblk", flags, buf);
+ // Ignore errors, maybe fuzzer can do something useful with fd alone.
+ }
+ }
+ th->res = fd;
+ }
}
int errno0 = errno;
th->cover_size = cover_read(th);
diff --git a/executor/syscalls.h b/executor/syscalls.h
index 560b9786b..e77958674 100644
--- a/executor/syscalls.h
+++ b/executor/syscalls.h
@@ -12,6 +12,8 @@ struct call_t {
#define __NR_syz_openpts 1000001
#define __NR_syz_dri_open 1000002
+#define __NR_syz_fuse_mount 1000003
+#define __NR_syz_fuseblk_mount 1000004
call_t syscalls[] = {
{"open", __NR_open},
@@ -518,4 +520,18 @@ call_t syscalls[] = {
{"ioctl$DRM_IOCTL_GET_CAP", __NR_ioctl},
{"ioctl$DRM_IOCTL_SET_CLIENT_CAP", __NR_ioctl},
{"ioctl$DRM_IOCTL_SET_VERSION", __NR_ioctl},
+ {"syz_fuse_mount", __NR_syz_fuse_mount},
+ {"syz_fuseblk_mount", __NR_syz_fuseblk_mount},
+ {"ioctl$FUSE_DEV_IOC_CLONE", __NR_ioctl},
+ {"write$fuse_init", __NR_write},
+ {"write$fuse_interrupt", __NR_write},
+ {"write$fuse_bmap", __NR_write},
+ {"write$fuse_ioctl", __NR_write},
+ {"write$fuse_poll", __NR_write},
+ {"write$fuse_notify_poll_wakeup", __NR_write},
+ {"write$fuse_notify_inval_inode", __NR_write},
+ {"write$fuse_notify_inval_entry", __NR_write},
+ {"write$fuse_notify_delete", __NR_write},
+ {"write$fuse_notify_store", __NR_write},
+ {"write$fuse_notify_retrieve", __NR_write},
};