R

Ralf Jung

ralfj.de · Personal blog · on HN since 2015

Ralf Jung, a researcher working on Rust's formal semantics, writes about pointer provenance, memory models, undefined behavior, and aliasing rules.

What HN saysCommenters cite his Pointers Are Complicated series as the go-to introduction to pointer provenance, and link his posts on Tree Borrows and provenance.
99HN people shared it
59linked it in comments
68stories submitted
2.3Kstory points
3 months agolast shared
2015people per year2026

Most-shared posts

  1. 1
    Pointers Are Complicated, or What’s in a Byte?
    15 people · 207 points · first shared 2018 · 61 comments on HN
  2. 2
  3. 3
  4. 4
    There is no memory safety without thread safety
    9 people · 456 points · first shared 2025 · 503 comments on HN
  5. 5
    From Stacks to Trees: A new aliasing model for Rust
    9 people · 6 points · first shared 2023 · 1 comments on HN
  6. 6
  7. 7
  8. 8
    Do we really need undefined behavior?
    5 people · 60 points · first shared 2021 · 39 comments on HN
  9. 9
  10. 10
    How to use storytelling to fit inline assembly into Rust
    5 people · 6 points · first shared 2026 · 1 comments on HN
  11. 11
    Why even unused data needs to be valid in Rust
    5 people · 3 points · first shared 2020 · discussion on HN
  12. 12
    The Scope of Unsafe
    4 people · 78 points · first shared 2016 · 46 comments on HN
  13. 13
    The Tale of a Bug in Arc: Synchronization and Data Races
    4 people · 5 points · first shared 2018 · discussion on HN
  14. 14
    How MutexGuard Was Sync When It Should Not Have Been
    3 people · 78 points · first shared 2017 · 8 comments on HN
  15. 15
    Rust Stacked Borrows Implemented
    3 people · 6 points · first shared 2018 · discussion on HN

What HN people wrote when they linked it

steveklabnik · 2025-07-17
> I haven't been following their work, though. It seems they are working on stacked borrows. We're a few years past that, there's now 'tree borrows' which just had its paper published: https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html
astrange · 2025-01-10
> data pointers are fungible regardless of where they originate. This was never true because of something called provenance: https://www.ralfj.de/blog/2020/12/14/provenance.html. Though it usually doesn't matter and I think it annoys anyone who finds out about it. But in practice it's not always true on Apple A12 or later because they support PAC (so pointers of different type to the same address can be not equal bit-wise) and is even less true on very latest Android because it supports the really big gun MTE. And MTE is great; you don't want to miss out on it. No explainer here because there's no Wikipedia article for it(!). Also becomes not true on any system if you use -fbounds-safety or some of the sanitizers.
LoganDark · 2023-06-23
> Pointers cease to be valid the moment you try to leave a single allocation. For the other readers who might not know what this is referring to, it's pointer provenance. For an introduction to the topic, I always recommend Ralf Jung's blog series, "Pointers Are Complicated": https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html
oconnor663 · 2022-05-03
The memory model people are intense but really quite friendly :) Here's a great recent post: https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html
nextaccountic · 2020-09-06
Here's the author of this article (Ralf Jung) writing about padding bytes in the context of Rust (but I suppose it mostly applies to C and C++, since Rust inherited their memory model) https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer… Also, he wrote a follow up article to deal just with uninitialized memory https://www.ralfj.de/blog/2019/07/14/uninit.html edit: the "other kind of byte" that is the padding byte is just uninitialized memory - so perhaps not another kind?
zokier · 2020-06-17
I recommend anyone saying that pointers are simple, just integer memory locations, to read this article: https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html The fact that in C pointers point to specifically objects and not just simply to a random place in memory makes all the difference and is source of oh so many bugs
foota · 2018-08-08
It's linked (with a broken link if the author's reading this) to in the article, but I would highly recommend reading https://www.ralfj.de/blog/2018/08/07/2018-07-24/pointers-and-bytes. It was a really amazing read.
NobodyNada · 2025-11-17
If no references are involved, writing unsafe Rust is significantly easier than writing correct C, because the semantics are much clearer and easier to find in the documentation, and there's no insane things like type-based aliasing rules. If references are involved, Rust becomes harder, because the precise semantics are not decided or documented. The semantics aren't complicated; they're along the lines of "while a reference is live, you can't perform a conflicting access from a pointer or reference not derived from that reference". But there aren't good resources for learning this or clarifying the precise details. This area is an active work-in-progress; there is a subteam of the Rust project led by Ralf Jung (https://www.ralfj.de/blog/) working on fully and clearly defining the language's operational semantics, and they are doing an excellent job of it. When it comes to Zig, the precise rules and semantics of the memory model are much less clear than C. There's essentially no documentation, and if you search GitHub issues a lot of it is undecided and not actively being worked on. This is completely understandable given Zig's stage in development, but for me "how easy it is to write UB-free code" boils down to "how easy is it to understand the rules and apply them correctly", and so to me Zig is very hard to write correctly if you can't even figure out what "correct" is. Once Zig and Rust both have their memory models fleshed out, I hope Zig lands somewhere comparable…
tialaramex · 2025-03-13
Interestingly although all of C's other types are in fact just the machine integers wearing funny hats (e.g. char is just either a signed or unsigned byte depending on platform, float is just the 32-bit unsigned integers as binary fractions) the pointers are not actually just integers. They could be, but it's much worse from a performance perspective if you just have these raw machine addresses rather than the pointers in the C language so actual C compilers haven't done that for many years. ISO/IEC TS 6010 describes the best current attempt to come up with coherent semantics for these pointers, or here's a Rustier perspective https://www.ralfj.de/blog/2020/12/14/provenance.html [today Rust specifically says its pointers have provenance and what that means, like that TS for the C language] Now, if you read Ralf's post and want to argue about that I'm afraid there are already lots of HN discussions and your point has probably already been made so: https://news.ycombinator.com/item?id=25419740 or https://news.ycombinator.com/item?id=42878450
aw1621107 · 2022-01-21
Ralf Jung has a blog post looking at some of the claims in this paper [0]. Some hopefully representative quotes: > The paper makes many good points, but I think the author is throwing out the baby with the bathwater by concluding that we should entirely get rid of this kind of Undefined Behavior. The point of this blog post is to argue that we do need UB by showing that even some of the most basic optimizations that all compilers perform require this far-reaching notion of Undefined Behavior. <snip> > I honestly think trying to write a highly optimizing compiler based on a different interpretation of UB would be a worthwhile experiment. We sorely lack data on how big the performance gain of exploiting UB actually is. However, I strongly doubt that the result would even come close to the most widely used compilers today—and programmers that can accept such a big performance hit would probably not use C to begin with. Certainly, any proposal for requiring compilers to curtail their exploitation of UB must come with evidence that this would even be possible while keeping C a viable language for performance-sensitive code. > To conclude, I fully agree with Yodaiken that C has a problem, and that reliably writing C has become incredibly hard since undefined behavior is so difficult to avoid. It is certainly worth reducing the amount of things that can cause UB in C, and developing practical tools to detect more advanced kinds of UB such as strict aliasing…
scaramanga · 2021-01-27
I think the practical issues you describe like rebuilds of packages and so on are very real if we're talking about general adoption. But if we're talking about recompiling a handful of SUID programs which make up a TCB then I think a proposition like that has a lot of merit and can't be easily dismissed. Any C code that needs changing to deal with fat-pointers is probably already UB in C (or at best, has some implementation-defined behaviour). That's because the representation of pointers themselves is undefined (so you can't get a valid result by looking at those). Pointer/integer casts (either direction) are implementation defined. And accesses via pointers to anything beyond their bounds is already UB. There's some good and interesting discussion of what's involved in all of this on: https://www.ralfj.de/blog/2020/12/14/provenance.html And there's already bodies of work within the Rust (and C/C++) communities around the concepts/technologies that would need to be developed to achieve something like a memory-safe UNIX TCB.
ammar2 · 2026-07-24
Of note, even that 97% of original code that is now "safe in the rust sense" could violate invariants through safe blocks that cause memory-safety issues. I can't say exactly how the LLM-ported Rust code made use of unsafe but see https://www.ralfj.de/blog/2016/01/09/the-scope-of-unsafe.html
zozbot234 · 2026-04-02
If you're talking about Rust codebases, I'm pretty sure that writing sound unsafe code is at least feasible. It's not easy, and it should be avoided if at all possible, but saying that 100% of those codebases are unsound is pessimistic. One feasible approach is to use "storytelling" as described here: https://www.ralfj.de/blog/2026/03/13/inline-asm.html That's talking about inline assembly, but in principle any other unsafe feature could be similarly modeled.
codys · 2025-12-26
Go can have memory unsafety leading to arbitrary memory reads and writes by having an interface replaced by another thread (go interfaces use 2 words, one for the vtable and another for the data. It does not replace these in a thread safe way, so one can use a vtable to work with an unexpected data pointer. Folks have shown this allows the kinds of arbitrary memory reads/writes that folks normally ban in their definition of memory safe (and this post's website has a definition does as well): https://research.swtch.com/gorace https://blog.stalkr.net/2015/04/golang-data-races-to-break-memory-safe… https://www.ralfj.de/blog/2025/07/24/memory-safety.html
afiori · 2025-12-21
I agree that it is a bad idea to describe rust this way but they likely meant memory safety as used in https://www.ralfj.de/blog/2025/07/24/memory-safety.html . Meaning that shared mutable is thread unsafe, I am unsure about Java and JavaScript but I think that almost every language on the popular memory safe list fails this test. Again the statement is probably still untrue and bad marketing, but I suspect this kind of reasoning was behind it Of course Rust technically fails too since `unsafe` is a language feature
cetra3 · 2025-11-23
> Node.js and Go are considered practically safe language Node JS has had vulnerabilities in the past: https://www.cvedetails.com/cve/CVE-2021-22940/ Go is also not Memory safe: https://www.ralfj.de/blog/2025/07/24/memory-safety.html
ralfj · 2025-07-25
> There is plenty of undefined behavior that can't lead to violating memory safety. For example, in many languages, argument evaluation order is undefined. If you have some code like: You are mixing up non-determinism and UB. Sadly that's a common misunderstanding. See https://www.ralfj.de/blog/2021/11/18/ub-good-idea.html for an explanation of what UB is, though I don't go into the distinction to non-determinism there.
kibwen · 2025-07-09
Recent blog post from Ralf Jung providing some extra context: https://www.ralfj.de/blog/2025/07/07/tree-borrows-paper.html Bonus: recent talk from Ralf Jung on his group's efforts to precisely specify Rust's operational semantics in executable form in a dialect of Rust: https://youtube.com/watch?v=yoeuW_dSe0o
pcwalton · 2025-05-21
jcranmer is correct and pointer provenance-related issues are not "boxed and contained". Start here: https://www.ralfj.de/blog/2020/12/14/provenance.html
lidavidm · 2025-01-30
https://www.ralfj.de/blog/2019/07/14/uninit.html perhaps (the OP also talks about this when linking to a talk about jemalloc)
dwattttt · 2024-06-26
> the program continued using the same pointer, not the realloc call result, since the old pointer did not change > In this context, it is a bug in the application This is a non-intuitive result by the normal lens C pointers are viewed by; these two pointers compared equal, how could using one differ from using the other? Pointer provenance rears its ugly head here though; one of those "pointers that have the same value"'s provenance is different to the other. By the standard, they _are_ allowed to be treated differently, and honestly the standard requires it. https://www.ralfj.de/blog/2020/12/14/provenance.html is pretty accessible, and shockingly on point: "just because two pointers point to the same address, does not mean they are equal in the sense that they can be used interchangeably". EDIT: spelling
sans-seraph · 2024-06-06
Rust's linting tool, Clippy, provides a lint that will produce a warning when a function pointer is cast to any integral type: https://rust-lang.github.io/rust-clippy/master/index.html#/fn_to_numer… The broader topic of whether it is safe, or wise, to cast between pointers and integers in general is an area of active research. Ralf Jung's blog is required reading on this topic: https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html
sondr3 · 2024-05-02
This also made me scratch my beard, but I think he maybe means allowing multiple mutable references to a variable when the compiler can prove that nothing bad happens. It's something a bunch of people have been trying to solve with different models, the most recent I know of is Tree Borrows [1]. [1]: https://www.ralfj.de/blog/2023/06/02/tree-borrows.html
skitter · 2023-10-15
That's not about 'using' a reference, it's about constructing an invalid value – no different than doing transmute::<u32,char>(0x110000). See https://www.ralfj.de/blog/2020/07/15/unused-data.html for an explanation why it's UB.
iudqnolq · 2023-09-04
Problem is an uninit value is unstable. The compiler could be using it to store something else, and could change what it's storing at different times. You need a freeze operation to say "I don't care what that this is, but it should have one value". https://www.ralfj.de/blog/2019/07/14/uninit.html
volta87 · 2023-08-20
The arguments in this blogpost are fundamentally flawed. The fact that they opened a bug based on them but got shut down should have raised all red flags. When compiling and running a C program, the only thing that matters is "what the C abstract machine does". Programs that exhibit UB in the abstract machine are allowed to do "anything". Trying to scope that down using arguments of the form "but what the hardware does is X" are fundamentally flawed, because anything means anything, and what the hardware does doesn't change that, and therefore it doesn't matter. This blogpost "What The Hardware Does is not What Your Program Does" explains this in more detail and with more examples. https://www.ralfj.de/blog/2019/07/14/uninit.html
chrismorgan · 2023-06-12
To expand a little on this, by pulling out one key piece of the article: This is what the footnote of the bit about &(i32, Cell<i32>) clarifies (which footnote was added due to this misunderstanding, discussed in https://old.reddit.com/r/rust/comments/13y8a9b/from_stacks_to_trees_a_… > In particular, for &(i32, Cell<i32>), TB allows mutating both fields, including the first field which is a regular i32, since it just treats the entire reference as “this allows aliasing”.¹ > ¹ 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 safety invariant instead of a validity invariant […] This matter of safety versus validity invariants is key (https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html).
Semaphor · 2022-11-28
This [0] is the linked article for that point, I don’t do systems programming, so I can’t say I understand if this is trivial or not ;) [0]: https://www.ralfj.de/blog/2020/07/15/unused-data.html

Keyboard shortcuts

/Search
j kNext / previous blog
EnterOpen selected blog
vVisit the blog site
fFollow / unfollow
lSwitch grid / list view
dSwitch dark mode
g hHome
g pTop posts
g tTopics
g fFollowing
g lLiked
g yHistory
g qFAQ
EscBack / close