Skip to content

holepunch: initialize timeout before registering notifiee - #3529

Merged
sukunrt merged 2 commits into
libp2p:masterfrom
Tristan-Wilson:fix/holepunch-init-race
Aug 3, 2026
Merged

holepunch: initialize timeout before registering notifiee#3529
sukunrt merged 2 commits into
libp2p:masterfrom
Tristan-Wilson:fix/holepunch-init-race

Conversation

@Tristan-Wilson

Copy link
Copy Markdown
Contributor

Description

waitForPublicAddr constructed a holePuncher, whose constructor registered the network notifiee, and only then assigned directDialTimeout to the returned object.

An inbound relayed connection could therefore enter DirectConnect and read the timeout concurrently with that assignment. Besides being a data race, a callback in this startup window could observe the zero value and create an immediately expired dial context.

This change passes the configured timeout into the constructor and initializes it before Network.Notify publishes the hole puncher. It also adds a deterministic regression test that inspects the configured value at the registration boundary.

No exported API or lifecycle behavior changes.

Reproduction

This was found while running a private downstream project's end-to-end DCUtR test under the race detector. It was initially observed with go-libp2p v0.48.0 and reproduced on current master before this change:

WARNING: DATA RACE
Read at ...:
  holepunch.(*holePuncher).directConnect()
      p2p/protocol/holepunch/holepuncher.go:115

Previous write at ...:
  holepunch.(*Service).waitForPublicAddr()
      p2p/protocol/holepunch/svc.go:152

The new unit test provides a deterministic form of the same ordering check. Without the fix it observes:

directDialTimeout at registration = 0s, want 137ms

Testing

go test -race ./p2p/protocol/holepunch \
  -run '^(TestHolePuncherFullyInitializedBeforeNetworkNotify|TestWaitForPublicAddr_NoDeadlockOnCancel)$' \
  -count=100
go vet ./p2p/protocol/holepunch

The private downstream DCUtR reproduction also passed 20 consecutive race-detector runs after the fix, followed by its package-level and full repository race suites.

AI assistance disclosure

This PR was developed with substantial OpenAI Codex assistance. Codex analyzed the race, authored the minimal code change and regression test, ran the validation described above, and drafted this description. I reviewed the result and am submitting it.

@sukunrt sukunrt 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 for the fix. The fix is fine, but the test seems too tailored to the internals. Can you see if there's a less intrusive way to test this? Maybe we can run lots of hosts in parallel and try to trigger the race?

Otherwise It's fine to fix this without a test.

@Tristan-Wilson

Copy link
Copy Markdown
Contributor Author

Thanks for the fix. The fix is fine, but the test seems too tailored to the internals. Can you see if there's a less intrusive way to test this? Maybe we can run lots of hosts in parallel and try to trigger the race?

Otherwise It's fine to fix this without a test.

Thanks for the feedback, I'm traveling and should be able to get to it the week after next.

Tristan-Wilson and others added 2 commits August 3, 2026 17:58
The hole-punch network notifiee was registered before the service copied
directDialTimeout onto it. An inbound relayed connection could therefore
read the field concurrently and observe the zero value.

Pass the configured timeout into the constructor so every field is
initialized before Network.Notify publishes the object. Add a
deterministic regression test that inspects the timeout at registration.

Signed-off-by: Tristan Wilson <commits@tristanwilson.earth>
The test depends too much on the package internals. The race window it
checks is closed by the previous commit, and is infeasible to trigger
with real hosts.

Assisted-By: Claude Fable 5
@sukunrt
sukunrt force-pushed the fix/holepunch-init-race branch from ab0548f to 894fa09 Compare August 3, 2026 12:28
@sukunrt

sukunrt commented Aug 3, 2026

Copy link
Copy Markdown
Member

That's fine, I can do that.

@sukunrt
sukunrt merged commit e20bb60 into libp2p:master Aug 3, 2026
8 of 9 checks passed
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