-
Notifications
You must be signed in to change notification settings - Fork 28
Description
.. SPDX-License-Identifier: MIT OR Apache-2.0
SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors
.. default-domain:: coding-guidelines
.. guideline:: Do not compare or subtract pointers derived from different allocations
🆔 gui_PtrOrderCompare
:category: required
:status: draft
:release: 1.85.0
:fls: fls_ppd1xwve3tr7
:decidability: undecidable
:scope: expression
:tags: pointers, safety, undefined-behavior
Do not use ordering operators (<, >, <=, >=) to compare pointers that originated
from different allocations.
Only equality comparisons (==, !=) are defined for pointers from different allocations.
Narrow (thin) pointers point to types whose size is known at compile time (sized types),
while wide (fat) pointers point to dynamically-sized types (DSTs) and carry extra runtime metadata,
usually the length or a vtable pointer.
Comparing wide pointer comparisons also compares their metadata.
Pointers may only be ordered when:
- Both pointers point within the same allocation, or
- One pointer points one-past-the-end of the same allocation
.. rationale::
🆔 rat_PtrOrderCompareReason
:status: draft
Comparing the address from different allocations is semantically meaningless.
.. non_compliant_example::
🆔 non_compl_ex_PtrOrder1
:status: draft
This noncompliant example directly compares pointers from different allocations using
ordering operators.
.. code-block:: rust
fn compare_allocations() {
let x = Box::new(42);
let y = Box::new(17);
let x_ptr = &*x as *const i32;
let y_ptr = &*y as *const i32;
// Noncompliant: ordering comparison across allocations
if x_ptr < y_ptr {
println!("x is before y");
}
}
.. non_compliant_example::
🆔 non_compl_ex_PtrOrder2
:status: draft
This noncompliant example attempts to sort pointers from different allocations.
.. code-block:: rust
fn sort_pointers(ptrs: &mut [*const u8]) {
ptrs.sort_by(|a, b| a.cmp(b)); // noncompliant
}
fn main() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
let vec3 = vec![7, 8, 9];
let mut ptrs = vec![
vec1.as_ptr(),
vec2.as_ptr(),
vec3.as_ptr(),
];
sort_pointers(&mut ptrs);
}
.. non_compliant_example::
🆔 non_compl_ex_PtrOrder3
:status: draft
This noncompliant example uses ordering to check if one pointer is in a higher memory address.
.. code-block:: rust
struct MemoryRegion {
data: Vec<u8>,
}
fn is_higher_address(region1: &MemoryRegion, region2: &MemoryRegion) -> bool {
let ptr1 = region1.data.as_ptr();
let ptr2 = region2.data.as_ptr();
// Noncompliant: ordering pointers from different allocations
ptr1 > ptr2
}
.. non_compliant_example::
🆔 non_compl_ex_PtrOrder4
:status: draft
This noncompliant example attempts to find a pointer in a range using ordering comparisons
across allocations.
.. code-block:: rust
fn find_allocation<'a>(
needle: *const u8,
allocations: &[&'a Vec<u8>]
) -> Option<&'a Vec<u8>> {
allocations.iter().find(|alloc| {
let start = alloc.as_ptr();
let end = unsafe { start.add(alloc.len()) };
needle >= start && needle < end // noncompliant
}).copied()
}
.. non_compliant_example::
🆔 non_compl_ex_PtrOrder5
:status: draft
This noncompliant example compares addresses obtained from pointers to different allocations
using the `.addr() <https://doc.rust-lang.org/std/primitive.pointer.html#method.addr>`__ method.
.. code-block:: rust
fn compare_allocations() {
let x = Box::new(42);
let y = Box::new(17);
let x_ptr = &*x as *const i32;
let y_ptr = &*y as *const i32;
if x_ptr.addr() < y_ptr.addr() { // noncompliant
println!("x is before y in address space");
}
}
.. compliant_example::
🆔 compl_ex_PtrOrder3
:status: draft
This compliant example tests two pointers for equality to determine if they point to the same allocation.
.. code-block:: rust
struct MemoryRegion {
data: Vec<u8>,
}
fn are_same_allocation(region1: &MemoryRegion, region2: &MemoryRegion) -> bool {
let ptr1 = region1.data.as_ptr();
let ptr2 = region2.data.as_ptr();
std::ptr::eq(ptr1, ptr2) // OK
}
.. compliant_example::
🆔 compl_ex_PtrOrder4
:status: draft
When checking if a pointer falls within an allocation, use address comparisons
for the initial check.
.. code-block:: rust
fn is_within_allocation(needle: *const u8, allocation: &[u8]) -> bool {
let start = allocation.as_ptr();
let end = unsafe { start.add(allocation.len()) };
// Compliant: using address comparisons
let needle_addr = needle.addr();
let start_addr = start.addr();
let end_addr = end.addr();
needle_addr >= start_addr && needle_addr < end_addr
}
.. compliant_example::
🆔 compl_ex_PtrOrder5
:status: draft
Ordering comparisons within the same allocation are always valid.
.. code-block:: rust
fn find_in_slice(slice: &[i32], target: &i32) -> Option<usize> {
let slice_start = slice.as_ptr();
let slice_end = unsafe { slice_start.add(slice.len()) };
let target_ptr = target as *const i32;
// First verify the pointer could be in this slice using addresses
let target_addr = target_ptr.addr();
let start_addr = slice_start.addr();
let end_addr = slice_end.addr();
if target_addr < start_addr || target_addr >= end_addr {
return None;
}
// Compliant: if we know they're from the same allocation,
// pointer arithmetic is safe
let offset = unsafe { target_ptr.offset_from(slice_start) };
if offset >= 0 && (offset as usize) < slice.len() {
Some(offset as usize)
} else {
None
}
}
.. bibliography::
.. [RUST-PTR-ADDR] Rust Project Developers. "Primitive Pointer Methods — ``pointer::addr``."
*The Rust Standard Library*. Accessed December 11, 2025.
https://doc.rust-lang.org/std/primitive.pointer.html#method.addr.
.. [JUNG-PTR] RalfJung. "Pointers Are Complicated, or: What's in a Byte?" *Ralf’s Ramblings* (blog),
July 24, 2018. Accessed December 11, 2025.
https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html. :contentReference[oaicite:0]{index=0}