-
-
Notifications
You must be signed in to change notification settings - Fork 117
Description
The reverse search implementations (memrchr
) seem illegal under stacked borrows. They all follow the same pattern, so here I'll only annotate one. It retrieves a raw pointer to the end of the haystack from a reference to an empty slice, but then uses that pointer to iterate backwards by offsetting it with negative indices. Under strict rules, that pointer would however only be valid for access to the bytes that the reference covered from which it was cast, i.e. a zero-length slice at the end.
To my understanding, this is very likely illegal but not yet caught by MIRI since it does not strictly track the source for raw pointers (^source). @RalfJung might be able to provide more definitive insights.
Relevant code (inserted comments marked as // !
):
pub fn memrchr(n1: u8, haystack: &[u8]) -> Option<usize> {
// [...]
let start_ptr = haystack.as_ptr();
// ! This pointer only covers the same slice that the reference does.
// ! Would need to create these manually from offsetting the start pointer
// ! which covers the whole array.
let end_ptr = haystack[haystack.len()..].as_ptr();
let mut ptr = end_ptr;
unsafe {
// [...]
ptr = (end_ptr as usize & !align) as *const u8;
// [...]
while loop_size == LOOP_SIZE && ptr >= ptr_add(start_ptr, loop_size) {
// [...]
// ! These are outside the bounds of the reference from which ptr was created.
let a = *(ptr_sub(ptr, 2 * USIZE_BYTES) as *const usize);
let b = *(ptr_sub(ptr, 1 * USIZE_BYTES) as *const usize);
// [...]
ptr = ptr_sub(ptr, loop_size);
}
// [...]
}
}
Library code reduced to that version of memrchr.
The fix is simple, create ptr
from manually offsetting haystack.as_ptr()
which is valid for the whole haystack. I also don't expect any miscompilation.