Skip to content
Permalink
Browse files

executor: fix syz_mount_image

1. It always crashed in cover_reset when coverage is disabled.
2. Use NONFAILING when accessing image segments.
3. Give it additional 100 ms as it may be slow.
4. Add a test for syz_mount_image.
  • Loading branch information
dvyukov committed Dec 10, 2019
1 parent 5a5826a commit cb704a294c54aed90281c016a6dc0c40ae295601
Showing with 53 additions and 59 deletions.
  1. +23 −32 executor/common_linux.h
  2. +2 −0 executor/executor.cc
  3. +7 −1 executor/executor_linux.h
  4. +1 −0 pkg/csource/csource.go
  5. +18 −26 pkg/csource/generated.go
  6. +2 −0 sys/linux/test/syz_mount_image
@@ -1230,16 +1230,14 @@ struct fs_image_segment {
#elif GOARCH_ppc64le
#define sys_memfd_create 360
#endif
#endif

#if SYZ_EXECUTOR || __NR_syz_read_part_table
// syz_read_part_table(size intptr, nsegs len[segments], segments ptr[in, array[fs_image_segment]])
static long syz_read_part_table(volatile unsigned long size, volatile unsigned long nsegs, volatile long segments)
static unsigned long fs_image_segment_check(unsigned long size, unsigned long nsegs, long segments)
{
char loopname[64], linkname[64];
int loopfd, err = 0, res = -1;
unsigned long i, j;
// See the comment in syz_mount_image.
unsigned long i;
// Strictly saying we ought to do a nonfailing copyout of segments into a local var.
// But some filesystems have large number of segments (2000+),
// we can't allocate that much on stack and allocating elsewhere is problematic,
// so we just use the memory allocated by fuzzer.
struct fs_image_segment* segs = (struct fs_image_segment*)segments;

if (nsegs > IMAGE_MAX_SEGMENTS)
@@ -1255,6 +1253,18 @@ static long syz_read_part_table(volatile unsigned long size, volatile unsigned l
}
if (size > IMAGE_MAX_SIZE)
size = IMAGE_MAX_SIZE;
return size;
}
#endif

#if SYZ_EXECUTOR || __NR_syz_read_part_table
// syz_read_part_table(size intptr, nsegs len[segments], segments ptr[in, array[fs_image_segment]])
static long syz_read_part_table(volatile unsigned long size, volatile unsigned long nsegs, volatile long segments)
{
char loopname[64], linkname[64];
int loopfd, err = 0, res = -1;
unsigned long i, j;
NONFAILING(size = fs_image_segment_check(size, nsegs, segments));
int memfd = syscall(sys_memfd_create, "syz_read_part_table", 0);
if (memfd == -1) {
err = errno;
@@ -1265,9 +1275,8 @@ static long syz_read_part_table(volatile unsigned long size, volatile unsigned l
goto error_close_memfd;
}
for (i = 0; i < nsegs; i++) {
if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
debug("syz_read_part_table: pwrite[%u] failed: %d\n", (int)i, errno);
}
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
NONFAILING(pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset));
}
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
loopfd = open(loopname, O_RDWR);
@@ -1339,25 +1348,8 @@ static long syz_mount_image(volatile long fsarg, volatile long dir, volatile uns
char loopname[64], fs[32], opts[256];
int loopfd, err = 0, res = -1;
unsigned long i;
// Strictly saying we ought to do a nonfailing copyout of segments into a local var.
// But some filesystems have large number of segments (2000+),
// we can't allocate that much on stack and allocating elsewhere is problematic,
// so we just use the memory allocated by fuzzer.
struct fs_image_segment* segs = (struct fs_image_segment*)segments;

if (nsegs > IMAGE_MAX_SEGMENTS)
nsegs = IMAGE_MAX_SEGMENTS;
for (i = 0; i < nsegs; i++) {
if (segs[i].size > IMAGE_MAX_SIZE)
segs[i].size = IMAGE_MAX_SIZE;
segs[i].offset %= IMAGE_MAX_SIZE;
if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size)
segs[i].offset = IMAGE_MAX_SIZE - segs[i].size;
if (size < segs[i].offset + segs[i].offset)
size = segs[i].offset + segs[i].offset;
}
if (size > IMAGE_MAX_SIZE)
size = IMAGE_MAX_SIZE;
NONFAILING(size = fs_image_segment_check(size, nsegs, segments));
int memfd = syscall(sys_memfd_create, "syz_mount_image", 0);
if (memfd == -1) {
err = errno;
@@ -1368,9 +1360,8 @@ static long syz_mount_image(volatile long fsarg, volatile long dir, volatile uns
goto error_close_memfd;
}
for (i = 0; i < nsegs; i++) {
if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
debug("syz_mount_image: pwrite[%u] failed: %d\n", (int)i, errno);
}
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
NONFAILING(pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset));
}
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
loopfd = open(loopname, O_RDWR);
@@ -709,6 +709,8 @@ void execute_one()
call_extra_timeout = 300;
if (strncmp(syscalls[call_num].name, "syz_open_dev$hiddev", strlen("syz_open_dev$hiddev")) == 0)
call_extra_timeout = 50;
if (strncmp(syscalls[call_num].name, "syz_mount_image", strlen("syz_mount_image")) == 0)
call_extra_timeout = 50;
uint64 copyout_index = read_input(&input_pos);
uint64 num_args = read_input(&input_pos);
if (num_args > kMaxArgs)
@@ -139,8 +139,14 @@ static void cover_enable(cover_t* cov, bool collect_comps, bool extra)

static void cover_reset(cover_t* cov)
{
if (cov == 0)
// Callers in common_linux.h don't check this flag.
if (!flag_coverage)
return;
if (cov == 0) {
if (current_cover == 0)
fail("cover_reset: current_cover == 0");
cov = current_cover;
}
*(uint64*)cov->data = 0;
}

@@ -81,6 +81,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) {
"syz_usb_ep_read": 300,
"syz_usb_disconnect": 300,
"syz_open_dev$hiddev": 50,
"syz_mount_image": 100,
}
timeoutExpr := "45"
for i, call := range p.Calls {
@@ -3385,14 +3385,10 @@ struct fs_image_segment {
#elif GOARCH_ppc64le
#define sys_memfd_create 360
#endif
#endif
#if SYZ_EXECUTOR || __NR_syz_read_part_table
static long syz_read_part_table(volatile unsigned long size, volatile unsigned long nsegs, volatile long segments)
static unsigned long fs_image_segment_check(unsigned long size, unsigned long nsegs, long segments)
{
char loopname[64], linkname[64];
int loopfd, err = 0, res = -1;
unsigned long i, j;
unsigned long i;
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
if (nsegs > IMAGE_MAX_SEGMENTS)
@@ -3408,6 +3404,17 @@ static long syz_read_part_table(volatile unsigned long size, volatile unsigned l
}
if (size > IMAGE_MAX_SIZE)
size = IMAGE_MAX_SIZE;
return size;
}
#endif
#if SYZ_EXECUTOR || __NR_syz_read_part_table
static long syz_read_part_table(volatile unsigned long size, volatile unsigned long nsegs, volatile long segments)
{
char loopname[64], linkname[64];
int loopfd, err = 0, res = -1;
unsigned long i, j;
NONFAILING(size = fs_image_segment_check(size, nsegs, segments));
int memfd = syscall(sys_memfd_create, "syz_read_part_table", 0);
if (memfd == -1) {
err = errno;
@@ -3418,9 +3425,8 @@ static long syz_read_part_table(volatile unsigned long size, volatile unsigned l
goto error_close_memfd;
}
for (i = 0; i < nsegs; i++) {
if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
debug("syz_read_part_table: pwrite[%u] failed: %d\n", (int)i, errno);
}
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
NONFAILING(pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset));
}
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
loopfd = open(loopname, O_RDWR);
@@ -3484,21 +3490,8 @@ static long syz_mount_image(volatile long fsarg, volatile long dir, volatile uns
char loopname[64], fs[32], opts[256];
int loopfd, err = 0, res = -1;
unsigned long i;
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
if (nsegs > IMAGE_MAX_SEGMENTS)
nsegs = IMAGE_MAX_SEGMENTS;
for (i = 0; i < nsegs; i++) {
if (segs[i].size > IMAGE_MAX_SIZE)
segs[i].size = IMAGE_MAX_SIZE;
segs[i].offset %= IMAGE_MAX_SIZE;
if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size)
segs[i].offset = IMAGE_MAX_SIZE - segs[i].size;
if (size < segs[i].offset + segs[i].offset)
size = segs[i].offset + segs[i].offset;
}
if (size > IMAGE_MAX_SIZE)
size = IMAGE_MAX_SIZE;
NONFAILING(size = fs_image_segment_check(size, nsegs, segments));
int memfd = syscall(sys_memfd_create, "syz_mount_image", 0);
if (memfd == -1) {
err = errno;
@@ -3509,9 +3502,8 @@ static long syz_mount_image(volatile long fsarg, volatile long dir, volatile uns
goto error_close_memfd;
}
for (i = 0; i < nsegs; i++) {
if (pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset) < 0) {
debug("syz_mount_image: pwrite[%u] failed: %d\n", (int)i, errno);
}
struct fs_image_segment* segs = (struct fs_image_segment*)segments;
NONFAILING(pwrite(memfd, segs[i].data, segs[i].size, segs[i].offset));
}
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
loopfd = open(loopname, O_RDWR);
@@ -0,0 +1,2 @@
syz_mount_image$ext4(&(0x7f0000000000)='tmpfs\x00', &(0x7f0000000100)='./file0\x00', 0x0, 0x0, 0x0, 0x0, 0x0)
syz_mount_image$ext4(&(0x7f0000000000)='tmpfs\x00', &(0x7f0000000100)='./file0\x00', 0x0, 0x1, 0x0, 0x0, 0x0)

0 comments on commit cb704a2

Please sign in to comment.
You can’t perform that action at this time.