-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Hello,
First and foremost, thank you for your work on this library. Our team is developing a static analysis tool, and it helped us identify a potential soundness issue we wanted to bring to your attention.
The public function fast_prefetch_range is a safe function that accepts a raw pointer (*const u8) and a size. However, it does not perform any validation on these inputs before passing them to the internal prefetch_range method.
Lines 1108 to 1110 in 723fe1a
| pub fn fast_prefetch_range(start: *const u8, size: usize) { | |
| get_global_simd_ops().prefetch_range(start, size) | |
| } |
Lines 294 to 310 in 723fe1a
| pub fn prefetch_range(&self, start: *const u8, size: usize) { | |
| if !self.cache_config.enable_prefetch || size == 0 { | |
| return; | |
| } | |
| let distance = self.cache_config.prefetch_distance; | |
| let cache_line_size = self.cache_config.cache_line_size; | |
| // Prefetch in cache line increments | |
| let mut addr = start; | |
| let end = unsafe { start.add(size) }; | |
| while addr < end { | |
| self.prefetch(addr, PrefetchHint::T0); | |
| addr = unsafe { addr.add(cache_line_size.min(distance)) }; | |
| } | |
| } |
This allows safe code to trigger Undefined Behavior by providing a size so large that the start.add(size) operation inside the implementation violates the safety contract of pointer::add. The computation of a pointer that wraps around the address space or leaves its original allocation is immediate UB.
Proof of Concept:
The following minimal example demonstrates the vulnerability. It uses completely safe code to call the public API with malicious arguments, which will reliably cause a UB
use zipora::memory::simd_ops::fast_prefetch_range;
fn main() {
let data = vec![0u8; 64];
let start_ptr = data.as_ptr();
let malicious_size = usize::MAX;
fast_prefetch_range(start_ptr, malicious_size);
}The most idiomatic and safe way to fix this soundness hole is to change the function's public signature to accept a slice (&[u8]). This leverages Rust's type system to guarantee that the pointer and length refer to a single, valid memory allocation.
We hope this report is helpful. Thank you again for your time and for maintaining this library!