Member-only story
🦀Rust Pointer Provenance
Why Your usize Round Trip Is Undefined Behavior
Every systems programmer carries the same mental model of a pointer: it is a number. A slightly special number, one that names a byte in memory, but a number all the same. You can print it, compare it, hash it, mask off its low bits and put a flag there, and turn it back into a pointer when you are done. The hardware agrees with you. On x86-64 and arm64 a pointer is a 64-bit integer in a register and nothing else.
The compiler does not agree with you, and it has not agreed with you for a long time. To the optimizer, a pointer is a number plus an invisible piece of permission called its provenance: which allocation it was derived from, and therefore which bytes it is allowed to touch. Two pointers can hold the same address and still be different values, because one of them has the right to write there and the other does not. When you send a pointer through usize and back, you are asking the compiler to reconstruct that permission from a bare integer, and the rules for when it can do that are much narrower than most hand-written tagged pointers assume.
This matters most in exactly the code where people reach for integer tricks: lock-free data structures that pack a mark bit into a next pointer, epoch-based reclamation schemes that store tags beside their addresses, arenas…







