Skip to content

fix(driver): queue the io_uring SQE only after its key is leaked - #1136

Open
spnngl wants to merge 1 commit into
compio-rs:masterfrom
spnngl:fix/iour-push-key-order
Open

spnngl wants to merge 1 commit into
compio-rs:masterfrom
spnngl:fix/iour-push-key-order

Conversation

@spnngl

@spnngl spnngl commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

iour::Driver::push_raw_with_key queued the SQE first, with the key pointer as user_data, and only then recorded the key in in_flight and leaked it with into_raw. Anything that panicked in between unwound with the key still owned by the stack, so the key and its buffers were freed while the SQE sat in the ring (or was already running under SQPOLL). The CQE then made poll_entries call ErasedKey::from_raw on freed memory, which is a use-after-free followed by a double free.

One panic is reachable through the public API. On a fusion build, Proactor::push_with_extra accepts an Extra from a proactor of the other driver type, and set_in_flight goes through as_iour_mut, which panics with "Current driver is not io_uring" on a Poll Extra.

Fix

  • push_raw_with_key first gets the io_uring Extra, which is the only step that can panic, before anything is recorded. It then records the key in in_flight and leaks it, and queues the SQE as its last step. If push_raw fails, the key is taken back with from_raw and removed from in_flight.
  • push_with_extra checks the Extra against the driver type with a new crate-private Extra::is_for. Only the fusion driver can mismatch. On a mismatch, the op completes with InvalidInput and is handed back in the BufResult, so the signature doesn't change.

Test

compio-driver/tests/foreign_extra.rs pushes a PollOnce with a Poll Extra into an io_uring proactor, and an io_uring Extra into a poll proactor. It's skipped when io_uring isn't available.

  • Before the fix, it panics with "Current driver is not io_uring".
  • After the fix, both pushes return InvalidInput.
  • The full workspace suites pass with --features all and --features all,polling, apart from compio-process's arg0, which fails locally because busybox sh dispatches on argv[0]. The poll-only driver suite (--no-default-features --features polling,sync) passes too.
  • Clippy is clean on Linux (with and without polling) and macOS.
  • The runs used Linux 7.0.14 on arm64, in Docker with seccomp disabled.

Caveats

  • The rollback when push_raw fails has no test, because submit_auto can't be made to fail on demand.
  • When the SQ is full, push_raw reaps CQEs and wakes their futures while our key is already in in_flight but its SQE isn't queued. If a waker panics there, the key stays in in_flight without an SQE. That's safe, since the kernel never saw the pointer. Today Driver::drop frees it. With the teardown drain from fix(driver): drain in-flight io_uring ops before teardown #1135, teardown would wait the full timeout for a CQE that never comes, then leak the key.

push_raw_with_key queued the SQE first, then recorded the key in
in_flight and leaked it. set_in_flight panics when the op carries a
Poll Extra (reachable through Proactor::push_with_extra on a fusion
build); the unwind then freed the key while the kernel held its
pointer as user_data, a use-after-free once the CQE arrived.

- push_raw_with_key records and leaks the key first, so queueing the
  SQE is its last step; a failed push takes the key back
- push_with_extra completes the op with InvalidInput when the Extra
  was created for another driver type, instead of panicking
@github-actions github-actions Bot added bug Something isn't working package: driver Related to compio-driver labels Nov 7, 2026
@Berrysoft

Copy link
Copy Markdown
Member

Please split this PR into two ones. One for possible double-free, and one for driver type validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working package: driver Related to compio-driver

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants