Signed-off-by: Gabriel Krisman Bertazi --- test/Makefile | 1 + test/mmap.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 test/mmap.c diff --git a/test/Makefile b/test/Makefile index 64862f34..8fe56ae4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -141,6 +141,7 @@ test_srcs := \ link_drain.c \ link-timeout.c \ linked-defer-close.c \ + mmap.c \ madvise.c \ min-timeout.c \ min-timeout-wait.c \ diff --git a/test/mmap.c b/test/mmap.c new file mode 100644 index 00000000..b0ec326b --- /dev/null +++ b/test/mmap.c @@ -0,0 +1,372 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: test mmap operation + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "liburing.h" +#include "helpers.h" + +static bool check_hugetlb() +{ + /* Cheap (to implement) check whether can mmap a 2MB hugetlb + * page. + */ + void *x = mmap(NULL, 2*1024*1024, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|MAP_HUGE_2MB, + -1, 0); + if (x == (void*)-1) { + munmap(x, 2*1024*1024); + return false; + } + munmap(x, 2*1024*1024); + return true; +} + +static unsigned char buf[BUFSIZ]; +#define MAGIC_CHAR 0xf3 + +const char *func; +int pos; +#define CATCH_FAULT(x) (func = __func__, pos=__LINE__, x) + +static void do_sigsev(int sig, siginfo_t *si, void *unused) +{ + printf("SIGSEGV on 0x%lx (%s:%d). OP_MMAP likely broken\n", + (long) si->si_addr, func, pos); + exit(T_EXIT_FAIL); +} + +int create_memfd(int size, int flags) +{ + int fd; + int remain = size; + int ret; + char path[10]; + static int i = 0; + + /* just a unique name for each call */ + snprintf(path, 10, "t%d", i++); + + if (size % sizeof(int)) { + printf("memfd bad size %d\n", size); + return -1; + } + fd = memfd_create(path, flags); + if (!fd) { + printf("memfd %d\n", fd); + return -1; + } + if (ftruncate(fd, size)) { + fprintf(stderr, "ftruncate"); + return -1; + } + while (remain > 0) { + ret = write(fd, buf, MIN(size,BUFSIZ)); + if (ret < 0) { + fprintf(stderr, "write"); + return -1; + } + remain -= ret; + } + return fd; +} + +int uring_mmap(struct io_uring *ring, int sqe_flags, + int fd, struct io_uring_mmap_desc *descs, + int nr_descs, int flags) +{ + struct io_uring_sqe *sqe = io_uring_get_sqe(ring); + struct io_uring_cqe *cqe; + int ret; + + io_uring_prep_mmap(sqe, fd, descs, nr_descs, flags); + sqe->flags |= sqe_flags; + io_uring_submit(ring); + io_uring_wait_cqe(ring, &cqe); + + ret = cqe->res; + io_uring_cqe_seen(ring, cqe); + return ret; +} + +int test_map_file(struct io_uring *ring, unsigned int ring_flags) +{ + int fd, ret; + struct io_uring_mmap_desc desc = { + .addr = NULL, + .len = BUFSIZ, + .pgoff = 0, + .prot = (PROT_READ|PROT_WRITE), + .flags = MAP_PRIVATE + }; + + fd = create_memfd(BUFSIZ, 0); + if (fd < 0) { + return T_EXIT_SKIP; + } + + ret = uring_mmap(ring, 0, fd, &desc, 1, 0); + if (ret != 1) { + fprintf(stderr, "mmap at %s:%d failed. got %d", + func, __LINE__, ret); + return T_EXIT_FAIL; + } + + if ((long long) desc.addr <= 0) { + fprintf(stderr, "bad desc.addr fail %lld\n", + (long long)desc.addr); + return T_EXIT_FAIL; + } + + if (CATCH_FAULT(memcmp(desc.addr, buf, BUFSIZ))) { + fprintf(stderr, "check_buf fail\n"); + return T_EXIT_FAIL; + } + if (munmap(desc.addr, BUFSIZ)) { + fprintf(stderr, "unmap fail\n"); + return T_EXIT_FAIL; + } + /* same, with a fixed file. */ + desc.addr = NULL; + if (io_uring_register_files(ring, &fd, 1)) { + fprintf(stderr, "register fail\n"); + return T_EXIT_FAIL; + } + + ret = uring_mmap(ring, IOSQE_FIXED_FILE, 0, &desc, 1, 0); + if (ret != 1) { + fprintf(stderr, "mmap at %s:%d failed. got %d", + func, __LINE__, ret); + return T_EXIT_FAIL; + } + + if ((long long)(desc.addr) < 0) { + fprintf(stderr, "bad desc.addr fail %lld\n", + (long long)desc.addr); + return T_EXIT_FAIL; + } + + if (CATCH_FAULT(memcmp(desc.addr, buf, BUFSIZ))) { + fprintf(stderr, "check_buf fail\n"); + return T_EXIT_FAIL; + } + return 0; +} + +int test_map_anon(struct io_uring *ring, unsigned int ring_flags) +{ + struct io_uring_mmap_desc descs[4]; + void *addrs[4]; + int ret; + + for (int i = 0 ; i < 4; i++) { + descs[i].addr = NULL; + descs[i].len = 1024; + descs[i].pgoff = 0; + descs[i].prot = (PROT_READ|PROT_WRITE); + descs[i].flags = MAP_PRIVATE; + } + + ret = uring_mmap(ring, 0, -1, descs, 4, MAP_ANONYMOUS); + if (ret != 4) { + fprintf(stderr, "mmap at %s:%d failed. got %d\n", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + for (int i = 0 ; i < 4; i++) { + + if ((long long)(descs[i].addr) < 0) { + fprintf(stderr, "bad desc.addr fail %lld\n", + (long long)descs[i].addr); + return T_EXIT_FAIL; + } + + /* Ensure it is mapped. SIGSEV on error */ + CATCH_FAULT(*((char*)descs[i].addr) = MAGIC_CHAR); + munmap((char*)descs[i].addr, 1024); + } + + /* reuse the addr from the first test to verify MAP_FIXED. We + * know the region is free since we just unmapped it. + */ + for (int i = 0 ; i < 4; i++) { + addrs[i] = descs[i].addr; + descs[i].flags |= MAP_FIXED; + } + + ret = uring_mmap(ring, 0, -1, descs, 4, MAP_ANONYMOUS); + if (ret != 4) { + fprintf(stderr, "mmap at %s:%d failed. got %d\n", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + for (int i = 0 ; i < 4; i++) { + /* Ensure it is mapped. SIGSEV on error */ + CATCH_FAULT(*((char*)descs[i].addr) = MAGIC_CHAR); + + if (descs[i].addr != addrs[i]) { + fprintf(stderr, "MAP_FIXED failed"); + return T_EXIT_FAIL; + } + } + return 0; +} + +int test_map_anon_hugetlb(struct io_uring *ring, unsigned int ring_flags) +{ + struct io_uring_mmap_desc desc = { + .addr = NULL, + .len = 1*1024*1024, + .pgoff = 0, + .prot = (PROT_READ|PROT_WRITE), + .flags = MAP_PRIVATE|MAP_HUGE_2MB, + }; + int ret; + + ret = uring_mmap(ring, 0, -1, &desc, 1, + MAP_ANONYMOUS|MAP_HUGETLB); + if (ret != 1) { + fprintf(stderr, "mmap at %s:%d failed. got %d\n", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + if ((long long)(desc.addr) < 0) { + fprintf(stderr, "bad desc.addr fail %lld\n", + (long long)desc.addr); + return T_EXIT_FAIL; + } + + /* Ensure it is mapped. SIGSEV on error */ + CATCH_FAULT(*((char*)desc.addr) = MAGIC_CHAR); + return 0; +} + + +int test_weird(struct io_uring *ring, unsigned int ring_flags) +{ + struct io_uring_mmap_desc desc; + int ret, fd = create_memfd(BUFSIZ, 0); + if (fd < 0) { + return T_EXIT_SKIP; + } + + desc.addr = NULL; + desc.len = 1024; + desc.pgoff = 0; + desc.prot = (PROT_READ|PROT_WRITE); + desc.flags = MAP_PRIVATE; + + /* This is wrong because attempt MAP_ANONYMOUS with fd. It + * fails early, because bad flag is on SQE. + */ + ret = uring_mmap(ring, 0, fd, &desc, 1, MAP_ANONYMOUS); + if (ret != -EINVAL) { + fprintf(stderr, "mmap at %s:%d failed. got %d", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + /* This is wrong because attempt MAP_ANONYMOUS with fd. It + * fails late, because bad flag is on desc. + */ + desc.flags = MAP_PRIVATE|MAP_ANONYMOUS; + ret = uring_mmap(ring, 0, fd, &desc, 1, 0); + if (ret != 1) { + fprintf(stderr, "mmap at %s:%d failed. got %d", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + if ((long long) desc.addr != -EINVAL) { + printf("mmap ANONYMOUS with fd should have failed %lx\n", + (uintptr_t)desc.addr); + } + + /* This is wrong because we can't have MAP_HUGETLB in the + * descriptor. It fails late, because bad flag is on desc. + */ + desc.flags = MAP_PRIVATE|MAP_HUGETLB; + ret = uring_mmap(ring, 0, fd, &desc, 1, 0); + if (ret != 1) { + fprintf(stderr, "mmap at %s:%d failed. got %d", + __func__, __LINE__, ret); + return T_EXIT_FAIL; + } + + if ((long long) desc.addr != -EINVAL) { + printf("mmap ANONYMOUS with fd should have failed %lx\n", + (uintptr_t)desc.addr); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct io_uring_probe *probe; + struct io_uring ring; + int ret = 0; + struct sigaction sa; + bool has_hugetlb = check_hugetlb(); + + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = do_sigsev; + if (sigaction(SIGSEGV, &sa, NULL) == -1) { + fprintf(stderr, + "failed to register signal handle. continuing."); + } + memset(buf, MAGIC_CHAR, BUFSIZ); + + probe = io_uring_get_probe(); + if (!probe) + return T_EXIT_SKIP; + if (!io_uring_opcode_supported(probe, IORING_OP_MMAP)) + return T_EXIT_SKIP; + + ret = t_create_ring(10, &ring, IORING_SETUP_SUBMIT_ALL); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", + strerror(-ret)); + return T_SETUP_SKIP; + } + + ret |= test_map_anon(&ring, 0); + if (ret) { + fprintf(stderr, "test_map_anon failed\n"); + return T_EXIT_FAIL; + } + + ret |= test_map_file(&ring, 0); + if (ret) { + fprintf(stderr, "test_map_file failed\n"); + return T_EXIT_FAIL; + } + + if (has_hugetlb) { + ret |= test_map_anon_hugetlb(&ring, 0); + if (ret) { + fprintf(stderr, "test_map_file failed\n"); + return T_EXIT_FAIL; + } + } + + ret |= test_weird(&ring, 0); + if (ret) { + fprintf(stderr, "test_map_weird failed\n"); + } + + return ret; +} -- 2.52.0