Skip to content

Slices that cover the last byte of the address space are invalid #83996

Open
@joboet

Description

@joboet

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsA-iteratorsArea: IteratorsT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions