diff options
Diffstat (limited to 'na')
| -rw-r--r-- | na/arch/riscv/uaccess/uaccess_memcpy.S | 117 | ||||
| -rw-r--r-- | na/include/lume/uaccess.h | 27 | ||||
| -rw-r--r-- | na/kernel/uaccess.cc | 67 |
3 files changed, 211 insertions, 0 deletions
diff --git a/na/arch/riscv/uaccess/uaccess_memcpy.S b/na/arch/riscv/uaccess/uaccess_memcpy.S new file mode 100644 index 0000000..615680d --- /dev/null +++ b/na/arch/riscv/uaccess/uaccess_memcpy.S @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#include <arch/extable.h> + +/* + * extern "C" int __uaccess_memcpy(void* dst, const void* src, uint64 len); + * + * a0: dst + * a1: src + * a2: len + * Returns: 0 on success, -EFAULT (-14) on page fault. + */ +.global __uaccess_memcpy +.type __uaccess_memcpy, @function +__uaccess_memcpy: + /* If len == 0, return 0 */ + beqz a2, .Lmemcpy_done + + /* Enable SUM (Supervisor User Memory) bit: sstatus.SUM is bit 18 */ + li t0, (1 << 18) + csrs sstatus, t0 + +.Lmemcpy_loop: + /* Load byte from src */ +1: lb t1, 0(a1) + /* Store byte to dst */ +2: sb t1, 0(a0) + + addi a0, a0, 1 + addi a1, a1, 1 + addi a2, a2, -1 + bnez a2, .Lmemcpy_loop + +.Lmemcpy_done: + /* Disable SUM */ + li t0, (1 << 18) + csrc sstatus, t0 + li a0, 0 + ret + +.Lmemcpy_fault: + /* Fixup routine on fault */ + /* Disable SUM */ + li t0, (1 << 18) + csrc sstatus, t0 + li a0, -14 /* -EFAULT */ + ret + +/* Exception table entries */ +EXTABLE(1b, .Lmemcpy_fault) +EXTABLE(2b, .Lmemcpy_fault) + + +/* + * extern "C" int64 __uaccess_strncpy(char* dst, const char* src, uint64 max_len); + * + * a0: dst + * a1: src + * a2: max_len + * Returns: number of bytes copied (excluding null), or -EFAULT (-14) on page fault. + */ +.global __uaccess_strncpy +.type __uaccess_strncpy, @function +__uaccess_strncpy: + /* If max_len == 0, return 0 */ + beqz a2, .Lstrncpy_done_zero + + /* Enable SUM */ + li t0, (1 << 18) + csrs sstatus, t0 + + /* t2 will store the length copied */ + li t2, 0 + +.Lstrncpy_loop: + /* Load byte from src */ +3: lb t1, 0(a1) + /* Store byte to dst */ +4: sb t1, 0(a0) + + /* If it was the null terminator, we're done */ + beqz t1, .Lstrncpy_done + + addi a0, a0, 1 + addi a1, a1, 1 + addi t2, t2, 1 + addi a2, a2, -1 + bnez a2, .Lstrncpy_loop + + /* Reached max_len without finding null */ + /* We must null-terminate at max_len - 1 as per typical strncpy_from_user semantics */ + addi a0, a0, -1 + sb zero, 0(a0) + addi t2, t2, -1 /* length of string without the forced null terminator */ + +.Lstrncpy_done: + /* Disable SUM */ + li t0, (1 << 18) + csrc sstatus, t0 + mv a0, t2 + ret + +.Lstrncpy_done_zero: + li a0, 0 + ret + +.Lstrncpy_fault: + /* Fixup routine on fault */ + li t0, (1 << 18) + csrc sstatus, t0 + li a0, -14 /* -EFAULT */ + ret + +/* Exception table entries */ +EXTABLE(3b, .Lstrncpy_fault) +EXTABLE(4b, .Lstrncpy_fault) diff --git a/na/include/lume/uaccess.h b/na/include/lume/uaccess.h new file mode 100644 index 0000000..5a9ed71 --- /dev/null +++ b/na/include/lume/uaccess.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +/* + * User-space memory access — safe copy routines. + * + * Phase 2: Software page-table validation. + * Phase 3+: Exception Table (zero-cost happy path). + * + * Reference: docs/specs/syscall.md §8 + */ + +#include <lume/types.h> + +/* Copy len bytes from user address to kernel buffer. + * Returns 0 on success, -EFAULT if user address is invalid. */ +int copy_from_user(void* kernel_dst, uint64 user_src, uint64 len); + +/* Copy len bytes from kernel buffer to user address. + * Returns 0 on success, -EFAULT if user address is invalid. */ +int copy_to_user(uint64 user_dst, const void* kernel_src, uint64 len); + +/* Copy NUL-terminated string from user space. + * Returns string length (excluding NUL) or -EFAULT. */ +int64 strncpy_from_user(char* kernel_dst, uint64 user_src, uint64 max_len); diff --git a/na/kernel/uaccess.cc b/na/kernel/uaccess.cc new file mode 100644 index 0000000..4a5b963 --- /dev/null +++ b/na/kernel/uaccess.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * User-Kernel Memory Copy — Phase 3 (Exception Tables) + * + * Validates user pointers by checking they fall within user address space + * (below KERNEL_VA_BASE). Uses assembly-level safe copy functions that + * integrate with the kernel's Exception Table mechanism. + * + * Reference: docs/specs/syscall.md §8 + */ + +#include <lume/uaccess.h> +#include <lume/task.h> +#include <lume/errno.h> +#include <lume/config.h> + +/* Kernel VA base — addresses at or above this are kernel space */ +inline constexpr uint64 kKernelVABase = 0xFFFFFFC000000000ULL; + +extern "C" int __uaccess_memcpy(void* dst, const void* src, uint64 len); +extern "C" int64 __uaccess_strncpy(char* dst, const char* src, uint64 max_len); + +/* Validate that [addr, addr+len) is entirely in user space */ +static bool is_user_range(uint64 addr, uint64 len) +{ + if (addr >= kKernelVABase) + return false; + if (addr + len < addr) /* overflow */ + return false; + if (addr + len > kKernelVABase) + return false; + return true; +} + +int copy_from_user(void* kernel_dst, uint64 user_src, uint64 len) +{ + if (len == 0) return 0; + if (!is_user_range(user_src, len)) return -EFAULT; + + return __uaccess_memcpy(kernel_dst, reinterpret_cast<const void*>(user_src), len); +} + +int copy_to_user(uint64 user_dst, const void* kernel_src, uint64 len) +{ + if (len == 0) return 0; + if (!is_user_range(user_dst, len)) return -EFAULT; + + return __uaccess_memcpy(reinterpret_cast<void*>(user_dst), kernel_src, len); +} + +int64 strncpy_from_user(char* kernel_dst, uint64 user_src, uint64 max_len) +{ + if (!is_user_range(user_src, 1)) return -EFAULT; + + /* We must also check that the string does not cross into kernel space. + This is safely handled by the assembly routine which will fault if it hits + an unmapped page. However, to strictly respect kKernelVABase, we should + restrict max_len to the distance to kKernelVABase. */ + uint64 max_allowed = kKernelVABase - user_src; + if (max_len > max_allowed) { + max_len = max_allowed; + } + + return __uaccess_strncpy(kernel_dst, reinterpret_cast<const char*>(user_src), max_len); +} |
