diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b33e6098..d585831ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,15 @@ jobs: cxx_pkg: g++-x86-64-linux-gnu cc: x86_64-linux-gnu-gcc cxx: x86_64-linux-gnu-g++ + sanitize: 0 + + # x86-64 gcc asan + - arch: x86_64 + cc_pkg: gcc-x86-64-linux-gnu + cxx_pkg: g++-x86-64-linux-gnu + cc: x86_64-linux-gnu-gcc + cxx: x86_64-linux-gnu-g++ + sanitize: 1 # x86-64 clang - arch: x86_64 @@ -28,6 +37,7 @@ jobs: cxx: clang++ liburing_extra_flags: -Wshorten-64-to-32 extra_flags: -Wmissing-prototypes -Wstrict-prototypes -Wunreachable-code-loop-increment -Wunreachable-code -Wmissing-variable-declarations -Wextra-semi-stmt + sanitize: 0 # x86 (32-bit) gcc - arch: i686 @@ -35,6 +45,7 @@ jobs: cxx_pkg: g++-i686-linux-gnu cc: i686-linux-gnu-gcc cxx: i686-linux-gnu-g++ + sanitize: 0 # aarch64 gcc - arch: aarch64 @@ -42,6 +53,7 @@ jobs: cxx_pkg: g++-aarch64-linux-gnu cc: aarch64-linux-gnu-gcc cxx: aarch64-linux-gnu-g++ + sanitize: 0 # arm (32-bit) gcc - arch: arm @@ -49,6 +61,7 @@ jobs: cxx_pkg: g++-arm-linux-gnueabi cc: arm-linux-gnueabi-gcc cxx: arm-linux-gnueabi-g++ + sanitize: 0 # riscv64 - arch: riscv64 @@ -56,6 +69,7 @@ jobs: cxx_pkg: g++-riscv64-linux-gnu cc: riscv64-linux-gnu-gcc cxx: riscv64-linux-gnu-g++ + sanitize: 0 # powerpc64 - arch: powerpc64 @@ -63,6 +77,7 @@ jobs: cxx_pkg: g++-powerpc64-linux-gnu cc: powerpc64-linux-gnu-gcc cxx: powerpc64-linux-gnu-g++ + sanitize: 0 # powerpc - arch: powerpc @@ -70,6 +85,7 @@ jobs: cxx_pkg: g++-powerpc-linux-gnu cc: powerpc-linux-gnu-gcc cxx: powerpc-linux-gnu-g++ + sanitize: 0 # alpha - arch: alpha @@ -77,6 +93,7 @@ jobs: cxx_pkg: g++-alpha-linux-gnu cc: alpha-linux-gnu-gcc cxx: alpha-linux-gnu-g++ + sanitize: 0 # mips64 - arch: mips64 @@ -84,6 +101,7 @@ jobs: cxx_pkg: g++-mips64-linux-gnuabi64 cc: mips64-linux-gnuabi64-gcc cxx: mips64-linux-gnuabi64-g++ + sanitize: 0 # mips - arch: mips @@ -91,6 +109,7 @@ jobs: cxx_pkg: g++-mips-linux-gnu cc: mips-linux-gnu-gcc cxx: mips-linux-gnu-g++ + sanitize: 0 # hppa - arch: hppa @@ -98,9 +117,11 @@ jobs: cxx_pkg: g++-hppa-linux-gnu cc: hppa-linux-gnu-gcc cxx: hppa-linux-gnu-g++ + sanitize: 0 env: FLAGS: -g -O3 -Wall -Wextra -Werror -Wno-sign-compare ${{matrix.extra_flags}} + SANITIZE: ${{matrix.sanitize}} # Flags for building sources in src/ dir only. LIBURING_CFLAGS: ${{matrix.liburing_extra_flags}} @@ -128,10 +149,17 @@ jobs: ${{matrix.cxx}} --version; - name: Build + if: ${{matrix.sanitizer == '0'}} run: | ./configure --cc=${{matrix.cc}} --cxx=${{matrix.cxx}}; make -j$(nproc) V=1 CPPFLAGS="-Werror" CFLAGS="$FLAGS" CXXFLAGS="$FLAGS"; + - name: Build + if: ${{matrix.sanitizer == '1'}} + run: | + ./configure --cc=${{matrix.cc}} --cxx=${{matrix.cxx}} --enable-sanitizer; + make -j$(nproc) V=1 CPPFLAGS="-Werror" CFLAGS="$FLAGS" CXXFLAGS="$FLAGS"; + - name: Test install command run: | sudo make install; diff --git a/CHANGELOG b/CHANGELOG index 6565c8c0a..ee41c190b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ liburing-2.8 release fixes. - Man page updates - Don't leak _GNU_SOURCE via pkb-config --cflags +- Support for address sanitizer liburing-2.7 release diff --git a/configure b/configure index c7ed5b060..e2221d35d 100755 --- a/configure +++ b/configure @@ -28,6 +28,8 @@ for opt do ;; --use-libc) use_libc=yes ;; + --enable-sanitizer) use_sanitizer=yes + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -76,6 +78,7 @@ Options: [defaults in brackets after descriptions] --cc=CMD use CMD as the C compiler --cxx=CMD use CMD as the C++ compiler --use-libc use libc for liburing (useful for hardening) + --enable-sanitizer compile liburing with the address and undefined behaviour sanitizers. (useful for debugging) EOF exit 0 fi @@ -533,6 +536,12 @@ fi if test "$ublk_header" = "yes"; then output_sym "CONFIG_HAVE_UBLK_HEADER" fi +if test "$use_sanitizer" = "yes"; then + output_sym "CONFIG_USE_SANITIZER" + print_config "use sanitizer" "yes" +else + print_config "use sanitizer" "no" +fi echo "CC=$cc" >> $config_host_mak print_config "CC" "$cc" diff --git a/examples/Makefile b/examples/Makefile index 7c27d8fbd..a78297482 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -13,6 +13,12 @@ endif LDFLAGS ?= override LDFLAGS += -L../src/ -luring -lpthread +ifeq ($(CONFIG_USE_SANITIZER),y) + override CFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls + override CPPFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls + override LDFLAGS += -fsanitize=address,undefined +endif + example_srcs := \ io_uring-close-test.c \ io_uring-cp.c \ diff --git a/src/Makefile b/src/Makefile index 13675c7c1..30e4ffc46 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,8 +16,6 @@ override CFLAGS += -Wno-unused-parameter \ $(LIBURING_CFLAGS) SO_CFLAGS=-fPIC $(CFLAGS) L_CFLAGS=$(CFLAGS) -LINK_FLAGS=-Wl,-z,defs -LINK_FLAGS+=$(LDFLAGS) ENABLE_SHARED ?= 1 soname=liburing.so.$(VERSION_MAJOR) @@ -39,6 +37,13 @@ ifneq ($(MAKECMDGOALS),clean) include ../config-host.mak endif +ifeq ($(CONFIG_USE_SANITIZER),y) + LINK_FLAGS= +else + LINK_FLAGS=-Wl,-z,defs +endif +LINK_FLAGS+=$(LDFLAGS) + all: $(all_targets) liburing_srcs := setup.c queue.c register.c syscall.c version.c @@ -50,6 +55,13 @@ ifeq ($(CONFIG_NOLIBC),y) override LINK_FLAGS += -nostdlib -nodefaultlibs $(libgcc_link_flag) endif +ifeq ($(CONFIG_USE_SANITIZER),y) + override CFLAGS += -fsanitize=address,undefined -g -fno-omit-frame-pointer -fno-optimize-sibling-calls + override CPPFLAGS += -fsanitize=address,undefined -g -fno-omit-frame-pointer -fno-optimize-sibling-calls + override LINK_FLAGS += -fsanitize=address,undefined + liburing_srcs += sanitize.c +endif + override CPPFLAGS += -MT "$@" -MMD -MP -MF "$@.d" liburing_objs := $(patsubst %.c,%.ol,$(liburing_srcs)) liburing_sobjs := $(patsubst %.c,%.os,$(liburing_srcs)) @@ -89,6 +101,7 @@ install: $(all_targets) install -D -m 644 include/liburing.h $(includedir)/liburing.h install -D -m 644 include/liburing/compat.h $(includedir)/liburing/compat.h install -D -m 644 include/liburing/barrier.h $(includedir)/liburing/barrier.h + install -D -m 644 include/liburing/sanitize.h $(includedir)/liburing/sanitize.h install -D -m 644 include/liburing/io_uring_version.h $(includedir)/liburing/io_uring_version.h install -D -m 644 liburing.a $(libdevdir)/liburing.a install -D -m 644 liburing-ffi.a $(libdevdir)/liburing-ffi.a @@ -106,6 +119,7 @@ uninstall: @rm -f $(includedir)/liburing.h @rm -f $(includedir)/liburing/compat.h @rm -f $(includedir)/liburing/barrier.h + @rm -f $(includedir)/liburing/sanitize.h @rm -f $(includedir)/liburing/io_uring_version.h @rm -f $(libdevdir)/liburing.a @rm -f $(libdevdir)/liburing-ffi.a diff --git a/src/include/liburing.h b/src/include/liburing.h index 0dc445da7..767bf296a 100644 --- a/src/include/liburing.h +++ b/src/include/liburing.h @@ -18,6 +18,8 @@ #include "liburing/io_uring.h" #include "liburing/io_uring_version.h" #include "liburing/barrier.h" +#include "liburing/sanitize.h" + #ifndef uring_unlikely #define uring_unlikely(cond) __builtin_expect(!!(cond), 0) @@ -1511,6 +1513,8 @@ IOURINGINLINE void io_uring_buf_ring_add(struct io_uring_buf_ring *br, { struct io_uring_buf *buf = &br->bufs[(br->tail + buf_offset) & mask]; + liburing_sanitize_region(addr, len); + buf->addr = (unsigned long) (uintptr_t) addr; buf->len = len; buf->bid = bid; diff --git a/src/include/liburing/sanitize.h b/src/include/liburing/sanitize.h new file mode 100644 index 000000000..7b598eb13 --- /dev/null +++ b/src/include/liburing/sanitize.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef LIBURING_SANITIZE_H +#define LIBURING_SANITIZE_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct io_uring; +struct iovec; + +#if defined(CONFIG_USE_SANITIZER) +void liburing_sanitize_ring(struct io_uring *ring); +void liburing_sanitize_address(const void *addr); +void liburing_sanitize_region(const void *addr, unsigned int len); +void liburing_sanitize_iovecs(const struct iovec *iovecs, unsigned nr); +#else +#define __maybe_unused __attribute__((__unused__)) +static inline void liburing_sanitize_ring(struct io_uring __maybe_unused *ring) {} +static inline void liburing_sanitize_address(const void __maybe_unused *addr) {} +static inline void liburing_sanitize_region(const void __maybe_unused *addr, unsigned int __maybe_unused len) {} +static inline void liburing_sanitize_iovecs(const struct iovec __maybe_unused *iovecs, unsigned __maybe_unused nr) {} +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/queue.c b/src/queue.c index 9bc3cd6a4..10c98821c 100644 --- a/src/queue.c +++ b/src/queue.c @@ -5,6 +5,7 @@ #include "syscall.h" #include "liburing.h" #include "int_flags.h" +#include "liburing/sanitize.h" #include "liburing/compat.h" #include "liburing/io_uring.h" @@ -405,6 +406,8 @@ static int __io_uring_submit(struct io_uring *ring, unsigned submitted, unsigned flags; int ret; + liburing_sanitize_ring(ring); + flags = 0; if (sq_ring_needs_enter(ring, submitted, &flags) || cq_needs_enter) { if (cq_needs_enter) diff --git a/src/register.c b/src/register.c index b370f077c..f1edb0223 100644 --- a/src/register.c +++ b/src/register.c @@ -13,6 +13,8 @@ static inline int do_register(struct io_uring *ring, unsigned int opcode, { int fd; + liburing_sanitize_address(arg); + if (ring->int_flags & INT_FLAG_REG_REG_RING) { opcode |= IORING_REGISTER_USE_REGISTERED_RING; fd = ring->enter_ring_fd; @@ -28,6 +30,8 @@ int io_uring_register_buffers_update_tag(struct io_uring *ring, unsigned off, const __u64 *tags, unsigned nr) { + liburing_sanitize_iovecs(iovecs, nr); + struct io_uring_rsrc_update2 up = { .offset = off, .data = (unsigned long)iovecs, @@ -43,6 +47,8 @@ int io_uring_register_buffers_tags(struct io_uring *ring, const __u64 *tags, unsigned nr) { + liburing_sanitize_iovecs(iovecs, nr); + struct io_uring_rsrc_register reg = { .nr = nr, .data = (unsigned long)iovecs, @@ -65,6 +71,8 @@ int io_uring_register_buffers_sparse(struct io_uring *ring, unsigned nr) int io_uring_register_buffers(struct io_uring *ring, const struct iovec *iovecs, unsigned nr_iovecs) { + liburing_sanitize_iovecs(iovecs, nr_iovecs); + return do_register(ring, IORING_REGISTER_BUFFERS, iovecs, nr_iovecs); } @@ -77,6 +85,9 @@ int io_uring_register_files_update_tag(struct io_uring *ring, unsigned off, const int *files, const __u64 *tags, unsigned nr_files) { + liburing_sanitize_address(files); + liburing_sanitize_address(tags); + struct io_uring_rsrc_update2 up = { .offset = off, .data = (unsigned long)files, @@ -97,6 +108,8 @@ int io_uring_register_files_update_tag(struct io_uring *ring, unsigned off, int io_uring_register_files_update(struct io_uring *ring, unsigned off, const int *files, unsigned nr_files) { + liburing_sanitize_address(files); + struct io_uring_files_update up = { .offset = off, .fds = (unsigned long) files, @@ -148,6 +161,9 @@ int io_uring_register_files_sparse(struct io_uring *ring, unsigned nr) int io_uring_register_files_tags(struct io_uring *ring, const int *files, const __u64 *tags, unsigned nr) { + liburing_sanitize_address(files); + liburing_sanitize_address(tags); + struct io_uring_rsrc_register reg = { .nr = nr, .data = (unsigned long)files, @@ -175,6 +191,8 @@ int io_uring_register_files(struct io_uring *ring, const int *files, { int ret, did_increase = 0; + liburing_sanitize_address(files); + do { ret = do_register(ring, IORING_REGISTER_FILES, files, nr_files); if (ret >= 0) @@ -329,6 +347,8 @@ int io_uring_unregister_buf_ring(struct io_uring *ring, int bgid) int io_uring_buf_ring_head(struct io_uring *ring, int buf_group, uint16_t *head) { + liburing_sanitize_address(head); + struct io_uring_buf_status buf_status = { .buf_group = buf_group, }; diff --git a/src/sanitize.c b/src/sanitize.c new file mode 100644 index 000000000..4d7bdd2aa --- /dev/null +++ b/src/sanitize.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: MIT */ + +#include "liburing/sanitize.h" + +#if defined(CONFIG_USE_SANITIZER) + +#include +#include +#include "liburing.h" + +static inline void sanitize_sqe_addr(struct io_uring_sqe *sqe) +{ + if (__asan_address_is_poisoned((void *) (unsigned long) sqe->addr) != 0) { + __asan_describe_address((void *) (unsigned long) sqe->addr); + exit(1); + } +} +static inline void sanitize_sqe_optval(struct io_uring_sqe *sqe) +{ + if (__asan_region_is_poisoned((void *) (unsigned long) sqe->optval, sqe->optlen) != 0) { + __asan_describe_address((void *) (unsigned long) sqe->optval); + exit(1); + } +} +static inline void sanitize_sqe_addr2(struct io_uring_sqe *sqe) +{ + if (__asan_address_is_poisoned((void *) (unsigned long) sqe->addr2) != 0) { + __asan_describe_address((void *) (unsigned long) sqe->addr2); + exit(1); + } +} +static inline void sanitize_sqe_addr3(struct io_uring_sqe *sqe) +{ + if (__asan_address_is_poisoned((void *) (unsigned long) sqe->addr3) != 0) { + __asan_describe_address((void *) (unsigned long) sqe->addr3); + exit(1); + } +} +static inline void sanitize_sqe_addr_and_add2(struct io_uring_sqe *sqe) +{ + sanitize_sqe_addr(sqe); + sanitize_sqe_addr2(sqe); +} +static inline void sanitize_sqe_addr_and_add3(struct io_uring_sqe *sqe) +{ + sanitize_sqe_addr(sqe); + sanitize_sqe_addr3(sqe); +} +static inline void sanitize_sqe_nop(struct io_uring_sqe *sqe) +{ +} + +typedef void (*sanitize_sqe_handler)(struct io_uring_sqe *sqe); +sanitize_sqe_handler sanitize_handlers[IORING_OP_LAST]; +bool sanitize_handlers_initialized = false; + +static inline void initialize_sanitize_handlers() +{ + if (sanitize_handlers_initialized) + return; + + sanitize_handlers[IORING_OP_NOP] = sanitize_sqe_nop; + sanitize_handlers[IORING_OP_READV] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_WRITEV] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FSYNC] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_READ_FIXED] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_WRITE_FIXED] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_POLL_ADD] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_POLL_REMOVE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SYNC_FILE_RANGE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SENDMSG] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_RECVMSG] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_TIMEOUT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_TIMEOUT_REMOVE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_ACCEPT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_ASYNC_CANCEL] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_LINK_TIMEOUT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_CONNECT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FALLOCATE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_OPENAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_CLOSE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FILES_UPDATE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_STATX] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_READ] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_WRITE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FADVISE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_MADVISE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SEND] = sanitize_sqe_addr_and_add2; + sanitize_handlers[IORING_OP_RECV] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_OPENAT2] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_EPOLL_CTL] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SPLICE] = sanitize_sqe_nop; + sanitize_handlers[IORING_OP_PROVIDE_BUFFERS] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_REMOVE_BUFFERS] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_TEE] = sanitize_sqe_nop; + sanitize_handlers[IORING_OP_SHUTDOWN] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_RENAMEAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_UNLINKAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_MKDIRAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SYMLINKAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_LINKAT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_MSG_RING] = sanitize_sqe_addr_and_add3; + sanitize_handlers[IORING_OP_FSETXATTR] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SETXATTR] = sanitize_sqe_addr_and_add3; + sanitize_handlers[IORING_OP_FGETXATTR] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_GETXATTR] = sanitize_sqe_addr_and_add3; + sanitize_handlers[IORING_OP_SOCKET] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_URING_CMD] = sanitize_sqe_optval; + sanitize_handlers[IORING_OP_SEND_ZC] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_SENDMSG_ZC] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_READ_MULTISHOT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_WAITID] = sanitize_sqe_addr_and_add2; + sanitize_handlers[IORING_OP_FUTEX_WAIT] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FUTEX_WAKE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FUTEX_WAITV] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FIXED_FD_INSTALL] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_FTRUNCATE] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_BIND] = sanitize_sqe_addr; + sanitize_handlers[IORING_OP_LISTEN] = sanitize_sqe_addr; + sanitize_handlers_initialized = true; +} + +void liburing_sanitize_ring(struct io_uring *ring) +{ + struct io_uring_sq *sq = &ring->sq; + struct io_uring_sqe *sqe; + unsigned int head; + int shift = 0; + + initialize_sanitize_handlers(); + + if (ring->flags & IORING_SETUP_SQE128) + shift = 1; + if (!(ring->flags & IORING_SETUP_SQPOLL)) + head = *sq->khead; + else + head = io_uring_smp_load_acquire(sq->khead); + + while (head != sq->sqe_tail) { + sqe = &sq->sqes[(head & sq->ring_mask) << shift]; + if (sqe->opcode < IORING_OP_LAST) + sanitize_handlers[sqe->opcode](sqe); + head++; + } +} + +void liburing_sanitize_address(const void *addr) +{ + if (__asan_address_is_poisoned(addr) != 0) { + __asan_describe_address((void *)addr); + exit(1); + } +} + +void liburing_sanitize_region(const void *addr, unsigned int len) +{ + if (__asan_region_is_poisoned((void *)addr, len) != 0) { + __asan_describe_address((void *)addr); + exit(1); + } +} + +void liburing_sanitize_iovecs(const struct iovec *iovecs, unsigned nr) +{ + unsigned i; + + if (__asan_address_is_poisoned((void *)iovecs) != 0) { + __asan_describe_address((void *)iovecs); + exit(1); + } + + for (i = 0; i < nr; i++) { + if (__asan_region_is_poisoned((void *)iovecs[i].iov_base, iovecs[i].iov_len) != 0) { + __asan_describe_address((void *)iovecs[i].iov_base); + exit(1); + } + } +} +#else +void liburing_sanitize_ring(struct io_uring *ring) +{ +} + +void liburing_sanitize_address(const void *addr) +{ +} + +void liburing_sanitize_region(const void *addr, unsigned int len) +{ +} + +void liburing_sanitize_iovecs(const struct iovec *iovecs, unsigned nr) +{ +} +#endif diff --git a/src/setup.c b/src/setup.c index 1997d2536..761d6244c 100644 --- a/src/setup.c +++ b/src/setup.c @@ -433,7 +433,7 @@ __cold void io_uring_queue_exit(struct io_uring *ring) struct io_uring_cq *cq = &ring->cq; size_t sqe_size; - if (!sq->ring_sz) { + if (!sq->ring_sz && !(ring->int_flags & INT_FLAG_APP_MEM)) { sqe_size = sizeof(struct io_uring_sqe); if (ring->flags & IORING_SETUP_SQE128) sqe_size += 64; diff --git a/test/35fa71a030ca.c b/test/35fa71a030ca.c index 0b1af2c63..7914f5957 100644 --- a/test/35fa71a030ca.c +++ b/test/35fa71a030ca.c @@ -28,6 +28,7 @@ #include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER #if !defined(SYS_futex) && defined(SYS_futex_time64) # define SYS_futex SYS_futex_time64 #endif @@ -327,3 +328,9 @@ int main(int argc, char *argv[]) loop(); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/500f9fbadef8.c b/test/500f9fbadef8.c index dad230781..69dd9d0ac 100644 --- a/test/500f9fbadef8.c +++ b/test/500f9fbadef8.c @@ -78,10 +78,12 @@ int main(int argc, char *argv[]) close(fd); unlink(buf); + free(iov.iov_base); return T_EXIT_PASS; err: close(fd); unlink(buf); + free(iov.iov_base); return T_EXIT_FAIL; skipped: fprintf(stderr, "Polling not supported in current dir, test skipped\n"); diff --git a/test/917257daa0fe.c b/test/917257daa0fe.c index aecdb7bcf..f4fcd7abc 100644 --- a/test/917257daa0fe.c +++ b/test/917257daa0fe.c @@ -14,6 +14,7 @@ #include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER int main(int argc, char *argv[]) { if (argc > 1) @@ -52,3 +53,9 @@ int main(int argc, char *argv[]) __sys_io_uring_setup(0x7a6, (struct io_uring_params *) 0x20000000UL); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/Makefile b/test/Makefile index b1bc3966b..140345963 100644 --- a/test/Makefile +++ b/test/Makefile @@ -27,6 +27,10 @@ ifdef CONFIG_HAVE_ARRAY_BOUNDS XCFLAGS += -Warray-bounds=0 endif +ifeq ($(CONFIG_USE_SANITIZER),y) + XCFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls +endif + CXXFLAGS ?= $(CFLAGS) override CFLAGS += $(XCFLAGS) -DLIBURING_BUILD_TEST override CXXFLAGS += $(XCFLAGS) -std=c++11 -DLIBURING_BUILD_TEST @@ -227,6 +231,12 @@ test_srcs := \ xattr.c \ # EOL +# Please keep this list sorted alphabetically. +asan_test_srcs := \ + xfail_prep_link_timeout_out_of_scope.c \ + xfail_register_buffers_out_of_scope.c \ + # EOL + all_targets := include ../Makefile.quiet @@ -250,7 +260,15 @@ test_targets := $(patsubst %,%.t,$(test_targets)) all_targets += $(test_targets) helpers = helpers.o -all: $(test_targets) +ifeq ($(CONFIG_USE_SANITIZER),y) + asan_test_targets := $(patsubst %.c,%,$(asan_test_srcs)) + asan_test_targets := $(patsubst %.cc,%,$(asan_test_targets)) + asan_run_test_targets := $(patsubst %,%.run_test,$(asan_test_targets)) + asan_test_targets := $(patsubst %,%.t,$(asan_test_targets)) + all_targets += $(asan_test_targets) +endif + +all: $(test_targets) $(asan_test_targets) helpers.o: helpers.c $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< @@ -287,10 +305,10 @@ clean: @rm -rf output/ runtests: all - @./runtests.sh $(test_targets) + @./runtests.sh $(test_targets) $(asan_test_targets) runtests-loop: all - @./runtests-loop.sh $(test_targets) + @./runtests-loop.sh $(test_targets) $(asan_test_targets) %.run_test: %.t @./runtests-quiet.sh $< diff --git a/test/a0908ae19763.c b/test/a0908ae19763.c index ffb7ace44..2df550163 100644 --- a/test/a0908ae19763.c +++ b/test/a0908ae19763.c @@ -14,6 +14,7 @@ #include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER static uint64_t r[1] = {0xffffffffffffffff}; int main(int argc, char *argv[]) @@ -57,3 +58,9 @@ int main(int argc, char *argv[]) __sys_io_uring_register(r[0], 2, (const void *) 0x20000280, 1); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/a4c0b3decb33.c b/test/a4c0b3decb33.c index 20a447cec..60cbf749d 100644 --- a/test/a4c0b3decb33.c +++ b/test/a4c0b3decb33.c @@ -24,6 +24,7 @@ #include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER static void sleep_ms(uint64_t ms) { usleep(ms * 1000); @@ -179,3 +180,9 @@ int main(int argc, char *argv[]) loop(); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/accept.c b/test/accept.c index a92dd8c67..04d1e1bf1 100644 --- a/test/accept.c +++ b/test/accept.c @@ -60,7 +60,7 @@ static void close_sock_fds(int s_fd[], int c_fd[], int nr, bool fixed) close_fds(c_fd, nr); } -static void queue_send(struct io_uring *ring, int fd) +static void *queue_send(struct io_uring *ring, int fd) { struct io_uring_sqe *sqe; struct data *d; @@ -72,9 +72,11 @@ static void queue_send(struct io_uring *ring, int fd) sqe = io_uring_get_sqe(ring); io_uring_prep_writev(sqe, fd, &d->iov, 1, 0); sqe->user_data = 1; + + return d; } -static void queue_recv(struct io_uring *ring, int fd, bool fixed) +static void *queue_recv(struct io_uring *ring, int fd, bool fixed) { struct io_uring_sqe *sqe; struct data *d; @@ -88,6 +90,8 @@ static void queue_recv(struct io_uring *ring, int fd, bool fixed) sqe->user_data = 2; if (fixed) sqe->flags |= IOSQE_FIXED_FILE; + + return d; } static void queue_accept_multishot(struct io_uring *ring, int fd, @@ -274,6 +278,8 @@ static int test_loop(struct io_uring *ring, int nr_fds = multishot ? MAX_FDS : 1; int multishot_idx = multishot ? INITIAL_USER_DATA : 0; int err_ret = T_EXIT_FAIL; + void* send_d = 0; + void* recv_d = 0; if (args.overflow) cause_overflow(ring); @@ -340,8 +346,8 @@ static int test_loop(struct io_uring *ring, goto out; } - queue_send(ring, c_fd[0]); - queue_recv(ring, s_fd[0], fixed); + send_d = queue_send(ring, c_fd[0]); + recv_d = queue_recv(ring, s_fd[0], fixed); ret = io_uring_submit_and_wait(ring, 2); assert(ret != -1); @@ -365,9 +371,13 @@ static int test_loop(struct io_uring *ring, } out: + free(send_d); + free(recv_d); close_sock_fds(s_fd, c_fd, nr_fds, fixed); return T_EXIT_PASS; err: + free(send_d); + free(recv_d); close_sock_fds(s_fd, c_fd, nr_fds, fixed); return err_ret; } diff --git a/test/b19062a56726.c b/test/b19062a56726.c index 8bd384ace..dba563cb3 100644 --- a/test/b19062a56726.c +++ b/test/b19062a56726.c @@ -14,6 +14,7 @@ #include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER int main(int argc, char *argv[]) { if (argc > 1) @@ -52,3 +53,9 @@ int main(int argc, char *argv[]) __sys_io_uring_setup(0xc9f, (struct io_uring_params *) 0x20000200); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/buf-ring-nommap.c b/test/buf-ring-nommap.c index 32a372c8e..a4396e8d1 100644 --- a/test/buf-ring-nommap.c +++ b/test/buf-ring-nommap.c @@ -46,8 +46,10 @@ int main(int argc, char *argv[]) p.flags = IORING_SETUP_NO_MMAP; ret = io_uring_queue_init_mem(1, &ring, &p, ring_mem, 16384); if (ret < 0) { - if (ret == -EINVAL || ret == -ENOMEM) + if (ret == -EINVAL || ret == -ENOMEM) { + free(ring_mem); return T_EXIT_SKIP; + } fprintf(stderr, "queue init failed %d\n", ret); return T_EXIT_FAIL; } @@ -62,8 +64,10 @@ int main(int argc, char *argv[]) ret = io_uring_register_buf_ring(&ring, ®, 0); if (ret) { - if (ret == -EINVAL) + if (ret == -EINVAL) { + free(ring_mem); return T_EXIT_SKIP; + } fprintf(stderr, "reg buf ring: %d\n", ret); return T_EXIT_FAIL; } @@ -73,8 +77,10 @@ int main(int argc, char *argv[]) br = mmap(NULL, ring_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, ring.ring_fd, off); if (br == MAP_FAILED) { - if (errno == ENOMEM) + if (errno == ENOMEM) { + free(ring_mem); return T_EXIT_SKIP; + } perror("mmap"); return T_EXIT_FAIL; } @@ -119,5 +125,6 @@ int main(int argc, char *argv[]) io_uring_cqe_seen(&ring, cqe); io_uring_queue_exit(&ring); + free(ring_mem); return T_EXIT_PASS; } diff --git a/test/buf-ring.c b/test/buf-ring.c index f81aa622e..924b5df5b 100644 --- a/test/buf-ring.c +++ b/test/buf-ring.c @@ -57,6 +57,7 @@ static int test_mixed_reg2(int bgid) io_uring_free_buf_ring(&ring, br, 32, bgid); io_uring_queue_exit(&ring); + free(bufs); return 0; } @@ -99,6 +100,7 @@ static int test_mixed_reg(int bgid) } io_uring_queue_exit(&ring); + free(bufs); return 0; } diff --git a/test/coredump.c b/test/coredump.c index 17b6ab9fd..9abc7a309 100644 --- a/test/coredump.c +++ b/test/coredump.c @@ -16,6 +16,7 @@ #include "liburing.h" #include "helpers.h" +#ifndef CONFIG_USE_SANITIZER static void test(void) { struct io_uring_sqe *sqe; @@ -58,3 +59,9 @@ int main(int argc, char *argv[]) unlink("core"); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/cq-overflow.c b/test/cq-overflow.c index 4297d578f..c2a6848e9 100644 --- a/test/cq-overflow.c +++ b/test/cq-overflow.c @@ -88,8 +88,10 @@ static int test_io(const char *file, unsigned long usecs, unsigned *drops, goto err; } offset = BS * (rand() % BUFFERS); - if (fault && i == ENTRIES + 4) + if (fault && i == ENTRIES + 4) { + free(vecs[i].iov_base); vecs[i].iov_base = NULL; + } io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset); ret = io_uring_submit(&ring); @@ -523,8 +525,18 @@ int main(int argc, char *argv[]) } unlink(fname); + if(vecs != NULL) { + for (i = 0; i < BUFFERS; i++) + free(vecs[i].iov_base); + } + free(vecs); return T_EXIT_PASS; err: unlink(fname); + if(vecs != NULL) { + for (i = 0; i < BUFFERS; i++) + free(vecs[i].iov_base); + } + free(vecs); return T_EXIT_FAIL; } diff --git a/test/d4ae271dfaae.c b/test/d4ae271dfaae.c index 6c0db2da4..9621cf803 100644 --- a/test/d4ae271dfaae.c +++ b/test/d4ae271dfaae.c @@ -22,7 +22,7 @@ int main(int argc, char *argv[]) int i, fd, ret, __e; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; - struct iovec *iovecs; + struct iovec *iovecs = NULL; struct io_uring_params p; char *fname; void *buf; @@ -95,5 +95,10 @@ int main(int argc, char *argv[]) close(fd); out: io_uring_queue_exit(&ring); + if (iovecs != NULL) { // + for (i = 0; i < 10; i++) + free(iovecs[i].iov_base); + free(iovecs); + } return ret; } diff --git a/test/defer-tw-timeout.c b/test/defer-tw-timeout.c index cd455505e..27742c41e 100644 --- a/test/defer-tw-timeout.c +++ b/test/defer-tw-timeout.c @@ -128,6 +128,7 @@ static int test_file(struct io_uring *ring, char *__fname) if (ret != 1) { fprintf(stderr, "unexpected wait ret %d\n", ret); close(fd); + free(buf); return T_EXIT_FAIL; } @@ -141,10 +142,12 @@ static int test_file(struct io_uring *ring, char *__fname) if (i != 1) { fprintf(stderr, "Got %d request, expected 1\n", i); close(fd); + free(buf); return T_EXIT_FAIL; } close(fd); + free(buf); return T_EXIT_PASS; } diff --git a/test/double-poll-crash.c b/test/double-poll-crash.c index 8d312de28..6ddf4317a 100644 --- a/test/double-poll-crash.c +++ b/test/double-poll-crash.c @@ -114,7 +114,7 @@ static uint64_t r[4] = {0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff}; int main(int argc, char *argv[]) { void *mmap_ret; -#if !defined(__i386) && !defined(__x86_64__) +#if (!defined(__i386) && !defined(__x86_64__)) || defined(CONFIG_USE_SANITIZER) return T_EXIT_SKIP; #endif diff --git a/test/eeed8b54e0df.c b/test/eeed8b54e0df.c index d97796da5..58fd9d93b 100644 --- a/test/eeed8b54e0df.c +++ b/test/eeed8b54e0df.c @@ -113,8 +113,10 @@ int main(int argc, char *argv[]) } close(fd); + free(iov.iov_base); return ret; err: close(fd); + free(iov.iov_base); return T_EXIT_FAIL; } diff --git a/test/exit-no-cleanup.c b/test/exit-no-cleanup.c index aadef8c2b..9389af885 100644 --- a/test/exit-no-cleanup.c +++ b/test/exit-no-cleanup.c @@ -19,6 +19,11 @@ #include "liburing.h" #include "helpers.h" +// on fast enough machines with enough cores, the first few threads will post +// enough sem's to cause the main thread to exit while some threads are half way +// initialization. This causes a null deference somewhere in thread cleanup, +// which trips ASAN. +#ifndef CONFIG_USE_SANITIZER #define IORING_ENTRIES 8 static pthread_t *threads; @@ -115,3 +120,9 @@ int main(int argc, char *argv[]) // Exit without resource cleanup exit(EXIT_SUCCESS); } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/fadvise.c b/test/fadvise.c index b5feb3fc6..7a1b61104 100644 --- a/test/fadvise.c +++ b/test/fadvise.c @@ -68,7 +68,7 @@ static long do_read(int fd, char *buf) perror("lseek"); return -1; } - + gettimeofday(&tv, NULL); ret = read(fd, buf, FILE_SIZE); t = utime_since_now(&tv); @@ -126,9 +126,12 @@ static int test_fadvise(struct io_uring *ring, const char *filename) return 1; if (cached_read < uncached_read && - cached_read2 < uncached_read) + cached_read2 < uncached_read) { + free(buf); return 0; + } + free(buf); return 2; } diff --git a/test/fdinfo.c b/test/fdinfo.c index 4ec765535..b4ce028bf 100644 --- a/test/fdinfo.c +++ b/test/fdinfo.c @@ -292,6 +292,7 @@ static int has_nonvec_read(void) if (ret == -EINVAL) { out: io_uring_queue_exit(&ring); + free(p); return 0; } else if (ret) { fprintf(stderr, "register_probe: %d\n", ret); @@ -303,6 +304,7 @@ static int has_nonvec_read(void) if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED)) goto out; io_uring_queue_exit(&ring); + free(p); return 1; } diff --git a/test/file-register.c b/test/file-register.c index 34d4287e5..a08cf9c60 100644 --- a/test/file-register.c +++ b/test/file-register.c @@ -120,16 +120,21 @@ static int test_grow(struct io_uring *ring) fds = open_files(1, 0, off); ret = io_uring_register_files_update(ring, off, fds, 1); if (ret != 1) { - if (off == 300 && ret == -EINVAL) + if (off == 300 && ret == -EINVAL) { + free(fds); break; + } fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret); + free(fds); break; } if (off >= 300) { fprintf(stderr, "%s: Succeeded beyond end-of-list?\n", __FUNCTION__); + free(fds); goto err; } off++; + free(fds); } while (1); ret = io_uring_unregister_files(ring); @@ -363,8 +368,10 @@ static int test_basic(struct io_uring *ring, int fail) ret = io_uring_register_files(ring, files, 100); if (ret) { if (fail) { - if (ret == -EBADF || ret == -EFAULT) + if (ret == -EBADF || ret == -EFAULT) { + close_files(files, nr_files, 0); return 0; + } } fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret); goto err; @@ -631,6 +638,7 @@ static int test_sparse_updates(void) ret = io_uring_register_files(&ring, fds, 256); if (ret) { fprintf(stderr, "file_register: %d\n", ret); + free(fds); return ret; } @@ -639,6 +647,7 @@ static int test_sparse_updates(void) ret = io_uring_register_files_update(&ring, i, &newfd, 1); if (ret != 1) { fprintf(stderr, "file_update: %d\n", ret); + free(fds); return ret; } } @@ -650,6 +659,7 @@ static int test_sparse_updates(void) ret = io_uring_register_files(&ring, fds, 256); if (ret) { fprintf(stderr, "file_register: %d\n", ret); + free(fds); return ret; } @@ -658,12 +668,14 @@ static int test_sparse_updates(void) ret = io_uring_register_files_update(&ring, i, &newfd, 1); if (ret != 1) { fprintf(stderr, "file_update: %d\n", ret); + free(fds); return ret; } } io_uring_unregister_files(&ring); io_uring_queue_exit(&ring); + free(fds); return 0; } diff --git a/test/fixed-buf-iter.c b/test/fixed-buf-iter.c index 4a20d364f..fdd945966 100644 --- a/test/fixed-buf-iter.c +++ b/test/fixed-buf-iter.c @@ -63,7 +63,7 @@ static int test(struct io_uring *ring) return 1; } - if (cqe->res < 0) { + if (cqe->res < 0) { fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res)); return 1; } @@ -87,6 +87,8 @@ static int test(struct io_uring *ring) return 1; } io_uring_cqe_seen(ring, cqe); + for (i = 0; i < BUFFERS; i++) + free(iov[i].iov_base); return 0; } diff --git a/test/fsnotify.c b/test/fsnotify.c index 4fbccf9ac..27ca0197d 100644 --- a/test/fsnotify.c +++ b/test/fsnotify.c @@ -90,6 +90,7 @@ int main(int argc, char *argv[]) wait(&wstat); if (!WEXITSTATUS(wstat)) err = T_EXIT_PASS; + free(buf); } else { struct fanotify_event_metadata m; int fret; diff --git a/test/futex.c b/test/futex.c index baaaaa5ce..2054f07be 100644 --- a/test/futex.c +++ b/test/futex.c @@ -120,6 +120,7 @@ static int __test(struct io_uring *ring, int vectored, int async, if (cqe->res == -EINVAL || cqe->res == -EOPNOTSUPP) { no_futex = 1; + free(futex); return 0; } io_uring_cqe_seen(ring, cqe); @@ -134,6 +135,7 @@ static int __test(struct io_uring *ring, int vectored, int async, for (i = 0; i < nfutex; i++) pthread_join(threads[i], &tret); + free(futex); return 0; } @@ -145,7 +147,7 @@ static int test(int flags, int vectored) ret = io_uring_queue_init(8, &ring, flags); if (ret) return ret; - + for (i = 0; i < LOOPS; i++) { int async_cancel = (!i % 2); int async_wait = !(i % 3); @@ -242,6 +244,7 @@ static int test_order(int vectored, int async) } io_uring_queue_exit(&ring); + free(futex); return 0; } @@ -322,6 +325,7 @@ static int test_multi_wake(int vectored) } io_uring_queue_exit(&ring); + free(futex); return 0; } @@ -378,6 +382,7 @@ static int test_wake_zero(void) } io_uring_queue_exit(&ring); + free(futex); return 0; } @@ -459,6 +464,7 @@ static int test_invalid(void) io_uring_cqe_seen(&ring, cqe); io_uring_queue_exit(&ring); + free(futex); return 0; } diff --git a/test/init-mem.c b/test/init-mem.c index 8a0ee250a..f6ef64c5f 100644 --- a/test/init-mem.c +++ b/test/init-mem.c @@ -68,6 +68,7 @@ static int setup_ctx(struct ctx *ctx, struct q_entries *q) static void clean_ctx(struct ctx *ctx) { io_uring_queue_exit(&ctx->ring); + free(ctx->mem); } static int check_red(struct ctx *ctx, unsigned long i) @@ -94,10 +95,12 @@ static int test(struct q_entries *q) int j, ret, batch; ret = setup_ctx(&ctx, q); - if (ret == T_EXIT_SKIP) + if (ret == T_EXIT_SKIP) { + clean_ctx(&ctx); return T_EXIT_SKIP; - else if (ret != T_EXIT_PASS) + } else if (ret != T_EXIT_PASS) { return ret; + } batch = 64; if (batch > q->sqes) diff --git a/test/iopoll-leak.c b/test/iopoll-leak.c index 8dec15e30..e0faf53c0 100644 --- a/test/iopoll-leak.c +++ b/test/iopoll-leak.c @@ -41,6 +41,8 @@ static int do_iopoll(const char *fname) io_uring_submit(&ring); close(fd); + free(iov->iov_base); + free(iov); return T_EXIT_PASS; } diff --git a/test/kallsyms.c b/test/kallsyms.c index 3f8856876..e528766ed 100644 --- a/test/kallsyms.c +++ b/test/kallsyms.c @@ -148,6 +148,7 @@ static int has_nonvec_read(void) if (ret == -EINVAL) { out: io_uring_queue_exit(&ring); + free(p); return 0; } else if (ret) { fprintf(stderr, "register_probe: %d\n", ret); @@ -159,6 +160,7 @@ static int has_nonvec_read(void) if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED)) goto out; io_uring_queue_exit(&ring); + free(p); return 1; } diff --git a/test/madvise.c b/test/madvise.c index fc2deeceb..dd461518f 100644 --- a/test/madvise.c +++ b/test/madvise.c @@ -121,9 +121,12 @@ static int test_madvise(struct io_uring *ring, const char *filename) return 1; if (cached_read < uncached_read && - cached_read2 < uncached_read) + cached_read2 < uncached_read) { + free(buf); return 0; + } + free(buf); return 2; } diff --git a/test/no-mmap-inval.c b/test/no-mmap-inval.c index 5d5e7fc6a..22f322ac0 100644 --- a/test/no-mmap-inval.c +++ b/test/no-mmap-inval.c @@ -32,11 +32,13 @@ int main(int argc, char *argv[]) ret = io_uring_queue_init_params(2, &ring, &p); if (ret == -EINVAL) { /* kernel doesn't support SETUP_NO_MMAP */ + free(addr); return T_EXIT_SKIP; } else if (ret && (ret != -EFAULT && ret != -ENOMEM)) { fprintf(stderr, "Got %d, wanted -EFAULT\n", ret); return T_EXIT_FAIL; } + free(addr); return T_EXIT_PASS; } diff --git a/test/read-mshot-empty.c b/test/read-mshot-empty.c index 04d723dfe..81e0cfb0f 100644 --- a/test/read-mshot-empty.c +++ b/test/read-mshot-empty.c @@ -1,153 +1,156 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Description: test that multishot read correctly keeps reading until all - * data has been emptied. the original implementation failed - * to do so, if the available buffer size was less than what - * was available, hence requiring multiple reads to empty the - * file buffer. - */ -#include -#include -#include -#include -#include -#include -#include - -#include "liburing.h" -#include "helpers.h" - -#define BGID 17 -#define NR_BUFS 4 -#define BR_MASK (NR_BUFS - 1) -#define BUF_SIZE 32 - -static int do_write(int fd, void *buf, int buf_size) -{ - int ret; - - ret = write(fd, buf, buf_size); - if (ret < 0) { - perror("write"); - return 0; - } else if (ret != buf_size) { - fprintf(stderr, "bad write size %d\n", ret); - return 0; - } - - return 1; -} - -static void *thread_fn(void *data) -{ - char w1[BUF_SIZE], w2[BUF_SIZE]; - int *fds = data; - - memset(w1, 0x11, BUF_SIZE); - memset(w2, 0x22, BUF_SIZE); - - if (!do_write(fds[1], w1, BUF_SIZE)) - return NULL; - if (!do_write(fds[1], w2, BUF_SIZE)) - return NULL; - - usleep(100000); - - if (!do_write(fds[1], w1, BUF_SIZE)) - return NULL; - if (!do_write(fds[1], w2, BUF_SIZE)) - return NULL; - - return NULL; -} - -int main(int argc, char *argv[]) -{ - struct io_uring_buf_ring *br; - struct io_uring_sqe *sqe; - struct io_uring_cqe *cqe; - struct io_uring ring; - pthread_t thread; - int i, ret, fds[2]; - void *buf, *tret; - - if (argc > 1) - return T_EXIT_SKIP; - - if (pipe(fds) < 0) { - perror("pipe"); - return T_EXIT_FAIL; - } - - ret = io_uring_queue_init(8, &ring, 0); - if (ret) { - fprintf(stderr, "queue_init: %d\n", ret); - return T_EXIT_FAIL; - } - - br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret); - if (!br) { - if (ret == -EINVAL) - return T_EXIT_SKIP; - fprintf(stderr, "failed buffer ring %d\n", ret); - return T_EXIT_FAIL; - } - - buf = malloc(NR_BUFS * BUF_SIZE); - for (i = 0; i < NR_BUFS; i++) { - void *this_buf = buf + i * BUF_SIZE; - - io_uring_buf_ring_add(br, this_buf, BUF_SIZE, i, BR_MASK, i); - } - io_uring_buf_ring_advance(br, NR_BUFS); - - sqe = io_uring_get_sqe(&ring); - io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BGID); - - ret = io_uring_submit(&ring); - if (ret != 1) { - fprintf(stderr, "bad submit %d\n", ret); - return T_EXIT_FAIL; - } - - /* - * read multishot not available would be ready as a cqe when - * submission returns, check and skip if not. - */ - ret = io_uring_peek_cqe(&ring, &cqe); - if (!ret) { - if (cqe->res == -EINVAL || cqe->res == -EBADF) - return T_EXIT_SKIP; - } - - pthread_create(&thread, NULL, thread_fn, fds); - - for (i = 0; i < 4; i++) { - int buf_index; - - ret = io_uring_wait_cqe(&ring, &cqe); - if (ret) { - fprintf(stderr, "wait %d\n", ret); - break; - } - - if (cqe->res != BUF_SIZE) { - fprintf(stderr, "size %d\n", cqe->res); - return T_EXIT_FAIL; - } - if (!(cqe->flags & IORING_CQE_F_BUFFER)) { - fprintf(stderr, "buffer not set\n"); - return T_EXIT_FAIL; - } - if (!(cqe->flags & IORING_CQE_F_MORE)) { - fprintf(stderr, "more not set\n"); - return T_EXIT_FAIL; - } - buf_index = cqe->flags >> 16; - assert(buf_index >= 0 && buf_index <= NR_BUFS); - io_uring_cqe_seen(&ring, cqe); - } - - pthread_join(thread, &tret); - return T_EXIT_PASS; -} +/* SPDX-License-Identifier: MIT */ +/* + * Description: test that multishot read correctly keeps reading until all + * data has been emptied. the original implementation failed + * to do so, if the available buffer size was less than what + * was available, hence requiring multiple reads to empty the + * file buffer. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "liburing.h" +#include "helpers.h" + +#define BGID 17 +#define NR_BUFS 4 +#define BR_MASK (NR_BUFS - 1) +#define BUF_SIZE 32 + +static int do_write(int fd, void *buf, int buf_size) +{ + int ret; + + ret = write(fd, buf, buf_size); + if (ret < 0) { + perror("write"); + return 0; + } else if (ret != buf_size) { + fprintf(stderr, "bad write size %d\n", ret); + return 0; + } + + return 1; +} + +static void *thread_fn(void *data) +{ + char w1[BUF_SIZE], w2[BUF_SIZE]; + int *fds = data; + + memset(w1, 0x11, BUF_SIZE); + memset(w2, 0x22, BUF_SIZE); + + if (!do_write(fds[1], w1, BUF_SIZE)) + return NULL; + if (!do_write(fds[1], w2, BUF_SIZE)) + return NULL; + + usleep(100000); + + if (!do_write(fds[1], w1, BUF_SIZE)) + return NULL; + if (!do_write(fds[1], w2, BUF_SIZE)) + return NULL; + + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct io_uring_buf_ring *br; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + pthread_t thread; + int i, ret, fds[2]; + void *buf, *tret; + + if (argc > 1) + return T_EXIT_SKIP; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "queue_init: %d\n", ret); + return T_EXIT_FAIL; + } + + br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret); + if (!br) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "failed buffer ring %d\n", ret); + return T_EXIT_FAIL; + } + + buf = malloc(NR_BUFS * BUF_SIZE); + for (i = 0; i < NR_BUFS; i++) { + void *this_buf = buf + i * BUF_SIZE; + + io_uring_buf_ring_add(br, this_buf, BUF_SIZE, i, BR_MASK, i); + } + io_uring_buf_ring_advance(br, NR_BUFS); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BGID); + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "bad submit %d\n", ret); + return T_EXIT_FAIL; + } + + /* + * read multishot not available would be ready as a cqe when + * submission returns, check and skip if not. + */ + ret = io_uring_peek_cqe(&ring, &cqe); + if (!ret) { + if (cqe->res == -EINVAL || cqe->res == -EBADF) { + free(buf); + return T_EXIT_SKIP; + } + } + + pthread_create(&thread, NULL, thread_fn, fds); + + for (i = 0; i < 4; i++) { + int buf_index; + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait %d\n", ret); + break; + } + + if (cqe->res != BUF_SIZE) { + fprintf(stderr, "size %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_BUFFER)) { + fprintf(stderr, "buffer not set\n"); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_MORE)) { + fprintf(stderr, "more not set\n"); + return T_EXIT_FAIL; + } + buf_index = cqe->flags >> 16; + assert(buf_index >= 0 && buf_index <= NR_BUFS); + io_uring_cqe_seen(&ring, cqe); + } + + pthread_join(thread, &tret); + free(buf); + return T_EXIT_PASS; +} diff --git a/test/read-write.c b/test/read-write.c index 27b0cc730..639852439 100644 --- a/test/read-write.c +++ b/test/read-write.c @@ -340,6 +340,7 @@ static int has_nonvec_read(void) if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED)) goto out; io_uring_queue_exit(&ring); + free(p); return 1; } @@ -974,6 +975,12 @@ int main(int argc, char *argv[]) goto err; } + if(vecs != NULL) { + for (i = 0; i < BUFFERS; i++) + free(vecs[i].iov_base); + } + free(vecs); + srand((unsigned)time(NULL)); if (create_nonaligned_buffers()) { fprintf(stderr, "file creation failed\n"); diff --git a/test/recv-msgall-stream.c b/test/recv-msgall-stream.c index 65b4d2268..ff9fd2a2a 100644 --- a/test/recv-msgall-stream.c +++ b/test/recv-msgall-stream.c @@ -317,6 +317,7 @@ static int do_send(struct recv_data *rd) if (cqe->res == -EINVAL) { fprintf(stdout, "send not supported, skipping\n"); close(sockfd); + free(buf); return 0; } if (cqe->res != iov.iov_len) { @@ -328,10 +329,12 @@ static int do_send(struct recv_data *rd) shutdown(sockfd, SHUT_RDWR); close(sockfd); + free(buf); return 0; err: shutdown(sockfd, SHUT_RDWR); close(sockfd); + free(buf); return 1; } diff --git a/test/recv-msgall.c b/test/recv-msgall.c index d1fcdb0d5..0461c158c 100644 --- a/test/recv-msgall.c +++ b/test/recv-msgall.c @@ -171,12 +171,14 @@ static int do_send(void) sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket"); + free(buf); return 1; } ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { perror("connect"); + free(buf); return 1; } @@ -201,6 +203,7 @@ static int do_send(void) if (cqe->res == -EINVAL) { fprintf(stdout, "send not supported, skipping\n"); close(sockfd); + free(buf); return 0; } if (cqe->res != iov.iov_len) { @@ -211,9 +214,11 @@ static int do_send(void) } close(sockfd); + free(buf); return 0; err: close(sockfd); + free(buf); return 1; } diff --git a/test/recvsend_bundle.c b/test/recvsend_bundle.c index 46308d656..afb774c46 100644 --- a/test/recvsend_bundle.c +++ b/test/recvsend_bundle.c @@ -114,6 +114,9 @@ static int recv_prep(struct io_uring *ring, struct recv_data *rd, int *sock) pthread_barrier_wait(&rd->connect); + if (rd->abort) + goto err; + socklen = sizeof(saddr); use_fd = accept(sockfd, (struct sockaddr *)&saddr, &socklen); if (use_fd < 0) { @@ -325,6 +328,7 @@ static void *recv_fn(void *data) close(rd->accept_fd); io_uring_queue_exit(&ring); err: + free(buf); return (void *)(intptr_t)ret; } @@ -592,8 +596,13 @@ static int test(int backlog, unsigned int max_sends, int *to_eagain, } ret = do_send(&rd); - if (no_send_mshot) + if (no_send_mshot) { + fprintf(stderr, "no_send_mshot, aborting (ignore other errors)\n"); + rd.abort = 1; + pthread_barrier_wait(&rd.connect); + pthread_join(recv_thread, &retval); return 0; + } if (ret) return ret; diff --git a/test/regbuf-merge.c b/test/regbuf-merge.c index d813d5915..9e5aa3d81 100644 --- a/test/regbuf-merge.c +++ b/test/regbuf-merge.c @@ -13,6 +13,7 @@ #include "helpers.h" +#ifndef CONFIG_USE_SANITIZER #ifndef __NR_io_uring_register #define __NR_io_uring_register 427 #endif @@ -89,3 +90,9 @@ int main(int argc, char *argv[]) syscall(__NR_io_uring_register, r[0], 0ul, 0x20002840ul, 2ul); return T_EXIT_PASS; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/ringbuf-read.c b/test/ringbuf-read.c index f9dffc4cc..cfcc3d8d0 100644 --- a/test/ringbuf-read.c +++ b/test/ringbuf-read.c @@ -125,6 +125,7 @@ static int test(const char *filename, int dio, int async) if (verify_buffer(buf + ((bid - 1) * BUF_SIZE), ud)) return 1; } + free(buf); return 0; } diff --git a/test/ringbuf-status.c b/test/ringbuf-status.c index d3ae3c5e0..0794bfba1 100644 --- a/test/ringbuf-status.c +++ b/test/ringbuf-status.c @@ -109,6 +109,7 @@ static int test(int invalid) if (!br) { if (ret == -EINVAL) { no_buf_ring = 1; + free(buf); return 0; } fprintf(stderr, "Buffer ring register failed %d\n", ret); @@ -131,10 +132,13 @@ static int test(int invalid) if (ret) { if (ret == -EINVAL) { no_buf_ring_status = 1; + free(buf); return T_EXIT_SKIP; } - if (invalid && ret == -ENOENT) + if (invalid && ret == -ENOENT) { + free(buf); return T_EXIT_PASS; + } fprintf(stderr, "buf_ring_head: %d\n", ret); return T_EXIT_FAIL; } diff --git a/test/runtests.sh b/test/runtests.sh index 924fdce82..3654d586f 100755 --- a/test/runtests.sh +++ b/test/runtests.sh @@ -114,9 +114,12 @@ run_test() elif [ "$status" -eq 77 ]; then echo "Skipped" SKIPPED+=("<$test_string>") - elif [ "$status" -ne 0 ]; then + elif [[ $test_string != xfail* ]] && [ "$status" -ne 0 ]; then echo "Test $test_name failed with ret $status" FAILED+=("<$test_string>") + elif [[ $test_string == xfail* ]] && [ "$status" -ne 1 ]; then + echo "Test $test_name expected fail status 1 but returned $status" + FAILED+=("<$test_string>") elif ! _check_dmesg "$dmesg_marker" "$test_name"; then echo "Test $test_name failed dmesg check" FAILED+=("<$test_string>") diff --git a/test/short-read.c b/test/short-read.c index a6f262049..a14abfd7a 100644 --- a/test/short-read.c +++ b/test/short-read.c @@ -71,5 +71,6 @@ int main(int argc, char *argv[]) } io_uring_cqe_seen(&ring, cqe); + free(vec.iov_base); return 0; } diff --git a/test/splice.c b/test/splice.c index 5e9b78926..a883587f1 100644 --- a/test/splice.c +++ b/test/splice.c @@ -191,7 +191,7 @@ static int do_splice(struct io_uring *ring, IORING_OP_SPLICE); } -static int do_tee(struct io_uring *ring, int fd_in, int fd_out, +static int do_tee(struct io_uring *ring, int fd_in, int fd_out, unsigned int len) { return do_splice_op(ring, fd_in, 0, fd_out, 0, len, IORING_OP_TEE); @@ -504,6 +504,8 @@ int main(int argc, char *argv[]) splice_flags = SPLICE_F_FD_IN_FIXED; sqe_flags = IOSQE_FIXED_FILE; ret = test_splice(&ring, &ctx); + free(ctx.buf_in); + free(ctx.buf_out); if (ret) { fprintf(stderr, "registered fds splice tests failed\n"); return ret; diff --git a/test/sqpoll-disable-exit.c b/test/sqpoll-disable-exit.c index e8df2f53a..bc4c2fbc7 100644 --- a/test/sqpoll-disable-exit.c +++ b/test/sqpoll-disable-exit.c @@ -22,8 +22,10 @@ #include #include "liburing.h" +#include "helpers.h" #include "../src/syscall.h" +#ifndef CONFIG_USE_SANITIZER static void sleep_ms(uint64_t ms) { usleep(ms * 1000); @@ -194,3 +196,9 @@ int main(void) loop(); return 0; } +#else +int main(int argc, char *argv[]) +{ + return T_EXIT_SKIP; +} +#endif diff --git a/test/stdout.c b/test/stdout.c index ade100a12..a699447e8 100644 --- a/test/stdout.c +++ b/test/stdout.c @@ -89,6 +89,7 @@ static int test_pipe_io_fixed(struct io_uring *ring) io_uring_cqe_seen(ring, cqe); } io_uring_unregister_buffers(ring); + free(vecs[0].iov_base); return 0; err: return 1; @@ -143,6 +144,7 @@ static int test_stdout_io_fixed(struct io_uring *ring) } io_uring_cqe_seen(ring, cqe); io_uring_unregister_buffers(ring); + free(vecs.iov_base); return 0; err: return 1; diff --git a/test/uring_cmd_ublk.c b/test/uring_cmd_ublk.c index 7daf6aef0..28174027b 100644 --- a/test/uring_cmd_ublk.c +++ b/test/uring_cmd_ublk.c @@ -354,8 +354,10 @@ static struct ublk_dev *ublk_ctrl_init(void) int ret; dev->ctrl_fd = open(CTRL_DEV, O_RDWR); - if (dev->ctrl_fd < 0) + if (dev->ctrl_fd < 0) { + free(dev); return NULL; + } info->max_io_buf_bytes = UBLK_IO_MAX_BYTES; diff --git a/test/xfail_prep_link_timeout_out_of_scope.c b/test/xfail_prep_link_timeout_out_of_scope.c new file mode 100644 index 000000000..0de7948db --- /dev/null +++ b/test/xfail_prep_link_timeout_out_of_scope.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: Check to see if the asan checks catch an stack-use-after-free for prep_link_timeout + */ + +#include +#include +#include +#include +#include +#include "liburing.h" +#include "helpers.h" + +#include + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + struct io_uring_sqe *sqe; + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret < 0) { + printf("io_uring_queue_init ret %i\n", ret); + return T_EXIT_PASS; // this test expects an inverted exit code + } + + // force timespec to go out of scope, test "passes" if asan catches this bug. + { + struct __kernel_timespec timespec; + timespec.tv_sec = 0; + timespec.tv_nsec = 5000; + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_timeout(sqe, ×pec, 0, 0); + io_uring_sqe_set_data(sqe, (void *) 1); + } + + ret = io_uring_submit_and_wait(&ring, 1); + printf("submit_and_wait %i\n", ret); + + return T_EXIT_PASS; // this test expects an inverted exit code +} diff --git a/test/xfail_register_buffers_out_of_scope.c b/test/xfail_register_buffers_out_of_scope.c new file mode 100644 index 000000000..cbb4ca351 --- /dev/null +++ b/test/xfail_register_buffers_out_of_scope.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: Check to see if the asan checks catch an stack-use-after-free for io_uring_sqe_set_data + */ + +#include +#include +#include +#include +#include +#include +#include "liburing.h" +#include "helpers.h" + +#include + +#define BUFFERS 8 +#define BUFFER_SIZE 128 + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + struct iovec *iovs; + int i; + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret < 0) { + printf("io_uring_queue_init ret %i\n", ret); + return T_EXIT_PASS; // this test expects an inverted exit code + } + + iovs = calloc(BUFFERS, sizeof(struct iovec)); + for (i = 0; i < BUFFERS; i++) { + iovs[i].iov_base = malloc(BUFFER_SIZE); + iovs[i].iov_len = BUFFER_SIZE; + } + // force one iov_base to be freed, test "passes" if asan catches this bug. + free(iovs[4].iov_base); + + ret = io_uring_register_buffers(&ring, iovs, BUFFERS); + printf("io_uring_register_buffers %i\n", ret); + + ret = io_uring_submit_and_wait(&ring, 1); + printf("submit_and_wait %i\n", ret); + + return T_EXIT_PASS; // this test expects an inverted exit code +}