Skip to content

[Coding Guideline in RST]: Do not perform ordering comparisons on pointers from different allocations #259

@rcseacord

Description

@rcseacord

.. 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}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions