Skip to content

Require that an RwLock has max_readers != 0#8076

Merged
Darksonn merged 2 commits intotokio-rs:tokio-1.47.xfrom
Darksonn:alice/rwlock-max-readers
May 7, 2026
Merged

Require that an RwLock has max_readers != 0#8076
Darksonn merged 2 commits intotokio-rs:tokio-1.47.xfrom
Darksonn:alice/rwlock-max-readers

Conversation

@Darksonn
Copy link
Copy Markdown
Member

If an RwLock is created with max readers equal to zero, then write locks do not ensure mutual exclusion because they acquire 0 permits. This can be abused to trigger UB.

let v = RwLock::with_max_readers(vec![1], 0);
let mut w1 = v.write().await;
let w2 = v.write().await;
let one = &w2[0];
*w1 = Vec::new();
println!("{one}"); // UAF

This bug was discovered by asking Gemini to look for bugs in the with_max_readers method of tokio/src/sync/rwlock.rs.

@Darksonn Darksonn added A-tokio Area: The main tokio crate M-sync Module: tokio/sync labels Apr 20, 2026
@github-actions github-actions Bot added the R-loom-sync Run loom sync tests on this PR label Apr 20, 2026
Copy link
Copy Markdown
Member

@ADD-SP ADD-SP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this example using Miri, and Miri also reports the UAF.

use tokio::sync::RwLock;

#[tokio::main]
async fn main () {
    let v = RwLock::with_max_readers(vec![1], 0);
    let mut w1 = v.write().await;
    let w2 = v.write().await;
    let one = &w2[0];
    *w1 = Vec::new();
    println!("{one}"); // UAF
}
  Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.95s
     Running `/playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/playground`
error: Undefined Behavior: constructing invalid value of type &i32: encountered a dangling reference (use-after-free)
    --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:2872:71
     |
2872 |             fn fmt(&self, f: &mut Formatter<'_>) -> Result { $tr::fmt(&**self, f) }
     |                                                                       ^^^^^^^ Undefined Behavior occurred here
...
2882 | fmt_refs! { Debug, Display, Octal, Binary, LowerHex, UpperHex, LowerExp, UpperExp }
     | ----------------------------------------------------------------------------------- in this macro invocation
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
     = note: this is on thread `main`
     = note: stack backtrace:
             0: <&i32 as std::fmt::Display>::fmt
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:2872:71: 2872:78
             1: core::fmt::rt::Argument::<'_>::fmt
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/rt.rs:152:76: 152:95
             2: std::fmt::write
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:1685:17: 1687:80
             3: std::io::default_write_fmt::<std::io::StdoutLock<'_>>
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:621:11: 621:40
             4: <std::io::StdoutLock<'_> as std::io::Write>::write_fmt
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:1976:13: 1976:42
             5: <&std::io::Stdout as std::io::Write>::write_fmt
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:834:9: 834:36
             6: <std::io::Stdout as std::io::Write>::write_fmt
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:808:9: 808:33
             7: std::io::stdio::print_to::<std::io::Stdout>
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1164:21: 1164:47
             8: std::io::_print
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/stdio.rs:1275:5: 1275:37
             9: main::{closure#0}
                 at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/macros.rs:146:9: 146:62
             10: tokio::runtime::park::CachedParkThread::block_on::<{async block@src/main.rs:3:1: 3:15}>::{closure#0}
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/runtime/park.rs:284:60: 284:84
             11: tokio::task::coop::with_budget::<std::task::Poll<()>, {closure@tokio::runtime::park::CachedParkThread::block_on<{async block@src/main.rs:3:1: 3:15}>::{closure#0}}>
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/task/coop/mod.rs:167:5: 167:8
             12: tokio::task::coop::budget::<std::task::Poll<()>, {closure@tokio::runtime::park::CachedParkThread::block_on<{async block@src/main.rs:3:1: 3:15}>::{closure#0}}>
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/task/coop/mod.rs:133:5: 133:38
             13: tokio::runtime::park::CachedParkThread::block_on::<{async block@src/main.rs:3:1: 3:15}>
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/runtime/park.rs:284:31: 284:85
             14: tokio::runtime::context::blocking::BlockingRegionGuard::block_on::<{async block@src/main.rs:3:1: 3:15}>
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/runtime/context/blocking.rs:66:9: 66:25
             15: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::<{async block@src/main.rs:3:1: 3:15}>::{closure#0}
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/runtime/scheduler/multi_thread/mod.rs:92:13: 92:38
             16: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::<{async block@src/main.rs:3:1: 3:15}>
                 at /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.0/src/runtime/scheduler/multi_thread/mod.rs:91:9: 93:11
             17: main
                 at src/main.rs:10:5: 10:23
     = note: this error originates in the macro `fmt_refs` (in Nightly builds, run with -Z macro-backtrace for more info)

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

error: aborting due to 1 previous error

@Darksonn
Copy link
Copy Markdown
Member Author

This probably requires a backport, so it should not be merged until base branch is updated.

Comment thread tokio/src/sync/rwlock.rs Outdated
Comment thread tokio/src/sync/rwlock.rs Outdated
Comment thread tokio/src/sync/rwlock.rs Outdated
Comment thread tokio/src/sync/rwlock.rs
Comment thread tokio/tests/sync_rwlock.rs Outdated
Comment thread tokio/tests/sync_rwlock.rs Outdated
Darksonn added 2 commits May 2, 2026 16:25
Backport of tokio-rs#8076 to 1.47.x.

If an RwLock is created with max readers equal to zero, then write locks do not ensure mutual exclusion because they acquire 0 permits. This can be abused to trigger UB.
@Darksonn Darksonn force-pushed the alice/rwlock-max-readers branch from 5d57db2 to 5862018 Compare May 2, 2026 16:57
@Darksonn Darksonn changed the base branch from master to tokio-1.47.x May 2, 2026 16:57
@Darksonn Darksonn merged commit 30d25cc into tokio-rs:tokio-1.47.x May 7, 2026
86 checks passed
@Darksonn Darksonn deleted the alice/rwlock-max-readers branch May 7, 2026 07:32
eleboucher pushed a commit to eleboucher/towonel that referenced this pull request May 9, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tokio](https://tokio.rs) ([source](https://github.com/tokio-rs/tokio)) | workspace.dependencies | patch | `1.52.2` → `1.52.3` |

---

### Release Notes

<details>
<summary>tokio-rs/tokio (tokio)</summary>

### [`v1.52.3`](https://github.com/tokio-rs/tokio/releases/tag/tokio-1.52.3): Tokio v1.52.3

[Compare Source](tokio-rs/tokio@tokio-1.52.2...tokio-1.52.3)

### 1.52.3 (May 8th, 2026)

##### Fixed

- sync: fix underflow in mpsc channel `len()` ([#&#8203;8062])
- sync: notify receivers in mpsc `OwnedPermit::release()` method ([#&#8203;8075])
- sync: require that an `RwLock` has `max_readers != 0` ([#&#8203;8076])
- sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#&#8203;8074])

[#&#8203;8062]: tokio-rs/tokio#8062

[#&#8203;8074]: tokio-rs/tokio#8074

[#&#8203;8075]: tokio-rs/tokio#8075

[#&#8203;8076]: tokio-rs/tokio#8076

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/towonel/pulls/36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-tokio Area: The main tokio crate M-sync Module: tokio/sync R-loom-sync Run loom sync tests on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants