<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pointers on Sofía Belén López Vicens</title><link>https://sofiabelen.github.io/tags/pointers/</link><description>Recent content in Pointers on Sofía Belén López Vicens</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 02 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://sofiabelen.github.io/tags/pointers/index.xml" rel="self" type="application/rss+xml"/><item><title>Pointer Provenance in Rust vs C++</title><link>https://sofiabelen.github.io/projects/pointer-provenance-in-rust-vs-cpp/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://sofiabelen.github.io/projects/pointer-provenance-in-rust-vs-cpp/</guid><description>&lt;img src="https://sofiabelen.github.io/projects/pointer-provenance-in-rust-vs-cpp/yves-alarie-MmZet_7mzMY-unsplash.jpg" alt="Featured image of post Pointer Provenance in Rust vs C++" />&lt;p>I&amp;rsquo;ve been exploring Rust, and one topic that got me intrigued is pointer
provenance, particularly because it&amp;rsquo;s not easy to visualize.&lt;/p>
&lt;p>To get a feel for
&lt;a class="link" href="https://sofiabelen.github.io/projects/visualizing-rusts-vtables-how-dyn-trait-works-in-memory/" target="_blank" rel="noopener"
>vtables&lt;/a>,
what I wanted was to look at the actual memory, to see what it really looked
like and prove that I could in fact call these functions through their
respective function pointers. With provenance, I wanted to take it apart the
same way, but it turns out it&amp;rsquo;s not so straightforward. This only got me more
intrigued, so let&amp;rsquo;s see what we can find!&lt;/p>
&lt;p>My first time hearing about provenance, I wondered, why hadn&amp;rsquo;t I heard about it
before? It seems so crucial, yet a bit of an obscure concept.&lt;/p>
&lt;h2 id="what-is-provenance-pointers-are-not-just-glorified-ints">What is Provenance: Pointers Are Not Just Glorified Ints
&lt;/h2>&lt;figure>
&lt;img src="chuttersnap-Fy7guhZfmAM-unsplash.jpg" alt="">
&lt;figcaption>
Victoria crowned pigeon: some will say it's just a pigeon.
&lt;p>Photo by &lt;a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">CHUTTERSNAP&lt;/a> on &lt;a href="https://unsplash.com/photos/red-and-blue-bird-with-white-wings-Fy7guhZfmAM?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>Let&amp;rsquo;s start by taking a good look at this snippet. It&amp;rsquo;s short, but our intuition
can trick us if it&amp;rsquo;s our first time coming across this concept. &lt;code>void opaque(int*)&lt;/code> is forward declared here, so for us it&amp;rsquo;s a black box. Neither we
nor the compiler can rule out that this function dereferences &lt;code>p&lt;/code> and writes to
memory.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we compile this with &lt;code>-O2&lt;/code>, Clang optimizes this to return 1. Let&amp;rsquo;s try this
on Godbolt:&lt;/p>
&lt;iframe width="800px" height="200px" src="https://godbolt.org/e?hideEditorToolbars=true#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:8,positionColumn:1,positionLineNumber:8,selectionStartColumn:1,selectionStartLineNumber:8,startColumn:1,startLineNumber:8),source:'void+opaque(int*)%3B%0A%0Aint+f(int*+p)+%7B%0A++++int+x+%3D+1%3B%0A++++opaque(p)%3B%0A++++return+x%3B%0A%7D%0A'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:44.33887569279493,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang2310,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O2',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+23.1.0+(Editor+%231)',t:'0')),k:55.66112430720507,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4">&lt;/iframe>
&lt;!--
```assembly
f(int*):
sub rsp, 8
call opaque(int*)
mov eax, 1
add rsp, 8
ret
```
-->
&lt;p>The question is, how does it know that &lt;code>int* p&lt;/code> doesn&amp;rsquo;t point to the same memory
address where &lt;code>x&lt;/code> lives (meaning that &lt;code>opaque(p)&lt;/code> could end up modifying &lt;code>x&lt;/code>)?&lt;/p>
&lt;p>The compiler assumes &lt;code>p&lt;/code> can&amp;rsquo;t equal &lt;code>&amp;amp;x&lt;/code> because &lt;code>p&lt;/code> wasn&amp;rsquo;t &lt;em>derived&lt;/em> from &lt;code>x&lt;/code>.&lt;/p>
&lt;p>When I first heard about this, I thought it was a bit of a strange concept. What
does it mean that something is &lt;em>derived&lt;/em> from something?&lt;/p>
&lt;p>I mean, if pointers were just numbers (memory addresses), &lt;code>p&lt;/code> could
theoretically happen to hold the same memory address as &lt;code>x&lt;/code>&amp;rsquo;s allocation on the
stack. Good thing they&amp;rsquo;re not just numbers!&lt;/p>
&lt;p>Let us follow the white rabbit.&lt;/p>
&lt;figure>
&lt;img src="sincerely-media-2yL77QTLCq4-unsplash.jpg" alt="">
&lt;figcaption>
Yes, I've been trying to get more rabbits on my page. Somehow, only cats seemed to make the cut. This one reminds me of my childhood pet, Kaisie (honorary mention).
&lt;p>Photo by &lt;a href="https://unsplash.com/@sincerelymedia?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sincerely Media&lt;/a> on &lt;a href="https://unsplash.com/photos/white-and-black-rabbit-2yL77QTLCq4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>This brings us to the core of provenance. &lt;mark>That &amp;ldquo;&lt;em>derived from&lt;/em>&amp;rdquo; relation
is &lt;strong>provenance&lt;/strong>: for every pointer, the compiler keeps track of which
allocation it belongs to and may, therefore, touch.&lt;/mark>&lt;/p>
&lt;p>I tried to find a way to print this mysterious provenance, but it turns out that
it&amp;rsquo;s purely a compiler abstraction. It does not exist as an actual data field
stored alongside the memory address. (There are exceptions though: feel free to
look up the CHERI architecture to go down another rabbit hole).&lt;/p>
&lt;h2 id="inspecting-provenance-llvm-ir">Inspecting Provenance: LLVM IR
&lt;/h2>&lt;p>So what can we do to inspect it? If the compiler knows about it then it must
exist somewhere. If so, there must be a way to &lt;em>see&lt;/em> it.&lt;/p>
&lt;p>This is how I learned about LLVM IR (intermediate representation), which is
essentially a low-level programming language that acts as a bridge between
high-level languages (like Rust, C++, etc) and machine code.&lt;/p>
&lt;p>Let&amp;rsquo;s try it for this code instead, where we also pass &lt;code>&amp;amp;x&lt;/code> to &lt;code>opaque&lt;/code> to see
what happens when the compiler doesn&amp;rsquo;t optimize that part away:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can use the &lt;code>-emit-llvm&lt;/code> flag on Godbolt to &lt;a class="link" href="https://godbolt.org/#g:!%28%28g:!%28%28g:!%28%28h:codeEditor,i:%28filename:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,selection:%28endColumn:2,endLineNumber:8,positionColumn:2,positionLineNumber:8,selectionStartColumn:2,selectionStartLineNumber:8,startColumn:2,startLineNumber:8%29,source:%27void&amp;#43;opaque%28int*%29%3B%0A%0Aint&amp;#43;f%28int*&amp;#43;p%29&amp;#43;%7B%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;int&amp;#43;x&amp;#43;%3D&amp;#43;1%3B%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;opaque%28p%29%3B%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;opaque%28%26x%29%3B%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;return&amp;#43;x%3B%0A%7D%27%29,l:%275%27,n:%270%27,o:%27C%2B%2B&amp;#43;source&amp;#43;%231%27,t:%270%27%29%29,k:44.33887569279493,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27%29,%28g:!%28%28h:compiler,i:%28compiler:clang2310,filters:%28b:%270%27,binary:%271%27,binaryObject:%271%27,commentOnly:%270%27,debugCalls:%271%27,demangle:%270%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%270%27,trim:%271%27,verboseDemangling:%270%27%29,flagsViewOpen:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,libs:!%28%29,options:%27-O2&amp;#43;&amp;#43;-emit-llvm%27,overrides:!%28%29,selection:%28endColumn:29,endLineNumber:24,positionColumn:1,positionLineNumber:1,selectionStartColumn:29,selectionStartLineNumber:24,startColumn:1,startLineNumber:1%29,source:1%29,l:%275%27,n:%270%27,o:%27&amp;#43;x86-64&amp;#43;clang&amp;#43;23.1.0&amp;#43;%28Editor&amp;#43;%231%29%27,t:%270%27%29%29,k:55.66112430720507,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27%29%29,l:%272%27,n:%270%27,o:%27%27,t:%270%27%29%29,version:4" target="_blank" rel="noopener"
>see the LLVM
IR&lt;/a>:&lt;/p>
&lt;pre>&lt;code>
define dso_local noundef i32 @f(int*)(ptr noundef &lt;mark>%0&lt;/mark>) local_unnamed_addr #0 !dbg !14 {
&lt;mark>%2 = alloca i32, align 4&lt;/mark> // --> &lt;mark>root of %2 allocation (x)&lt;/mark>
#dbg_assign(i1 poison, !22, !DIExpression(), !23, ptr %2, !DIExpression(), !24)
#dbg_value(ptr %0, !21, !DIExpression(), !24)
call void @llvm.lifetime.start.p0(ptr nonnull %2) #3, !dbg !25
store i32 1, ptr %2, align 4, !dbg !26
#dbg_assign(i32 1, !22, !DIExpression(), !28, ptr %2, !DIExpression(), !24)
tail call void @opaque(int*)(&lt;mark>ptr noundef %0&lt;/mark>), !dbg !29
call void @opaque(int*)(&lt;mark>ptr noundef nonnull %2&lt;/mark>), !dbg !30
%3 = load i32, ptr %2, align 4, !dbg !31
call void @llvm.lifetime.end.p0(ptr nonnull %2) #3, !dbg !32
ret i32 %3, !dbg !33
}
declare void @llvm.lifetime.start.p0(ptr captures(none)) #1
declare !dbg !34 void @opaque(int*)(ptr noundef) local_unnamed_addr #2
declare void @llvm.lifetime.end.p0(ptr captures(none)) #1
attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #3 = { nounwind }
&lt;/code>&lt;/pre>
&lt;p>What the compiler does is to conceptually construct a DAG (directed acyclic
graph), to answer the question of where a pointer comes from and whether two
pointers can touch the same memory.&lt;/p>
&lt;p>If the root origins don&amp;rsquo;t match, LLVM can guarantee that writing through &lt;code>%0&lt;/code>
will never alter the value stored at &lt;code>%2&lt;/code>.&lt;/p>
&lt;style>
#mermaid-slider-0.mermaid-slider {
display: flex;
flex-direction: column;
align-items: center;
background: transparent;
padding: 10px;
width: 100%;
position: relative;
}
#mermaid-slider-0 .m-canvas {
width: 100%;
height: 520px;
display: flex;
align-items: center;
justify-content: center;
overflow-x: auto;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 6px;
}
#mermaid-slider-0 .m-canvas svg {
flex: none;
margin: auto;
width: auto !important;
height: 400px !important;
zoom: 1.15;
}
#mermaid-slider-0 .m-canvas {
scrollbar-width: thin;
scrollbar-color: #d97d90 transparent;
}
#mermaid-slider-0 .m-canvas::-webkit-scrollbar { height: 8px; }
#mermaid-slider-0 .m-canvas::-webkit-scrollbar-track { background: transparent; }
#mermaid-slider-0 .m-canvas::-webkit-scrollbar-thumb {
background: #d97d90;
border-radius: 4px;
}
#mermaid-slider-0 .m-controls {
display: flex;
gap: 12px;
margin-top: 16px;
}
#mermaid-slider-0 .m-controls button {
padding: 6px 18px;
background: #f8c1cc;
color: #333333;
border: 1px solid #e8a7b5;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-size: 14px;
transition: background 0.2s ease;
}
#mermaid-slider-0 .m-controls button:hover:not(:disabled) {
background: #e8a7b5;
}
#mermaid-slider-0 .m-controls button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
#mermaid-slider-0 .m-zoom-btn,
#mermaidModal-0 .m-tool-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
font-size: 18px;
line-height: 1;
background: rgba(255, 255, 255, 0.95);
color: #333333;
border: 1px solid #d97d90;
border-radius: 8px;
cursor: pointer;
padding: 0;
}
#mermaid-slider-0 .m-zoom-btn:hover,
#mermaidModal-0 .m-tool-btn:hover {
background: #fcdbe2;
}
#mermaid-slider-0 .m-zoom-btn {
position: absolute;
top: 14px;
right: 18px;
z-index: 100;
}
#mermaid-slider-0 .mermaid .node rect,
#mermaid-slider-0 .mermaid .node circle,
#mermaid-slider-0 .mermaid .node polygon,
#mermaidModal-0 .node rect,
#mermaidModal-0 .node circle,
#mermaidModal-0 .node polygon {
fill: #fcdbe2 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-0 .mermaid .cluster rect,
#mermaidModal-0 .cluster rect {
fill: #fff0f3 !important;
stroke: #d97d90 !important;
stroke-width: 2px !important;
}
#mermaid-slider-0 .mermaid .edgePath .path,
#mermaidModal-0 .edgePath .path {
stroke: #d97d90 !important;
stroke-width: 3px !important;
}
#mermaid-slider-0 .mermaid marker,
#mermaid-slider-0 .mermaid marker path,
#mermaid-slider-0 .mermaid [id*="flowchart-pointEnd"],
#mermaidModal-0 marker,
#mermaidModal-0 marker path,
#mermaidModal-0 [id*="flowchart-pointEnd"] {
fill: #d97d90 !important;
stroke: #d97d90 !important;
}
#mermaid-slider-0 .mermaid text,
#mermaid-slider-0 .mermaid .cluster-label,
#mermaidModal-0 svg text,
#mermaidModal-0 .cluster-label {
fill: #333333 !important;
font-size: 14px !important;
font-weight: 600 !important;
font-family: inherit !important;
}
#mermaid-slider-0 .mermaid .flowchartTitleText,
#mermaidModal-0 .flowchartTitleText {
fill: #d97d90 !important;
font-size: 18px !important;
font-weight: bold !important;
}
#mermaidModal-0.mermaid-modal {
display: none;
position: fixed;
z-index: 99999;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(4px);
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.25s ease-in-out;
}
#mermaidModal-0.mermaid-modal.active {
display: flex;
opacity: 1;
}
#mermaidModal-0 .mermaid-modal-content {
background: #ffffff;
padding: 56px 20px 20px;
border-radius: 12px;
width: 90vw;
height: 85vh;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
position: relative;
display: flex;
overflow: hidden;
}
#mermaidModal-0 .modal-svg-container {
width: 100%;
height: 100%;
display: flex;
overflow: auto;
}
#mermaidModal-0 .modal-svg-container svg {
flex: none;
margin: auto;
}
#mermaidModal-0 .mermaid-modal-tools {
position: absolute;
top: 12px;
left: 16px;
display: flex;
gap: 6px;
align-items: center;
z-index: 100;
}
#mermaidModal-0 .m-zoom-level {
font-size: 12px;
min-width: 44px;
text-align: center;
color: #555;
font-variant-numeric: tabular-nums;
}
#mermaidModal-0 .mermaid-modal-close {
position: absolute;
top: 12px;
right: 16px;
font-size: 28px;
font-weight: bold;
color: #555;
cursor: pointer;
background: none;
border: none;
line-height: 1;
z-index: 100;
}
#mermaidModal-0 .mermaid-modal-close:hover {
color: #000;
}
&lt;/style>
&lt;div id="mermaid-slider-0" class="mermaid-slider">
&lt;button class="m-zoom-btn" type="button" aria-label="Zoom diagram" title="Zoom">🔍&lt;/button>
&lt;div id="mermaidTarget-0" class="m-canvas">&lt;/div>
&lt;div class="m-controls">
&lt;button class="prevBtn" disabled>Prev&lt;/button>
&lt;button class="nextBtn">Next&lt;/button>
&lt;/div>
&lt;/div>
&lt;div id="mermaidModal-0" class="mermaid-modal">
&lt;div class="mermaid-modal-content">
&lt;div class="mermaid-modal-tools">
&lt;button class="m-tool-btn m-zoom-out" type="button" aria-label="Zoom out" title="Zoom out">−&lt;/button>
&lt;span class="m-zoom-level">100%&lt;/span>
&lt;button class="m-tool-btn m-zoom-in" type="button" aria-label="Zoom in" title="Zoom in">+&lt;/button>
&lt;button class="m-tool-btn m-zoom-reset" type="button" aria-label="Reset zoom" title="Reset">⟲&lt;/button>
&lt;/div>
&lt;button class="mermaid-modal-close" aria-label="Close modal">&amp;times;&lt;/button>
&lt;div class="modal-svg-container">&lt;/div>
&lt;/div>
&lt;/div>
&lt;script>
(function() {
function loadMermaid(callback) {
if (window.mermaid) {
callback();
return;
}
if (document.getElementById('mermaid-js-script')) {
document.getElementById('mermaid-js-script').addEventListener('load', callback);
return;
}
const script = document.createElement('script');
script.id = 'mermaid-js-script';
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
script.onload = callback;
document.head.appendChild(script);
}
loadMermaid(function() {
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
primaryColor: '#fcdbe2',
primaryBorderColor: '#d97d90',
primaryTextColor: '#333333',
lineColor: '#d97d90',
tertiaryColor: '#fff0f3'
}
});
window.mermaidInitialized = true;
}
const container = document.getElementById("mermaid-slider-0");
const target = document.getElementById("mermaidTarget-0");
const prevBtn = container.querySelector(".prevBtn");
const nextBtn = container.querySelector(".nextBtn");
const diagrams = ["---\ntitle: \"Allocation Tree for x\"\n---\ngraph TD\n R2[\"Root: stack allocation (alloca)\"]\n P2[\"%2 = alloca i32\"]\n S[\"store i32 1, ptr %2\"]\n C[\"call @opaque(ptr %2)\"]\n L[\"load i32, ptr %2\"]\n\n R2 --\u003e P2\n P2 --\u003e S\n P2 --\u003e C\n P2 --\u003e L","---\ntitle: \"Allocation Tree for p\"\n---\ngraph TD\n R1[\"Root: external allocation (belongs to the caller)\"]\n P0[\"%0 (function parameter)\"]\n C0[\"call @opaque(ptr %0)\"]\n\n R1 --\u003e P0\n P0 --\u003e C0"];
let currentIdx = 0;
async function render() {
target.innerHTML = `&lt;div class="mermaid">${diagrams[currentIdx].trim()}&lt;/div>`;
const el = target.querySelector('.mermaid');
el.removeAttribute('data-processed');
await mermaid.run({ nodes: [el] });
prevBtn.disabled = currentIdx === 0;
nextBtn.disabled = currentIdx === diagrams.length - 1;
}
function step(dir) {
currentIdx = Math.max(0, Math.min(diagrams.length - 1, currentIdx + dir));
render();
}
prevBtn.addEventListener("click", () => step(-1));
nextBtn.addEventListener("click", () => step(1));
render();
const modal = document.getElementById("mermaidModal-0");
const modalContainer = modal.querySelector(".modal-svg-container");
const closeBtn = modal.querySelector(".mermaid-modal-close");
const openBtn = container.querySelector(".m-zoom-btn");
const levelLabel = modal.querySelector(".m-zoom-level");
const MIN = 0.5, MAX = 6, STEP = 0.25;
let scale = 1;
function applyZoom() {
const svg = modalContainer.querySelector("svg");
if (!svg) return;
const pct = (scale * 100) + "%";
svg.style.setProperty("width", pct, "important");
svg.style.setProperty("height", pct, "important");
svg.style.setProperty("max-width", "none", "important");
svg.style.setProperty("max-height", "none", "important");
levelLabel.textContent = Math.round(scale * 100) + "%";
}
function setZoom(next) {
scale = Math.min(MAX, Math.max(MIN, next));
applyZoom();
}
function openModal() {
const svg = target.querySelector("svg");
if (!svg) return;
modalContainer.innerHTML = svg.outerHTML;
const copy = modalContainer.querySelector("svg");
copy.removeAttribute("style");
copy.removeAttribute("width");
copy.removeAttribute("height");
scale = 1;
applyZoom();
modalContainer.scrollTo(0, 0);
modal.classList.add("active");
document.body.style.overflow = "hidden";
}
function closeModal() {
modal.classList.remove("active");
document.body.style.overflow = "";
}
openBtn.addEventListener("click", openModal);
closeBtn.addEventListener("click", closeModal);
modal.querySelector(".m-zoom-in").addEventListener("click", () => setZoom(scale + STEP));
modal.querySelector(".m-zoom-out").addEventListener("click", () => setZoom(scale - STEP));
modal.querySelector(".m-zoom-reset").addEventListener("click", () => setZoom(1));
modalContainer.addEventListener("wheel", function(e) {
if (!(e.ctrlKey || e.metaKey)) return;
e.preventDefault();
setZoom(scale + (e.deltaY &lt; 0 ? STEP : -STEP));
}, { passive: false });
modal.addEventListener("click", function(e) {
if (e.target === modal) closeModal();
});
document.addEventListener("keydown", function(e) {
if (!modal.classList.contains("active")) return;
if (e.key === "Escape") closeModal();
if (e.key === "+" || e.key === "=") setZoom(scale + STEP);
if (e.key === "-") setZoom(scale - STEP);
if (e.key === "0") setZoom(1);
});
});
})();
&lt;/script>
&lt;p>There is so much more to explore here that it&amp;rsquo;s hard not to get carried away
because it&amp;rsquo;s truly fascinating. However, my original goal was to compare how C++
and Rust handle this, so let&amp;rsquo;s continue.&lt;/p>
&lt;h2 id="c-what-happens-if-we-cast-to-an-integer">C++: What Happens If We Cast to an Integer?
&lt;/h2>&lt;p>If we were to cast &lt;code>&amp;amp;x&lt;/code> to an integer, then LLVM must assume that anyone could
rebuild a pointer to &lt;code>x&lt;/code> from that integer.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">g&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">uintptr_t&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">uintptr_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">));&lt;/span> &lt;span class="c1">// address of x is now &amp;#39;exposed&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">opaque&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result is that the compiler gives up the optimization:&lt;/p>
&lt;iframe width="800px" height="200px" src="https://godbolt.org/e?hideEditorToolbars=true#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:2,endLineNumber:11,positionColumn:2,positionLineNumber:11,selectionStartColumn:2,selectionStartLineNumber:11,startColumn:2,startLineNumber:11),source:'%23include+%3Ccstdint%3E%0A%0Avoid+g(std::uintptr_t)%3B%0Avoid+opaque(int*)%3B%0A%0Aint+f(int*+p)+%7B%0A++++int+x+%3D+1%3B%0A++++g(reinterpret_cast%3Cstd::uintptr_t%3E(%26x))%3B+//+address+of+x+is+now+!'exposed!'%0A++++opaque(p)%3B%0A++++return+x%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:44.33887569279493,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:clang2310,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'-O2+',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+clang+23.1.0+(Editor+%231)',t:'0')),k:55.66112430720507,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4">&lt;/iframe>
&lt;p>That&amp;rsquo;s a pity if we only wanted to read the address and never intended to cast
it back to a pointer. Wouldn&amp;rsquo;t it be nice if we could communicate this intent to
the compiler?&lt;/p>
&lt;h2 id="rust-making-your-intent-explicit">Rust: Making Your Intent Explicit
&lt;/h2>&lt;p>This is exactly what Rust provides with its powerful provenance APIs.&lt;/p>
&lt;h3 id="inspecting-without-exposing">Inspecting Without Exposing
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>: &lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>: &lt;span class="kt">usize&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>p.addr()&lt;/code> extracts the raw memory address integer while telling Rust&amp;rsquo;s abstract
machine:&lt;/p>
&lt;blockquote>
&lt;p>I am reading this numeric address for inspection, but I guarantee I WILL NOT
cast it back to a valid pointer.&lt;/p>&lt;/blockquote>
&lt;p>Provenance is therefore not exposed.&lt;/p>
&lt;p>To mirror our previous experiments, we can try to see if the optimization was
avoided or not:&lt;/p>
&lt;iframe width="800px" height="200px" src="https://rust.godbolt.org/e?readOnly=true&amp;hideEditorToolbars=true#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:rust,selection:(endColumn:2,endLineNumber:14,positionColumn:2,positionLineNumber:14,selectionStartColumn:2,selectionStartLineNumber:14,startColumn:2,startLineNumber:14),source:'unsafe+extern+%22C%22+%7B%0A++++fn+g(addr:+usize)%3B%0A++++fn+opaque(p:+*mut+i32)%3B%0A%7D%0A%0A%23%5Bunsafe(no_mangle)%5D%0Apub+unsafe+fn+f_addr(p:+*mut+i32)+-%3E+i32+%7B%0A++++let+mut+x+%3D+1%3B%0A++++unsafe+%7B%0A++++++++g((%26raw+mut+x).addr())%3B%0A++++++++opaque(p)%3B%0A++++%7D%0A++++x%0A%7D'),l:'5',n:'0',o:'Rust+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:nightly,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:rust,libs:!(),options:'-C+opt-level%3D3++-C+debuginfo%3D0',overrides:!((name:edition,value:'2024')),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+rustc+nightly+(Editor+%231)',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4">&lt;/iframe>
&lt;p>What we see is that &lt;code>mov eax, dword ptr [rsp + 12]&lt;/code> reloads &lt;code>x&lt;/code> after
&lt;code>opaque(p)&lt;/code>, meaning that, at least for now, the optimization is given up. Same
as in the C++ case.&lt;/p>
&lt;p>Still, we can benefit from using the more expressive API, and not relying on
exposure semantics. It gives the compiler room to optimize in the future, and,
by telling Miri explicitly what we mean, it can better help us catch bugs.&lt;/p>
&lt;h3 id="preserving-provenance">Preserving Provenance
&lt;/h3>&lt;figure>
&lt;img src="michael-myers-FvuisLAN-rA-unsplash.jpg" alt="">
&lt;figcaption>
Pointers reproduce like rabbits and someone has to keep track of the family
tree.
&lt;p>Photo by &lt;a href="https://unsplash.com/@myers2021?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Michael Myers&lt;/a> on &lt;a href="https://unsplash.com/photos/a-group-of-rabbits-sitting-on-top-of-a-pile-of-hay-FvuisLAN-rA?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>&lt;/p>
&lt;/figcaption>
&lt;/figure>
&lt;p>In lock-free data structures, we sometimes need to store status flags in the
lower unused bits of a pointer. It&amp;rsquo;s a very cool trick, which left me with my
eyes wide open for a minute when I first read about it. I talk more about it in
&lt;a class="link" href="https://sofiabelen.github.io/projects/visualizing-crossbeam-epoch-based-reclamation/#q2-dissecting-crossbeamepochshared" target="_blank" rel="noopener"
>this
post&lt;/a>.
Essentially, due to alignment, the lower bits of pointers are &amp;ldquo;wasted&amp;rdquo; (always
0), so we can use them to store flags.&lt;/p>
&lt;p>What we can do is work with an intermediate pointer value, which will have the
flag bit set BUT that also inherits provenance from the original pointer. This
is exactly the functionality that &lt;code>with_addr&lt;/code> provides us. After we&amp;rsquo;re done, we
can recreate our valid initial pointer.&lt;/p>
&lt;p>The tagged value is stays a pointer rather than &amp;lsquo;usize&amp;rsquo; because an integer is
just a number and can&amp;rsquo;t carry provenance.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tagged&lt;/span>: &lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">with_addr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">revived_ptr&lt;/span>: &lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tagged&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">with_addr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tagged&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Keep in mind that &lt;code>tagged&lt;/code> isn&amp;rsquo;t dereferenceable, given that &lt;code>p.addr() | 1&lt;/code> is
misaligned for &lt;code>u32&lt;/code>. Dereferencing it would be UB.&lt;/p>
&lt;p>In short:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>addr()&lt;/code> reads the address without exposing provenance.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>with_addr()&lt;/code> builds a new pointer with a new address but the &lt;em>same&lt;/em>
provenance as the original.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="legacy-rust-similar-to-c">Legacy Rust, Similar To C++
&lt;/h3>&lt;p>In legacy code, we&amp;rsquo;d have to do something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">tagged_addr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">revived_ptr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tagged_addr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;*revived_ptr: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">revived_ptr&lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This code will run, but if we run it with Miri, it emits a warning:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-mysql" data-lang="mysql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">warning&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">integer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pointer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cast&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">--&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">src&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">rs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">revived_ptr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tagged_addr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">u32&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">integer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pointer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">cast&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">program&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">is&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">using&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">integer&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">pointer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">casts&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">or&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">equivalently&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">ptr&lt;/span>&lt;span class="p">::&lt;/span>&lt;span class="n">with_exposed_provenance&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">which&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">means&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Miri&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">might&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">miss&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pointer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bugs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">program&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">see&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">https&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">rust&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lang&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nightly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ptr&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">fn&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">with_exposed_provenance&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">html&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">more&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">details&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">operation&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ensure&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">that&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Miri&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">does&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">miss&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bugs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">in&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">your&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">program&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">use&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Strict&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Provenance&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">APIs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">https&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="o">//&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">rust&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">lang&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">org&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">nightly&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">ptr&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">index&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">html&lt;/span>&lt;span class="c1">#strict-provenance, https://crates.io/crates/sptr) instead
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">you&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">can&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">then&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">set&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">MIRIFLAGS&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="n">Zmiri&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">strict&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">provenance&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">to&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ensure&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">you&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">are&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">not&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">relying&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">on&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">with_exposed_provenance&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">semantics&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">help&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">alternatively&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="n">MIRIFLAGS&lt;/span>&lt;span class="o">=-&lt;/span>&lt;span class="n">Zmiri&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">permissive&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="n">provenance&lt;/span>&lt;span class="o">`&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">disables&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">this&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">warning&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">revived_ptr&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="n">warning&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">warning&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">emitted&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Actually, the reason that it runs is also interesting. What happens is similar
to the C++ example. &lt;code>p as usize&lt;/code> exposes the provenance of &lt;code>p&lt;/code>, and since the
reconstructed pointer points to the same allocation, provenance is recovered.&lt;/p>
&lt;p>Provenance would be lost if we did some pointer arithmetic and landed in a
different allocation.&lt;/p>
&lt;h3 id="casting-without-exposing-provenance">Casting Without Exposing Provenance
&lt;/h3>&lt;figure>
&lt;img src="dimitry-b-GNu4g4Z5jN4-unsplash.jpg" alt="">
&lt;figcaption>
Photo by &lt;a href="https://unsplash.com/@dimitry_b?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Dimitry B&lt;/a> on &lt;a href="https://unsplash.com/photos/brown-monkey-photograph-GNu4g4Z5jN4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/figcaption>
&lt;/figure>
&lt;p>To see what happens when provenance is &lt;strong>not&lt;/strong> exposed, we can use
&lt;code>ptr::without_provenance&lt;/code>, which creates a bare address with no allocation
behind it. (It&amp;rsquo;s useful for dangling or sentinel pointers, but it can&amp;rsquo;t be
dereferenced).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bare_ptr&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">ptr&lt;/span>::&lt;span class="n">without_provenance&lt;/span>::&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">u32&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">addr&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;*tagged_ptr: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">bare_ptr&lt;/span>&lt;span class="p">});&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As expected, Miri complains about a &lt;mark>&lt;em>dangling pointer&lt;/em>&lt;/mark> and tells us explicitly that &lt;mark>it has no provenance.&lt;/mark>&lt;/p>
&lt;pre>&lt;code>
error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got 0x20b28[noalloc] which is a &lt;mark>dangling pointer (it has no provenance)&lt;/mark>
--> src/main.rs:11:42
|
11 | println!("*tagged_ptr: {}", unsafe { *bare_ptr});
| ^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
&lt;/code>&lt;/pre>
&lt;h2 id="recap">Recap
&lt;/h2>&lt;p>Provenance, in short, is a hidden piece of information tracking &amp;ldquo;which
allocation&amp;rdquo; a pointer originates from, which LLVM uses for optimizations.&lt;/p>
&lt;p>In C++, &lt;code>uintptr_t&lt;/code> casts force the optimizer to prepare for the worst case by
disabling some optimizations.&lt;/p>
&lt;p>In Rust, &lt;code>addr()&lt;/code>, &lt;code>with_addr()&lt;/code> and &lt;code>without_provenance()&lt;/code> let you state your
intent explicitly: you only want the number, or you are deriving a new pointer
from an existing one. In principle, that gives the compiler room to optimize
more aggressively. In our experiment, however, the Rust version is unoptimized
too: &lt;code>x&lt;/code> was still reloaded from the stack. Using more expressive
APIs is still valuable though, as Miri can enforce the rules and catch bugs that
stem from unintended usage.&lt;/p>
&lt;p>I had planned to compare what Miri and ASan can catch, but I&amp;rsquo;ll save that for
another post. It was enough new info as it is and it takes some time to fully
wrap my head around it.&lt;/p>
&lt;p>I&amp;rsquo;m currently between jobs and looking for a Rust role, so writing these posts
keeps me motivated and has helped me find a sense of community. Thank you for
reading, and I hope you enjoyed it!&lt;/p></description></item></channel></rss>