{
  "version": "https://jsonfeed.org/version/1",
  "title": "Ian's Digital Garden",
  "home_page_url": "https://ianwwagner.com/",
  "feed_url": "https://ianwwagner.com//tag-software-reliability.json",
  "description": "",
  "items": [
    {
      "id": "https://ianwwagner.com//even-safer-rust-with-miri.html",
      "url": "https://ianwwagner.com//even-safer-rust-with-miri.html",
      "title": "Even Safer Rust with Miri",
      "content_html": "<p>Recently some of the Miri contributors published a <a href=\"https://plf.inf.ethz.ch/research/popl26-miri.html\">paper that was accepted to POPL</a>.\nI've been using Rust professionally for about 7 years now,\nand while I'd <em>heard of</em> Miri several times over the years,\nI think there's a wide lack of knowledge about what it does, and why anyone should care.\nI only recently started using it myself, so I'm writing this post to share\nwhat Miri is, why you should care, and how you can get started easily.</p>\n<h1><a href=\"#what-is-miri\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-miri\"></a>What is Miri?</h1>\n<p>Miri is an interpreter for Rust's mid-level intermediate representation (MIR; hence the acronym).\nThat's how I first remember seeing it described years ago,\nand that's what the GitHub project description still says.</p>\n<p>The latest README is a bit more helpful though: it's a tool for detecting <em>undefined behavior</em> (UB) in Rust code.\nIn other words, it helps you identify code that's unsafe or unsound.\nWhile it would be a bug to hit such behaviors in safe Rust,\nif you're using <code>unsafe</code> (or any of your dependency chain does!),\nthen this is a real concern!\nMiri has in fact even found soundness bugs in the Rust standard library,\nso even a transitive sort of <code>#![forbid(unsafe_code)]</code> won't help you.</p>\n<h1><a href=\"#what-is-ub-and-why-is-it-bad\" aria-hidden=\"true\" class=\"anchor\" id=\"what-is-ub-and-why-is-it-bad\"></a>What is UB (and why is it bad)?</h1>\n<p>I think to understand why Miri matters,\nwe first need to understand why UB is bad.\nThis is not something that most professional programmers have a great understanding of (myself included).</p>\n<p>In abstract, UB can mean &quot;anything that isn't specified&quot;, or something like that...\nBut that's not very helpful!\nAnd it doesn't really explain the stakes if we don't avoid it.\nThe Rust Reference has a <a href=\"https://doc.rust-lang.org/reference/behavior-considered-undefined.html\">list</a>\nof behaviors that are considered to be undefined in Rust,\nbut they note that this list is not exhaustive.</p>\n<p>When searching for a better understanding,\nI've seen people online make statements like\n&quot;UB means your program can do literally anything at this point, like launch nuclear missiles.&quot;\nWhile this is technically true, this isn't particularly helpful to most readers.\nI want something more concrete...</p>\n<p>The authors of the paper put UB's consequences in terms which really &quot;clicked&quot; for me\nusing a logical equivalence, which I'll quote here:</p>\n<blockquote>\n<p>Furthermore, Undefined Behavior is a massive security problem. Around 70% of critical security vulnerabilities are caused by memory safety violations [38, 18, 32], and all of these memory safety violations are instances of Undefined Behavior. After all, if the attacker overflows a buffer to eventually execute their own code, this is not something that the program does because the C or C++ specification says so—the specification just says that doing out-of-bounds writes (or overwriting the vtable, or calling a function pointer that does not actually point to a function, or doing any of the other typical first steps of an exploit chain) is Undefined Behavior, and executing the attacker’s code is just how Undefined Behavior happens to play out in this particular case.</p>\n</blockquote>\n<p>I never made this connection on my own.\nI equate UB most often with things like data races between threads,\nwhere you can have unexpected update visibility without atomics or locks.\nOr maybe torn reads of shared memory that's not properly synchronized.\nBut this is a new way of looking at it that makes the stakes more clear,\nespecially if you're doing anything with pointers.</p>\n<p>Another connection I never made previously is that UB is relative to a very specific context.\nHere's another quote from the paper:</p>\n<blockquote>\n<p>The standard random number crate used across the Rust ecosystem performed an unaligned memory access. Interestingly, the programmers seemed to have been aware that alignment is a problem in this case: there were dedicated code paths for x86 and for other architectures. Other architectures used read_unaligned, but the x86 code path had a comment saying that x86 allows unaligned reads, so we do not need to use this (potentially slower) operation. Unfortunately, this is a misconception: even though x86 allows unaligned accesses, Rust does not, no matter the target architecture—and this can be relevant for optimizations.</p>\n</blockquote>\n<p>This is REALLY interesting to me!\nIt makes sense in retrospect, but it's not exactly obvious.\nLanguages are free to define their own semantics in addition to or independently of hardware.\nI suspect Rust's specification here is somehow related to its concept of allocations\n(which the paper goes into more detail about).</p>\n<p>It is obviously not &quot;undefined&quot; what the hardware will do when given a sequence of instructions.\nBut it <em>is</em> undefined in Rust, which controls how those instructions are generated.\nAnd here the Rust Reference is explicit in calling this UB.\n(NOTE: I don't actually know what the &quot;failure modes&quot; are here, but you can imagine they could be very bad\nsince it could enable the compiler to make a bad assumption that leads to a program correctness or memory safety vulnerability.)</p>\n<p>I actually encountered the same confusion re: what the CPU guarantees vs what Rust guarantees for unaligned reads in <a href=\"https://github.com/stadiamaps/valinor/blob/5e75b2b8267cee2a57d4f22fcc5605728e0cf76e/valhalla-graphtile/src/graph_tile.rs#L857\">one of my own projects</a>,\nas a previous version of this function didn't account for alignment.\nI addressed the issue by using the native zerocopy <a href=\"https://docs.rs/zerocopy/latest/zerocopy/byteorder/struct.U32.html\"><code>U32</code></a> type,\nwhich is something I'd have needed to do anyways to ensure correctness regardless of CPU endianness.\n(If you need to do something like this at a lower level for some reason, there's a <a href=\"https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html\"><code>read_unaligned</code> function in <code>std::ptr</code></a>).</p>\n<p>TL;DR - UB is both a correctness and a security issue, so it's really bad!</p>\n<h1><a href=\"#using-miri-for-great-good\" aria-hidden=\"true\" class=\"anchor\" id=\"using-miri-for-great-good\"></a>Using Miri for great good</h1>\n<p>One of the reasons I write pretty much everything that I can in Rust is because\nit naturally results in more correct and maintainable software.\nThis is a result of the language guarantees of safe Rust,\nthe powerful type system,\nand the whole ecosystem of excellent tooling.\nIt's a real <a href=\"https://blog.codinghorror.com/falling-into-the-pit-of-success/\">pit of success</a> situation.</p>\n<p>While you can run a program under Miri as a one-shot test,\nthis isn't a practical approach to ensuring correctness long-term.\nMiri is a <em>complementary</em> tool to existing things that you should be doing already.\nAutomated testing is the most obvious one,\nbut fuzzing and other strategies may also be relevant for you.</p>\n<p>If you're already running automated tests in CI, adding Miri is easy.\nHere's an example of how I use it in GitHub actions:</p>\n<pre><code class=\"language-yaml\">steps:\n    - uses: actions/checkout@v4\n    - uses: taiki-e/install-action@nextest\n\n    - name: Build workspace\n      run: cargo build --verbose\n\n    - name: Run tests\n      run: cargo nextest run --no-fail-fast\n\n    - name: Run doc tests (not currently supported by nextest https://github.com/nextest-rs/nextest/issues/16)\n      run: cargo test --doc\n\n    - name: Install big-endian toolchain (s390x)\n      run: rustup target add s390x-unknown-linux-gnu\n\n    - name: Install s390x cross toolchain and QEMU (Ubuntu only)\n      run: sudo apt-get update &amp;&amp; sudo apt-get install -y gcc-s390x-linux-gnu g++-s390x-linux-gnu libc6-dev-s390x-cross qemu-user-static\n\n    - name: Run tests (big-endian s390x)\n      run: cargo nextest run --no-fail-fast --target s390x-unknown-linux-gnu\n\n    - name: Install Miri\n      run: rustup +nightly component add miri\n\n    - name: Run tests in Miri\n      run: cargo +nightly miri nextest run --no-fail-fast\n      env:\n        RUST_BACKTRACE: 1\n        MIRIFLAGS: -Zmiri-disable-isolation\n\n    - name: Run doc tests in Miri\n      run: cargo +nightly miri test --doc\n      env:\n        RUST_BACKTRACE: 1\n        MIRIFLAGS: -Zmiri-disable-isolation\n\n    - name: Install nightly big-endian toolchain (s390x)\n      run: rustup +nightly target add s390x-unknown-linux-gnu\n\n    - name: Run tests in Miri (big-endian s390x)\n      run: cargo +nightly miri nextest run --no-fail-fast --target s390x-unknown-linux-gnu\n      env:\n        RUST_BACKTRACE: 1\n        MIRIFLAGS: -Zmiri-disable-isolation\n</code></pre>\n<p>I know that's a bit longer than what you'll find in the README,\nbut I wanted to highlight my usage in a more complex codebase\nsince these examples are less common.\n(NOTE: I assume an Ubuntu runner here, since Linux has the best support for Miri right now.)\nSome things to highlight:</p>\n<ul>\n<li>I use <a href=\"https://nexte.st/\">nextest</a>, which is significantly faster for large suites. (NOTE: It <a href=\"https://github.com/nextest-rs/nextest/issues/16\">does not support doc tests</a> at the time of this writing).</li>\n<li>I pass some <code>MIRIFLAGS</code> to disable host isolation for my tests, since they require direct filesystem access. You may not need this for your project, but I do for mine.</li>\n<li>Partly because I can, and partly because big-endian CPUS do still exist, I do tests under two targets. Miri is capable of doing this with target flags, which is REALLY cool, and the <code>s390x-unknown-linux-gnu</code> is the &quot;big-endian target of choice&quot; from the Miri authors. This requires a few dependencies and flags.</li>\n<li>Note that cargo doc tests <a href=\"https://github.com/rust-lang/cargo/issues/6460\">do not support building for alternate targets</a>.</li>\n</ul>\n<p>Hopefully you learned something from this post.\nI'm pretty sure I wrote my first line of unsafe Rust less than a year ago\n(after using it professionally for over 6 years prior),\nso even if you don't need this today, file it away for later.\nAs I said at the start, I'm still not an expert,\nso if you spot any errors, please reach out to me on Mastodon!</p>\n",
      "summary": "",
      "date_published": "2026-01-07T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "rust",
        "software-reliability"
      ],
      "language": "en"
    }
  ]
}