Helper Functions
Relevant source files
This document describes the utility functions in the axptr library that support safe user-space memory access in kernel code. These helper functions implement the core safety mechanisms behind the UserPtr
and UserConstPtr
types but are not typically used directly by client code. For information about the user pointer types themselves, see UserPtr API and UserConstPtr API.
Per-CPU Flag System
The foundation of axptr's safety system is a per-CPU boolean flag that tracks when the kernel is accessing user memory.
flowchart TD A["User Memory Access Request"] B["is_accessing_user_memory()"] C["ACCESSING_USER_MEM flag"] D["OS allows page faultsfrom kernel mode"] E["OS handles as regularkernel page fault"] F["access_user_memory()"] G["ACCESSING_USER_MEM = true"] H["Execute memory accesscallback function"] I["ACCESSING_USER_MEM = false"] J["Return result"] A --> B B --> C C --> D C --> E F --> G G --> H H --> I I --> J
Sources: src/lib.rs(L11 - L29)
The library provides two key functions for working with this system:
is_accessing_user_memory()
: A public function that returns the current state of theACCESSING_USER_MEM
flag. Operating system implementations should check this flag when handling page faults in kernel mode - if it returns true, page faults should be allowed to proceed (as they might be from legitimate user memory access attempts).access_user_memory<R>(f: impl FnOnce() -> R) -> R
: An internal function that executes a callback with the user memory access flag set to true. This function:
- Sets the
ACCESSING_USER_MEM
flag to true - Executes the provided callback function
- Restores the flag to false
- Returns the result of the callback
The ACCESSING_USER_MEM
flag is implemented as a per-CPU variable using the percpu
crate to ensure thread safety without locking overhead.
Memory Region Validation
Before accessing user memory, axptr performs thorough validation using the check_region
function.
flowchart TD A["check_region()"] B["Memory aligned?"] C["Return EFAULT"] D["Access permissionsgranted?"] E["Populate page tables"] F["Return error"] G["Return OK"] A --> B B --> C B --> D D --> C D --> E E --> F E --> G
Sources: src/lib.rs(L31 - L54)
The check_region
function performs several critical checks:
- Alignment Validation: Verifies that the start address has proper alignment for the requested data type. If misaligned, returns
EFAULT
. - Access Permission Check: Uses the
AddrSpace.check_region_access()
method to verify that the memory region has the appropriate access permissions (read/write). - Page Table Population: Calls
AddrSpace.populate_area()
to ensure that page tables are set up correctly for the memory region. This may involve mapping physical pages if they're not already mapped.
The library also provides a wrapper function check_region_with
that works with the AddrSpaceProvider
trait, simplifying its usage from the pointer types.
Null-Terminated Data Processing
A specialized helper function handles the common case of accessing null-terminated data (like C strings) from user space.
flowchart TD subgraph subGraph0["Page Boundary Handling"] E["Scan memory for null terminator"] F["Reached page boundary?"] G["Page has accesspermission?"] H["Return EFAULT"] I["Move to next page"] J["Found nullterminator?"] K["Advance to next element"] L["End scan"] end A["check_null_terminated()"] B["Memory aligned?"] C["Return EFAULT"] D["Set ACCESSING_USER_MEM = true"] M["Set ACCESSING_USER_MEM = false"] N["Return pointer and length"] A --> B B --> C B --> D D --> E E --> F F --> G F --> J G --> H G --> I I --> F J --> K J --> L K --> F L --> M M --> N
Sources: src/lib.rs(L56 - L107)
The check_null_terminated<T>
function provides a safe way to access variable-length, null-terminated data from user space:
- Initial Alignment Check: Verifies the start address has proper alignment for type T.
- Page-by-Page Scanning: Processes memory one page at a time, checking permissions at each page boundary. This approach allows handling of strings that span multiple pages.
- Safe Memory Access: Uses the
access_user_memory()
function to set theACCESSING_USER_MEM
flag during scanning, allowing proper handling of page faults that might occur. - Null Terminator Detection: Reads each element using
read_volatile()
and compares it to the default value (T::default()
) to find the null terminator.
This function supports the implementation of get_as_null_terminated()
in both UserPtr
and UserConstPtr
types, as well as get_as_str()
for UserConstPtr<c_char>
.
Integration With Address Space Provider
The helper functions integrate with the address space abstraction through the check_region_with
function.
Sources: src/lib.rs(L110 - L117) src/lib.rs(L119 - L126)
The check_region_with
function serves as a bridge between the high-level pointer types and the low-level memory region validation:
- It accepts an
AddrSpaceProvider
implementation (typically a reference to anAddrSpace
) - It calls
with_addr_space()
on the provider to get access to the actualAddrSpace
- It passes
check_region()
as a callback, forwarding the memory validation request - It returns the result of the validation
This design reduces code duplication and avoids excessive generic function instantiations, as noted in the source code comment.
Helper Function Usage Patterns
The following table summarizes how the helper functions are used by the public API:
Helper Function | Used By | Purpose |
---|---|---|
is_accessing_user_memory() | OS implementation | Determine if page faults in kernel mode should be allowed |
access_user_memory() | check_null_terminated() | Set flag during user memory scanning |
check_region() | check_region_with() | Validate memory region alignment and permissions |
check_null_terminated() | get_as_null_terminated() | Safely scan for null-terminated data |
check_region_with() | UserPtr::get(),UserConstPtr::get(), etc. | Bridge between pointer types and memory validation |
Sources: src/lib.rs(L175 - L182) src/lib.rs(L204 - L216) src/lib.rs(L258 - L266) src/lib.rs(L282 - L291)
These helper functions work together to create a comprehensive safety system that prevents the kernel from crashing when accessing user memory, while maintaining good performance and ergonomics.