From 19b6584f719a7fd28b8d0851c4e9b5cb20be35df Mon Sep 17 00:00:00 2001 From: Stefano Duo Date: Wed, 22 Jul 2020 13:47:01 +0000 Subject: executor/common_linux.h: add syz_fuse_handle_req() At the moment syzkaller is able to respond to FUSE with a syntactically correct response using the specific write$FUSE_*() syscalls, but most of the times these responses are not related to the type of request that was received. With this pseudo-syscall we are able to provide the correct response type while still allowing the fuzzer to fuzz its content. This is done by requiring each type of response as an input parameter and then choosing the correct one based on the request opcode. Notice that the fuzzer is still free to mix write$FUSE_*() and syz_fuse_handle_req() syscalls, so it is not losing any degree of freedom. syz_fuse_handle_req() retrieves the FUSE request and resource fuse_unique internally (by performing a read() on the /dev/fuse file descriptor provided as input). For this reason, a new template argument has been added to fuse_out (renamed to _fuse_out) so that the unique field can be both an int64 (used by syz_fuse_handle_req()) and a fuse_unique resource (used by the write$FUSE_*() syscalls) without any code duplication. --- sys/linux/fs_fuse.txt | 37 +++++++++++++++++++++++++++++++++++-- sys/linux/test/syz_fuse_handle_req | 8 ++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 sys/linux/test/syz_fuse_handle_req (limited to 'sys') diff --git a/sys/linux/fs_fuse.txt b/sys/linux/fs_fuse.txt index 5d3efa8a6..d7d887a9e 100644 --- a/sys/linux/fs_fuse.txt +++ b/sys/linux/fs_fuse.txt @@ -41,6 +41,8 @@ write$FUSE_NOTIFY_STORE(fd fd_fuse, arg ptr[in, fuse_notify[FUSE_NOTIFY_STORE, f write$FUSE_NOTIFY_RETRIEVE(fd fd_fuse, arg ptr[in, fuse_notify[FUSE_NOTIFY_RETRIEVE, fuse_notify_retrieve_out]], len bytesize[arg]) write$FUSE_NOTIFY_DELETE(fd fd_fuse, arg ptr[in, fuse_notify[FUSE_NOTIFY_DELETE, fuse_notify_delete_out]], len bytesize[arg]) +syz_fuse_handle_req(fd fd_fuse, buf ptr[in, read_buffer], len bytesize[buf], res ptr[in, syz_fuse_req_out]) + type fuse_ino int64[0:6] type fuse_gen int64[0:3] @@ -62,13 +64,20 @@ type fuse_in[PAYLOAD] { payload PAYLOAD } [packed] -type fuse_out[PAYLOAD] { +type fuse_out_t[UNIQUE, PAYLOAD] { len len[parent, int32] err flags[fuse_errors, int32] - unique fuse_unique + unique UNIQUE payload PAYLOAD } [packed] +type fuse_out[PAYLOAD] fuse_out_t[fuse_unique, PAYLOAD] +# This response header is used by syz_fuse_handle_req(). It defines the FUSE unique +# identifier as int64 because syz_fuse_handle_req() retrieves it internally +# (defining it as a resource would create a dependency with read$FUSE() which is +# incorrect). +type syz_fuse_out[PAYLOAD] fuse_out_t[int64, PAYLOAD] + # -ENOENT, -EAGAIN, -ENOSYS fuse_errors = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -11, -38 @@ -154,6 +163,10 @@ fuse_write_out { padding const[0, int32] } +fuse_read_out { + content string +} + fuse_open_out { fh const[0, int64] open_flags flags[fuse_open_flags, int32] @@ -278,3 +291,23 @@ fuse_opts [ fuse_mode = S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK, S_IFLNK, S_IFDIR fuse_block_sizes = 512, 1024, 2048, 4096 + +# Used by syz_fuse_handle_req() to mimic a FUSE daemon. +syz_fuse_req_out { + init ptr[in, syz_fuse_out[fuse_init_out]] + lseek ptr[in, syz_fuse_out[fuse_lseek_out]] + bmap ptr[in, syz_fuse_out[fuse_bmap_out]] + poll ptr[in, syz_fuse_out[fuse_poll_out]] + getxattr ptr[in, syz_fuse_out[fuse_getxattr_out]] + lk ptr[in, syz_fuse_out[fuse_lk_out]] + statfs ptr[in, syz_fuse_out[fuse_statfs_out]] + write ptr[in, syz_fuse_out[fuse_write_out]] + read ptr[in, syz_fuse_out[fuse_read_out]] + open ptr[in, syz_fuse_out[fuse_open_out]] + attr ptr[in, syz_fuse_out[fuse_attr_out]] + entry ptr[in, syz_fuse_out[fuse_entry_out]] + dirent ptr[in, syz_fuse_out[array[fuse_dirent]]] + direntplus ptr[in, syz_fuse_out[array[fuse_direntplus]]] + create_open ptr[in, syz_fuse_out[fuse_create_open_out]] + ioctl ptr[in, syz_fuse_out[fuse_ioctl_out]] +} diff --git a/sys/linux/test/syz_fuse_handle_req b/sys/linux/test/syz_fuse_handle_req new file mode 100644 index 000000000..a26592b9d --- /dev/null +++ b/sys/linux/test/syz_fuse_handle_req @@ -0,0 +1,8 @@ +mkdirat(0xffffffffffffff9c, &AUTO='./file0\x00', 0x0) +r0 = openat$fuse(0xffffffffffffff9c, &AUTO='/dev/fuse\x00', 0x2, 0x0) +mount$fuse(0x0, &AUTO='./file0\x00', &AUTO='fuse\x00', 0x0, &AUTO={{'fd', 0x3d, r0}, 0x2c, {'rootmode', 0x3d, 0x4000}, 0x2c, {'user_id', 0x3d, 0x0}, 0x2c, {'group_id', 0x3d, 0x0}, 0x2c, {[], [], 0x0}}) +r1 = openat$dir(0xffffffffffffff9c, &AUTO='./file0\x00', 0x0, 0x0) +# FUSE_INIT +syz_fuse_handle_req(r0, &AUTO=""/8192, AUTO, &AUTO={&AUTO={AUTO, 0x0, 0x0, {AUTO, AUTO, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, AUTO, AUTO, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}}, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}) +# FUSE_OPENDIR +syz_fuse_handle_req(r0, &AUTO=""/8192, AUTO, &AUTO={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, &AUTO={AUTO, 0x0, 0x0, {0x0, 0x0, 0x0}}, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}) -- cgit mrf-deployment