From c3485d4ac1b4b8a5ee3d7435400bf768ca0ff565 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Wed, 13 May 2026 16:30:54 +0800 Subject: feat: add self-tests for uaccess functionality --- na/test/test_uaccess.cc | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 na/test/test_uaccess.cc diff --git a/na/test/test_uaccess.cc b/na/test/test_uaccess.cc new file mode 100644 index 0000000..d5795fc --- /dev/null +++ b/na/test/test_uaccess.cc @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#include +#include +#include + +#include +#include +#include + +constexpr uint64 kKernelVABase = 0xFFFFFFC000000000ULL; + +void selftest_uaccess() +{ + /* To test valid user addresses (< kKernelVABase), we need a writable page mapped there. + * We cannot use VmSpace::create() because it does not yet inherit kernel mappings, + * so switching to it hangs. We also cannot map it with the U bit because S-mode + * would need the SUM bit enabled. + * So we manually allocate a physical page and map it into the CURRENT page table + * at 0x100000 without the U bit, just for this test. + */ + uint64 mock_user_addr = 0x100000; + + uint64 satp; + asm volatile("csrr %0, satp" : "=r"(satp)); + uint64 root_pa = (satp & 0xFFFFFFFFFFFULL) << 12; + + Page* page = pmm_alloc_page(); + ST_ASSERT(page != nullptr); + uint64 paddr = page_to_pa(page).raw; + + pmap::map(root_pa, mock_user_addr, paddr, pmap::PTE_R | pmap::PTE_W | pmap::PTE_V | pmap::PTE_A | pmap::PTE_D); + + /* Flush TLB to see the new mapping */ + asm volatile("sfence.vma" : : : "memory"); + + st_begin("uaccess: copy_to_user valid range"); + char kernel_buf[32] = "Hello User Space!"; + int ret = copy_to_user(mock_user_addr, kernel_buf, 18); + ST_ASSERT_EQ(ret, 0); + st_pass(); + + st_begin("uaccess: copy_from_user valid range"); + char read_buf[32] = {0}; + /* We wrote "Hello User Space!" to mock_user_addr previously */ + ret = copy_from_user(read_buf, mock_user_addr, 18); + ST_ASSERT_EQ(ret, 0); + ST_ASSERT(read_buf[0] == 'H'); + ST_ASSERT(read_buf[1] == 'e'); + ST_ASSERT(read_buf[6] == 'U'); + st_pass(); + + st_begin("uaccess: strncpy_from_user valid range"); + char str_buf[32] = {0}; + int64 len = strncpy_from_user(str_buf, mock_user_addr, 32); + ST_ASSERT_EQ(len, 17); /* "Hello User Space!" is 17 chars long */ + ST_ASSERT(str_buf[0] == 'H'); + st_pass(); + + st_begin("uaccess: strncpy_from_user max_len truncation"); + char trunc_buf[32] = {0}; + len = strncpy_from_user(trunc_buf, mock_user_addr, 6); + ST_ASSERT_EQ(len, 5); /* Will copy 5 chars and NUL terminate */ + ST_ASSERT(trunc_buf[4] == 'o'); + ST_ASSERT(trunc_buf[5] == '\0'); + st_pass(); + + st_begin("uaccess: copy_to_user invalid range (crosses boundary)"); + ret = copy_to_user(kKernelVABase - 10, kernel_buf, 20); + ST_ASSERT_EQ(ret, -EFAULT); + st_pass(); + + st_begin("uaccess: copy_to_user invalid range (entirely in kernel)"); + ret = copy_to_user(kKernelVABase + 0x1000, kernel_buf, 20); + ST_ASSERT_EQ(ret, -EFAULT); + st_pass(); + + st_begin("uaccess: copy_to_user invalid range (overflow)"); + ret = copy_to_user(0xFFFFFFFFFFFFFFF0ULL, kernel_buf, 0x100); + ST_ASSERT_EQ(ret, -EFAULT); + st_pass(); + + st_begin("uaccess: copy_from_user invalid ranges"); + ret = copy_from_user(read_buf, kKernelVABase - 5, 10); + ST_ASSERT_EQ(ret, -EFAULT); + + ret = copy_from_user(read_buf, kKernelVABase, 10); + ST_ASSERT_EQ(ret, -EFAULT); + st_pass(); + + st_begin("uaccess: strncpy_from_user invalid range"); + uint64 invalid_addr = kKernelVABase + 0x1000; + len = strncpy_from_user(str_buf, invalid_addr, 10); + ST_ASSERT_EQ(len, -EFAULT); + st_pass(); +} -- cgit v1.2.3