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 /pkg/csource | |
| 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 'pkg/csource')
| -rw-r--r-- | pkg/csource/generated.go | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 2da36802c..c28b1ccaf 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -8926,6 +8926,174 @@ static void setup_usb() #define CAST(f) ({void* p = (void*)f; p; }) #endif +#if SYZ_EXECUTOR || __NR_syz_fuse_handle_req +#include <fcntl.h> +#include <linux/fuse.h> +#include <stddef.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +struct syz_fuse_req_out { + struct fuse_out_header* init; + struct fuse_out_header* lseek; + struct fuse_out_header* bmap; + struct fuse_out_header* poll; + struct fuse_out_header* getxattr; + struct fuse_out_header* lk; + struct fuse_out_header* statfs; + struct fuse_out_header* write; + struct fuse_out_header* read; + struct fuse_out_header* open; + struct fuse_out_header* attr; + struct fuse_out_header* entry; + struct fuse_out_header* dirent; + struct fuse_out_header* direntplus; + struct fuse_out_header* create_open; + struct fuse_out_header* ioctl; +}; +static int +fuse_send_response(int fd, + const struct fuse_in_header* in_hdr, + struct fuse_out_header* out_hdr) +{ + if (!out_hdr) { + debug("fuse_send_response: received a NULL out_hdr\n"); + return -1; + } + + out_hdr->unique = in_hdr->unique; + if (write(fd, out_hdr, out_hdr->len) == -1) { + debug("fuse_send_response > write failed: %d\n", errno); + return -1; + } + + return 0; +} +static volatile long syz_fuse_handle_req(volatile long a0, + volatile long a1, + volatile long a2, + volatile long a3) +{ + struct syz_fuse_req_out* req_out = (struct syz_fuse_req_out*)a3; + struct fuse_out_header* out_hdr = NULL; + char* buf = (char*)a1; + int buf_len = (int)a2; + int fd = (int)a0; + + if (!req_out) { + debug("syz_fuse_handle_req: received a NULL syz_fuse_req_out\n"); + return -1; + } + if (buf_len < FUSE_MIN_READ_BUFFER) { + debug("FUSE requires the read buffer to be at least %u\n", FUSE_MIN_READ_BUFFER); + return -1; + } + + int ret = read(fd, buf, buf_len); + if (ret == -1) { + debug("syz_fuse_handle_req > read failed: %d\n", errno); + return -1; + } + if ((size_t)ret < sizeof(struct fuse_in_header)) { + debug("syz_fuse_handle_req: received a truncated FUSE header\n"); + return -1; + } + + const struct fuse_in_header* in_hdr = (const struct fuse_in_header*)buf; + debug("syz_fuse_handle_req: received opcode %d\n", in_hdr->opcode); + if (in_hdr->len > (uint32)ret) { + debug("syz_fuse_handle_req: received a truncated message\n"); + return -1; + } + + switch (in_hdr->opcode) { + case FUSE_GETATTR: + case FUSE_SETATTR: + out_hdr = req_out->attr; + break; + case FUSE_LOOKUP: + case FUSE_SYMLINK: + case FUSE_LINK: + case FUSE_MKNOD: + case FUSE_MKDIR: + out_hdr = req_out->entry; + break; + case FUSE_OPEN: + case FUSE_OPENDIR: + out_hdr = req_out->open; + break; + case FUSE_STATFS: + out_hdr = req_out->statfs; + break; + case FUSE_RMDIR: + case FUSE_RENAME: + case FUSE_RENAME2: + case FUSE_FALLOCATE: + case FUSE_SETXATTR: + case FUSE_REMOVEXATTR: + case FUSE_FSYNCDIR: + case FUSE_FSYNC: + case FUSE_SETLKW: + case FUSE_SETLK: + case FUSE_ACCESS: + case FUSE_FLUSH: + case FUSE_RELEASE: + case FUSE_RELEASEDIR: + out_hdr = req_out->init; + if (!out_hdr) { + debug("syz_fuse_handle_req: received a NULL out_hdr\n"); + return -1; + } + out_hdr->len = sizeof(struct fuse_out_header); + break; + case FUSE_READ: + out_hdr = req_out->read; + break; + case FUSE_READDIR: + out_hdr = req_out->dirent; + break; + case FUSE_READDIRPLUS: + out_hdr = req_out->direntplus; + break; + case FUSE_INIT: + out_hdr = req_out->init; + break; + case FUSE_LSEEK: + out_hdr = req_out->lseek; + break; + case FUSE_GETLK: + out_hdr = req_out->lk; + break; + case FUSE_BMAP: + out_hdr = req_out->bmap; + break; + case FUSE_POLL: + out_hdr = req_out->poll; + break; + case FUSE_GETXATTR: + case FUSE_LISTXATTR: + out_hdr = req_out->getxattr; + break; + case FUSE_WRITE: + out_hdr = req_out->write; + break; + case FUSE_FORGET: + return 0; + case FUSE_CREATE: + out_hdr = req_out->create_open; + break; + case FUSE_IOCTL: + out_hdr = req_out->ioctl; + break; + default: + debug("syz_fuse_handle_req: unknown FUSE opcode\n"); + return -1; + } + + return fuse_send_response(fd, in_hdr, out_hdr); +} +#endif + #elif GOOS_test #include <stdlib.h> |
