Skip to content

[Coding Guideline]: Do not compare raw pointers to allocations with different provenance #246

@rcseacord

Description

@rcseacord

Chapter

Types and Traits

Guideline Title

Do not compare raw pointers to allocations with different provenance

Category

Required

Status

Draft

Release Begin

unclear

Release End

latest

FLS Paragraph ID

fls_bw8zutjcteki

Decidability

Decidable

Scope

System

Tags

surprising-behavior

Amplification

Do not compare raw pointers to allocations with different provenances for equality, inequality, or ordering.

Pointer comparisons are permitted only when both pointers are guaranteed to reference the same allocation or subobject.

Code shall not rely on:

  • Layout of variables in memory
  • Assumed field layout of structs without repr(C) or repr(packed)
  • Outcomes of pointer arithmetic across allocation boundaries

Exception(s)

No response

Rationale

Although raw pointer comparison is not itself undefined behavior;
comparing pointers with different provenance can give surprising results which might cause logic errors,
portability issues, and inconsistent behavior across different optimization levels, builds, or platforms.
Specifically, the result of comparing pointers with different providence is guaranteed to be the comparison of the pointer addresses.
However, the addresses that are selected for allocations is unspecified.

Pointer equality or ordering is only meaningful when both pointers are derived from the same allocated object or block of memory.
Comparisons across unrelated allocations are semantically meaningless and must be avoided.

Non-Compliant Example - Prose

This noncompliant example allocates two local u32 variables on the stack.
The order of these two variables in memory is unspecified behavior.
The code then creates a raw pointer to v2 and a raw pointer to v1.
Adds the address stored in v1 to 1 × size_of::<u32>() = 4 bytes using wrapping_offset which:

  • ignores provenance
  • may produce an arbitrary, invalid, or meaningless pointer
  • is always allowed but does not guarantee the pointer points to anything valid

Comparing two values of raw pointer types compares the addresses of the values.

This code then compares ptr (a pointer to v2) with ptr2 (a pointer to v1 + 4 bytes).
Because the stack layout is unspecified behavior, the result of this comparison depends on how the compiler the memory layout for v1 and v2 on the stack.
The result may change across:

  • compiler versions
  • optimization levels
  • targets
  • small code changes
  • builds with or without link-time optimization

This noncompliant example does not contain undefined behavior (because no pointer is dereferenced) but it does depend on unspecified behavior, meaning that the program is valid, but the results are undefined.

Non-Compliant Example - Code

pub fn raw_ptr_comparison(){
    let v1: u32 = 1;
    let v2: u32 = 2;
    let ptr = &v2 as *const u32;
    let ptr2 = (&v1 as *const u32).wrapping_offset(1);
    if ptr == ptr2 {
        println!("Same");
    }
    else{
        println!("Not the same");
    }
}

Compliant Example - Prose

This compliant example creates a mutable array of 16 bytes on the stack where all bytes are zero-initialized.
The entire array is one contiguous allocation.
The code creates a raw pointer p of type *const u8 to the first element of the array (that is, buf[0]).
The ptr p points at the start of the allocation.
The code then uses pointer arithmetic to compute a pointer q which points 4 elements past p.
Because the element type is u8, this means “4 bytes past p”.
The pointer arithmetic is safe as long as the resulting pointer stays within the same allocation (it does).
This is permitted because pointer arithmetic is allowed within the same allocated object.

Finally, the code compares the numerical address values of p and q.
Pointer comparison is always allowed.
Comparing pointers from the same allocation is meaningful and defined.
Because p points to the beginning and q to a later part of the same array, same_block becomes true.

Compliant Example - Code

let mut buf = [0u8; 16];
let p = buf.as_ptr();
let q = unsafe { p.add(4) };

let same_block = p < q; // ok: comparison within same allocation

Metadata

Metadata

Assignees

Labels

category: requiredA coding guideline with category requiredchapter: types-and-traitscoding guidelineAn issue related to a suggestion for a coding guidelinedecidability: decidableA coding guideline which can be checked automaticallyscope: systemA coding guideline that can be determined applied only when entire source is inspectedstatus: draft

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions