ATF_TC_BODY(setrlimit_memlock, tc) { struct rlimit res; void *buf; long page; pid_t pid; int sta; #ifdef __FreeBSD__ /* Set max_wired really really high to avoid EAGAIN */ set_vm_max_wired(INT_MAX); #endif page = sysconf(_SC_PAGESIZE); ATF_REQUIRE(page >= 0); buf = malloc(page); pid = fork(); if (buf == NULL || pid < 0) atf_tc_fail("initialization failed"); if (pid == 0) { /* * Try to lock a page while * RLIMIT_MEMLOCK is zero. */ if (mlock(buf, page) != 0) _exit(EXIT_FAILURE); if (munlock(buf, page) != 0) _exit(EXIT_FAILURE); res.rlim_cur = 0; res.rlim_max = 0; if (setrlimit(RLIMIT_MEMLOCK, &res) != 0) _exit(EXIT_FAILURE); if (mlock(buf, page) != 0) _exit(EXIT_SUCCESS); (void)munlock(buf, page); _exit(EXIT_FAILURE); } free(buf); (void)wait(&sta); if (WIFEXITED(sta) == 0 || WEXITSTATUS(sta) != EXIT_SUCCESS) atf_tc_fail("RLIMIT_MEMLOCK not enforced"); }
ATF_TC_BODY(mlock_err, tc) { #ifdef __NetBSD__ unsigned long vmin = 0; size_t len = sizeof(vmin); #endif void *invalid_ptr; int null_errno = ENOMEM; /* error expected for NULL */ #ifdef __FreeBSD__ #ifdef VM_MIN_ADDRESS if ((uintptr_t)VM_MIN_ADDRESS > 0) null_errno = EINVAL; /* NULL is not inside user VM */ #endif /* Set max_wired really really high to avoid EAGAIN */ set_vm_max_wired(INT_MAX); #else if (sysctlbyname("vm.minaddress", &vmin, &len, NULL, 0) != 0) atf_tc_fail("failed to read vm.minaddress"); if (vmin > 0) null_errno = EINVAL; /* NULL is not inside user VM */ #endif errno = 0; ATF_REQUIRE_ERRNO(null_errno, mlock(NULL, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(null_errno, mlock((char *)0, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(EINVAL, mlock((char *)-1, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(null_errno, munlock(NULL, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(null_errno, munlock((char *)0, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(EINVAL, munlock((char *)-1, page) == -1); /* * Try to create a pointer to an unmapped page - first after current * brk will likely do. */ invalid_ptr = (void*)(((uintptr_t)sbrk(0)+page) & ~(page-1)); printf("testing with (hopefully) invalid pointer %p\n", invalid_ptr); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, mlock(invalid_ptr, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock(invalid_ptr, page) == -1); }
ATF_TC_BODY(mlock_mmap, tc) { #ifdef __NetBSD__ static const int flags = MAP_ANON | MAP_PRIVATE | MAP_WIRED; #else static const int flags = MAP_ANON | MAP_PRIVATE; #endif void *buf; #ifdef __FreeBSD__ /* Set max_wired really really high to avoid EAGAIN */ set_vm_max_wired(INT_MAX); #endif /* * Make a wired RW mapping and check that mlock(2) * does not fail for the (already locked) mapping. */ buf = mmap(NULL, page, PROT_READ | PROT_WRITE, flags, -1, 0); ATF_REQUIRE(buf != MAP_FAILED); #ifdef __FreeBSD__ /* * The duplicate mlock call is added to ensure that the call works * as described above without MAP_WIRED support. */ ATF_REQUIRE(mlock(buf, page) == 0); #endif ATF_REQUIRE(mlock(buf, page) == 0); ATF_REQUIRE(munlock(buf, page) == 0); ATF_REQUIRE(munmap(buf, page) == 0); ATF_REQUIRE(munlock(buf, page) != 0); /* * But it should be impossible to mlock(2) a PROT_NONE mapping. */ buf = mmap(NULL, page, PROT_NONE, flags, -1, 0); ATF_REQUIRE(buf != MAP_FAILED); #ifdef __FreeBSD__ ATF_REQUIRE_ERRNO(ENOMEM, mlock(buf, page) != 0); #else ATF_REQUIRE(mlock(buf, page) != 0); #endif ATF_REQUIRE(munmap(buf, page) == 0); }
ATF_TC_BODY(mlock_nested, tc) { const size_t maxiter = 100; void *buf; #ifdef __FreeBSD__ /* Set max_wired really really high to avoid EAGAIN */ set_vm_max_wired(INT_MAX); #endif buf = malloc(page); ATF_REQUIRE(buf != NULL); for (size_t i = 0; i < maxiter; i++) ATF_REQUIRE(mlock(buf, page) == 0); ATF_REQUIRE(munlock(buf, page) == 0); free(buf); }
ATF_TC_BODY(mlock_err, tc) { #ifdef __NetBSD__ unsigned long vmin = 0; size_t len = sizeof(vmin); #endif #if !defined(__aarch64__) && !defined(__riscv) void *invalid_ptr; #endif int null_errno = ENOMEM; /* error expected for NULL */ void *buf; #ifdef __FreeBSD__ #ifdef VM_MIN_ADDRESS if ((uintptr_t)VM_MIN_ADDRESS > 0) null_errno = EINVAL; /* NULL is not inside user VM */ #endif /* Set max_wired really really high to avoid EAGAIN */ set_vm_max_wired(INT_MAX); #else if (sysctlbyname("vm.minaddress", &vmin, &len, NULL, 0) != 0) atf_tc_fail("failed to read vm.minaddress"); /* * Any bad address must return ENOMEM (for lock & unlock) */ errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, mlock(NULL, page) == -1); if (vmin > 0) null_errno = EINVAL; /* NULL is not inside user VM */ #endif errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, mlock((char *)0, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, mlock((char *)-1, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock(NULL, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock((char *)0, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock((char *)-1, page) == -1); buf = malloc(page); ATF_REQUIRE(buf != NULL); /* * unlocking memory that is not locked is an error... */ errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock(buf, page) == -1); /* There is no sbrk on AArch64 and RISC-V */ #if !defined(__aarch64__) && !defined(__riscv) /* * These are permitted to fail (EINVAL) but do not on NetBSD */ ATF_REQUIRE(mlock((void *)(((uintptr_t)buf) + page/3), page/5) == 0); ATF_REQUIRE(munlock((void *)(((uintptr_t)buf) + page/3), page/5) == 0); (void)free(buf); /* * Try to create a pointer to an unmapped page - first after current * brk will likely do. */ invalid_ptr = (void*)(((uintptr_t)sbrk(0)+page) & ~(page-1)); printf("testing with (hopefully) invalid pointer %p\n", invalid_ptr); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, mlock(invalid_ptr, page) == -1); errno = 0; ATF_REQUIRE_ERRNO(ENOMEM, munlock(invalid_ptr, page) == -1); #endif }