diff options
| author | Stefano Duo <stefanoduo@google.com> | 2020-07-22 13:47:01 +0000 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2020-08-14 18:55:11 +0200 |
| commit | 19b6584f719a7fd28b8d0851c4e9b5cb20be35df (patch) | |
| tree | 761dbdd47e30aa1182bdce59220df778c9d808dd /sys/linux | |
| parent | 3d9b8afae8832eb188d0ae71e1f73383f12d3944 (diff) | |
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.
Diffstat (limited to 'sys/linux')
| -rw-r--r-- | sys/linux/fs_fuse.txt | 37 | ||||
| -rw-r--r-- | sys/linux/test/syz_fuse_handle_req | 8 |
2 files changed, 43 insertions, 2 deletions
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}) |
