Skip to content

fix(netwatch): guard divide-by-zero in poll_recv_noq trace when stride == 0#149

Merged
flub merged 1 commit into
n0-computer:mainfrom
mkdir700:fix/netwatch-udp-stride-divide-by-zero
May 19, 2026
Merged

fix(netwatch): guard divide-by-zero in poll_recv_noq trace when stride == 0#149
flub merged 1 commit into
n0-computer:mainfrom
mkdir700:fix/netwatch-udp-stride-divide-by-zero

Conversation

@mkdir700
Copy link
Copy Markdown
Contributor

Description

netwatch::udp::UdpSocket::poll_recv_noq evaluates meta.len / meta.stride directly inside trace!() macro arguments. noq_udp::RecvMeta.stride is allowed to be 0 in practice on the GRO path (empty datagrams, kernel falling back to a non-segmented receive), which makes the trace argument panic with attempt to divide by zero and terminate the host process — the panic originates inside a tokio task with no upstream catch.

This PR replaces the bare division with checked_div(...).unwrap_or(0), behavior-preserving for all non-zero strides (the only case currently observable) and emitting count = 0 when stride is 0.

-                            count = meta.len / meta.stride,
+                            count = meta.len.checked_div(meta.stride).unwrap_or(0),

Observed in production on:

  • Windows 11 24H2 (build 26200) — netwatch 0.16.0
  • macOS 15.4.1 and 26.x — netwatch 0.16.0

across 6 distinct fatal events since 2026-05-12. Same code path is present on main and in published 0.16.0 / 0.17.0.

Closes #148.

Breaking Changes

None. The new expression is identical to the original for any stride > 0. When stride == 0 the field now reports 0 instead of panicking.

Notes & open questions

  • The doc comment on noq_udp::RecvMeta::stride does not forbid stride == 0, so guarding here looks correct regardless of whether the upstream noq-udp / kernel behaviour is intentional.
  • No test added: this is a one-line guard inside a trace!() field, and reproducing stride == 0 requires either kernel/GRO state or a mocked noq_udp::State::recv. Happy to add one if a maintainer prefers; otherwise the simpler patch seemed appropriate.

Change checklist

`UdpSocket::poll_recv_noq` evaluated `meta.len / meta.stride` directly
inside `trace!()` macro arguments. `noq_udp::RecvMeta.stride` is allowed
to be `0` in practice on the GRO path (empty datagrams, kernel falling
back to a non-segmented receive), which crashes the host process with
`attempt to divide by zero` — observed on Windows 11 24H2 and macOS
15.4/26.x against netwatch 0.16.0/0.17.0.

Replace the bare division with `checked_div(...).unwrap_or(0)` so the
trace field reports `0` when `stride == 0` and stays identical
otherwise.

Closes n0-computer#148.
@flub flub added this to the net-tools: iroh v1.0-rc1 milestone May 19, 2026
@flub flub added this to iroh May 19, 2026
@github-project-automation github-project-automation Bot moved this to 🚑 Needs Triage in iroh May 19, 2026
@flub flub enabled auto-merge (squash) May 19, 2026 15:14
@flub flub merged commit d032e20 into n0-computer:main May 19, 2026
23 of 28 checks passed
@github-project-automation github-project-automation Bot moved this from 🚑 Needs Triage to ✅ Done in iroh May 19, 2026
@flub
Copy link
Copy Markdown
Contributor

flub commented May 19, 2026

ugh, no checks were required so github merged this immediately and then all CI fails because the commit is no longer there as it was squash-merged 😭

anyway, should be fine. thanks for the PR!

@mkdir700 mkdir700 deleted the fix/netwatch-udp-stride-divide-by-zero branch May 19, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

netwatch: divide-by-zero panic in UdpSocket::poll_recv_noq when RecvMeta.stride == 0

2 participants