If you’re new to Rust and you see the version jump from 1.91.0 to 1.91.1, it’s tempting to think:

“This is just a tiny point release, probably nothing important.”

But the official Rust blog makes it very clear: 1.91.1 doesn’t add shiny new features – it exists almost entirely to fix two regressions introduced in 1.91.0. Both issues live in places you usually don’t think about (Wasm imports and build directory locking), but when they go wrong, the fallout is very real.

In this post we’ll walk through, in friendly, non‑scary terms:

  • What exactly changed in Rust 1.91.1
  • How these bugs relate to safety and reliability
  • Whether you, as a Rust beginner, should care (and what to do next)

What’s actually in Rust 1.91.1?

From the official announcement Announcing Rust 1.91.1 , there are two main fixes:

  1. A regression in #[link(wasm_import_module)] on Wasm targets

    • Could cause linker errors with messages like "import module mismatch"
    • Worse, it could cause the wrong function to be called at runtime, leading to undefined behavior, crashes, or silent data corruption
  2. Broken Cargo target directory locking on illumos

    • Due to an oversight, the standard library’s File::lock always returned Unsupported on illumos
    • Cargo assumed “locking isn’t supported here” and simply didn’t lock the target/ directory
    • That opened the door to concurrent builds stepping on each other’s files

So yes, this is “just” a patch release, but it’s about fixing regressions that can:

  • Break your builds in confusing ways, or
  • Make your binaries behave differently from what you tested

1. The Wasm bug: when “same name” doesn’t mean “same function”

How Wasm identifies imports (in simple terms)

On typical targets (Linux, macOS, Windows), a symbol is mostly identified by its namefoo, bar, etc.

In WebAssembly (Wasm), an imported function is identified by:

  • A module name, and
  • A symbol (function) name

Think of it like:

  • Module name = folder name
  • Symbol name = file name

Same check function name in different modules (auth vs log) should absolutely not be treated as the same thing.

In Rust, you can tell the compiler which Wasm module an extern block should import from:

#[link(wasm_import_module = "hello")]
extern "C" {
    pub fn world();
}

Here:

  • #[link(wasm_import_module = "hello")] says:
    “The functions in this extern block are imported from the Wasm module hello.”
  • world is the function name.

So the “real identity” is (module name = “hello”, symbol name = “world”).

What went wrong in 1.91.0?

Rust 1.91.0 introduced a regression around this attribute. The short version:

When the same symbol name was imported from different Wasm modules across multiple crates, Rust could:

  • Fail at link time with "import module mismatch"
  • Or worse, succeed, but end up calling the wrong function at runtime

A security‑flavored example:

  • Module auth exposes check() for permission checks
  • Module log exposes check() just to record logs
  • Different crates import check from these modules using #[link(wasm_import_module = "...")]

With the 1.91.0 bug, under the right conditions Rust could mix these up:

  • You think you’re calling auth::check to enforce permissions
  • At runtime, Rust calls log::check, which only logs but doesn’t enforce anything

The official post calls out that this can lead to:

  • Undefined behavior
  • Crashes and silent data corruption
  • And in security‑sensitive code, potentially skipped checks or bypassed logic

Why this matters for safety

Wasm is often seen as “safe by design” because of its sandbox and memory isolation. That’s true for memory safety, but this bug is about logic safety:

  • If your Wasm module handles:
    • Authentication / authorization
    • Request filtering or risk checks
    • Pre‑checks before sensitive operations (payments, password changes)
  • And the runtime silently calls the wrong function,
    • Validations may not run
    • Wrong data may be read or written
    • Security assumptions can quietly fall apart

To make it trickier, this kind of bug often:

  • Doesn’t show up immediately in simple local tests
  • Only appears under certain combinations of crates and module imports
  • Might only be triggered in production‑like deployments

What 1.91.1 does about it

Rust 1.91.1 fixes the regression by restoring correct behavior:

  • The compiler/linker again distinguish imports by (module name, symbol name)
  • You no longer risk two modules with the same function name being confused with each other
  • That closes the door on the “call the wrong function” class of issues here

For more details, see the linked issue in the announcement:

rust-lang/rust#148347


2. illumos and Cargo: when builds stop being deterministic

The second bug only affects illumos, but it’s worth understanding because it’s about build correctness.

Why Cargo locks the target/ directory

When you run cargo build, all the intermediate artifacts and final binaries end up under target/.

If two builds write to the same target/ at the same time:

  • One process might read a file that’s only half‑written
  • One build might overwrite the other’s artifacts
  • You can end up with binaries that don’t match any single clean build

To avoid this, Cargo locks the target/ directory:

  • If locking succeeds: this build “owns” the directory for now
  • If the OS clearly says “this filesystem doesn’t support locking”, Cargo proceeds without locking but assumes the risk

What changed in 1.91.0

Before 1.91.0, Cargo used its own OS‑level locking code.

In 1.91.0, the maintainers refactored things to use the recently stabilized standard library API:

std::fs::File::lock and related methods (stabilized in Rust 1.89.0).

This is a good idea in general – fewer custom code paths, more consistent behavior.

The catch on illumos:

  • Due to an oversight, File::lock on illumos always returned Unsupported
  • Cargo saw that and thought:
    “OK, the OS doesn’t support locking here, I’ll just skip it.”

The result:

On illumos, Cargo would never lock target/, even if the filesystem actually could support locks.

What that means in practice

If you’re on illumos and:

  • Your editor or IDE runs background builds
  • You manually run cargo build or cargo test at the same time
  • Or your CI does multiple jobs sharing the same workspace

Then you risk:

  • Builds occasionally failing in strange ways
  • Artifacts sometimes being corrupted or inconsistent
  • “Ghost bugs” that only happen in CI or production, not locally

Is this a security issue?

It’s not a classic remote exploit, but it is a supply‑chain / reliability issue:

  • If your builds aren’t deterministic, you can’t be fully confident in what you’re shipping
  • It becomes harder to reason about:
    • Which source produced which binary
    • Whether the binary you tested is really the one you deployed

Rust 1.91.1 fixes this indirectly by:

  • Correctly enabling the File::lock APIs on illumos
  • Making Cargo’s new locking code behave as intended again on that platform

3. So… should you upgrade?

Let’s summarize the two fixes in “decision‑making” terms:

  • Wasm regression

    • Affects: projects using #[link(wasm_import_module)], especially with the same symbol name imported from different modules across crates
    • Risk: calling the wrong function at runtime, undefined behavior, and potential security logic failures
  • illumos + Cargo regression

    • Affects: illumos users, especially with concurrent builds sharing target/
    • Risk: non‑deterministic builds, corrupted artifacts, hard‑to‑reproduce CI / production issues

Common theme:

Both are regressions introduced in 1.91.0. They’re not ancient bugs finally being fixed; they’re “you were fine before, then upgraded and stepped on a rake” type issues.

This is exactly the kind of patch release you do want to adopt quickly:

  • It doesn’t try to push new features
  • It focuses on restoring the reliability you had before 1.91.0
  • Even if you’re not currently affected, future dependencies or deployment patterns might trigger these edge cases

4. What should a beginner do, concretely?

If you’re just starting out with Rust, here’s a simple checklist.

1. Check your current version

rustc --version

If you see 1.91.0, upgrading is strongly recommended.
If you’re on something older, you might as well jump straight to the latest stable.

2. Upgrade via rustup

The recommended way to stay current on stable Rust is:

rustup update stable

If you don’t have rustup yet, install it from:

3. Even if you don’t use Wasm or illumos… still upgrade

Right now you might only be writing small CLI tools on Linux or Windows, but:

  • You could easily end up using Wasm in the future (serverless, edge computing, browser integrations)
  • Tooling, CI images, and third‑party crates tend to assume “recent stable Rust”

Starting from a version without these regressions saves you from weird, time‑consuming debugging sessions later.

4. If you maintain anything Wasm/illumos‑related, be extra careful

  • For Wasm projects using #[link(wasm_import_module)]:

    • Look for patterns where the same symbol name is imported from different modules
    • After upgrading, re‑run your critical paths and integration tests
  • For illumos builds:

    • Upgrade and watch whether flaky build issues disappear
    • Avoid sharing a single target/ directory across many parallel jobs when you can

5. One‑sentence takeaway

If you want the TL;DR in one line:

Rust 1.91.1 is the kind of “small” release that quietly fixes deep issues – you don’t have to understand every detail, but you really don’t want to stay on 1.91.0.


References and further reading

If you’d like to dig into the primary sources and related docs: