Skip to content

Harden Win32 FFI boundary and fix safety/correctness issues (0.7.0) - #39

Merged
wiresock merged 4 commits into
mainfrom
fix/win32-safety-review-0.7.0
Jun 24, 2026
Merged

Harden Win32 FFI boundary and fix safety/correctness issues (0.7.0)#39
wiresock merged 4 commits into
mainfrom
fix/win32-safety-review-0.7.0

Conversation

@wiresock

@wiresock wiresock commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses safety, correctness, and robustness findings from a review of the Win32/driver boundary, and bumps the crate to 0.7.0. Every finding was reviewed and validated before fixing; the change set builds clean and passes fmt/clippy -D warnings/test.

⚠️ Breaking changes

  • Ndisapi no longer implements Clone. It owns the driver HANDLE and closes it on Drop, so cloning risked a double-close / use of a reused OS handle. Use Arc<Ndisapi> for shared ownership (the async API and examples already do).
  • ndis_get_request / ndis_set_request are now unsafe fn. The driver reads/writes the generic T as raw bytes, so T must be plain-old-data. The safe u32 wrappers (get_hw_packet_filter / set_hw_packet_filter) are unchanged.

Fixes

Critical

  • IPv6 truncation + uninitialized memory (sockaddr_storage.rs, network_adapter_info.rs): added length-aware from_raw_sockaddr(*const SOCKADDR, len) and switched adapter enumeration to it so IPv6 addresses are no longer truncated to a 16-byte SOCKADDR; all from_sockaddr* constructors now zero-initialize the storage.
  • Unsound generic OID API and Ndisapi: Clone double-close: see Breaking changes.

High

  • Async event-stream races (win32_event_stream.rs): the waker is registered before the readiness re-check (lost-wakeup fix); UnregisterWaitEx(.., INVALID_HANDLE_VALUE) waits for in-flight callbacks, and on failure deliberately leaks instead of freeing the callback / closing the event (avoids use-after-free / use-after-close).
  • Async event-handle leak (async_api.rs): AsyncNdisapiAdapter::new no longer leaks the event on partial construction.
  • Registry getter UB + HKEY leak (static_api.rs): getters now write through a real &mut, validate REG_DWORD/size, and always close the key via an RAII guard.
  • set_friendly_name wrote UTF-8 as REG_SZ (network_adapter_info.rs): now writes UTF-16 and updates the cached name only after the write succeeds.

Medium

  • IntermediateBuffer::set_length clamps to MAX_ETHER_FRAME; data accessors clamp defensively (no panics).
  • Centralized IPv4 -> IN_ADDR conversion to network byte order (the from_ip_string path stored host order, reversing addresses on little-endian Windows); fixed add_ndp_entry_ipv4; added round-trip tests.
  • delete_routes / reset_* / delete_unicast_address_* accumulate per-entry failures instead of masking them.
  • get_tcpip_bound_adapters_info clamps the driver-reported count to ADAPTER_LIST_SIZE and uses lossy UTF-8 for names.
  • Async batch send (send_packets_to_adapter/mstcp) returns the number of packets submitted instead of the always-zero packet_success counter.

Version

0.6.6 -> 0.7.0 (minor bump signals the breaking changes per 0.x semver) in Cargo.toml and README.md.

Verification

  • cargo build --all-targets - pass
  • cargo test - pass (31 unit + 8 doctests; driver-dependent tests are #[ignore]d)
  • cargo fmt --check - pass
  • cargo clippy -- -D warnings - pass (also fixed pre-existing doc-comment lints)

Addresses a batch of safety, correctness, and robustness findings around
the Win32/driver boundary, and bumps the crate to 0.7.0.

Breaking changes:
- Ndisapi no longer implements Clone. It owns the driver HANDLE and closes
  it on Drop, so cloning risked a double-close / use of a reused OS handle.
  Use Arc<Ndisapi> for shared ownership (as the async API and examples do).
- ndis_get_request / ndis_set_request are now `unsafe fn`: the driver
  reads/writes the generic T as raw bytes, so T must be plain old data.
  The safe u32 wrappers (get/set_hw_packet_filter) are unchanged.

Critical / High:
- SockAddrStorage: add length-aware from_raw_sockaddr and use it during
  adapter enumeration so IPv6 addresses are no longer truncated to 16
  bytes; zero-initialize storage in all constructors (no more reads of
  uninitialized memory).
- win32_event_stream: register the waker before re-checking readiness
  (fixes a lost-wakeup race); UnregisterWaitEx now waits for in-flight
  callbacks (INVALID_HANDLE_VALUE) and, if that fails, leaks rather than
  freeing the callback / closing the event (avoids use-after-free and
  use-after-close).
- AsyncNdisapiAdapter::new no longer leaks the event handle on partial
  construction.
- static_api registry getters wrote through a *mut derived from a shared
  reference (UB) and leaked the HKEY; now use a real &mut, validate
  REG_DWORD/size, and close the key via an RAII guard.
- set_friendly_name now writes a UTF-16 REG_SZ value (was UTF-8) and only
  updates the cached name after the registry write succeeds.

Medium:
- IntermediateBuffer::set_length clamps to MAX_ETHER_FRAME and the data
  accessors clamp defensively, so a bad length can no longer panic.
- Centralize IPv4 -> IN_ADDR conversion to network byte order (the
  from_ip_string path stored host order, reversing addresses on
  little-endian Windows); fix add_ndp_entry_ipv4 to match; add round-trip
  tests.
- delete_routes / reset_* / delete_unicast_address_* accumulate per-entry
  failures instead of masking them.
- get_tcpip_bound_adapters_info clamps the driver-reported adapter count to
  ADAPTER_LIST_SIZE and uses lossy UTF-8 for adapter names.
- Async batch send (send_packets_to_adapter/mstcp) returns the number of
  packets submitted instead of the always-zero packet_success counter.

Also runs cargo fmt and fixes doc-comment lints so `cargo clippy -- -D
warnings` is clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 24, 2026 18:09

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several critical safety, correctness, and resource management improvements across the ndisapi crate. Key changes include introducing RAII guards for registry keys to prevent leaks, fixing socket address parsing to prevent IPv6 truncation, ensuring consistent network byte order for IPv4 addresses, and replacing unsafe MaybeUninit patterns with zero-initialization to avoid undefined behavior. Additionally, API safety is improved by marking OID request methods as unsafe, removing the Clone implementation from Ndisapi to prevent double-closes, and clamping buffer lengths to prevent out-of-bounds panics. The review feedback correctly points out a potential issue in get_tcpip_bound_adapters_info where using String::from_utf8_lossy on the entire fixed-size adapter name array could retain garbage bytes after the first NUL terminator, suggesting converting only up to the first NUL byte instead.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ndisapi/base_api.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the Win32/driver FFI boundary in the ndisapi crate and ships a 0.7.0 release with breaking API changes intended to eliminate UB and resource-lifetime hazards.

Changes:

  • Fixes socket-address handling (IPv6 length-awareness, zero-initialized storage) and centralizes IPv4 byte-order conversion with added round-trip tests.
  • Hardens async Win32 event-stream teardown and construction to prevent lost wakeups, leaks on partial initialization, and potential use-after-free/use-after-close.
  • Tightens unsafe driver OID APIs (ndis_get_request/ndis_set_request now unsafe), removes Ndisapi: Clone, and improves robustness of several IP Helper / registry helpers.

Reviewed changes

Copilot reviewed 15 out of 20 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/netlib/ip_helper/sockaddr_storage.rs Adds length-aware raw sockaddr conversion, zero-inits storage, centralizes IPv4 IN_ADDR encoding, and adds endianness/round-trip tests.
src/netlib/ip_helper/network_adapter_info/routing.rs Accumulates per-route deletion failures instead of masking earlier failures.
src/netlib/ip_helper/network_adapter_info/ndp.rs Fixes IPv4 address byte-order storage when creating NDP entries.
src/netlib/ip_helper/network_adapter_info/address.rs Accumulates per-entry deletion failures for unicast address removal.
src/netlib/ip_helper/network_adapter_info.rs Uses SOCKET_ADDRESS length-aware conversion for adapter address enumeration; fixes registry friendly-name write encoding semantics.
src/netlib/ip_helper/if_luid.rs Doc comment formatting cleanup.
src/netlib.rs Doc comment formatting cleanup.
src/ndisapi/static_api.rs Adds HKEY RAII guard and safe REG_DWORD query/set helpers; removes prior UB/leak patterns.
src/ndisapi/base_api.rs Marks generic OID APIs as unsafe, clamps driver-reported adapter counts, and avoids UTF-8 panics via lossy conversion.
src/ndisapi.rs Removes Clone from Ndisapi and documents shared-ownership via Arc.
src/lib.rs Re-formats re-exports for readability.
src/driver/filters.rs Doc comment formatting cleanup.
src/driver/base.rs Clamps IntermediateBuffer length and defensively clamps slice access to prevent panics.
src/driver.rs Doc comment formatting cleanup.
src/async_api/win32_event_stream.rs Fixes lost-wakeup race, and hardens unregister/teardown to avoid UAF/UAC by waiting for callbacks.
src/async_api.rs Prevents Win32 event handle leaks on partial construction; adjusts batch-send return values to reflect submissions.
README.md Bumps documented dependency version to 0.7.0.
examples/listadapters.rs Updates call sites for newly-unsafe OID getter API.
examples/async-packthru.rs Whitespace cleanup.
Cargo.toml Bumps crate version to 0.7.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/netlib/ip_helper/network_adapter_info.rs
Comment thread src/netlib/ip_helper/sockaddr_storage.rs Outdated
Comment thread src/netlib/ip_helper/sockaddr_storage.rs Outdated
Comment thread src/netlib/ip_helper/sockaddr_storage.rs Outdated
Comment thread src/ndisapi/static_api.rs
- get_tcpip_bound_adapters_info: convert the adapter name only up to the
  first NUL, so trailing garbage in the fixed-size buffer is ignored
  rather than embedded in the name (gemini-code-assist).
- socket_address_to_ip: reject a non-positive iSockaddrLength via
  usize::try_from so a signed/negative length cannot wrap to a huge usize
  and over-read the source sockaddr (Copilot).
- set_registry_dword: use to_le_bytes to document the little-endian
  REG_DWORD encoding instead of relying on host endianness (Copilot).
- sockaddr_storage: update from_sockaddr_in/from_sockaddr_in6 docs to match
  the zero-initialized implementation (drop the stale MaybeUninit/assume_init
  "# Safety" text) and reword the ipv4_to_in_addr comment to focus on the
  to_be/to_le inconsistency (Copilot).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 20 changed files in this pull request and generated 1 comment.

Comment thread src/netlib/ip_helper/sockaddr_storage.rs Outdated
Reword the doc comment to describe the in-memory octet layout ([a, b, c, d],
first octet at the lowest address) explicitly instead of calling it "network
byte order", which could be misread as a claim about the u32's numeric
endianness. Addresses PR review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 20 changed files in this pull request and generated 1 comment.

Comment thread src/driver.rs Outdated
Addresses PR review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 20 changed files in this pull request and generated no new comments.

@wiresock
wiresock merged commit 767cf4d into main Jun 24, 2026
6 checks passed
@wiresock
wiresock deleted the fix/win32-safety-review-0.7.0 branch June 24, 2026 19:08
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.

2 participants