The current implementation uses pointer::add to compute the end pointer for the bounds check:
|
pub(super) fn new(slice: &'a [T]) -> Self { |
|
let ptr = slice.as_ptr(); |
|
// SAFETY: Similar to `IterMut::new`. |
|
unsafe { |
|
assume(!ptr.is_null()); |
|
|
|
let end = if mem::size_of::<T>() == 0 { |
|
(ptr as *const u8).wrapping_add(slice.len()) as *const T |
|
} else { |
|
ptr.add(slice.len()) |
|
}; |
|
|
|
Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData } |
|
} |
|
} |
The method requires that the calculation will not overflow a
usize, however that is not always the case. For instance, an allocator might return the last available page (
0xfffff000 on x86) and correctly return a slice of
u8 (with size 4096 on x86). If a program now iterates over the slice, the end pointer will overflow, wrapping around the address space and thus creating UB.
This behaviour is extremely unlikely and only occurs with no_std as most kernels reserve the higher half of the address space anyway.
Solutions
- Use wrapping_add instead, which avoids the UB, but might disable some optimizations
- Update the requirements for allocators so they aren't allowed to return the last bit of memory
- …
The current implementation uses
pointer::addto compute the end pointer for the bounds check:rust/library/core/src/slice/iter.rs
Lines 88 to 102 in 69e1d22
The method requires that the calculation will not overflow a
usize, however that is not always the case. For instance, an allocator might return the last available page (0xfffff000on x86) and correctly return a slice ofu8(with size 4096 on x86). If a program now iterates over the slice, the end pointer will overflow, wrapping around the address space and thus creating UB.This behaviour is extremely unlikely and only occurs with
no_stdas most kernels reserve the higher half of the address space anyway.Solutions