diff options
author | Andrew Kaster <akaster@serenityos.org> | 2021-07-06 20:21:14 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-07-11 19:42:00 +0200 |
commit | 4cc75501d754b1b78cd21cf53291d3feac832df6 (patch) | |
tree | 99288611462f566ef426363587c9b144760ca61b /Tests/Kernel | |
parent | 578d45b480c5b167df916ebc42e13bc1bd6153c1 (diff) | |
download | serenity-4cc75501d754b1b78cd21cf53291d3feac832df6.zip |
Utilities+Tests: Convert test_efault to be LibTest based
This test exposed a kernel panic in is_user_range calculations, so let's
convert it to be a LibTest test so we can prevent regressions in mmap,
the page allocator, and the memory manager.
Diffstat (limited to 'Tests/Kernel')
-rw-r--r-- | Tests/Kernel/TestEFault.cpp | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/Tests/Kernel/TestEFault.cpp b/Tests/Kernel/TestEFault.cpp new file mode 100644 index 0000000000..831f843cc1 --- /dev/null +++ b/Tests/Kernel/TestEFault.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Andrew Kaster <akaster@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/Assertions.h> +#include <AK/Format.h> +#include <AK/Types.h> +#include <LibTest/TestCase.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/mman.h> +#include <unistd.h> + +#define EXPECT_OK(syscall, address, size) \ + do { \ + rc = syscall(fd, (void*)(address), (size_t)(size)); \ + EXPECT(rc >= 0); \ + if (rc < 0) { \ + warnln("Expected success: " #syscall "({:p}, {}), got rc={}, errno={}", (void*)(address), (size_t)(size), rc, errno); \ + } \ + } while (0) + +#define EXPECT_EFAULT(syscall, address, size) \ + do { \ + rc = syscall(fd, (void*)(address), (size_t)(size)); \ + EXPECT(rc < 0); \ + EXPECT_EQ(errno, EFAULT); \ + if (rc >= 0 || errno != EFAULT) { \ + warnln("Expected EFAULT: " #syscall "({:p}, {}), got rc={}, errno={}", (void*)(address), (size_t)(size), rc, errno); \ + } \ + } while (0) + +#define EXPECT_EFAULT_NO_FD(syscall, address, size) \ + do { \ + rc = syscall((address), (size_t)(size)); \ + EXPECT(rc < 0); \ + EXPECT_EQ(errno, EFAULT); \ + if (rc >= 0 || errno != EFAULT) { \ + warnln("Expected EFAULT: " #syscall "({:p}, {}), got rc={}, errno={}", (void*)(address), (size_t)(size), rc, errno); \ + } \ + } while (0) + +TEST_CASE(test_efault) +{ + int fd = open("/dev/zero", O_RDONLY); + int rc = -1; + + // Test a one-page mapping (4KB) + u8* one_page = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + VERIFY(one_page); + EXPECT_OK(read, one_page, 4096); + EXPECT_EFAULT(read, one_page, 4097); + EXPECT_EFAULT(read, one_page - 1, 4096); + + // Test a two-page mapping (8KB) + u8* two_page = (u8*)mmap(nullptr, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); + VERIFY(two_page); + + EXPECT_OK(read, two_page, 4096); + EXPECT_OK(read, two_page + 4096, 4096); + EXPECT_OK(read, two_page, 8192); + EXPECT_OK(read, two_page + 4095, 4097); + EXPECT_OK(read, two_page + 1, 8191); + EXPECT_EFAULT(read, two_page, 8193); + EXPECT_EFAULT(read, two_page - 1, 1); + + // Check validation of pages between the first and last address. + ptrdiff_t distance = two_page - one_page; + EXPECT_EFAULT(read, one_page, (u32)distance + 1024); + + // Test every kernel page just because. + constexpr auto user_range_ceiling = 0xbe000000u; + u8* jerk_page = nullptr; + for (u64 kernel_address = user_range_ceiling; kernel_address <= 0xffffffff; kernel_address += PAGE_SIZE) { + jerk_page = (u8*)mmap((void*)kernel_address, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0); + EXPECT_EQ(jerk_page, MAP_FAILED); + EXPECT_EQ(errno, EFAULT); + } + + // Test the page just below where the user VM ends. + jerk_page = (u8*)mmap((void*)(user_range_ceiling - PAGE_SIZE), PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0); + EXPECT_EQ(jerk_page, MAP_FAILED); + EXPECT_EQ(errno, EFAULT); + + // Test the page just before that + jerk_page = (u8*)mmap((void*)(user_range_ceiling - (2 * PAGE_SIZE)), PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + EXPECT_EQ(jerk_page, (u8*)(user_range_ceiling - (2 * PAGE_SIZE))); + + EXPECT_OK(read, jerk_page, 4096); + EXPECT_EFAULT(read, jerk_page, 4097); + + // Test something that would wrap around the 2^32 mark. + EXPECT_EFAULT(read, jerk_page, 0x50000000); +} |