<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>blog.habets.se</title>
  <subtitle>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</subtitle>
  <id>https://aggr.aymericbeaumet.com/sources/blog.habets.se/</id>
  <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/sources/blog.habets.se/"/>
  <link rel="self" type="application/atom+xml" href="https://aggr.aymericbeaumet.com/sources/blog.habets.se/atom.xml"/>
  <updated>2026-10-23T20:47:58+00:00</updated>
  <author>
    <name>aymeric&#39;s</name>
  </author>
  <generator version="2.0.0" uri="https://github.com/aymericbeaumet/aggr">aggr</generator>
  <entry>
    <id>urn:aggr:item:fe341f4c22e4fd1c6c5031499f356e6ccddd2911</id>
    <title type="text">Attention people who think UB is fine</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-23-attention-people-who-think-ub-is-fine/"/>
    <link rel="via" type="text/html" href="https://blog.habets.se/2026/10/Attention-people-who-think-UB-is-fine.html"/>
    <published>2026-10-23T20:47:58+00:00</published>
    <updated>2026-10-23T20:47:58+00:00</updated>
    <author>
      <name>blog.habets.se via abareplace</name>
    </author>
    <category term="news"/>
    <category term="c"/>
    <category term="c++"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;After &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://blog.habets.se/2026/05/Everything-in-C-is-undefined-behavior.html&quot;&gt;my post on undefined behavior in C&lt;/a&gt; some people insisted that it’s demonstrably fine, we know what the code will do, and at worst we’ll get a segfault.&lt;/p&gt;
&lt;p&gt;This is false.&lt;/p&gt;
&lt;p&gt;Here’s an example.&lt;/p&gt;
&lt;h2&gt;C code&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;C&quot;&gt;&lt;span class=&quot;syntax-source syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-preprocessor syntax-include syntax-c&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-control syntax-import syntax-include syntax-c&quot;&gt;#include&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-quoted syntax-other syntax-lt-gt syntax-include syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-c&quot;&gt;&amp;lt;&lt;/span&gt;utility&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-c&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Let&amp;#39;s pretend this is a memory mapped device controlling
&lt;/span&gt;&lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; motors and gears and stuff.
&lt;/span&gt;&lt;span class=&quot;syntax-storage syntax-modifier syntax-c&quot;&gt;volatile&lt;/span&gt; &lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;int&lt;/span&gt; device_command &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;serve_hot_chocolate&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
        device_command &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; Oh no, we spilled some hot chocolate, invoking UB.
&lt;/span&gt;        &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt; The definition of `std::unreachable()` is:
&lt;/span&gt;        &lt;span class=&quot;syntax-comment syntax-line syntax-double-slash syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-comment syntax-c&quot;&gt;//&lt;/span&gt;     Invokes undefined behavior at a given point.
&lt;/span&gt;        std:&lt;span class=&quot;syntax-keyword syntax-operator syntax-ternary syntax-c&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-c&quot;&gt;unreachable&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;syntax-storage syntax-type syntax-c&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-c&quot;&gt;explode&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-begin syntax-c&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-parameters syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-group syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-group syntax-end syntax-c&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt; &lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-begin syntax-c&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;
        device_command &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-c&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-c&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-punctuation syntax-terminator syntax-c&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function syntax-c&quot;&gt;&lt;span class=&quot;syntax-meta syntax-block syntax-c&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-section syntax-block syntax-end syntax-c&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example assembly output&lt;/h2&gt;
&lt;p&gt;With GCC 16.2 with &lt;code&gt;-O1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nasm&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Nasm&quot;&gt;&amp;quot;serve_hot_chocolate()&amp;quot;:
        mov     DWORD PTR &amp;quot;device_command&amp;quot;[rip], 42
&amp;quot;explode()&amp;quot;:
        mov     DWORD PTR &amp;quot;device_command&amp;quot;[rip], 3
        ret
&amp;quot;device_command&amp;quot;:
        .zero   4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ruh roh. Exploding chocolate! The function &lt;code&gt;explode()&lt;/code&gt; will be executed, even though it was never called!&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://godbolt.org/z/Y5844eTKe&quot;&gt;godbolt link&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Another example: dereferencing a null pointer&lt;/h2&gt;
&lt;p&gt;Some people insisted that dereferencing a null pointer is &lt;em&gt;fine&lt;/em&gt;. It’ll just try to access address zero! And these people supposedly know what happens when you try: if nothing is mapped there, you’ll get a segfault.&lt;/p&gt;
&lt;p&gt;Again, not true.&lt;/p&gt;
&lt;p&gt;Let’s say we change &lt;code&gt;std::unreachable()&lt;/code&gt; to &lt;code&gt;int* ptr = nullptr; *ptr = 99;&lt;/code&gt; in the previous example. Now we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://godbolt.org/z/efqKrzefa&quot;&gt;get this&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nasm&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Nasm&quot;&gt;&amp;quot;serve_hot_chocolate()&amp;quot;:
        mov     DWORD PTR &amp;quot;device_command&amp;quot;[rip], 42
        mov     DWORD PTR ds:0, 0
        ud2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Curious, eh? First it writes zero, &lt;strong&gt;not&lt;/strong&gt; 99, to memory address zero. Then it crashes the program using an invalid instruction. The number 99 never appears in the generated program. But the null pointer write does. Presumably the null pointer write is done in order to improve debugger error messages.&lt;/p&gt;
&lt;p&gt;Now let’s check &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://godbolt.org/z/86M7MWTT8&quot;&gt;clang with godbolt&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nasm&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Nasm&quot;&gt;&amp;quot;serve_hot_chocolate()&amp;quot;:
        mov     dword ptr [rip + &amp;quot;device_command&amp;quot;], 42

&amp;quot;explode()&amp;quot;:
        mov     dword ptr [rip + &amp;quot;device_command&amp;quot;], 3
        ret

&amp;quot;device_command&amp;quot;:
        .long   0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again we get exploding chocolate.&lt;/p&gt;
&lt;h2&gt;Let’s do some pointer arithmetic on the null pointer&lt;/h2&gt;
&lt;p&gt;Ok, so what happens if we add &lt;code&gt;ptr += 10&lt;/code&gt;. That’s UB for three reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;using a null pointer for anything other than comparison against other pointers,&lt;/li&gt;
&lt;li&gt;even if an object would be valid at the null pointer (which it’s not), creating a pointer that points outside that object, or if an array, beyond one-past the array, is UB,&lt;/li&gt;
&lt;li&gt;even if that were valid (which it’s not), we then dereference the address.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://godbolt.org/z/8z6hPPsvG&quot;&gt;try it&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nasm&quot;&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Nasm&quot;&gt;&amp;quot;serve_hot_chocolate&amp;quot;:
        mov     DWORD PTR &amp;quot;device_command&amp;quot;[rip], 42
        mov     DWORD PTR ds:40, 99
        ret
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wow. &lt;em&gt;Now&lt;/em&gt; it did what someone naively would expect, taking the code at face value. Lucky.&lt;/p&gt;
&lt;p&gt;Why did GCC (and clang, by the way) not detect that &lt;code&gt;*(ptr + 10) = 99&lt;/code&gt; is equally UB as &lt;code&gt;*ptr = 99&lt;/code&gt;, and do the same thing?&lt;/p&gt;
&lt;p&gt;Wrong question. No compiler is obligated to “detect” UB at all. Looks to me like GCC and clang, if we want to anthropomorphize them, saw “oh, null pointer dereference? Well that can’t happen, so I’ll stop emitting instructions for this function, to save on the binary size”. Because of the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Halting_problem&quot;&gt;halting problem&lt;/a&gt;, it cannot in the general case know if the code will ever be executed.&lt;/p&gt;
&lt;h2&gt;So what does UB do?&lt;/h2&gt;
&lt;p&gt;Anything. I have now demonstrated three things UB can do, when UB was triggered in the same place:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run whatever happens to be next in memory (exploding chocolate).&lt;/li&gt;
&lt;li&gt;Store a (for all intents and purposes) &lt;em&gt;random&lt;/em&gt; value at address zero (wrote zero, not 99), and then deliberately crash.&lt;/li&gt;
&lt;li&gt;Do what one would naively expect: write the value to the address seen in the code at face value.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And to think that some people don’t believe in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Undefined_behavior&quot;&gt;nasal demons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://news.ycombinator.com/item?id=50203996&quot;&gt;Someone submitted this to HN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;disqus has started showing ads. :-(&lt;/p&gt;
&lt;p&gt;Showing (probably incomplete) comments in a static read-only view. Click button to be able to leave comments.&lt;/p&gt;
</content>
  </entry>
</feed>
