aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNecip Fazil Yildiran <necip@google.com>2020-07-29 07:36:25 +0000
committerDmitry Vyukov <dvyukov@google.com>2020-07-29 13:44:49 +0200
commitcbca8e0f043495ea2332604d8ce066891710e861 (patch)
treecf7eb1825cd4133c77d9ce8c4697190baa21b755
parenta3d497bf6ccc7141b66ada4b01a05068a09b31c3 (diff)
executor: added syz_io_uring_setup to wrap both setup and mmap
It is hard for the fuzzer to generate correct programs using mmap calls with fuzzer-provided mmap length. This wrapper ensures correct length computation.
-rw-r--r--executor/common_linux.h86
-rw-r--r--pkg/csource/generated.go74
-rw-r--r--pkg/host/syscalls_linux.go2
-rw-r--r--sys/linux/io_uring.txt5
-rw-r--r--sys/linux/test/io_uring6
5 files changed, 165 insertions, 8 deletions
diff --git a/executor/common_linux.h b/executor/common_linux.h
index 546e60b79..a7dfa2374 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -1355,7 +1355,7 @@ static long syz_emit_ethernet(volatile long a0, volatile long a1, volatile long
}
#endif
-#if SYZ_EXECUTOR || __NR_syz_io_uring_submit || __NR_syz_io_uring_complete
+#if SYZ_EXECUTOR || __NR_syz_io_uring_submit || __NR_syz_io_uring_complete || __NR_syz_io_uring_setup
#define SIZEOF_IO_URING_SQE 64
#define SIZEOF_IO_URING_CQE 16
@@ -1435,6 +1435,90 @@ static long syz_io_uring_complete(volatile long a0)
#endif
+#if SYZ_EXECUTOR || __NR_syz_io_uring_setup
+
+struct io_sqring_offsets {
+ uint32 head;
+ uint32 tail;
+ uint32 ring_mask;
+ uint32 ring_entries;
+ uint32 flags;
+ uint32 dropped;
+ uint32 array;
+ uint32 resv1;
+ uint64 resv2;
+};
+
+struct io_cqring_offsets {
+ uint32 head;
+ uint32 tail;
+ uint32 ring_mask;
+ uint32 ring_entries;
+ uint32 overflow;
+ uint32 cqes;
+ uint64 resv[2];
+};
+
+struct io_uring_params {
+ uint32 sq_entries;
+ uint32 cq_entries;
+ uint32 flags;
+ uint32 sq_thread_cpu;
+ uint32 sq_thread_idle;
+ uint32 features;
+ uint32 resv[4];
+ struct io_sqring_offsets sq_off;
+ struct io_cqring_offsets cq_off;
+};
+
+#define IORING_OFF_SQ_RING 0
+#define IORING_OFF_SQES 0x10000000ULL
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#ifndef __NR_io_uring_setup
+#ifdef __alpha__
+#define __NR_io_uring_setup 535
+#else // !__alpha__
+#define __NR_io_uring_setup 425
+#endif
+#endif // __NR_io_uring_setup
+
+// Wrapper for io_uring_setup and the subsequent mmap calls that map the ring and the sqes
+static long syz_io_uring_setup(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5)
+{
+ // syzlang: syz_io_uring_setup(entries int32[1:IORING_MAX_ENTRIES], params ptr[inout, io_uring_params], addr_ring vma, addr_sqes vma, ring_ptr ptr[out, ring_ptr], sqes_ptr ptr[out, sqes_ptr]) fd_io_uring
+ // C: syz_io_uring_setup(uint32 entries, struct io_uring_params* params, void* mmap_addr_ring, void* mmap_addr_sqes, void** ring_ptr_out, void** sqes_ptr_out) // returns uint32 fd_io_uring
+
+ // Cast to original
+ uint32 entries = (uint32)a0;
+ struct io_uring_params* setup_params = (struct io_uring_params*)a1;
+ void* vma1 = (void*)a2;
+ void* vma2 = (void*)a3;
+ void** ring_ptr_out = (void**)a4;
+ void** sqes_ptr_out = (void**)a5;
+
+ uint32 fd_io_uring = syscall(__NR_io_uring_setup, entries, setup_params);
+
+ // Compute the ring sizes
+ uint32 sq_ring_sz = setup_params->sq_off.array + setup_params->sq_entries * sizeof(uint32);
+ uint32 cq_ring_sz = setup_params->cq_off.cqes + setup_params->cq_entries * SIZEOF_IO_URING_CQE;
+
+ // Asssumed IORING_FEAT_SINGLE_MMAP, which is always the case with the current implementation
+ // The implication is that the sq_ring_ptr and the cq_ring_ptr are the same but the
+ // difference is in the offsets to access the fields of these rings.
+ uint32 ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz;
+ *ring_ptr_out = mmap(vma1, ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQ_RING);
+
+ uint32 sqes_sz = setup_params->sq_entries * SIZEOF_IO_URING_SQE;
+ *sqes_ptr_out = mmap(vma2, sqes_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQES);
+
+ return fd_io_uring;
+}
+
+#endif
+
#if SYZ_EXECUTOR || __NR_syz_io_uring_submit
static long syz_io_uring_submit(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 3c25c2570..6234a1262 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -3539,7 +3539,7 @@ static long syz_emit_ethernet(volatile long a0, volatile long a1, volatile long
}
#endif
-#if SYZ_EXECUTOR || __NR_syz_io_uring_submit || __NR_syz_io_uring_complete
+#if SYZ_EXECUTOR || __NR_syz_io_uring_submit || __NR_syz_io_uring_complete || __NR_syz_io_uring_setup
#define SIZEOF_IO_URING_SQE 64
#define SIZEOF_IO_URING_CQE 16
@@ -3586,6 +3586,78 @@ static long syz_io_uring_complete(volatile long a0)
#endif
+#if SYZ_EXECUTOR || __NR_syz_io_uring_setup
+
+struct io_sqring_offsets {
+ uint32 head;
+ uint32 tail;
+ uint32 ring_mask;
+ uint32 ring_entries;
+ uint32 flags;
+ uint32 dropped;
+ uint32 array;
+ uint32 resv1;
+ uint64 resv2;
+};
+
+struct io_cqring_offsets {
+ uint32 head;
+ uint32 tail;
+ uint32 ring_mask;
+ uint32 ring_entries;
+ uint32 overflow;
+ uint32 cqes;
+ uint64 resv[2];
+};
+
+struct io_uring_params {
+ uint32 sq_entries;
+ uint32 cq_entries;
+ uint32 flags;
+ uint32 sq_thread_cpu;
+ uint32 sq_thread_idle;
+ uint32 features;
+ uint32 resv[4];
+ struct io_sqring_offsets sq_off;
+ struct io_cqring_offsets cq_off;
+};
+
+#define IORING_OFF_SQ_RING 0
+#define IORING_OFF_SQES 0x10000000ULL
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+#ifndef __NR_io_uring_setup
+#ifdef __alpha__
+#define __NR_io_uring_setup 535
+#else
+#define __NR_io_uring_setup 425
+#endif
+#endif
+static long syz_io_uring_setup(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5)
+{
+ uint32 entries = (uint32)a0;
+ struct io_uring_params* setup_params = (struct io_uring_params*)a1;
+ void* vma1 = (void*)a2;
+ void* vma2 = (void*)a3;
+ void** ring_ptr_out = (void**)a4;
+ void** sqes_ptr_out = (void**)a5;
+
+ uint32 fd_io_uring = syscall(__NR_io_uring_setup, entries, setup_params);
+ uint32 sq_ring_sz = setup_params->sq_off.array + setup_params->sq_entries * sizeof(uint32);
+ uint32 cq_ring_sz = setup_params->cq_off.cqes + setup_params->cq_entries * SIZEOF_IO_URING_CQE;
+ uint32 ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz;
+ *ring_ptr_out = mmap(vma1, ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQ_RING);
+
+ uint32 sqes_sz = setup_params->sq_entries * SIZEOF_IO_URING_SQE;
+ *sqes_ptr_out = mmap(vma2, sqes_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQES);
+
+ return fd_io_uring;
+}
+
+#endif
+
#if SYZ_EXECUTOR || __NR_syz_io_uring_submit
static long syz_io_uring_submit(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go
index 8e9702ee6..715deada4 100644
--- a/pkg/host/syscalls_linux.go
+++ b/pkg/host/syscalls_linux.go
@@ -226,7 +226,7 @@ func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (b
return onlySandboxNone(sandbox)
case "syz_execute_func":
return true, ""
- case "syz_io_uring_submit", "syz_io_uring_complete", "syz_memcpy_off":
+ case "syz_io_uring_submit", "syz_io_uring_complete", "syz_io_uring_setup", "syz_memcpy_off":
// syz_memcpy_off is only used for io_uring descriptions, thus, enable it
// only if io_uring syscalls are enabled.
ioUringSyscallName := "io_uring_setup"
diff --git a/sys/linux/io_uring.txt b/sys/linux/io_uring.txt
index a2d384319..f4de4a0a9 100644
--- a/sys/linux/io_uring.txt
+++ b/sys/linux/io_uring.txt
@@ -16,6 +16,11 @@ resource ioring_personality_id[int16]
define IORING_MAX_ENTRIES 32768
define IORING_MAX_CQ_ENTRIES (2 * IORING_MAX_ENTRIES)
+# First does the setup calling io_uring_setup, than calls mmap to map the ring and
+# the sqes. It is hard for the fuzzer to generate correct programs using mmap calls
+# with fuzzer-provided mmap length. This wrapper ensures correct length computation.
+syz_io_uring_setup(entries int32[1:IORING_MAX_ENTRIES], params ptr[inout, io_uring_params], addr_ring vma, addr_sqes vma, ring_ptr ptr[out, ring_ptr], sqes_ptr ptr[out, sqes_ptr]) fd_io_uring
+
io_uring_setup(entries int32[1:IORING_MAX_ENTRIES], params ptr[inout, io_uring_params]) fd_io_uring
io_uring_enter(fd fd_io_uring, to_submit int32[0:IORING_MAX_ENTRIES], min_complete int32[0:IORING_MAX_CQ_ENTRIES], flags flags[io_uring_enter_flags], sigmask ptr[in, sigset_t], size len[sigmask])
io_uring_register$IORING_REGISTER_BUFFERS(fd fd_io_uring, opcode const[IORING_REGISTER_BUFFERS], arg ptr[in, array[iovec_out]], nr_args len[arg])
diff --git a/sys/linux/test/io_uring b/sys/linux/test/io_uring
index 3e28259d1..1d93c33e6 100644
--- a/sys/linux/test/io_uring
+++ b/sys/linux/test/io_uring
@@ -1,9 +1,5 @@
# Create an io_uring instance
-r0 = io_uring_setup(0x1, &AUTO={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0x0, 0x0, 0x0], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]})
-
-# mmap the ring and the sqes
-r1 = mmap$IORING_OFF_SQ_RING(&(0x7f00000a0000)=nil, 0x184, 0x3, 0x8001, r0, AUTO)
-r2 = mmap$IORING_OFF_SQES(&(0x7f00000b0000)=nil, 0x40, 0x3, 0x8001, r0, AUTO)
+r0 = syz_io_uring_setup(0x1, &AUTO={0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, [0x0, 0x0, 0x0], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, &(0x7f00000a0000)=nil, &(0x7f00000b0000)=nil, &AUTO=<r1=>0x0, &AUTO=<r2=>0x0)
# Set IORING_CQ_EVENTFD_DISABLED. Has no side-effect for the test,
# only tests syz_memcpy_off().