Skip to content

net: close CreateFileW handle on SetNamedPipeHandleState failure - #8418

Merged
Darksonn merged 3 commits into
tokio-rs:masterfrom
philphauler:fix-named-pipe-handle-leak
Sep 28, 2026
Merged

Darksonn merged 3 commits into
tokio-rs:masterfrom
philphauler:fix-named-pipe-handle-leak

Conversation

@philphauler

Copy link
Copy Markdown
Contributor

Motivation

In ClientOptions::open_with_security_attributes_raw, the raw handle returned
by CreateFileW (line 2555) is only wrapped into an owner at line 2582
(NamedPipeClient::from_raw_handle). The early return at line 2578
(SetNamedPipeHandleState failure for message-mode on a byte-mode server)
leaves the handle open forever.

Solution

Capture last_os_error(), call CloseHandle(h), then return the error.

Testing

Windows 11, 16 iterations of ClientOptions::new().pipe_mode(PipeMode::Message).open()
on a byte-mode ServerOptions::new().create():

  • HEAD: GetProcessHandleCount shows 16 leaked handles (before=72, after=88)
  • with fix: 0 leaked handles

The early return leaks the handle returned by CreateFileW. Measured:
16 handles leaked in 16 iterations of message-mode open on a byte-mode
server.
@ADD-SP ADD-SP added A-tokio Area: The main tokio crate M-net Module: tokio/net labels Sep 6, 2026
Comment thread tokio/src/net/windows/named_pipe.rs Outdated
if result == 0 {
return Err(io::Error::last_os_error());
let err = io::Error::last_os_error();
unsafe { windows_sys::CloseHandle(h) };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Needs a safety comment.

Comment thread tokio/src/net/windows/named_pipe.rs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we create this type before the if matches!() { and rely on destructor?

Create the owned handle immediately after CreateFileW succeeds and
before the pipe-mode check, so the SetNamedPipeHandleState failure
path relies on OwnedHandle's destructor to close the handle instead
of a manual CloseHandle call. Add SAFETY comments to the unsafe
blocks this introduces.
@philphauler

Copy link
Copy Markdown
Contributor Author

Addressed both review comments:

  • Added SAFETY comments on all three unsafe blocks (344d587): the handle comes straight from CreateFileW, we are its sole owner, and ownership moves into NamedPipeClient at the end.

  • Restructured to create OwnedHandle right after CreateFileW so the destructor closes it on error, and the success path hands it over with into_raw_handle. Named pipe tests 6/6 and 38 doc tests pass on Windows.

Thanks for the review!

Comment on lines -14 to +16
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, RawHandle};
use crate::os::windows::io::{
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To use IntoRawHandle via this import, you must add the trait to src/doc/os.rs file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added IntoRawHandle to src/doc/os.rs in 7db644b. Without it, cargo +nightly doc -p tokio --features full with --cfg docsrs on Linux fails with unresolved import crate::os::windows::io::IntoRawHandle; with it the docs build passes. Sorry for the delay on this one.

named_pipe.rs imports IntoRawHandle through crate::os::windows::io, which
resolves to src/doc/os.rs when building docs on non-Windows. Without the
stub the docsrs build fails with an unresolved import. Refs tokio-rs#8418
@Darksonn Darksonn added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 25, 2026

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks.

@Darksonn
Darksonn merged commit e800714 into tokio-rs:master Sep 28, 2026
98 checks passed
@Darksonn Darksonn removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 28, 2026
@philphauler

Copy link
Copy Markdown
Contributor Author

✨πŸ₯ΉπŸš€

dadezzz pushed a commit to dadezzz/kube-dns-rs that referenced this pull request Oct 20, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [tokio](https://tokio.rs) ([source](https://github.com/tokio-rs/tokio)) | dependencies | minor | `1.53.3` β†’ `1.54.0` |

---

### Release Notes

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

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

[Compare Source](tokio-rs/tokio@tokio-1.53.3...tokio-1.54.0)

### 1.54.0 (October 19th, 2026)

The 1.54.0 release includes all changes up to 1.53.3.

The MSRV is increased to 1.85. ([#&#8203;8514])

##### Added

- fs: add `File::set_times`, `File::set_modified`, and `fs::FileTimes` ([#&#8203;8504])
- net: implement `Default` for `named_pipe::{ClientOptions, ServerOptions}` ([#&#8203;8378])
- runtime: add `Builder::max_io_events_per_busy_tick` ([#&#8203;8410])
- runtime: stabilize `tokio::runtime::worker_index()` ([#&#8203;8431])
- sync: add `SemaphorePermit::semaphore()` ([#&#8203;8482])
- sync: add `blocking_acquire` methods to `Semaphore` ([#&#8203;8269])
- sync: add `watch::Receiver::is_closed` ([#&#8203;8481])
- wasm: support JSPI for Emscripten target ([#&#8203;8285], [#&#8203;8627])
- wasm: support the `wasm32-unknown-emscripten` target ([#&#8203;8281], [#&#8203;8422], [#&#8203;8528], [#&#8203;8628])

##### Changed

- fs: reject zero in `File::set_max_buf_size` ([#&#8203;8449])
- io: make `copy_buf` cooperative ([#&#8203;8459])
- io: panic when `BufReader` or `BufStream` is created with zero capacity ([#&#8203;8494])
- io: panic when in-memory pipe is created with zero capacity ([#&#8203;8397])
- io: reject zero buffer size in `copy_bidirectional_with_sizes` ([#&#8203;8454])
- io: support vectored writes in `Sink` ([#&#8203;8663])
- net: start `UnixStream::pair` and `UnixDatagram::pair` sockets as writable ([#&#8203;8444])
- net: start accepted sockets as readable and writable ([#&#8203;8435])
- net: support `UCred` on Fuchsia ([#&#8203;8257])
- process: use `mio::unix::pipe` for Unix child stdio ([#&#8203;8603])
- runtime: cache-pad the shared injection queue and idle state ([#&#8203;8622])
- runtime: decide auto-boxing of spawned futures at monomorphization time ([#&#8203;8414])
- runtime: make `enable_io` available whenever `rt` is enabled ([#&#8203;8387])
- runtime: move `block_in_place` setup into a non-generic function ([#&#8203;8315])
- runtime: move the multi-thread inject queue to its own mutex ([#&#8203;8382])
- runtime: panic when `max_io_events_per_tick` is zero ([#&#8203;8476])
- runtime: suggest `.await` in nested `block_on` panic message ([#&#8203;8501])
- sync: avoid atomic reader count in broadcast slots ([#&#8203;8357])
- sync: use acquire/release orderings in `Notify` ([#&#8203;8325])
- time: remove the runtime handle from timers ([#&#8203;8458])

##### Fixed

- fs: restore `File` internal state when op fails ([#&#8203;8291])
- io: complete zero-length memory stream operations immediately ([#&#8203;8323])
- io: flush the writer when the reader stalls in `copy_buf` ([#&#8203;8424])
- io: keep the shutdown bit when clearing readiness ([#&#8203;8435])
- io: preserve the full `ScheduledIo` tick in readiness events ([#&#8203;8331])
- io: read lines as bytes so a partial line survives an I/O error ([#&#8203;8400])
- io: retry on `ErrorKind::Interrupted` in more I/O operations ([#&#8203;8417], [#&#8203;8432], [#&#8203;8448], [#&#8203;8453], [#&#8203;8455], [#&#8203;8460])
- io: use `spawn_mandatory_blocking` for stdout and stderr ([#&#8203;8506])
- io: wait for pending operations before `BufReader` seeks ([#&#8203;8599])
- net: close `CreateFileW` handle on `SetNamedPipeHandleState` failure ([#&#8203;8418])
- net: fix peer credentials on NetBSD ([#&#8203;8589])
- net: saturate `TcpSocket` and `UnixSocket` listen backlog ([#&#8203;8511])
- net: treat `ENOTCONN` as success in `UnixStream::shutdown` ([#&#8203;8526])
- runtime: allow building with `--all-features` without `tokio_unstable` ([#&#8203;8401])
- runtime: avoid overflow in `shutdown_timeout()` ([#&#8203;8497])
- runtime: fix `spawn_blocking` hang when only scheduler workers exist ([#&#8203;8408])
- runtime: fix misleading signal driver panic message ([#&#8203;8419])
- sync: preserve FIFO lock order when coop budget is exhausted ([#&#8203;8371])
- sync: use `usize` for permit count in `SemaphorePermit` ([#&#8203;8405])
- time: avoid interval deadline overflow ([#&#8203;8385])
- time: handle wrapped top-level timer-wheel slots ([#&#8203;8334])

##### Unstable

- fs: fall back when `io_uring` has no IO driver ([#&#8203;8165])
- metrics: cap log histogram precision ([#&#8203;8496])
- runtime: add an opt-in sharded `spawn_blocking` queue ([#&#8203;8337])
- runtime: expose schedule latency in task hooks ([#&#8203;8282])
- taskdump: fix idle bookkeeping when a task dump wakes parked workers ([#&#8203;8372])
- taskdump: skip non-idle tasks during taskdumps ([#&#8203;8568])

##### Documented

- docs: fix stray braces and broken links in rustdoc ([#&#8203;8605])
- fs: document cancellation behavior of `tokio::fs` ([#&#8203;8265])
- net: add Safety docs for named pipe `from_raw_handle` ([#&#8203;8378])
- net: document that `TcpStream::connect` returns only the last error ([#&#8203;8421])
- runtime: add error documentation for `runtime::Builder` ([#&#8203;8297])
- runtime: link `available_parallelism` in worker count docs ([#&#8203;8625])
- sync: clarify panic behavior of `broadcast::channel()` ([#&#8203;8420])
- sync: fix typo in `Semaphore` documentation ([#&#8203;8521])

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

</details>

---

### Configuration

πŸ“… **Schedule**: (UTC)

- 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 [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNjcuNSIsInVwZGF0ZWRJblZlciI6IjQ0LjE2Ny41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
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-net Module: tokio/net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants