Skip to content

Soundness Issue: Public API fast_prefetch_range can lead to Undefined Behavior #10

@lewismosciski

Description

@lewismosciski

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.

zipora/src/memory/simd_ops.rs

Lines 1108 to 1110 in 723fe1a

pub fn fast_prefetch_range(start: *const u8, size: usize) {
get_global_simd_ops().prefetch_range(start, size)
}

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!

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions