-
Notifications
You must be signed in to change notification settings - Fork 34
Closed
Description
event-listener
in its current state (531c106) is unsound. It's possible to trigger a use-after-free bug completely with safe Rust.
PoC:
use event_listener::{Event, EventListener};
fn main() {
let event = Event::new();
let event2 = Event::new();
let mut listener = Box::pin(EventListener::<()>::new());
listener.as_mut().listen(&event);
listener.as_mut().listen(&event2);
drop(listener);
event.notify(1);
}
cargo miri run
:
paul@Pauls-MBP ~/dev/event-listener-test (git)-[master] % cargo miri run
Preparing a sysroot for Miri (target: aarch64-apple-darwin)... done
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `/Users/paul/.rustup/toolchains/nightly-aarch64-apple-darwin/bin/cargo-miri runner target/miri/aarch64-apple-darwin/debug/event-listener-test`
error: Undefined Behavior: out-of-bounds pointer use: alloc846 has been freed, so this pointer is dangling
--> /Users/paul/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/non_null.rs:399:18
|
399 | unsafe { &*self.as_ptr().cast_const() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: alloc846 has been freed, so this pointer is dangling
|
= 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
help: alloc846 was allocated here:
--> src/main.rs:7:24
|
7 | let mut listener = Box::pin(EventListener::<()>::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: alloc846 was deallocated here:
--> src/main.rs:11:5
|
11 | drop(listener);
| ^^^^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `std::ptr::NonNull::<event_listener::sys::Link<()>>::as_ref::<'_>` at /Users/paul/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/non_null.rs:399:18: 399:46
= note: inside `event_listener::sys::Inner::<()>::notify::<event_listener::notify::Notify>` at /Users/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/event-listener-4.0.0/src/std.rs:263:42: 263:52
= note: inside `event_listener::sys::<impl event_listener::Inner<()>>::notify::<event_listener::notify::Notify>` at /Users/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/event-listener-4.0.0/src/std.rs:119:9: 119:35
= note: inside `event_listener::Event::notify::<i32>` at /Users/paul/.cargo/registry/src/index.crates.io-6f17d22bba15001f/event-listener-4.0.0/src/lib.rs:432:24: 432:44
note: inside `main`
--> src/main.rs:12:5
|
12 | event.notify(1);
| ^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
The issue is that EventListener::listen(mut self: Pin<&mut Self>, event: &Event<T>)
doesn't deal with the case when the EventListener
is already currently linked/associated with another/or same Event
. It just unconditionally overwrites the content of the EventListener's Listener
.
In my opinion, the general api design of Event/EventListener
is rather unnecessarily complex and introduces more overhead than needed. Tokio's Notify
api is much "better" in that regard. 🤷♂️
Metadata
Metadata
Assignees
Labels
No labels