Skip to content

Conversation

@paolobarbolini
Copy link
Contributor

This fixes a Stacked Borrows violation in IterMut. The issue was that IterMut::next and IterMut::next_back temporarily create an exclusive reference to the key. This invalidates the pointer held within KeyWrapper by the HashMap, but the HashMap still holds and accesses it on subsequent reads or writes to the LRU, which is unsound.

The implementation silently coerces the esclusive reference into a shared reference, but this does not undo the effect of the exclusive reference.

This can be seen by running miri on the test that I've added in the second commit without the fix in the first commit.

The solution is to use shared references throughout, rather than first requesting an exclusive reference and then converting it into a shared reference.

test tests::iter_mut_stacked_borrows_violation ... error: Undefined Behavior: trying to retag from <175909> for SharedReadOnly permission at alloc52275[0x10], but that tag does not exist in the borrow stack for this location
    --> src/lib.rs:147:28
     |
 147 |         let key = unsafe { &*self.k }.borrow();
     |                            ^^^^^^^^ this error occurs as part of retag at alloc52275[0x10..0x14]
     |
     = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
     = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <175909> was created by a SharedReadOnly retag at offsets [0x10..0x14]
    --> src/lib.rs:387:39
     |
 387 |                 let keyref = unsafe { (*node_ptr).key.as_ptr() };
     |                                       ^^^^^^^^^^^^^^^^^^^^^^^^
help: <175909> was later invalidated at offsets [0x10..0x14] by a Unique function-entry retag inside this call
    --> src/lib.rs:1736:35
     |
1736 |         let key = unsafe { &mut (*(*self.ptr).key.as_mut_ptr()) as &mut K };
     |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     = note: this is on thread `tests::iter_mut`
     = note: stack backtrace:
             0: <KeyRef<i32> as core::borrow::Borrow<KeyWrapper<i32>>>::borrow
                 at src/lib.rs:147:28: 147:36
             1: <KeyWrapper<i32> as hashbrown::Equivalent<KeyRef<i32>>>::equivalent
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/equivalent-1.0.2/src/lib.rs:89:29: 89:41
             2: hashbrown::map::equivalent_key::<KeyWrapper<i32>, KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>>::{closure#0}
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/map.rs:224:14: 224:32
             3: hashbrown::raw::RawTable::<(KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>)>::find::<{closure@hashbrown::map::equivalent_key<KeyWrapper<i32>, KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>>::{closure#0}}>::{closure#0}
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/raw/mod.rs:1236:48: 1236:79
             4: hashbrown::raw::RawTableInner::find_inner
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/raw/mod.rs:2075:27: 2075:36
             5: hashbrown::raw::RawTable::<(KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>)>::find::<{closure@hashbrown::map::equivalent_key<KeyWrapper<i32>, KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>>::{closure#0}}>
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/raw/mod.rs:1234:26: 1236:80
             6: hashbrown::raw::RawTable::<(KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>)>::get_mut::<{closure@hashbrown::map::equivalent_key<KeyWrapper<i32>, KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>>::{closure#0}}>
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/raw/mod.rs:1261:15: 1261:34
             7: hashbrown::HashMap::<KeyRef<i32>, core::ptr::NonNull<LruEntry<i32, i32>>>::get_mut::<KeyWrapper<i32>>
                 at /home/paolobarbolini/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hashbrown-0.16.1/src/map.rs:1459:19: 1459:62
             8: LruCache::<i32, i32>::get::<i32>
                 at src/lib.rs:451:29: 451:70
             9: tests::iter_mut_stacked_borrows_violation
                 at src/lib.rs:2865:20: 2865:33
             10: tests::iter_mut_stacked_borrows_violation::{closure#0}
                 at src/lib.rs:2855:44: 2855:44

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `/home/paolobarbolini/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner /tmp/lru-rs/target/miri/x86_64-unknown-linux-gnu/debug/deps/lru-560d2b17fca9fce5 iter_mut_stacked_borrows_violation` (exit status: 1)
note: test exited abnormally; to see the full output pass --no-capture to the harness.

@jeromefroe jeromefroe merged commit 62be24c into jeromefroe:master Jan 7, 2026
4 checks passed
@jeromefroe
Copy link
Owner

Thank you @paolobarbolini!

@link2xt
Copy link

link2xt commented Jan 7, 2026

Will there be a fix for 0.12.x and 0.13.x branches?

Asking because of chatmail/core#7692

This was referenced Jan 8, 2026
jpopesculian added a commit to jpopesculian/aws-sdk-rust that referenced this pull request Jan 8, 2026
Due to security vulnerability

```
ID: RUSTSEC-2026-0002
Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0002
Affected versions of this crate contain a soundness issue in the `IterMut`
iterator implementation. The `IterMut::next` and `IterMut::next_back`
methods temporarily create an exclusive reference to the key when
dereferencing the internal node pointer.
 
This invalidates the shared pointer held by the internal `HashMap`,
violating Stacked Borrows rules.
Announcement: jeromefroe/lru-rs#224
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants