<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.ralfj.de/blog/feed.xml</id>
    <link href="https://www.ralfj.de/blog/feed.xml" rel="self" type="application/atom+xml"/>
    
        <link href="https://www.ralfj.de/blog/"/>
    
    <updated>2026-03-13T00:00:00+01:00</updated>
    <author>
        <name>Ralf Jung</name>
    </author>
    <title>Ralf&apos;s Ramblings</title>

  <entry>
    <title>How to use storytelling to fit inline assembly into Rust</title>
    <link href="https://www.ralfj.de/blog/2026/03/13/inline-asm.html" />
    <updated>2026-03-13T00:00:00+01:00</updated>
    <id>https://www.ralfj.de/blog/2026/03/13/inline-asm.html</id>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;The Rust Abstract Machine is full of &lt;a href=&quot;/blog/2020/12/14/provenance.html&quot;&gt;wonderful oddities&lt;/a&gt; that do not exist on the &lt;a href=&quot;/blog/2019/07/14/uninit.html&quot;&gt;actual hardware&lt;/a&gt;.
Inevitably, every time this is discussed, someone asks: “But, what if I use inline assembly? What happens with provenance and uninitialized memory and Tree Borrows and all these other fun things you made up that don’t actually exist?”
This is a great question, but answering it properly requires some effort.
In this post, I will lay down my current thinking on how inline assembly fits into the Rust Abstract Machine by giving a &lt;em&gt;general principle&lt;/em&gt; that explains how anything we decide about the semantics of pure Rust impacts what inline assembly may or may not do.&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;p&gt;Note that everything I discuss here applies to FFI calls just as much as it applies to inline assembly.
Those mechanisms are fundamentally very similar: they allow Rust code to invoke code not written in Rust.&lt;sup id=&quot;fnref:xlang&quot;&gt;&lt;a href=&quot;#fn:xlang&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
I will not keep repeating “inline assembly or FFI” throughout the post, but every time I refer to inline assembly this is meant to also include FFI.&lt;/p&gt;

&lt;p&gt;To get started, let me explain why there are things that even inline assembly is fundamentally not allowed to do.&lt;/p&gt;

&lt;h2 id=&quot;why-cant-inline-assembly-do-whatever-it-wants&quot;&gt;Why can’t inline assembly do whatever it wants?&lt;/h2&gt;

&lt;p&gt;People like to think of inline assembly as freeing them from all the complicated requirements of the Abstract Machine.
Unfortunately, that’s a pipe dream.
Here is an example to demonstrate this:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;arch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;#[inline(never)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;innocent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Store 0 at the address given by x.&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;asm!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;mov dword ptr [{x}], 0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;innocent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When the compiler analyzes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, it realizes that only a shared reference is being passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innocent&lt;/code&gt;.
This means that whatever &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innocent&lt;/code&gt; does, it cannot change the value stored at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*x&lt;/code&gt;.
Therefore, the assertion can be optimized away.&lt;/p&gt;

&lt;p&gt;However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innocent&lt;/code&gt; actually does write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*x&lt;/code&gt;!
Therefore, the optimization changed the behavior of the program.
And indeed, this is &lt;a href=&quot;https://rust.godbolt.org/z/GG7YPsEcs&quot;&gt;exactly what happens&lt;/a&gt; with current versions of rustc:
without optimizations, the assertion fails, but with optimizations, it passes.
Therefore, either the optimization was wrong, or the program had Undefined Behavior.
And since this is an optimization that we really want to be able to perform, we can only pick the second option.&lt;sup id=&quot;fnref:nondet&quot;&gt;&lt;a href=&quot;#fn:nondet&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;However, where does the UB come from?
If the entire program was written in Rust, the answer would be “the aliasing model”.
Both Stacked Borrows and Tree Borrows, and any other aliasing model worth considering for Rust, will make it UB to write through pointers derived from a shared reference.
However, this time, parts of the program are not written in Rust, so things are not that simple.
How can we say that the inline asm block violated Tree Borrows, when it is written in a language that does not have anything even remotely comparable to Tree Borrows?
That’s what the rest of this post is about.&lt;/p&gt;

&lt;p&gt;I hope the example clearly demonstrates that we &lt;em&gt;cannot&lt;/em&gt; get away with having inline assembly just ignore Abstract Machine concepts such as Tree Borrows.
The inline asm block causes UB, we just have to figure out how and why—and more importantly, we have to figure out how people can ensure that their inline asm blocks do &lt;em&gt;not&lt;/em&gt; cause UB.&lt;/p&gt;

&lt;h2 id=&quot;when-is-inline-assembly-compatible-with-optimizations&quot;&gt;When is inline assembly compatible with optimizations?&lt;/h2&gt;

&lt;p&gt;It may seem like we now have to define a version of Tree Borrows that works with assembly code.
That would be an impossible task (Tree Borrows relies on pointer provenance, which does not exist in assembly).&lt;sup id=&quot;fnref:cheri&quot;&gt;&lt;a href=&quot;#fn:cheri&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
Lucky enough, this is also not necessary.&lt;/p&gt;

&lt;p&gt;Instead, we can piggy-back on the already existing definition of Tree Borrows and the rest of the Abstract Machine.
We do this by requiring the programmer to &lt;em&gt;tell a story&lt;/em&gt; about what the inline assembly block does in Rust terms.&lt;sup id=&quot;fnref:alice&quot;&gt;&lt;a href=&quot;#fn:alice&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;
(If this sounds strange, please bear with me. I will explain why this makes sense.)
Specifically, for every inline assembly block, there has to be a corresponding piece of Rust code that &lt;em&gt;does the same thing&lt;/em&gt; &lt;strong&gt;&lt;em&gt;as far as the state observable by pure Rust code is concerned&lt;/em&gt;&lt;/strong&gt;.
When reasoning about the behavior of the overall program, the inline assembly block then gets replaced by that “story” code.
You don’t have to actually write this code; what’s important is that the code exists and tells a coherent story with what the surrounding Rust code does.&lt;/p&gt;

&lt;p&gt;For our example above, this immediately explains what went wrong:
the story code for the inline assembly block would have to be something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(x as *const i32 as *mut i32).write(0)&lt;/code&gt;, and if we insert that code in place of the inline assembly block, we can immediately see (and Miri could confirm) that the program has UB.
An inline assembly block can have many possible stories, and it is enough to find &lt;em&gt;one&lt;/em&gt; story that makes everything work, but in this case, that is not possible.&lt;/p&gt;

&lt;p&gt;So, in slightly more detail, here is what I consider to be the rules for inline assembly:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;For every inline assembly block, pick a “story”: a piece of Rust code that serves as stand-in for what this inline assembly block does &lt;em&gt;to the Abstract Machine state&lt;/em&gt;.
This story code only has access to data that is made available to the inline assembly block (explicit operands and global variables).
When reasoning about soundness and correctness of the program on the Abstract Machine level, we pretend that the story code gets executed instead of the assembly code.&lt;/li&gt;
  &lt;li&gt;This piece of code has to satisfy all the requirements that are imposed on the asm block by attributes such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readonly&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nomem&lt;/code&gt; and honor operand constraints such as not mutating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in&lt;/code&gt; operands.&lt;/li&gt;
  &lt;li&gt;The actual assembly code has to &lt;em&gt;refine&lt;/em&gt; the story code, i.e., whatever the assembly code does to state which the Abstract Machine can observe (in particular, operands and global variables) has to be something that the story code could also have done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I should add the disclaimer that I do not have a formal theory that proves correctness of this approach.
However, I am reasonably confident, because this approach fits in very well with how we prove the correctness for optimizations such as the one in our example above:
At the heart of the correctness argument is a proof that &lt;em&gt;all&lt;/em&gt; Rust code satisfies some universal properties.
For instance, we can formalize and prove the claim that any Rust function which takes a shared reference without interior mutability as argument cannot write to that argument.
This isn’t the only such property; in fact the set of such properties isn’t fully known: we might discover a new property upheld by all Rust code tomorrow.
What’s crucial is that any property of the form “for all Rust programs, …” must also hold for the story code, since that is just regular Rust code!
Finally, because the actual assembly code refines the story code, we know that for the purpose of reasoning about the program, we can pretend that actually the story code gets executed and then, at the end of compilation, replace the story code by the desired assembly code without changing program behavior.&lt;/p&gt;

&lt;p&gt;So, that is why story code works.
But, doesn’t this make inline assembly entirely useless?
After all, the entire point of inline assembly is to do things I couldn’t already do in pure Rust!&lt;/p&gt;

&lt;h2 id=&quot;inline-assembly-stories-by-example&quot;&gt;Inline assembly stories by example&lt;/h2&gt;

&lt;p&gt;To convince you that the storytelling approach is feasible,
let us consider a few representative examples for inline assembly usage and what the corresponding story might look like.&lt;/p&gt;

&lt;h4 id=&quot;pure-instructions&quot;&gt;Pure instructions&lt;/h4&gt;

&lt;p&gt;The easiest case is code that wants to access a new hardware operation that is not exposed in the language.
For instance, the inline assembly block might consist of a single instruction that returns the number of bits set to 1 in a register.
Here, storytelling is trivial:
we can just write some Rust code that does a bit of bit manipulation by hand to count the number of bits set to 1.&lt;/p&gt;

&lt;h4 id=&quot;page-table-manipulation&quot;&gt;Page table manipulation&lt;/h4&gt;

&lt;p&gt;That was easy, so let us crank up the difficulty and consider an OS kernel that manipulates page tables.
Rust has no notion of page tables.
What could the “story” possibly look like here?&lt;/p&gt;

&lt;p&gt;The answer is that Rust has something that is very similar to putting a new page into the page table—it is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alloc&lt;/code&gt;.
It also has something very similar to removing a page (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dealloc&lt;/code&gt;), and to moving a page to a different location in the address space (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realloc&lt;/code&gt;).
So, the story that an OS kernel would tell the compiler is that manipulating page tables is really just a funny kind of allocator.&lt;/p&gt;

&lt;p&gt;Slightly more concretely, “allocating” a page in a way that is compatible with the storytelling approach could look like this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;First, some Rust code performs the actual page table manipulation using volatile loads and stores.&lt;sup id=&quot;fnref:volatile&quot;&gt;&lt;a href=&quot;#fn:volatile&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;Then an asm block executes whatever barrier is needed on the current system to ensure the updated page table has taken effect.&lt;/li&gt;
  &lt;li&gt;Next, the address of the page is cast to a pointer (using &lt;a href=&quot;https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with_exposed_provenance&lt;/code&gt;&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;Finally, Rust code may use that pointer to access the new page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The story of this asm block is that it performs memory allocation at the given address, which we know to be unallocated.&lt;sup id=&quot;fnref:alloc-control&quot;&gt;&lt;a href=&quot;#fn:alloc-control&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;
This creates a fresh provenance that represents the new allocation.
This allocation is then immediately &lt;a href=&quot;https://doc.rust-lang.org/std/primitive.pointer.html#method.expose_provenance&quot;&gt;exposed&lt;/a&gt; by the story code.&lt;/p&gt;

&lt;p&gt;Even for architectures where no barrier is needed after a page table change, the asm block is still crucial:
it prevents the compiler from reordering accesses to the new pages up across the page table manipulation!
Using the usual rules for Rust programs, there is no way the compiler could figure out that there is any sort of dependency here.
The asm block therefore serves as a compiler fence: as far as the compiler is concerned, this block might actually invoke the story code we made up,
and therefore the new pointer and operations based on it cannot be moved to before the asm block.&lt;/p&gt;

&lt;p&gt;This is why people sometimes think of asm blocks as compiler fences: an asm block stands in for some arbitrary “story code” the compiler doesn’t know,
so the compiler has to treat this code &lt;em&gt;as if&lt;/em&gt; some arbitrary code was executing here,
which prevents most reorderings.
But the emphasis here is on &lt;em&gt;most&lt;/em&gt;: if the compiler has extra aliasing information, such as from an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut&lt;/code&gt; type, that lets the compiler reason about and reorder memory accesses even across unknown function calls and, therefore, inline asm blocks.
It is therefore incorrect to say that an asm block is a barrier preventing all reordering.
Thinking in terms of compiler barriers can provide useful intuition, but a rigorous correctness argument needs to go into more detail.&lt;/p&gt;

&lt;p&gt;There is another caveat in this story: with page table manipulation, one cannot just create new allocations, one can also grow and shrink existing allocations.
In fact, the same is possible from userspace with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mmap&lt;/code&gt;.
It turns out that &lt;em&gt;growing&lt;/em&gt; allocations is harmless, so this has been &lt;a href=&quot;https://github.com/llvm/llvm-project/pull/141338&quot;&gt;officially blessed&lt;/a&gt; in LLVM and we should find a way to also expose this on the Rust side.
However, &lt;em&gt;shrinking&lt;/em&gt; allocations is problematic—there are &lt;a href=&quot;https://github.com/llvm/llvm-project/pull/141338&quot;&gt;simple optimizations&lt;/a&gt; that LLVM might reasonably perform that would break code which shrinks allocations!
So, further work is required to ensure that Rust code (as well as C and C++ code) can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;munmap&lt;/code&gt; without risking miscompilations.
This is why it is so important to take a principled approach to language semantics and correctness: it would otherwise be way too easy to miss potential problems like this.&lt;/p&gt;

&lt;h4 id=&quot;page-table-manipulation-ii-duplicating-pages&quot;&gt;Page table manipulation II: duplicating pages&lt;/h4&gt;

&lt;p&gt;Next, let us consider another case of page table shenanigans: mapping a single page of physical memory into multiple locations in virtual memory.
That means the page is “mirrored” in multiple places, and mutating any one mirror changes all of them.
First of all, note that in general, this is plain unsound.
LLVM will freely assume that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptr&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptr.wrapping_offset(4096)&lt;/code&gt; do not alias, so mapping the same memory into multiple places and freely accessing all of them can lead to subtle miscompilations.
However, there is a restricted form of this where we can use inline assembly to come up with a “story” that fits into the Abstract Machine, and is therefore sound.&lt;/p&gt;

&lt;p&gt;The key limitation is that the program only gets to use one of the “mirrored” version of this memory at a time.
Changing which mirror is “active” requires an explicit barrier and returns a new pointer that has to be used for future accesses.
This barrier can be an empty inline assembly block that just returns the pointer unchanged, but the story we attach to it is all but empty:
we will say that this behaves like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realloc&lt;/code&gt;, logically moving the allocation from one mirror to another.
In other words, as far as the Rust Abstract Machine is concerned, only one of the mirrored versions of memory actually “exists”, and switching to a different one amounts to freeing the old allocation and creating a new one.
Crucially, as usual with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realloc&lt;/code&gt;, after each such switch all the old pointers to that memory become invalid and the new pointer returned by the switch is the only way to access that memory.&lt;sup id=&quot;fnref:intptr&quot;&gt;&lt;a href=&quot;#fn:intptr&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;
These inline asm blocks will also prevent LLVM from reordering accesses to different “mirrors” around each other, thus avoiding the aforementioned miscompilations.
In other words, changing our code in a way that lets us tell a proper story also introduced enough structure to prevent the optimizer from doing things it shouldn’t do.&lt;/p&gt;

&lt;p&gt;This may sound a bit contrived, but such a “purely logical” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;realloc&lt;/code&gt; actually comes up in more than one situation;
there even is &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/3700&quot;&gt;an open RFC&lt;/a&gt; proposing to add it to the language proper.&lt;/p&gt;

&lt;h4 id=&quot;non-temporal-stores&quot;&gt;Non-temporal stores&lt;/h4&gt;

&lt;p&gt;The previous example already showed that some hardware features are too intrusive to be freely available inside a high-level language such as Rust.
Non-temporal stores are another example of this.
Specifically, I am referring to the “streaming” store operations on x86 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mm_stream_ps&lt;/code&gt; and friends).
The main point of these operations is to avoid cluttering the cache with data that is unlikely to be read again soon,
but they also have the unfortunate side effect of breaking the usual “total store order” memory model of x86.
This is bad news because the compilation of the rest of the program relies on that memory model.&lt;/p&gt;

&lt;p&gt;To explain the problem, let us consider what the “story” for a non-temporal store might be.
The obvious choice is to make it just a regular write access—caching is not modeled in the Abstract Machine, after all.
Unfortunately, this does not work.
Consider the case where the streaming store is followed by an atomic release write.
Due to the total store order model of x86, this compiles to a regular write instruction without any extra fences.
However, streaming stores actually &lt;em&gt;do&lt;/em&gt; require a fence (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mm_sfence&lt;/code&gt;) for proper synchronization.
Therefore, one can write a Rust program that seems to be data-race-free (according to the story) but actually has a data race.
In other words, rule 3 (the inline asm block must refine the story code) is violated.&lt;/p&gt;

&lt;p&gt;The principled fix for this is to extend the C++ memory model (which is shared by Rust) with a notion of non-temporal stores so that one can reason about how they interact with everything else that can go on in a concurrent program.
This is &lt;a href=&quot;https://people.mpi-sws.org/~viktor/papers/oopsla2024-inline.pdf&quot;&gt;possible&lt;/a&gt;, but it requires re-proving compiler correctness results, and at least the approach taken in that paper is architecture-specific which does not scale to the large number of architectures Rust supports.
However, there is a simpler alternative: we can try coming up with a more complicated story such that rule 3 is not violated.
This is exactly what a bunch of folks did when the issue around non-temporal stores was discovered.
The story says that doing a non-temporal store corresponds to &lt;em&gt;spawning a thread&lt;/em&gt; that will asynchronously perform the actual store,
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mm_sfence&lt;/code&gt; corresponds to waiting for all those threads to finish.
This explains why release-acquire synchronization fails: synchronization picks up all writes performed by the releasing thread, but the streaming store conceptually happened on a different thread!
The new story code was the basis for the updated &lt;a href=&quot;https://doc.rust-lang.org/nightly/core/arch/x86/fn._mm_sfence.html&quot;&gt;documentation&lt;/a&gt; for streaming stores on x86,
and the code itself can even be found in a &lt;a href=&quot;https://github.com/rust-lang/rust/blob/cb3046e5f2f0736366c0fea4977a8df579d96311/library/stdarch/crates/core_arch/src/x86/sse.rs#L1456-L1481&quot;&gt;comment in the code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is one caveat:
The story we picked implies that it is UB for the thread that performed the streaming store to do a load from that memory before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mm_sfence&lt;/code&gt;, even though this operation would be well-defined on the underlying hardware.
This is the price we pay in exchange for having a principled argument for why code using streaming stores will not be miscompiled.
It is not a high price: streaming stores are used for data that is unlikely to be read again soon, that is their entire point.
None of the examples of streaming stores we found in the wild had a problem with this limitation.&lt;sup id=&quot;fnref:forgotten-fence&quot;&gt;&lt;a href=&quot;#fn:forgotten-fence&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h4 id=&quot;stack-painting&quot;&gt;Stack painting&lt;/h4&gt;

&lt;p&gt;Another possible use for inline assembly is measuring the stack consumption of a program using stack painting.
This was brought up as a question in the t-opsem Zulip, and I am including it here because it is a nice demonstration of how much freedom the storytelling approach provides, and which limitations it has.&lt;/p&gt;

&lt;p&gt;Roughly speaking, stack painting means that before the program starts, the memory region that will later become the stack is filled with a fixed bitpattern.
Later, we can then measure the maximum stack usage of the program by checking where the bit pattern is still intact and where it has been overwritten.
This can be done with inline assembly code that simply directly reads from the stack.&lt;/p&gt;

&lt;p&gt;The first reflex might be to say that this is obviously UB:
that stack memory might be subject to noalias constraints (due to a mutable reference pointing to the stack); you can’t just read from memory that you don’t have permission to read.
However, that presupposes that the story for this asm block involves reading memory.
An alternative story is to say that the asm block just returns some arbitrary, non-deterministically chosen value.
The upside of this story is that, as long as the read doesn’t trap, the story is always correct according to our rules:
whatever the assembly code actually does, it surely refines returning an arbitrary value.
However, the downside of this story is that when reasoning about our code, we cannot make &lt;em&gt;any&lt;/em&gt; assumptions about the value we read!
Correctness of our program is defined under the storytelling semantics, i.e., the program has to be correct no matter which values are returned by the inline asm.
That may sound like a problem, but for this use case, it is actually entirely fine:
stack painting anyway provides just an estimate of the real stack usage.
The compiler makes no &lt;em&gt;guarantees&lt;/em&gt; that the measurement produced this way is remotely accurate, but experiments show that this works well in practice.
Incorrect measurements do not lead to soundness or correctness issues, so providing accurate answers is “just” a quality-of-life concern.&lt;/p&gt;

&lt;h4 id=&quot;floating-point-status-and-control-register&quot;&gt;Floating-point status and control register&lt;/h4&gt;

&lt;p&gt;The final example I want to consider are floating-point status and control registers.
This is an example where the storytelling approach mostly serves to explain why using these registers is not possible or not useful.&lt;/p&gt;

&lt;p&gt;Programmers sometimes want to read the status register to check if a floating-point exception has been raised, and to write the control register to adjust the rounding mode or other aspects of floating-point computations.
However, actually supporting such control register mutations is a disaster for optimizations:
the control register is global (well, thread-local) state, meaning it affects all subsequent operations until the register is changed again.
This means that to optimize any floating-point operation that might need rounding, the compiler has to statically predict what the value of the control register will be.
That’s usually not going to be possible, so what compilers do instead is they just assume that the control register always remains in its default state.
(Sometimes they provide ways to opt-out of that, but this is hard to do well and Rust currently has no facilities for this.)
The status register is less obviously problematic, but note that if we say that a floating-point operation can mutate the status register, then it is no longer a pure operation, and therefore it cannot be freely reordered.
To allow compilers to do basic optimizations like common subexpression elimination on floating-point operations, languages therefore generally also say that they consider the status register to be not observable.&lt;/p&gt;

&lt;p&gt;What does this mean for inline assembly code that reads/writes these registers?
For reading the status register, it means that the story code has no way of saying that this has anything to do with actual floating-point operations.
The Abstract Machine has no floating-point status bits that the story code could read, so the best possible story is to return a non-deterministic value.
This directly reflects the fact that the compiler makes no guarantees for the value that the program will observe in the status register, and since floating-point operations can be arbitrarily reordered, this should be taken quite literally.&lt;/p&gt;

&lt;p&gt;For writing the control register, there simply is no possible story: no Rust operation exists that would change the rounding mode of subsequent floating-point operations.
Any inline asm block that changes the rounding mode therefore has undefined behavior (and the same goes for other flags that change the behavior of instructions used by the Rust compiler, like flushing subnormals to zero).&lt;/p&gt;

&lt;p&gt;While this may sound bleak, it is entirely possible to write an inline asm block that changes the rounding mode, performs some floating-point operations, and then changes it back!
The story code for this block can use a soft-float library to perform exactly the same floating-point operations with a non-default rounding mode.
Crucially, since the asm block overall leaves the control register unchanged, the story code does not even have to worry about that register.
In other words, having a single big asm block that performs floating-point operations in a non-default rounding mode is fine.
This also makes sense from an optimization perspective: there is no risk of the compiler moving a floating-point operation into the region of code where the rounding mode is different.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope these examples were useful to demonstrate both the flexibility and limitations of the storytelling approach.
In many cases, the inability to come up with a story directly correlates with potential miscompilations.
This is great!
Those are the kinds of inline asm blocks that we &lt;em&gt;have&lt;/em&gt; to rule out as incorrect.&lt;sup id=&quot;fnref:noopt&quot;&gt;&lt;a href=&quot;#fn:noopt&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;
In some cases, however, there are no obvious miscompilations.
And indeed, if we knew exactly which universal properties of Rust programs the compiler relies on, we could allow inline asm code that satisfies all those universal properties, even if it has no story which can be expressed in Rust source code.
Unfortunately, this approach would require us to commit to the full set of universal properties the compiler may ever use.
If we discover a new universal property tomorrow, we cannot use it since there might be an inline asm block for which the universal property does not hold.&lt;/p&gt;

&lt;p&gt;This is why I am proposing to take the conservative approach:
only allow inline asm blocks that are obviously compatible with all universal properties of actual Rust code, because their story can be expressed as actual Rust code.
If there is an operation we want to allow that currently has no valid story, we should just &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/3700&quot;&gt;add&lt;/a&gt; a &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/3605&quot;&gt;new language operation&lt;/a&gt;, which corresponds to officially blessing that operation as one the compiler will keep respecting.&lt;/p&gt;

&lt;p&gt;Right now, we have no official documentation or guidelines for how inline asm blocks and FFI interact with Rust-level UB,
but as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innocent&lt;/code&gt; example at the top of the post shows, we cannot leave inline asm blocks unconstrained like that.
The storytelling approach is my proposal for filling that gap.
I plan to eventually suggest it as the official rules for inline assembly.
But before I do that, I’d like to be more confident that this approach really can handle most real-world scenarios.
If you have examples of assembly blocks that cannot be explained with storytelling, but that you are convinced are correct and hence should be supported, please let us know, either in the immediate &lt;a href=&quot;https://www.reddit.com/r/rust/comments/1rshm93/how_to_use_storytelling_to_fit_inline_assembly/&quot;&gt;discussion&lt;/a&gt; for this blog post or (if you are reading this later) in the &lt;a href=&quot;https://rust-lang.zulipchat.com/#narrow/channel/136281-t-opsem&quot;&gt;t-opsem Zulip channel&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h4&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:xlang&quot;&gt;
      &lt;p&gt;FFI has one extra complication that does not arise with inline assembly, and that is cross-language LTO. That is its own separate can of worms and outside the scope of this post. &lt;a href=&quot;#fnref:xlang&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:nondet&quot;&gt;
      &lt;p&gt;The secret third option is that the program might be non-deterministic and allows both behaviors, but that definitely does not apply here. &lt;a href=&quot;#fnref:nondet&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:cheri&quot;&gt;
      &lt;p&gt;I can already sense that some people want to bring up CHERI as an apparent counterexample. CHERI has capabilities, which look and feel a bit like pointer provenance, but they are nowhere near fine-grained enough for Tree Borrows, so capabilities and provenance are still distinct concepts that should not be confused with each other. &lt;a href=&quot;#fnref:cheri&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:alice&quot;&gt;
      &lt;p&gt;Credits go to Alice Ryhl for suggesting the term “telling a story” for this model. &lt;a href=&quot;#fnref:alice&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:volatile&quot;&gt;
      &lt;p&gt;Why am I insisting on volatile accesses here? Because if you had the page tables inside a regular Rust allocation, writes to that page table could have “interesting” effects, and that doesn’t really correspond to anything that can happen when you write to a normal Rust allocation. In other words, I haven’t (yet) come up with a proper story that would allow for those writes to be non-volatile. &lt;a href=&quot;#fnref:volatile&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:alloc-control&quot;&gt;
      &lt;p&gt;This assumes that we refine our specification of how memory allocation works in Rust so that there are regions of memory that “native” Rust allocations such as stack and static variables do not use, and that are instead entirely controlled by the program. If the only allocation operation a language has is “non-deterministic allocation anywhere in the address space”, this story does not work. &lt;a href=&quot;#fnref:alloc-control&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:intptr&quot;&gt;
      &lt;p&gt;Long-lived pointers into duplicated memory don’t really work because they might point to the wrong duplicate. But if that can be avoided, then you can just store them as integers and &lt;a href=&quot;https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html&quot;&gt;cast them to a pointer&lt;/a&gt; for every access; this avoids any long-lived provenance that might let the compiler apply normal allocation-based reasoning to this memory. &lt;a href=&quot;#fnref:intptr&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:forgotten-fence&quot;&gt;
      &lt;p&gt;All of the examples we found forgot to insert the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_mm_sfence&lt;/code&gt;, which was clearly unsound. Thanks to the story, we now have a clear idea &lt;em&gt;why&lt;/em&gt; it is unsound, i.e., which rule of the Rust language was violated. &lt;a href=&quot;#fnref:forgotten-fence&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:noopt&quot;&gt;
      &lt;p&gt;This assumes that we do not want to sacrifice the optimizations in question. Since inline assembly could hide inside any function call, this typically becomes a language-wide trade off: either we forbid such inline asm blocks, or we cannot do the optimization even in pure Rust code. &lt;a href=&quot;#fnref:noopt&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>

  <entry>
    <title>What&apos;s &quot;new&quot; in Miri (and also, there&apos;s a Miri paper!)</title>
    <link href="https://www.ralfj.de/blog/2025/12/22/miri.html" />
    <updated>2025-12-22T00:00:00+01:00</updated>
    <id>https://www.ralfj.de/blog/2025/12/22/miri.html</id>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;It is time for another “what is happening in Miri” post.
In fact this is &lt;em&gt;way&lt;/em&gt; overdue, with the &lt;a href=&quot;/blog/2022/07/02/miri.html&quot;&gt;previous update&lt;/a&gt; being from more than 3 years ago (what even is time?!?), but it is also increasingly hard to find the time to blog, so… here we are.
Better late than never. :)&lt;/p&gt;

&lt;p&gt;For the uninitiated, &lt;a href=&quot;https://github.com/rust-lang/miri/&quot;&gt;Miri&lt;/a&gt; is an &lt;a href=&quot;https://doc.rust-lang.org/reference/behavior-considered-undefined.html&quot;&gt;Undefined Behavior&lt;/a&gt; testing tool for Rust.
This means it can find bugs in your unsafe code where you failed to uphold requirements like “all accesses must be aligned” or “mutable references must never alias” or “there must not be any data races”.
Miri’s claim to fame is that it is a practical tool that can find &lt;em&gt;all de-facto Undefined Behavior in deterministic Rust programs&lt;/em&gt;.
To my knowledge, no other freely available tool can claim this—for any language.&lt;!-- MORE --&gt;&lt;sup id=&quot;fnref:relwork&quot;&gt;&lt;a href=&quot;#fn:relwork&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;We can only talk about &lt;em&gt;de-facto UB&lt;/em&gt; because Rust has not yet stabilized its definition of Undefined Behavior.
In lieu of that, we carefully check what the compiler does to ensure, to the best of our abilities, that all the UB which a Rust program can encounter &lt;em&gt;today&lt;/em&gt; is caught by Miri.
This means a program that passes Miri should be compiled correctly on &lt;em&gt;today’s&lt;/em&gt; compiler, but the same program might suffer from UB in the future.
Furthermore, if a Rust program is &lt;em&gt;non-deterministic&lt;/em&gt;, that means it can execute in more than one way, and Miri will only execute it once.
You can ask Miri to randomly explore multiple possible executions with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-Zmiri-many-seeds&lt;/code&gt;, but there might always be more executions that Miri did not find yet.
This is a fundamental limitation of all testing tools; you generally have to reach for model checking or deductive verification to overcome this.&lt;/p&gt;

&lt;p&gt;To learn more about Miri, you can read the &lt;a href=&quot;https://plf.inf.ethz.ch/research/popl26-miri.html&quot;&gt;&lt;strong&gt;paper&lt;/strong&gt;&lt;/a&gt;.
Yes, there’s a paper! That’s the first bit of news.
&lt;em&gt;“Miri: Practical Undefined Behavior Detection for Rust”&lt;/em&gt; has been accepted to POPL 2026, one of the most prestigious and competitive conferences for fundamental research in Programming Languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (2026-02-04):&lt;/strong&gt; A recording of the conference talk about this paper is now also &lt;a href=&quot;https://www.youtube.com/watch?app=desktop&amp;amp;v=9A8ZeDIStAs&quot;&gt;available online&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;miri-progress&quot;&gt;Miri progress&lt;/h2&gt;

&lt;p&gt;The paper aside, what progress has Miri made in the last three years?
We have landed over 1500 PRs in that time, so it is impossible to go into all the details, but I will do my best to highlight the general trends and point out anything major.&lt;/p&gt;

&lt;h3 id=&quot;shims&quot;&gt;Shims&lt;/h3&gt;

&lt;p&gt;The bread-and-butter of new features added to Miri is to add shims for functions that are implemented outside of Rust and hence cannot be directly executed by Miri.
This is mostly about operating system APIs as well as CPU vendor intrinsics.
The following list attempts to summarize which shims have been added to Miri since the previous update:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Greatly expand support for Windows API shims, covering in particular basic file access (by @beepster4096, @CraftSpider).&lt;/li&gt;
  &lt;li&gt;Support for various new file descriptor kinds on Unix and specifically Linux, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;socketpair&lt;/code&gt; (only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SOCK_STREAM&lt;/code&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipe&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventfd&lt;/code&gt; (by @DebugSteven, @tiif, @RalfJung, @FrankReh).&lt;/li&gt;
  &lt;li&gt;Support for Linux &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;epoll&lt;/code&gt; (by @tiif with some groundwork and extensions by @DebugSteven, @FrankReh, @RalfJung).&lt;/li&gt;
  &lt;li&gt;Broaden the general file API support (by @Pointerbender, @Jefffrey, @tiif, @newpavlov).&lt;/li&gt;
  &lt;li&gt;Support for many Intel vendor intrinsics covering SSE2 all the way up to AVX2 (mostly by @eduardosm with some help by @TDecking, @Kixunil). Thanks to @folkertdev, Miri even supports some AVX-512 intrinsics, making it a suitable &lt;a href=&quot;https://trifectatech.org/blog/emulating-avx-512-intrinsics-in-miri/&quot;&gt;testbed&lt;/a&gt; for code you may not be able to run on real hardware!&lt;/li&gt;
  &lt;li&gt;Support for basic functionality on FreeBSD (by @devnexen and @LorrensP-2158466).&lt;/li&gt;
  &lt;li&gt;Support for basic functionality on Illumos and Solaris (by @devnexen).&lt;/li&gt;
  &lt;li&gt;Support for basic functionality on Android (by @YohDeadfall).&lt;/li&gt;
  &lt;li&gt;Improve shims for pthread synchronization operations (by @Mandragorian, @LorrensP-2158466, @RalfJung).&lt;/li&gt;
  &lt;li&gt;Extend our support for macOS-specific APIs (by @joboet).&lt;/li&gt;
  &lt;li&gt;Support for weak definitions (by @bjorn3).&lt;/li&gt;
  &lt;li&gt;Support for miscellaneous small system APIs (by @folkertdev, @Mandragorian, @tgross35, @rayslava, @LorrensP-2158466, @YohDeadfall, @vishruth-thimmaiah, @saethlin, @RalfJung).&lt;/li&gt;
  &lt;li&gt;Support for global constructors that execute before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; (by @ibraheemdev).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;diagnostics&quot;&gt;Diagnostics&lt;/h3&gt;

&lt;p&gt;Our diagnostics have improved dramatically since the last blog post, mostly thanks to @saethlin.
For instance, a data race error now points at both of the accesses that caused the race:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` at alloc87
  --&amp;gt; tests/fail/data_race/read_write_race.rs:24:13
   |
24 | ...   *c.0 = 64; //~ ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread ...
   |       ^^^^^^^^^ (2) just happened here
   |
help: and (1) occurred earlier here
  --&amp;gt; tests/fail/data_race/read_write_race.rs:19:24
   |
19 |             let _val = *c.0;
   |                        ^^^^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A use-after-free error shows where the allocation this pointer points to got created and where it got freed:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: Undefined Behavior: memory access failed: alloc194 has been freed, so this pointer is dangling
 --&amp;gt; tests/fail/dangling_pointers/dangling_pointer_deref.rs:9:22
  |
9 |     let x = unsafe { *p }; //~ ERROR: has been freed
  |                      ^^ Undefined Behavior occurred here
  |
help: alloc194 was allocated here:
 --&amp;gt; tests/fail/dangling_pointers/dangling_pointer_deref.rs:6:17
  |
6 |         let b = Box::new(42);
  |                 ^^^^^^^^^^^^
help: alloc194 was deallocated here:
 --&amp;gt; tests/fail/dangling_pointers/dangling_pointer_deref.rs:8:5
  |
8 |     };
  |     ^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A Stacked Borrows error shows where the relevant pointer got created, and where it got invalidated:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error: Undefined Behavior: attempting a write access using &amp;lt;254&amp;gt; at alloc115[0x0], but that tag does not exist in the borrow stack for this location
 --&amp;gt; tests/fail/stacked_borrows/illegal_write2.rs:8:14
  |
8 |     unsafe { *target2 = 13 }; //~ ERROR: /write access .* tag does not exist in the borrow stack/
  |              ^^^^^^^^^^^^^ this error occurs as part of an access at alloc115[0x0..0x4]
  |
help: &amp;lt;254&amp;gt; was created by a SharedReadWrite retag at offsets [0x0..0x4]
 --&amp;gt; tests/fail/stacked_borrows/illegal_write2.rs:5:19
  |
5 |     let target2 = target as *mut _;
  |                   ^^^^^^
help: &amp;lt;254&amp;gt; was later invalidated at offsets [0x0..0x4] by a Unique retag
 --&amp;gt; tests/fail/stacked_borrows/illegal_write2.rs:6:10
  |
6 |     drop(&amp;amp;mut *target); // reborrow
  |          ^^^^^^^^^^^^
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;@Vanille-N implemented similar tracking for Tree Borrows, so its errors are on par with those emitted by Stacked Borrows.
@Zoxc also contributed logic to improve data race errors that involve the aliasing model.&lt;/p&gt;

&lt;h3 id=&quot;optimizations&quot;&gt;Optimizations&lt;/h3&gt;

&lt;p&gt;Miri is still not exactly fast, but some performance work helped significantly speed up Miri’s aliasing checks:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;@saethlin added a garbage collector for pointer tags to Miri, allowing Stacked Borrows to skip a lot of work related to tracking pointers that do not exist anymore.&lt;/li&gt;
  &lt;li&gt;@JojoDeveloping added various optimizations to the Tree Borrows checker.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;improved-concurrency-support&quot;&gt;Improved concurrency support&lt;/h3&gt;

&lt;p&gt;The data race checker and weak memory support in Miri was originally based on a paper that followed the C++11 concurrency semantics.
However, Rust is specified to use the C++20 semantics, which required some adjustments.
@cbeuw did the bulk of that work, with help by @SabrinaJewson and @michaliskok.
(See &lt;a href=&quot;https://plf.inf.ethz.ch/research/popl26-miri.html&quot;&gt;§4 in the paper&lt;/a&gt; for more details on this.)
As part of writing the paper, I also found and fixed two flaws in the core of the weak memory implementation.&lt;/p&gt;

&lt;p&gt;On top of this, @geetanshjuneja adjusted Miri’s scheduler to be fully non-deterministic, making it possible to find issues that would not arise with round-robin scheduling.
Furthermore, @pvdrz added support for entirely “virtual” timekeeping to the scheduler, allowing Miri to support a monotone clock in a fully deterministic way.
I also made Miri correctly enforce the restrictions for atomic accesses in read-only memory (which is mostly forbidden, with a few exceptions).&lt;/p&gt;

&lt;p&gt;Finally, @Patrick-6 has laid the groundwork for integrating GenMC into Miri.
GenMC is a weak memory model checker written by @michaliskok, which means that it can enumerate &lt;em&gt;all&lt;/em&gt; behaviors of a concurrent program (as long as the program has no unbounded loops).
By combining it with Miri, we can do full UB checking for all these executions.
(When I mentioned model checking in the introduction, that was indeed foreshadowing. :)
At the moment, using Miri+GenMC is still highly experimental, slow, and requires a custom build of Miri, but the first steps are taken and I am quite excited about the future potential of this combination!&lt;/p&gt;

&lt;h3 id=&quot;invoking-native-code-from-miri&quot;&gt;Invoking native code from Miri&lt;/h3&gt;

&lt;p&gt;Did you know that Miri can execute native code invoked from Rust via FFI?
This support is very experimental and incomplete, and obviously the native code then runs without any UB checking, but there have been significant improvements since the previous update:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;@Strophox has implemented support for sharing Rust-allocated memory with the native code.&lt;/li&gt;
  &lt;li&gt;@nia-e has added some truly cursed magic to let Miri trace quite precisely which memory the native code accesses, and use that to improve Miri’s UB checking. I hear she has plans for making this even more powerful in the future. :)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;miscellaneous&quot;&gt;Miscellaneous&lt;/h3&gt;

&lt;p&gt;Finally, we had some contributions that are large enough to be worth mentioning, but did not fit any of the categories above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The memory leak detector can now take into account main-thread thread-local storage (by @max-heller).&lt;/li&gt;
  &lt;li&gt;Our representation of file descriptions is a lot more flexible and extensible (by @Luv-Ray, @oli-obk, @RalfJung).&lt;/li&gt;
  &lt;li&gt;Miri now makes the precision of some floating-point operations non-deterministic to catch code incorrectly relying on precise or deterministic answers (by @LorrensP-2158466).&lt;/li&gt;
  &lt;li&gt;Miri non-deterministically causes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt; operations to only process a part of the buffer to catch programs that incorrectly rely on such operations reliably completing immediately (by @RalfJung).&lt;/li&gt;
  &lt;li&gt;Tree Borrows now by default tracks &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; as precisely as Stacked Borrows, catching UB when other bytes behind the same reference are incorrectly mutated (by @yoctocell, @JojoDeveloping).&lt;/li&gt;
  &lt;li&gt;Tree Borrows now supports wildcard provenance, so Miri can check programs that use integer-to-pointer casts and still catch some of the bugs involving those pointers (by @royAmmerschuber).&lt;/li&gt;
  &lt;li&gt;Miri can detect UB related to in-place (“move”) function arguments (by @RalfJung).&lt;/li&gt;
  &lt;li&gt;Miri supports precise profiling, tracking where all the execution time is spent (by @Stypox).&lt;/li&gt;
  &lt;li&gt;Miri no longer needs xargo, cutting down the setup effort (by @RalfJung).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of this, there has been a lot of bug-fixing as well as continuous refactoring and cleanup to keep the code maintainable.
Thank you to everyone who contributed.
If your name should be on that list, then I am sorry for forgetting you.&lt;/p&gt;

&lt;h2 id=&quot;how-you-can-help&quot;&gt;How you can help&lt;/h2&gt;

&lt;p&gt;If you want to help improve Miri, that’s awesome!
The &lt;a href=&quot;https://github.com/rust-lang/miri/issues&quot;&gt;issue tracker&lt;/a&gt; is a good place to start; the list of issues is short enough that you can just browse through it rather quickly to see if anything piques your interest.
The ones that are particularly suited for getting started are marked with a green label.
Another good starting point is to try to implement the missing bit of functionality that keeps your test suite from working.
That said, you should have gathered some Rust experience in a simpler project before tackling Miri; Miri is not a good codebase for your first steps in Rust.
However, if you already know Rust, Miri can be a fun and rewarding next challenge!
If you need any mentoring, just &lt;a href=&quot;https://rust-lang.zulipchat.com/#narrow/stream/269128-miri&quot;&gt;get in touch&lt;/a&gt;. :)&lt;/p&gt;

&lt;p&gt;We are currently looking for someone who wants to maintain Miri’s support for wasm targets.
The wasm API is quite different from other OS APIs, so maintaining those shims without the help of someone who actually knows and understands the wasm ecosystem turned out to not be sustainable.
Furthermore, we are also looking for someone with Android experience to serve as Android target maintainer for Miri.
This mostly means fixing Android-specific issues that come up due to changes in the standard library – which should be rare, but when it happens it is really useful to have someone to ping.
Android is also fairly close to passing our entire test suite, so you could make it your challenge to fix the remaining bits and pieces.&lt;/p&gt;

&lt;p&gt;That’s all for now!
I am immensely proud of what Miri has become, and deeply grateful to everyone who helped us get here.
Thanks to Miri and other tools based on Miri’s understanding of UB, avoiding Undefined Behavior in unsafe code becomes practically possible even at scale, while at the same time giving us a chance to specify UB in a way that satisfies the needs of real-world unsafe code out there.
This has worked out way better than I could have ever imagined,
and it would not have been possible without the entire amazing Rust community being on-board – I am looking forward to seeing what’s next. :D&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:relwork&quot;&gt;
      &lt;p&gt;Please let me know if there is such a tool and I just missed it! The paper discusses why sanitizers and valgrind, while being immensely useful tools, still miss some UB. I just know of one commercial tool that can make similar claims to Miri, the “TrustInSoft Analyzer”. It requires a license, so I can’t say how good its coverage of the C standard is; in particular, it would be interesting to compare what GCC and clang think is UB with what the tool thinks. In Miri, we spend a lot of time discussing with compiler folks to ensure we all have a shared understanding of what is and is not UB. In principle, this should not be needed in C since it has a standard; in practice, the standard can &lt;a href=&quot;https://dl.acm.org/doi/10.1145/2908080.2908081&quot;&gt;diverge pretty far&lt;/a&gt; from what programmers expect and from what compilers implement. &lt;a href=&quot;#fnref:relwork&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>

  <entry>
    <title>There is no memory safety without thread safety</title>
    <link href="https://www.ralfj.de/blog/2025/07/24/memory-safety.html" />
    <updated>2025-07-24T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2025/07/24/memory-safety.html</id>
    
        <category term="programming" label="Programming"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;Memory safety is all the rage these days.
But what does the term even mean?
That turns out to be harder to nail down than you may think.
Typically, people use this term to refer to languages that make sure that there are no use-after-free or out-of-bounds memory accesses in the program.
This is then often seen as distinct from other notions of safety such as thread safety, which refers to programs that do not have certain kinds of concurrency bugs.
However, in this post I will argue that this distinction isn’t all that useful, and that the actual property we want our programs to have is &lt;em&gt;absence of Undefined Behavior&lt;/em&gt;.&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;h2 id=&quot;breaking-memory-safety-with-a-data-race&quot;&gt;Breaking memory safety with a data race&lt;/h2&gt;

&lt;p&gt;My main issue with the division of safety into fine-grained classes such as memory safety and thread safety is that there’s no meaningful sense in which a thread-unsafe language provides memory safety.
To see what I mean by this, consider this program written in Go, which &lt;a href=&quot;https://en.wikipedia.org/wiki/Go_(programming_language)&quot;&gt;according to Wikipedia&lt;/a&gt; is memory-safe:&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Just some arbitrary interface so we can later use an interface type.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Two types implementing the interface, with fields of very different types.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// A global variable of interface type, that we will swap back and&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// forth between pointing to an `Int` and to a `Ptr`.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;globalVar&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Repeatedly invoke the interface method on the global variable.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repeat_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;globalVar&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Repeatedly change the dynamic type of the global variable.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repeat_swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;globalVar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myval&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;globalVar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repeat_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;repeat_swap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you run this program (e.g. on the &lt;a href=&quot;https://go.dev/play/p/SC-o_Q8e-aK&quot;&gt;Go playground&lt;/a&gt;), it will crash very quickly:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x2a pc=0x468863]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is a segfault, not a normal Go panic, so something has gone horribly wrong.
Note that the address that caused the segfault is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x2a&lt;/code&gt;, the hex representation of 42.
What is happening here?&lt;/p&gt;

&lt;p&gt;This example exploits that Go stores values of interface types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thing&lt;/code&gt; as pairs of a pointer to the data and a pointer to the vtable.
Every time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repeat_swap&lt;/code&gt; stores a new value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;globalVar&lt;/code&gt;, it just does two separate stores to update those two pointers.
In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;repeat_get&lt;/code&gt;, there’s thus a small chance that when we read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;globalVar&lt;/code&gt; &lt;em&gt;in between&lt;/em&gt; those two stores, we get a mix of a pointer to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt; with the vtable for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ptr&lt;/code&gt;.
When that happens, we will run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ptr&lt;/code&gt; version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get&lt;/code&gt;, which will dereference the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Int&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;val&lt;/code&gt; field as a pointer – and hence the program accesses address 42, and crashes.&lt;/p&gt;

&lt;p&gt;One could easily turn this example into a function that casts an integer to a pointer, and then cause arbitrary memory corruption.&lt;/p&gt;

&lt;h2 id=&quot;what-about-other-languages&quot;&gt;What about other languages?&lt;/h2&gt;

&lt;p&gt;At this point you might be wondering, isn’t this a problem in many languages?
Doesn’t Java also allow data races?
And yes, Java does allow data races, but the Java developers spent a lot of effort to ensure that even programs with data races remain entirely well-defined and memory safe.
They even developed the &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_memory_model&quot;&gt;first industrially deployed concurrency memory model&lt;/a&gt; for this purpose, many years before the C++11 memory model.
The result of all of this work is that in a concurrent Java program, you might see unexpected outdated values for certain variables, such as a null pointer where you expected the reference to be properly initialized, but you will &lt;em&gt;never&lt;/em&gt; be able to actually break the language and dereference an invalid dangling pointer and segfault at address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x2a&lt;/code&gt;.
In that sense, all Java programs are thread-safe.&lt;sup id=&quot;fnref:java-safe&quot;&gt;&lt;a href=&quot;#fn:java-safe&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Generally, there are two options a language can pursue to ensure that concurrency does not break memory safety:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Ensure that arbitrary concurrent programs still uphold the typing discipline and key language invariants. This comes at a significant cost, restricting the language to never assume consistency of multi-word values and limiting which optimizations the compiler can perform. This is the route most languages take, from Java to C#, OCaml, JavaScript, and WebAssembly.&lt;sup id=&quot;fnref:multi-word&quot;&gt;&lt;a href=&quot;#fn:multi-word&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;Have a strong enough type system to fully rule out data races on most accesses, and pay the cost of having to safely deal with races for only a small subset of memory accesses. This is the approach that Rust first brought into practice, and that Swift is now also adopting with their &lt;a href=&quot;https://developer.apple.com/documentation/swift/adoptingswift6&quot;&gt;“strict concurrency”&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go, unfortunately, chose to do neither of these.
This means it is, strictly speaking, not a memory safe language: the best the language can promise is that &lt;em&gt;if&lt;/em&gt; a program has no data races (or more specifically, no data races on problematic values such as interfaces, slices, and maps), then its memory accesses will never go wrong.
Now, to be fair, Go comes with out-of-the-box tooling to detect data races, which quickly finds the issue in my example.
However, in a real program, that means you have to hope that your test suite covers all the situations your program might encounter in practice, which is &lt;em&gt;exactly&lt;/em&gt; the sort of issue that a strong type system and static safety guarantees are intended to avoid.
It is therefore not surprising that &lt;a href=&quot;https://arxiv.org/pdf/2204.00764&quot;&gt;data races are a huge problem in Go&lt;/a&gt;,
and there is at least &lt;a href=&quot;https://old.reddit.com/r/rust/comments/wbejky/a_succinct_comparison_of_memory_safety_in_rust_c/iid990t/?context=2&quot;&gt;anecdotal evidence of actual memory safety violations&lt;/a&gt;.
Even experienced Go programmers do not always realize that you can break memory safety without using any unsafe operations or exploiting any compiler or language bugs.
Go is a language &lt;em&gt;designed&lt;/em&gt; for concurrent programming, so people do not expect footguns of this sort.
I think that is a problematic blind spot.&lt;/p&gt;

&lt;p&gt;Of course, as all things in language design, in the end this is a trade-off and the Go folks are &lt;a href=&quot;https://research.swtch.com/gorace&quot;&gt;well aware&lt;/a&gt; of the problem.&lt;sup id=&quot;fnref:gosafe&quot;&gt;&lt;a href=&quot;#fn:gosafe&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
Go made the simplest possible choice here, which is entirely in line with the general design of the language.
There’s nothing fundamentally wrong with that.
However, putting Go into the &lt;a href=&quot;https://www.memorysafety.org/docs/memory-safety/&quot;&gt;same bucket&lt;/a&gt; as languages that actually &lt;em&gt;did&lt;/em&gt; go through the effort of solving the problem with data races misrepresents the safety promises of the language.
The &lt;a href=&quot;https://go.dev/ref/mem&quot;&gt;Go memory model documentation&lt;/a&gt; is not exactly upfront about this point either: the “Informal Overview” emphasizes that “most races have a limited number of outcomes” and remarks that Go is unlike “C and C++, where the meaning of any program with a race is entirely undefined”.
You could say that the use of “most” here is foreshadowing, but this section does not list any cases where the number of outcomes is unlimited, so this is easy to miss.
They even go so far as to claim that Go is “more like Java or JavaScript”, which I think is rather unfair, given the lengths to which those languages went to achieve the thread safety they have.
Only &lt;a href=&quot;https://go.dev/ref/mem#restrictions&quot;&gt;a later subsection&lt;/a&gt; explicitly admits to the fact that &lt;em&gt;some&lt;/em&gt; races in Go &lt;em&gt;do&lt;/em&gt; have entirely undefined behavior (which is very unlike Java or JavaScript).&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I would argue that the actual property people care about when talking about memory safety is that &lt;em&gt;the program cannot break the language&lt;/em&gt;.
All these &lt;a href=&quot;https://alexgaynor.net/2020/may/27/science-on-memory-unsafety-and-security/&quot;&gt;security vulnerabilities caused by memory safety violations&lt;/a&gt; are cases where the code did something which isn’t even possible in the language specification, such as jumping to some user-provided array and &lt;em&gt;executing it as assembly code&lt;/em&gt;.
The typical term we use for a program that breaks the language like that is &lt;em&gt;Undefined Behavior&lt;/em&gt;.
The moment your program has UB, all bets are off; whether or not an attacker can then control how exactly this UB manifests and exploit it to their advantage is mostly an implementation detail.&lt;sup id=&quot;fnref:mitigations&quot;&gt;&lt;a href=&quot;#fn:mitigations&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;In my view, there’s a bright line dividing “safe” languages where programs cannot have Undefined Behavior, and “unsafe” languages where they can.
There’s no meaningful sense in which this can be further subdivided into memory safety, thread safety, type safety, and whatnot – it doesn’t matter &lt;em&gt;why&lt;/em&gt; your program has UB, what matters is that a program with UB defies the basic abstractions of the language itself, and this is a perfect breeding ground for vulnerabilities.
Therefore, we shouldn’t call a language “memory safe” if the language does not systematically prevent Undefined Behavior.&lt;/p&gt;

&lt;p&gt;In practice, of course, safety is not binary, it is a spectrum, and on that spectrum Go is much closer to a typical safe language than to C.
It is plausible that UB caused by data races is less useful for attackers than UB caused by direct out-of-bounds or use-after-free accesses.
But at the same time I think it is important to understand which safety guarantees a language reliably provides, and where the fuzzy area of trade-offs begins.
I am in the business of &lt;a href=&quot;https://research.ralfj.de/thesis.html&quot;&gt;&lt;em&gt;proving&lt;/em&gt;&lt;/a&gt; safety claims of languages, and for Go, there’s not much one could actually prove.
I hope this post helps you to better understand some the non-trivial consequences of the choices different languages have made.&lt;sup id=&quot;fnref:go&quot;&gt;&lt;a href=&quot;#fn:go&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; :)&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:java-safe&quot;&gt;
      &lt;p&gt;Java programmers will sometimes use the terms “thread safe” and “memory safe” differently than C++ or Rust programmers would. From a Rust perspective, Java programs are memory- and thread-safe by construction. Java programmers take that so much for granted that they use the same term to refer to stronger properties, such as not having “unintended” data races or not having null pointer exceptions. However, such bugs cannot cause segfaults from invalid pointer uses, so these kinds of issues are qualitatively very different from the memory safety violation in my Go example. For the purpose of this blog post, I am using the low-level Rust and C++ meaning of these terms. &lt;a href=&quot;#fnref:java-safe&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:multi-word&quot;&gt;
      &lt;p&gt;Some hardware supports larger-than-pointer-sized atomic accesses, which could be used to ensure consistency of multi-word values. However, Go slices are three pointers large, and as far as I know no hardware supports atomic accesses which are &lt;em&gt;that&lt;/em&gt; big. &lt;a href=&quot;#fnref:multi-word&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:gosafe&quot;&gt;
      &lt;p&gt;I tried to figure out whether the Go developers themselves consider their language to be memory safe, but was not able to reach a firm conclusion. The Go website does not take a stance on the matter. In &lt;a href=&quot;https://www.youtube.com/watch?v=rKnDgT73v8s&amp;amp;t=463s&quot;&gt;this 2009 talk&lt;/a&gt;, Rob Pike says memory safety is a goal of Go, but in &lt;a href=&quot;https://go.dev/talks/2012/splash.slide#49&quot;&gt;this 2012 slide deck&lt;/a&gt; he calls the language “not purely memory safe” since “sharing is legal”. &lt;a href=&quot;#fnref:gosafe&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:mitigations&quot;&gt;
      &lt;p&gt;I am aware that there’s a &lt;em&gt;lot&lt;/em&gt; one can do with this “implementation detail”; that’s basically what all the mitigation techniques from basic non-executable stacks to fancy control-flow integrity are doing. But from a principled, formal perspective, those are all just various forms of limiting how UB manifests. We should absolutely keep doing that, but we should also do everything we can to prevent UB from happening in the first place. &lt;a href=&quot;#fnref:mitigations&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:go&quot;&gt;
      &lt;p&gt;In case you are wondering why I am focusing on Go so much here… well, I simply do not know of any other language that claims to be memory safe, but where memory safety can be violated with data races. I originally wanted to write this blog post years ago, when Swift was pretty much in the same camp as Go in this regard, but Swift has meanwhile introduced “strict concurrency” and joined Rust in the small club of languages that use fancy type system techniques to deal with concurrency issues. That’s awesome! Unfortunately for Go, that means it is the only language left that I can use to make my point here. This post is not meant to bash Go, but it is meant to put a little-known weakness of the language into the spotlight, because I think it is an instructive weakness. &lt;a href=&quot;#fnref:go&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>

  <entry>
    <title>The Tree Borrows paper is finally published</title>
    <link href="https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html" />
    <updated>2025-07-07T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html</id>
    
        <category term="research" label="Research"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;After several years of work, our Tree Borrows paper has finally been presented recently at PLDI 2025 in Seoul.
Tree Borrows has not changed much compared to what has previously been mentioned in this blog and on &lt;a href=&quot;https://perso.crans.org/vanille/treebor/&quot;&gt;Neven’s website&lt;/a&gt;.
We used all that extra time for &lt;em&gt;formal proofs&lt;/em&gt; that Tree Borrows indeed allows at least some of the optimizations that we hope to gain from it,
and to carry out an extensive evaluation of Tree Borrows on the 30 000 most-downloaded crates on crates.io.
This overall package of implementation, proof, and evaluation impressed the PLDI program committee enough that we got a &lt;em&gt;distinguished paper award&lt;/em&gt;. :-)
Thanks a lot to Neven and Johannes for all the hard work and congratulations on an amazing paper!&lt;/p&gt;

&lt;p&gt;If you want to check out the paper yourself, everything is &lt;a href=&quot;https://plf.inf.ethz.ch/research/pldi25-tree-borrows.html&quot;&gt;available under open access&lt;/a&gt;.
Neven’s amazing talk presenting the paper &lt;a href=&quot;https://www.youtube.com/watch?v=CJi_Fcs4bak&quot;&gt;can be found here&lt;/a&gt;.&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>The current state of MiniRust</title>
    <link href="https://www.ralfj.de/blog/2025/07/02/minirust-talk.html" />
    <updated>2025-07-02T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2025/07/02/minirust-talk.html</id>
    
        <category term="research" label="Research"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;A few weeks ago, many Rust folks met in Utrecht for RustWeek and we all had a great time.
As part of that, I also gave a talk titled “MiniRust: A core language for specifying Rust” about the current state of MiniRust.
This was my first time giving a talk in a (fully packed) movie theater; unfortunately, my special effects budget cannot keep up with the shows that would usually be presented there.
But nevertheless, if you would like to learn more about my vision for how we should specify the gnarly details of unsafe Rust, &lt;a href=&quot;https://www.youtube.com/watch?v=yoeuW_dSe0o&quot;&gt;please go watch my talk&lt;/a&gt;. :)&lt;/p&gt;

&lt;p&gt;Thanks to everyone who was there for being a great audience, and thanks to the organizers for an amazing week and high-quality recordings!&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>Program Logics à la Carte</title>
    <link href="https://www.ralfj.de/blog/2025/01/15/program-logics-a-la-carte.html" />
    <updated>2025-01-15T00:00:00+01:00</updated>
    <id>https://www.ralfj.de/blog/2025/01/15/program-logics-a-la-carte.html</id>
    
        <category term="research" label="Research"/>
    
    <content type="html">
        
        
            &lt;p&gt;The first paper by a PhD student I supervise has been accepted into POPL:
“Program Logics à la Carte” will be presented in Denver next week.
The paper proposes a framework of reusable building blocks to simplify constructing a program logic with its associated soundness proof.
This is built in Iris, and is similar to how Iris provides reusable building blocks for the ingredients of a powerful separation logic—but so far, everything directly related to the actual language had to be re-done for each new language.
By using ITrees as shared foundation, we show how this can be overcome.&lt;/p&gt;

&lt;p&gt;Congratulations, Max, on making it into POPL!
To learn more, &lt;a href=&quot;https://plf.inf.ethz.ch/research/popl25-itree-program-logic.html&quot;&gt;check out the paper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; A recording of the talk is now &lt;a href=&quot;https://www.youtube.com/watch?v=Jff0pIbj8PM&amp;amp;t=14362s&quot;&gt;available online&lt;/a&gt;.&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>Rustlantis: Randomized Differential Testing of the Rust Compiler</title>
    <link href="https://www.ralfj.de/blog/2024/11/25/rustlantis.html" />
    <updated>2024-11-25T00:00:00+01:00</updated>
    <id>https://www.ralfj.de/blog/2024/11/25/rustlantis.html</id>
    
        <category term="research" label="Research"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;The first paper produced entirely by my group has recently been published at OOPSLA. :)
The paper is about fuzzing the optimizations and code generation of the Rust compiler by randomly generating MIR programs and ensuring they behave the same across different backends, different optimization levels, and in Miri.
The core part of this work was done by Andy (Qian Wang) for his &lt;a href=&quot;https://ethz.ch/content/dam/ethz/special-interest/infk/inst-pls/plf-dam/documents/StudentProjects/MasterTheses/2023-Andy-Thesis.pdf&quot;&gt;master thesis&lt;/a&gt;.
This was already a strong thesis, but Andy kept working on this even after he started having a regular dayjob, and we ended up with a very nice paper.
In total, he found 22 new bugs in the Rust compiler, 12 of them in the LLVM backend that has already been extensively fuzzed by prior work.&lt;/p&gt;

&lt;p&gt;To learn more, &lt;a href=&quot;https://plf.inf.ethz.ch/research/oopsla24-rustlantis.html&quot;&gt;check out the paper&lt;/a&gt; or &lt;a href=&quot;https://www.youtube.com/watch?v=kHYEHSHLffU&amp;amp;t=20447s&quot;&gt;watch Andy’s talk&lt;/a&gt; (the timestamp link seems unreliable, seek to the 5h40min mark if it doesn’t do that automatically).&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>What is a place expression?</title>
    <link href="https://www.ralfj.de/blog/2024/08/14/places.html" />
    <updated>2024-08-14T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2024/08/14/places.html</id>
    
        <category term="programming" label="Programming"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;One of the more subtle aspects of the Rust language is the fact that there are actually two kinds of expressions:
&lt;em&gt;value expressions&lt;/em&gt; and &lt;em&gt;place expressions&lt;/em&gt;.
Most of the time, programmers do not have to think much about that distinction, as Rust will helpfully insert automatic conversions when one kind of expression is encountered but the other was expected.
However, when it comes to unsafe code, a proper understanding of this dichotomy of expressions can be required.
Consider the following &lt;a href=&quot;https://play.rust-lang.org/?version=nightly&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=9a8802d20da16d6569510124c5827794&quot;&gt;example&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// As a &quot;packed&quot; struct, this type has alignment 1.&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;#[repr(packed)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyStruct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyStruct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This line is fine.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// But this line has UB!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// `ptr` is a pointer to `i32` and thus requires 4-byte alignment on&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// memory accesses, but `x` is just 1-aligned.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here I am using the unstable but &lt;a href=&quot;https://github.com/rust-lang/rust/pull/127679&quot;&gt;soon-to-be-stabilized&lt;/a&gt; “raw borrow” operator, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const&lt;/code&gt;.
You may know it in its stable form as a macro, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptr::addr_of!&lt;/code&gt;, but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; syntax makes the interplay of places and values more explicit so we will use it here.&lt;/p&gt;

&lt;p&gt;The last line has Undefined Behavior (UB) because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptr&lt;/code&gt; points to a field of a packed struct, which is not sufficiently aligned.
But how can it be the case that evaluating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr&lt;/code&gt; is UB, but evaluating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *ptr&lt;/code&gt; is fine?
Evaluating an expression should proceed by first evaluating the sub-expressions and then doing something with the result.
However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr&lt;/code&gt; is a sub-expression of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *ptr&lt;/code&gt;, and we just said that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr&lt;/code&gt; is UB, so shouldn’t &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *ptr&lt;/code&gt; also be UB?
That is the topic of this post.&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;p&gt;(You might have already encountered the distinction of place expressions and value expressions in C and C++, where they are called lvalue expressions and rvalue expressions, respectively.
While the basic syntactic concept is the same as in Rust, the exact cases that are UB are different, so we will focus entirely on Rust here.)&lt;/p&gt;

&lt;h3 id=&quot;making-the-implicit-explicit&quot;&gt;Making the implicit explicit&lt;/h3&gt;

&lt;p&gt;The main reason why this dichotomy of place expressions and value expressions is so elusive is that it is entirely implicit.
Therefore, to understand what actually happens in code like the above, the first step is to add some new syntax that lets us make this implicit distinction explicit in the code.&lt;/p&gt;

&lt;p&gt;Normally, we may think of (a fragment of) the grammar of Rust expressions roughly as follows:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Expr&lt;/em&gt; ::= &lt;br /&gt;
   &lt;em&gt;Literal&lt;/em&gt; | &lt;em&gt;LocalVar&lt;/em&gt; | &lt;em&gt;Expr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;em&gt;Expr&lt;/em&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; &lt;em&gt;BorMod&lt;/em&gt; &lt;em&gt;Expr&lt;/em&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;em&gt;Expr&lt;/em&gt; | &lt;br /&gt;
   &lt;em&gt;Expr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; &lt;em&gt;Field&lt;/em&gt; | &lt;em&gt;Expr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;Expr&lt;/em&gt; | … &lt;br /&gt;
&lt;em&gt;BorMod&lt;/em&gt; ::= &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;​&lt;/code&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mut&lt;/code&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mut&lt;/code&gt; &lt;br /&gt;
&lt;em&gt;Statement&lt;/em&gt; ::= &lt;br /&gt;
   &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; &lt;em&gt;LocalVar&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;Expr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt; | …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This directly explains why we can write expressions like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr = *other_ptr + my_var&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, to understand places and values, it is instructive to consider a different grammar that explicitly has two kinds of expressions.
I will first give the grammar, and then explain it with some examples:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;ValueExpr&lt;/em&gt; ::= &lt;br /&gt;
   &lt;em&gt;Literal&lt;/em&gt; | &lt;em&gt;ValueExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; &lt;em&gt;ValueExpr&lt;/em&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; &lt;em&gt;BorMod&lt;/em&gt; &lt;em&gt;PlaceExpr&lt;/em&gt; | &lt;br /&gt;
   &lt;em&gt;PlaceExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;ValueExpr&lt;/em&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; &lt;em&gt;PlaceExpr&lt;/em&gt; &lt;br /&gt;
&lt;em&gt;PlaceExpr&lt;/em&gt; ::= &lt;br /&gt;
   &lt;em&gt;LocalVar&lt;/em&gt; | &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; &lt;em&gt;ValueExpr&lt;/em&gt; | &lt;em&gt;PlaceExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; &lt;em&gt;Field&lt;/em&gt; &lt;br /&gt;
&lt;em&gt;Statement&lt;/em&gt; ::= &lt;br /&gt;
   &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; &lt;em&gt;LocalVar&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;ValueExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt; | …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Value expressions&lt;/em&gt; are those expressions that compute a value: literals like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;, computations like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5 + 7&lt;/code&gt;,
but also expressions that compute values of pointer type like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;my_var&lt;/code&gt;.
However, the expression &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_var&lt;/code&gt; (referencing a local variable), according to this grammar, is &lt;em&gt;not&lt;/em&gt; a value expression, it is a &lt;em&gt;place expression&lt;/em&gt;.
This is because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my_var&lt;/code&gt; actually denotes a place in memory, and there’s multiple things one can do with a place:
one can load the contents of the place from memory (which produces a value), one can create a pointer to the place (which also produces a value, but does not access memory at all),
or one can store a value into this place (which in Rust produces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;()&lt;/code&gt; value, but the side-effect of changing the contents of memory is more relevant).
Besides local variables, the other main example of a place expression is the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; operator, which takes a &lt;em&gt;value&lt;/em&gt; (of pointer type) and turns it into a place.&lt;sup id=&quot;fnref:deref&quot;&gt;&lt;a href=&quot;#fn:deref&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
Furthermore, given a place of struct type, we can use a field projection to obtain a place just for that field.&lt;/p&gt;

&lt;p&gt;This may sound odd, because it means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let new_var = my_var;&lt;/code&gt; is not actually a valid statement in our grammar!
To accept this code, the Rust compiler will automatically convert this statement into a form that fits the grammar by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; whenever needed.&lt;sup id=&quot;fnref:desugar&quot;&gt;&lt;a href=&quot;#fn:desugar&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; takes a place and, as the name indicates, performs a load from memory to obtain the value currently stored in this place.
The desugared form of the statement therefore is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let new_var = load my_var;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To consider a more complicated example, the assignment expression &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr = *other_ptr + my_var&lt;/code&gt; mentioned above desugars to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*(load ptr) = load *(load other_ptr) + load my_var&lt;/code&gt;.
That’s a lot of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; expressions!
It is instructive to convince yourself that every one of them is necessary to make this term fit the grammar.
In particular, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; works on a value expression (so we need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load other_ptr&lt;/code&gt; to obtain the value stored in this place) and produces a place expression (so we need to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; again to obtain a value expression that we can use with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;).
However, the left-hand side of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; is a place expression, so we do not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; there.&lt;/p&gt;

&lt;p&gt;Since the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; operator is introduced implicitly, it is sometimes referred to a “place-to-value coercion”.
Understanding where place-to-value coercions or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; expressions are introduced is the key to understanding the example at the top of this blog post.
So let us write the relevant part of that example again, using our more explicit grammar:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This line is fine.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// But this line has UB!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Suddenly, it makes perfect sense why the last line has UB but the previous one does not!
The expression &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *(load ptr)&lt;/code&gt; merely computes the place &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*(load ptr)&lt;/code&gt; &lt;em&gt;without ever loading from it&lt;/em&gt;, and then uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const&lt;/code&gt; to turn that place into a value.
This is worth repeating: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; operator, usually referred to as “dereferencing a pointer”, &lt;em&gt;does not access memory in any way&lt;/em&gt;.
All it does is take a value of pointer type, and convert it into a place.
This is a pure operation that can never fail.
In the last line, there is an extra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; applied to the result of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;, and &lt;em&gt;that&lt;/em&gt; is where a memory access happens—and in this case, UB occurs since the place is not sufficiently aligned.&lt;/p&gt;

&lt;p&gt;It is completely legal to evaluate a place expression that produces an unaligned place, and it is also legal to then turn that unaligned place into a raw pointer value.
Generally, in terms of UB, you should think of places as being pretty much like raw pointers: there is no requirement that they point to valid values, or even to existing memory.&lt;sup id=&quot;fnref:field&quot;&gt;&lt;a href=&quot;#fn:field&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;
However, it is &lt;em&gt;not&lt;/em&gt; legal to load from (or store to) an unaligned place, which is why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load *(load ptr)&lt;/code&gt; is UB.&lt;/p&gt;

&lt;p&gt;In other words, when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr&lt;/code&gt; is used as a value expression (as it is in our example), then it is &lt;em&gt;not&lt;/em&gt; a sub-expression of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *ptr&lt;/code&gt; because the implicit place-to-value coercion adds an extra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*ptr&lt;/code&gt; that is not added in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;raw const *ptr&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;other-examples-of-place-expression-surprises&quot;&gt;Other examples of place expression surprises&lt;/h3&gt;

&lt;p&gt;The other main example where place expressions can lead to surprising behavior is in combination with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; pattern.
For instance:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is fine!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is UB.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the grammar above cannot represent this program: in the full grammar of Rust, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; syntax is something like “&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; &lt;em&gt;Pattern&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; &lt;em&gt;PlaceExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt;”,
and then pattern desugaring decides what to do with that place expression.
If the pattern is a binder (the common case), a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; gets inserted to compute the initial value for the local variable that this binder refers to.
However, if the pattern is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;, then the place expression still gets evaluated—but the result of that evaluation is simply discarded.
MIR uses a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PlaceMention&lt;/code&gt; statement to indicate these semantics.&lt;/p&gt;

&lt;p&gt;In particular, this means that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; pattern does &lt;em&gt;not&lt;/em&gt; incur a place-to-value coercion!
The desugared form of the relevant part of this code is:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;PlaceMention&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is fine!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is UB.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the first line does not actually load from the pointer (the only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; is there to load the pointer itself from the local variable that stores it).
No value is ever constructed when a place expression is used with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; pattern.
In contrast, the last line actually creates a new local variable, and therefore a place-to-value coercion is inserted to compute the initial value for that variable.&lt;/p&gt;

&lt;p&gt;The same also happens with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; statements:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;happy&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is fine!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_val&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;not happy&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is UB.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The scrutinee of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; expression is a place expression, and if the pattern is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; then a value is never constructed.
However, when an actual binder is present, this introduces a local variable and a place-to-value coercion is inserted to compute the value that will be stored in that local variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; blocks.&lt;/strong&gt;
Note that wrapping an expression in a block forces it to be a value expression.
This means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe { *ptr }&lt;/code&gt; always loads from the pointer!
In other words:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is fine!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This is UB.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The fact that braces force a value expression can occasionally be useful, but the fact that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; blocks do that is definitely quite unfortunate.&lt;/p&gt;

&lt;h3 id=&quot;are-there-also-value-to-place-coercions&quot;&gt;Are there also value-to-place coercions?&lt;/h3&gt;

&lt;p&gt;So far, we have discussed what happens when a place expression is encountered in a spot where a value expression was expected.
But what about the opposite case?
Consider:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;According to our grammar, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; (in this case with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mut&lt;/code&gt; modifier) needs a place expression, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15&lt;/code&gt; is a value expression.
How can the Rust compiler accept such code?&lt;/p&gt;

&lt;p&gt;In this case, the desugaring involves introducing new “temporary” local variables:&lt;/p&gt;
&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The exact scope in which this temporary is introduced is defined by &lt;a href=&quot;https://github.com/rust-lang/lang-team/blob/master/design-meeting-minutes/2023-03-15-temporary-lifetimes.md&quot;&gt;non-trivial rules&lt;/a&gt; that are outside the scope of this blog post;
the key point is that this transformation again makes the program valid according to the more explicit grammar.&lt;/p&gt;

&lt;p&gt;There is one exception to this rule, which is the left-hand side of an assignment operator: if you write something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15 = 12 + 19&lt;/code&gt;, the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;15&lt;/code&gt; is not turned into a temporary place, and instead the program is rejected.
Introducing temporaries here is very unlikely to produce a meaningful result, so there’s no good reason to accept such code.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Whenever we are using a place expression where a value is expected, or a value expression where a place is expected, the Rust compiler implicitly transforms our program into a form that matches the grammar given above.
If you are only writing safe code, you can almost always entirely forget about this transformation.
However, if you are writing unsafe code and want to understand why some programs have UB and others do not, it can be crucial to understand what exactly happens.
If you only remember one thing from this blog post, then remember that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; dereferences a pointer but &lt;em&gt;does not load from memory&lt;/em&gt;; instead, all it does is turn the pointer into a place—it is the subsequent implicit place-to-value conversion that performs the actual load.
I hope that giving a name to this implicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;load&lt;/code&gt; operator can help demystify the topic of places and values. :)&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:deref&quot;&gt;
      &lt;p&gt;&lt;strong&gt;Update (2025-12-26):&lt;/strong&gt; If you check the &lt;a href=&quot;https://doc.rust-lang.org/reference/expressions.html#r-expr.place-value.place-context&quot;&gt;Rust reference&lt;/a&gt;, you may notice that it actually says that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; takes a &lt;em&gt;place&lt;/em&gt; expression. This is a somewhat peculiar Rust design choice related to custom smart pointers implementing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deref&lt;/code&gt; trait, and related to borrow checking. That turns out to be easier if you only dereference places. However, when it comes to the operational semantics, the overall picture is much cleaner if we say that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; works on values. &lt;a href=&quot;#fnref:deref&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:desugar&quot;&gt;
      &lt;p&gt;The Rust compiler does not actually explicitly do such a desugaring, but this happens implicitly as part of compiling the program into MIR form. &lt;a href=&quot;#fnref:desugar&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:field&quot;&gt;
      &lt;p&gt;One subtlety, however, is that the &lt;em&gt;PlaceExpr&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; &lt;em&gt;Field&lt;/em&gt; expression performs &lt;em&gt;in-bounds&lt;/em&gt; pointer arithmetic using the rules of the &lt;a href=&quot;https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.offset&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offset&lt;/code&gt; method&lt;/a&gt;. This is the one case where a place expression does care about pointing to existing memory. This is unfortunate, but optimizations greatly benefit from this rule and since the introduction of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offset_of!&lt;/code&gt; macro, it should be extremely rare that unsafe code would want to do a field projection on a dangling pointer. &lt;a href=&quot;#fnref:field&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>

  <entry>
    <title>Sandboxing All The Things with Flatpak and BubbleBox</title>
    <link href="https://www.ralfj.de/blog/2024/04/14/bubblebox.html" />
    <updated>2024-04-14T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2024/04/14/bubblebox.html</id>
    
        <category term="sysadmin" label="Sysadmin"/>
    
    <content type="html">
        
        
            &lt;p&gt;A few years ago, I have &lt;a href=&quot;/blog/2019/03/09/firejail.html&quot;&gt;blogged&lt;/a&gt; about my approach to sandboxing less-trusted applications that I have to or want to run on my main machine.
The approach has changed since then, so it is time for an update.&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;p&gt;Over time I grew increasingly frustrated with Firejail: configurations would frequently break on updates,
and debugging Firejail profiles is extremely hard. When considering all the included files, we are talking
about many hundred lines of configuration with a subtle interplay of allowlists and blocklists.
Even when I knew which folder I wanted to give access to, it was often non-trivial to ensure that
this access would actually be possible.&lt;/p&gt;

&lt;p&gt;Now I am instead using a combination of two different approaches: Flatpak and BubbleBox.&lt;/p&gt;

&lt;h2 id=&quot;flatpak&quot;&gt;Flatpak&lt;/h2&gt;

&lt;p&gt;The easiest sandbox to maintain is the sandbox maintained by someone else.
So when a Flatpak exists for software I want to or have to use, such as Signal or Zoom, that is generally my preferred approach.&lt;/p&gt;

&lt;p&gt;Unfortunately, Flatpaks can come with extremely liberal default profiles that make the sandbox mostly pointless.
The following global overrides help ensure that this does not happen:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Context]
sockets=!gpg-agent;!pcsc;!ssh-auth;!system-bus;!session-bus
filesystems=~/.XCompose:ro;xdg-config/fontconfig:ro;!~/.gnupg;!~/.ssh;!xdg-documents;!home;!host

[Session Bus Policy]
org.freedesktop.Flatpak=none
org.freedesktop.secrets=none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also use &lt;a href=&quot;https://flathub.org/apps/com.github.tchx84.Flatseal&quot;&gt;Flatseal&lt;/a&gt;, an amazing application that helps to check which permissions applications get, and change them if necessary.&lt;/p&gt;

&lt;h2 id=&quot;bubblebox&quot;&gt;BubbleBox&lt;/h2&gt;

&lt;p&gt;However, not all software exists as Flatpak.
Also, sometimes I want software to run basically on my host system (i.e., to use the regular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr&lt;/code&gt;), just without access to literally &lt;em&gt;everything&lt;/em&gt; in my home directory.
Examples of this are Factorio and VSCodium.
The latter doesn’t work in Flatpak as I want to use it with LaTeX, and realistically this means it needs to run the LaTeX installed via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;.
The official recommendation is to effectively disable the Flatpak sandbox, but that entirely defeats the point, so I went looking for alternatives.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/containers/bubblewrap&quot;&gt;bubblewrap&lt;/a&gt; provides a very convenient solution: it can start an application in its own private filesystem namespace with full control over which part of the host file system is accessible from inside the sandbox.
I wrote a small wrapper around bubblewrap to make this configuration a bit more convenient to write and manage;
this project is called &lt;a href=&quot;/projects/bubblebox&quot;&gt;BubbleBox&lt;/a&gt;.
This week-end I finally got around to adding support for &lt;a href=&quot;https://github.com/flatpak/xdg-dbus-proxy&quot;&gt;xdg-dbus-proxy&lt;/a&gt; so that sandboxed applications can now access particular D-Bus functions without having access to the entire bus (which is in general not safe to expose to a sandboxed application).
That means it’s finally time to blog about this project, so here we go – if you are interested, check out &lt;a href=&quot;/projects/bubblebox&quot;&gt;BubbleBox&lt;/a&gt;;
the project page explains how you can use it to set up your own sandboxing.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I should also note that this is not the only bubblewrap-based sandboxing solution.
&lt;a href=&quot;https://github.com/igo95862/bubblejail&quot;&gt;bubblejail&lt;/a&gt; is fairly similar but provides a configuration GUI and a good set of default provides;
it was a very useful resource when figuring out the right bubblewrap flags to make complex GUI applications work properly.
(Incidentally, “bubblejail” is also how I called my own script originally, but then I realized that the name is already taken.)
Joachim Breitner also recently &lt;a href=&quot;https://www.joachim-breitner.de/blog/812-Convenient_sandboxed_development_environment&quot;&gt;blogged&lt;/a&gt; about his own bubblewrap-based sandboxing script.
sloonz has a similar &lt;a href=&quot;https://gist.github.com/sloonz/4b7f5f575a96b6fe338534dbc2480a5d&quot;&gt;script&lt;/a&gt; as well, with a nice yaml-based configuration format and &lt;a href=&quot;https://sloonz.github.io/posts/sandboxing-1/&quot;&gt;great explanations&lt;/a&gt; for what all the flags exactly do.
Had their script existed when I started what eventually became BubbleBox, I would have used it as a starting point.
But it was also fun to figure out my own solution.&lt;/p&gt;

&lt;p&gt;Using bubblewrap and xdg-dbus-proxy for this was an absolute joy.
Both of these components came out of the Flatpak project, but the authors realized that they could be independently useful,
so in best Unix tradition they turned them into tools that provide all the required mechanism without hard-coding any sort of policy.
Despite doing highly non-trivial tasks, they are both pretty easy to use and compose and very well-documented.
Thanks a lot to everyone involved!&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;One day I should probably rewrite this in Rust… maybe this will be my test project for when &lt;a href=&quot;https://rust-lang.github.io/rfcs/3424-cargo-script.html&quot;&gt;cargo-script&lt;/a&gt; becomes available. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>

  <entry>
    <title>Google Open Source Peer Bonus</title>
    <link href="https://www.ralfj.de/blog/2023/12/27/open-source-peer-bonus.html" />
    <updated>2023-12-27T00:00:00+01:00</updated>
    <id>https://www.ralfj.de/blog/2023/12/27/open-source-peer-bonus.html</id>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;We are all used to spam emails, supposedly from Google, that say “You won” and I just need to send all my data to somewhere to receive my lottery payout.
When I recently received an email about Google’s “Open Source Peer Bonus” program, I almost discarded it as yet another version of that kind of spam.
But it turns out sometimes these emails are real!
Meanwhile the &lt;a href=&quot;https://opensource.googleblog.com/2023/12/google-open-source-peer-bonus-program-announces-second-group-of-2023-winners.html&quot;&gt;official announcement&lt;/a&gt; has been released which lists me as a recipient of this bonus as a thank you for my work on Rust.
So this one time, it wasn’t spam after all!&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;p&gt;Thanks a lot to Google for this program at the $250 reward; it is great to see open source work honored this way.
I have donated the amount in full to &lt;a href=&quot;https://noyb.eu/en&quot;&gt;noyb&lt;/a&gt;, who I’m sure will be using it &lt;a href=&quot;https://noyb.eu/en/noyb-win-first-major-fine-eu-1-million-using-google-analytics&quot;&gt;for good&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (2024-01-07):&lt;/strong&gt;
In fact, this is already my second Google Open Source Peer Bonus.
The first was in the &lt;a href=&quot;https://opensource.googleblog.com/2023/05/google-open-source-peer-bonus-program-announces-first-group-of-winners-2023.html&quot;&gt;first half of 2023&lt;/a&gt;.
Due to issues with the payment process, it took a while for that bonus to be transferred, but I can confirm that it has now arrived in my bank account.
I will have to find a suitable non-for-profit to donate this to… or it might be noyb again, we will see.
&lt;strong&gt;/Update&lt;/strong&gt;&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>Talk about Undefined Behavior, unsafe Rust, and Miri</title>
    <link href="https://www.ralfj.de/blog/2023/06/13/undefined-behavior-talk.html" />
    <updated>2023-06-13T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2023/06/13/undefined-behavior-talk.html</id>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;I recently gave a talk at a local Rust meetup in Zürich about Undefined Behavior, unsafe Rust, and Miri.
The recording is available &lt;a href=&quot;https://www.youtube.com/watch?v=svR0p6fSUYY&quot;&gt;here&lt;/a&gt;.
It targets an audience that is familiar with Rust but not with the nasty details of unsafe code, so I hope many of you will enjoy it!
Have fun. :)&lt;/p&gt;

        
    </content>
  </entry>

  <entry>
    <title>From Stacks to Trees: A new aliasing model for Rust</title>
    <link href="https://www.ralfj.de/blog/2023/06/02/tree-borrows.html" />
    <updated>2023-06-02T00:00:00+02:00</updated>
    <id>https://www.ralfj.de/blog/2023/06/02/tree-borrows.html</id>
    
        <category term="research" label="Research"/>
    
        <category term="rust" label="Rust"/>
    
    <content type="html">
        
        
            &lt;p&gt;Since last fall, &lt;a href=&quot;https://perso.crans.org/vanille/&quot;&gt;Neven&lt;/a&gt; has been doing an internship to develop a new aliasing model for Rust: Tree Borrows.
Hang on a second, I hear you say – doesn’t Rust already have an aliasing model?
Isn’t there this “Stacked Borrows” that Ralf keeps talking about?
Indeed there is, but Stacked Borrows is just one proposal for a possible aliasing model – and it &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/133&quot;&gt;has&lt;/a&gt; &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/134&quot;&gt;its&lt;/a&gt; &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/256&quot;&gt;fair&lt;/a&gt; &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/274&quot;&gt;share&lt;/a&gt; &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/276&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/303&quot;&gt;problems&lt;/a&gt;.
The purpose of Tree Borrows is to take the lessons learned from Stacked Borrows to build a new model with fewer issues, and to take some different design decisions such that we get an idea of some of the trade-offs and fine-tuning we might do with these models before deciding on the official model for Rust.&lt;/p&gt;

&lt;p&gt;Neven has written a detailed introduction to Tree Borrows &lt;a href=&quot;https://perso.crans.org/vanille/treebor/&quot;&gt;on his blog&lt;/a&gt;, which you should go read first.
He presented this talk at a recent RFMIG meeting, so you can also &lt;a href=&quot;https://www.youtube.com/watch?v=zQ76zLXesxA&quot;&gt;watch his talk here&lt;/a&gt;.
In this post, I will focus on the differences to Stacked Borrows.
I assume you already know Stacked Borrows and want to understand what changes with Tree Borrows and why.&lt;/p&gt;

&lt;!-- MORE --&gt;

&lt;p&gt;As a short-hand, I will sometimes write SB for Stacked Borrows and TB for Tree Borrows.&lt;/p&gt;

&lt;h2 id=&quot;two-phase-borrows&quot;&gt;Two-phase borrows&lt;/h2&gt;

&lt;p&gt;The main novelty in Tree Borrows is that it comes with proper support for two-phase borrows.
Two-phase borrows are a mechanism introduced with NLL which allows code like the following to be accepted:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;two_phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The reason this code is tricky is that it desugars to something like this:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;two_phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arg1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This code clearly violates the regular borrow checking rules since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; is mutably borrowed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg0&lt;/code&gt; when we call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x.len()&lt;/code&gt;!
And yet, the compiler will accept this code.
The way this works is that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut x&lt;/code&gt; stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg0&lt;/code&gt; is split into two phases:
in the &lt;em&gt;reservation&lt;/em&gt; phase, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; can still be read via other references.
Only when we actually need to write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg0&lt;/code&gt; (or call a function that might write to it) will the reference be “activated”, and it is from that point onwards (until the end of the lifetime of the borrow) that no access via other references is allowed.
For more details, see &lt;a href=&quot;https://github.com/rust-lang/rfcs/blob/master/text/2025-nested-method-calls.md&quot;&gt;the RFC&lt;/a&gt; and &lt;a href=&quot;https://rustc-dev-guide.rust-lang.org/borrow_check/two_phase_borrows.html&quot;&gt;the rustc-dev-guide chapter on two-phase borrows&lt;/a&gt;.
The only point relevant for this blog post is that when borrowing happens implicitly for a method call (such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x.push(...)&lt;/code&gt;), Rust will treat this as a two-phase borrow.
When you write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut&lt;/code&gt; in your code, it is treated as a regular mutable reference without a “reservation” phase.&lt;/p&gt;

&lt;p&gt;For the aliasing model, two-phase borrows are a big problem: by the time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x.len()&lt;/code&gt; gets executed, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg0&lt;/code&gt; already exists, and as a mutable reference it really isn’t supposed to allow reads through other pointers.
Therefore Stacked Borrows just &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/85&quot;&gt;gives up&lt;/a&gt; here and basically treats two-phase borrows like raw pointers.
That is of course unsatisfying, so for Tree Borrows we are adding proper support for two-phase borrows.
What’s more, we are treating &lt;em&gt;all&lt;/em&gt; mutable references as two-phase borrows: this is more permissive than what the borrow checker accepts, but lets us treat mutable references entirely uniformly.
(This is a point we might want to tweak, but as we will see soon this decision actually has some major unexpected benefits.)&lt;/p&gt;

&lt;p&gt;This is why we need a tree in the first place: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg0&lt;/code&gt; and the reference passed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec::len&lt;/code&gt; are both children of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;.
A stack is no longer sufficient to represent the parent-child relationships here.
Once the use of a tree is established, modeling of two-phase borrows is fairly intuitive: they start out in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reserved&lt;/code&gt; state which tolerates reads from other, unrelated pointers.
Only when the reference (or one of its children) is written to for the first time, its state transitions to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Active&lt;/code&gt; and now reads from other, unrelated pointers are not accepted any more.
(See Neven’s post for more details. In particular note that there is one unpleasant surprise lurking here: if there are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; involved, then a reserved mutable reference actually has to tolerate &lt;em&gt;mutation&lt;/em&gt; via unrelated pointers!
In other words, the aliasing rules of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut T&lt;/code&gt; are now affected by the presence of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt;. I don’t think people realized this when two-phase borrows were introduced, but it also seems hard to avoid so even with hindsight, it is not clear what the alternative would have been.)&lt;/p&gt;

&lt;h2 id=&quot;delayed-uniqueness-of-mutable-references&quot;&gt;Delayed uniqueness of mutable references&lt;/h2&gt;

&lt;p&gt;One of the most common source of Stacked Borrows issues is its &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/133&quot;&gt;very eager enforcement of uniqueness of mutable references&lt;/a&gt;.
For example, the following code is illegal under Stacked Borrows:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_mut_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// `from` gets invalidated here&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copy_nonoverlapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The reason it is illegal is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_mut_ptr&lt;/code&gt; takes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut self&lt;/code&gt;, which asserts unique access to the entire array, therefore invalidating the previously created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; pointer.
In Tree Borrows, however, that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut self&lt;/code&gt; is a two-phase borrow! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_mut_ptr&lt;/code&gt; does not actually perform any writes, so the reference remains reserved and never gets activated.
That means the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; pointer remains valid and the entire program is well-defined.
The call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_mut_ptr&lt;/code&gt; is treated like a read of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*self&lt;/code&gt;, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; (and the shared reference it is derived from) are perfectly fine with reads via unrelated pointers.&lt;/p&gt;

&lt;p&gt;It happens to be the case that swapping the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; lines actually makes this code work in Stacked Borrows.
However, this is not for a good reason: this is a consequence of the rather not-stack-like rule in SB which says that on a read, we merely &lt;em&gt;disable all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt;&lt;/em&gt; above the tag used for the access, but we keep raw pointers derived from those &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; pointers enabled.
Basically, raw pointers can live longer than the mutable references they are derived from, which is highly non-intuitive and potentially problematic for program analyses.
With TB, the swapped program is still fine, but for a different reason:
when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; gets created first, it remains a reserved two-phase borrow.
This means that creating a shared reference and deriving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; from it (which acts like a read on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt;) is fine; reserved two-phase borrows tolerate reads via unrelated pointers.
Only when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; is written to does it (or rather the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut self&lt;/code&gt; it was created from) become an active mutable reference that requires uniqueness, but that is after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_ptr&lt;/code&gt; returns so there is no conflicting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;self&lt;/code&gt; reference.&lt;/p&gt;

&lt;p&gt;It turns out that consistently using two-phase borrows lets us entirely eliminate this hacky SB rule and also fix one of the most common sources of UB under SB.
I didn’t expect this at all, so this is a happy little accident. :)&lt;/p&gt;

&lt;p&gt;However, note that the following program is fine under SB but invalid under TB:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_mut_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copy_nonoverlapping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, the write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; activates the two-phase borrow, so uniqueness is enforced.
That means the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;self&lt;/code&gt; created for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_ptr&lt;/code&gt; (which is considered reading all of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt;) is incompatible with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt;, and so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; is invalidated (well, it is made read-only) when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from&lt;/code&gt; gets created.
So far, we do not have evidence that this pattern is common in the wild.
The way to avoid issues like the code above is to &lt;em&gt;set up all your raw pointers before you start doing anything&lt;/em&gt;.
Under TB, calling reference-receiving methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_ptr&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_mut_ptr&lt;/code&gt; and using the raw pointers they return on disjoint locations is fine even if these references overlap, but you must call all those methods before the first write to a raw pointer.
Once the first write happens, creating more references can cause aliasing violations.&lt;/p&gt;

&lt;h2 id=&quot;no-strict-confinement-of-the-accessible-memory-range&quot;&gt;No strict confinement of the accessible memory range&lt;/h2&gt;

&lt;p&gt;The other major source of trouble with Stacked Borrows is &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/134&quot;&gt;restricting raw pointers to the type and mutability they are initially created with&lt;/a&gt;.
Under SB, when a reference is cast to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*mut T&lt;/code&gt;, the resulting raw pointer is confined to access only the memory covered by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;.
This regularly trips people up when they take a raw pointer to one element of an array (or one field of a struct) and then use pointer arithmetic to access neighboring elements.
Moreover, when a reference is cast to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*const T&lt;/code&gt;, it is actually read-only, even if the reference was mutable!
Many people expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*const&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*mut&lt;/code&gt; not to matter for aliasing, so this is a regular source of confusion.&lt;/p&gt;

&lt;p&gt;Under TB, we resolve this by no longer doing any retagging for reference-to-raw-pointer casts.
A raw pointer simply uses the same tag as the parent reference it is derived from, thereby inheriting its mutability and the range of addresses it can access.
Moreover, references are not strictly confined to the memory range described by their type:
when an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut T&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;T&lt;/code&gt;) gets created from a parent pointer, we initially record the new reference to be allowed to access the memory range describe by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; (and we consider this a read access for that memory range).
However, we also perform &lt;em&gt;lazy initialization&lt;/em&gt;: when a memory location outside this initial range is accessed, we check if the parent pointer would have had access to that location, and if so then we also give the child the same access.
This is repeated recursively until we find a parent that has sufficient access, or we reach the root of the tree.&lt;/p&gt;

&lt;p&gt;This means TB is compatible with &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/243&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_of&lt;/code&gt;-style pointer arithmetic&lt;/a&gt; and &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/276&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extern&lt;/code&gt; types&lt;/a&gt;, overcoming two more SB limitations.&lt;/p&gt;

&lt;p&gt;This also means that the following code becomes legal under TB:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;addr_of_mut!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ptr&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Under SB, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptr&lt;/code&gt; and direct access to the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; used two different tags, so writing to the local invalidated all pointers to it.
Under TB, this is no longer the case; a raw pointer directly created to the local is allowed to alias arbitrarily with direct accesses to the local.&lt;/p&gt;

&lt;p&gt;Arguably the TB behavior is more intuitive, but it means we can no longer use writes to local variables as a signal that all possible aliases have been invalidated.
However, note that TB only allows this if there is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addr_of_mut&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addr_of&lt;/code&gt;) immediately in the body of a function!
If a reference &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;mut x&lt;/code&gt; is created, and then some other function derives a raw pointer from that, those raw pointers &lt;em&gt;do&lt;/em&gt; get invalidated on the next write to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;.
So to me this is a perfect compromise: code that uses raw pointers has a lower risk of UB, but code that does not use raw pointers (which is easy to see syntactically) can be optimized as much as with SB.&lt;/p&gt;

&lt;p&gt;Note that this entire approach in TB relies on TB &lt;em&gt;not&lt;/em&gt; needing the stack-violating hack mentioned in the previous section.
If raw pointers in SB just inherited their parent tag, then they would get invalidated together with the unique pointer they are derived from, disallowing all the code that this hack was specifically added to support.
This means that backporting these improvements to SB is unlikely to be possible.&lt;/p&gt;

&lt;h2 id=&quot;unsafecell&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The handling of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; also changed quite a bit with TB.
First of all, another &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/303&quot;&gt;major issue&lt;/a&gt; with SB was fixed: turning an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;i32&lt;/code&gt; into an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;Cell&amp;lt;i32&amp;gt;&lt;/code&gt; &lt;em&gt;and then never writing to it&lt;/em&gt; is finally allowed.
This falls out of how TB handles the aliasing allowed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt;: they are treated like casts to raw pointers, so reborrowing an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;Cell&amp;lt;i32&amp;gt;&lt;/code&gt; just inherits the tag (and therefore the permissions) of the parent pointer.&lt;/p&gt;

&lt;p&gt;More controversially, TB also changes how precisely things become read-only when an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;T&lt;/code&gt; involves &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; somewhere inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;.
In particular, for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;(i32, Cell&amp;lt;i32&amp;gt;)&lt;/code&gt;, TB allows mutating &lt;em&gt;both&lt;/em&gt; fields, including the first field which is a regular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i32&lt;/code&gt;, since it just treats the entire reference as “this allows aliasing”.&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
In contrast, SB actually figured out that the first 4 bytes are read-only and only the last 4 bytes allow mutation via aliased pointers.&lt;/p&gt;

&lt;p&gt;The reason for this design decision is that the general philosophy with TB was to err on the side of allowing more code, having less UB (which is the opposite direction than what I used with SB).
This is a deliberate choice to uncover as much of the design space as we can with these two models.
Of course we wanted to make sure that TB still allows all the desired optimizations, and still has enough UB to justify the LLVM IR that rustc generates – those were our “lower bounds” for the minimum amount of UB we need.
And it turns out that under these constraints, we can support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; with a fairly simple approach: for the aliasing rules of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;T&lt;/code&gt;, there are only 2 cases.
Either there is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; anywhere, then this reference is read-only, or else the reference allows aliasing.
As someone who thinks a lot about proving theorems about the full Rust semantics including its aliasing model, this approach seemed pleasingly simple. :)&lt;/p&gt;

&lt;p&gt;I expected this decision to be somewhat controversial, but the amount of pushback we received has still been surprising.
The good news is that this is far from set in stone: we can &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/issues/403&quot;&gt;change TB to treat &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; more like SB did&lt;/a&gt;.
Unlike the previously described differences, this one is entirely independent of our other design choices.
While I prefer the TB approach, the way things currently stand, I do expect that we will end up with SB-like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnsafeCell&lt;/code&gt; treatment eventually.&lt;/p&gt;

&lt;h2 id=&quot;what-about-optimizations&quot;&gt;What about optimizations?&lt;/h2&gt;

&lt;p&gt;I have written a lot about how TB differs from SB in terms of which coding patterns are UB.
But what about the other side of the coin, the optimizations?
Clearly, since SB has more UB, we have to expect TB to allow fewer optimizations.
And indeed there is a major class of optimizations that TB loses: speculative writes, i.e. inserting writes in code paths that would not previously have written to this location.
This is a powerful optimization and I was quite happy that SB could pull it off, but it also comes at a major cost: mutable references have to be “immediately unique”.
Given how common of a problem “overeager uniqueness” is, my current inclination is that we most likely would rather make all that code legal than allow speculative writes.
We still have extremely powerful optimization principles around reads, and when the code &lt;em&gt;does&lt;/em&gt; perform a write that gives rise to even more optimizations, so my feeling is that insisting on speculative writes is just pushing things too far.&lt;/p&gt;

&lt;p&gt;On another front, TB actually allows a set of crucial optimizations that SB ruled out by accident: reordering of reads!
The issue with SB is that if we start with “read mutable reference, then read shared reference”, and then reorder to “read shared reference, then read mutable reference”, then in the new program, reading the shared reference might invalidate the mutable reference – so the reordering might have introduced UB!
This optimization is possible without having any special aliasing model, so SB not allowing it is a rather embarrassing problem.
If it weren’t for the stack-violating hack that already came up several times above, I think there would be a fairly easy way of fixing this problem in SB, but alas, that hack is load-bearing and too much existing code is UB if we remove it.
Meanwhile, TB does not need any such hack, so we can do the Right Thing (TM): when doing a read, unrelated mutable references are not entirely disabled, they are just made read-only.
This means that “read shared reference, then read mutable reference” is equivalent to “read mutable reference, then read shared reference” and the optimization is saved.
(A consequence of this is that retags can also be reordered with each other, since they also act as reads. Hence the order in which you set up various pointers cannot matter, until you do the first write access with one of them.)&lt;/p&gt;

&lt;h2 id=&quot;future-possibility-unique&quot;&gt;Future possibility: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Tree Borrows paves the way for an extension that we have not yet implemented, but that I am quite excited to explore: giving meaning to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt;.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; is a private type in the Rust standard library that was originally meant to express &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noalias&lt;/code&gt; requirements.
However, it was never actually wired up to emit that attribute on the LLVM level.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; is mainly used in two places in the standard library: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;.
SB (and TB) treat &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; special (matching rustc itself), but not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt;, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; does not come with any aliasing requirements.
And indeed the SB approach totally does not work for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;, since we don’t actually know how much memory to make unique here.
However, with TB we have lazy initialization, so we don’t need to commit to a memory range upfront – we can make it unique “when accessed”.
This means we can explore giving meaning to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, this might not actually work.
People actually do blatantly-aliasing things with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;, e.g. to implement arenas.
On the other hand, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;’s uniqueness would only come in when it is moved or passed &lt;em&gt;by value&lt;/em&gt;, and only for the memory ranges that are actually being accessed.
So it is quite possible that this is compatible with arenas.
I think the best way to find out is to implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; semantics behind a flag and experiment.
If that works out, we might even be able to remove all special handling of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; and rely on the fact that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; is defined as a newtype over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt;.
This would slightly reduce the optimization potential (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;T&amp;gt;&lt;/code&gt; is known to point to a memory range at least the size of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;, whereas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; has no such information), but making &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; less magic is a long-standing quest so this might be an acceptable trade-off.&lt;/p&gt;

&lt;p&gt;I should note that there are many people who think neither &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; nor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; should have any aliasing requirements. I think it’s worth first exploring whether we can have aliasing requirements which are sufficiently light-weight that they are compatible with common coding patterns, but even if we end up saying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; behave like raw pointers, it can still be useful to have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unique&lt;/code&gt; in our toolbox and expose it for unsafe code authors to eke out the last bits of performance.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;These are the major differences between Stacked Borrows and Tree Borrows.
As you can see, almost all of them are cases where TB allows more code than SB, and indeed TB fixes what I consider to be SB’s two biggest problems: overeager uniqueness for mutable references, and confining references and raw pointers to the size of the type they are created with.
These are great news for unsafe code authors!&lt;/p&gt;

&lt;p&gt;What TB &lt;em&gt;doesn’t&lt;/em&gt; change is the presence of “protectors” to enforce that certain references remain valid for the duration of an entire function call (whether they are used again or not); protectors are absolutely required to justify the LLVM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noalias&lt;/code&gt; annotations we would like to emit and they also do enable some stronger optimizations than what would otherwise be possible.
I do expect protectors to be the main remaining source of unexpected UB from Tree Borrows, and I don’t think there is a lot of wiggle-room that we have here, so this might just be a case where we have to tell programmers to adjust their code, and invest in documentation material to make this subtle issue as widely known as possible.&lt;/p&gt;

&lt;p&gt;Neven has implemented Tree Borrows in Miri, so you can play around with it and check your own code by setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MIRIFLAGS=-Zmiri-tree-borrows&lt;/code&gt;.
If you run into any surprises or concerns, please let us know!
The &lt;a href=&quot;https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem&quot;&gt;t-opsem Zulip&lt;/a&gt; and the &lt;a href=&quot;https://github.com/rust-lang/unsafe-code-guidelines/&quot;&gt;UCG issue tracker&lt;/a&gt; are good places for such questions.&lt;/p&gt;

&lt;p&gt;That’s all I got, thanks for reading – and a shout out to Neven for doing all the actual work here (and for giving feedback on this blog post), supervising this project has been a lot of fun!
Remember to read &lt;a href=&quot;https://perso.crans.org/vanille/treebor/&quot;&gt;his write up&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/watch?v=zQ76zLXesxA&quot;&gt;watch his talk&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;This does not mean that we bless such mutation! It just means that the compiler cannot use immutability of the first field for its optimizations. Basically, immutability of that field becomes a &lt;a href=&quot;/blog/2018/08/22/two-kinds-of-invariants.html&quot;&gt;safety invariant instead of a validity invariant&lt;/a&gt;: when you call foreign code, you can still rely on it not mutating that field, but within the privacy of your own code you are allowed to mutate it. See &lt;a href=&quot;https://www.reddit.com/r/rust/comments/13y8a9b/comment/jmlvgun/&quot;&gt;my response here&lt;/a&gt; for some more background. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;

        
    </content>
  </entry>



</feed>
