stop named-pipe tls.connect({ socket }) re-emitting post-upgrade bytes on the original socket - #32372
stop named-pipe tls.connect({ socket }) re-emitting post-upgrade bytes on the original socket#32372Sids15 wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughA new internal symbol ChangesNamed-pipe TLS upgrade data leak fix
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/js/node/net.ts`:
- Line 1003: The assignment of connection[kupgradeDuplexFeeder] at line 1003 and
line 1042 is incomplete for non-Socket Duplex upgrades. Currently these only set
the feeder reference, but non-Socket Duplex types never execute through
SocketHandlers2.data, leaving the TLS feeder uninitialized. Restore the generic
Duplex data wiring by adding the necessary feeder invocation logic at both
locations (line 1003 and line 1042) to handle cases where SocketHandlers2.data
will not be called, ensuring the TLS upgrade path works for all Duplex types,
not just Bun Socket named-pipe paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9fd4d91b-61ac-48da-acb7-8d533e2b649d
📒 Files selected for processing (2)
src/js/node/net.tstest/js/node/tls/node-tls-namedpipes.test.ts
… bytes on the original socket
On Windows, tls.connect({ socket }) over a named-pipe net.Socket takes the
upgradeDuplexToTLS path, which (unlike the TCP upgradeTLS path) does not replace
connection._handle. The original native pipe handle keeps firing
SocketHandlers2.data, and the TLS layer was fed via connection.on("data", ...).
So post-upgrade ciphertext reached both the TLS feeder and any pre-existing user
data listener (the STARTTLS re-entry from oven-sh#32239), re-emitting encrypted bytes as
cleartext on the original socket.
The oven-sh#32241 kupgradedToTLS flag cannot be reused here: suppressing self.push()
would also starve the feeder, which consumes that same data event.
Fix: store the data feeder on the connection and have SocketHandlers2.data call
it directly, bypassing connection.push so no data event is emitted post-upgrade.
The socket goes quiet after the upgrade. Windows named pipes only.
Closes oven-sh#32242
a2398ce to
1ebb308
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/js/node/net.ts`:
- Around line 897-898: The upgrade feeder routing at lines 897-898 does not
account for Socket instances created with custom `onread` handlers, which
replace `this[khandlers]` around lines 763-775. These custom handlers bypass the
`kupgradeDuplexFeeder` check entirely, causing TLS upgrade bytes to be routed to
the `onread.callback` instead of the intended `events[0]` handler. To fix this,
modify the custom handler logic in the lines 763-775 region to also check for
and route data through `kupgradeDuplexFeeder` before delegating to the custom
`onread.callback`, ensuring that socket upgrade bytes are always processed by
the feeder mechanism regardless of whether the socket was constructed with an
`onread` handler.
In `@test/js/node/tls/node-tls-namedpipes.test.ts`:
- Around line 141-150: The test awaits multiple conditions without racing them
against error events: the `once(client, "secureConnect")` call, the
`messageReceived` promise, and the `clientReceived` promise are all awaited
independently. If any error event fires before these conditions are met, the
test will hang until timeout instead of failing immediately. Use Promise.race()
to race each awaited condition against the `done` promise, ensuring that if
`done` rejects (due to error events wired to `rejectDone`), all awaited
operations fail fast at the actual error cause rather than hanging.
- Around line 98-100: Remove the verbose comments from the test body that
explain bug mechanics and issue context. Keep only a terse reference to the
issue number (like `#32242`) at the top of the test if needed, and remove inline
comments throughout the test that restate the bug context or mechanics. Let the
test assertions demonstrate the invariant being tested rather than having
comments explain the expected behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: fa91279f-7da0-45aa-b6c5-a70ec36d4124
📒 Files selected for processing (2)
src/js/node/net.tstest/js/node/tls/node-tls-namedpipes.test.ts
- Route post-upgrade bytes through the TLS feeder in the custom onread data
handler too (sibling of SocketHandlers2.data), so onread sockets don't leak
ciphertext to onread.callback or starve the feeder.
- Restore pure-Duplex data wiring via attachUpgradeDuplexDataFeeder (named-pipe
net.Socket -> feeder; pure Duplex -> public data event).
- Test: trim comments to the issue URL; race awaits against the error promise."
|
Thanks again for this patch. It has been folded into #36534, which is now the single PR for this bug: it uses the same idea (feed the engine straight from the socket's native data handlers instead of its public data event), extended to the other upgrade sites that exist on current main, and your named-pipe round-trip test is carried there. You are credited as co-author on the commit. Closing this one so the fix lands in one place. |
What
Fixes #32242 — on Windows,
tls.connect({ socket })over a named-pipenet.Socketre-emits post-upgrade (encrypted)bytes on the original socket's pre-existing
datalisteners.Why
The named-pipe upgrade takes the
upgradeDuplexToTLSpath. Unlike the TCPupgradeTLSpath, it does not replaceconnection._handle, so the original native pipe handle keeps firingSocketHandlers2.datawithself = connection. The TLS layer was fed by subscribing to the connection's publicdataevent (connection.on("data", events[0])), so post-upgrade ciphertext flowed to both the TLS feeder (intended) and any pre-existing userdatalistener — the STARTTLS re-entry from #32239 — leaking encrypted bytes as cleartext.The
kupgradedToTLSflag from #32241 (TCP path) can't be reused here: suppressingself.push(buffer)would alsostarve the feeder, which consumes that very
dataevent, breaking TLS over named pipes entirely.How
Store the data feeder on the connection (
kupgradeDuplexFeeder) instead of subscribing to the publicdataevent,and have
SocketHandlers2.datacall it directly — bypassingconnection.push. Nodataevent is emittedpost-upgrade, so the original socket goes quiet while the TLS layer still receives every byte.
end/drain/closesubscriptions are unchanged (they carry no payload). Windows named pipes only.
Test
Adds a regression test to
test/js/node/tls/node-tls-namedpipes.test.ts(it.if(isWindows)): attaches apre-existing
datalistener on the originalnet.Socket, upgrades viatls.connect({ socket }), round-trips TLSapp data, and asserts the original listener received zero bytes. It fails on the unfixed build (listener receives
ciphertext) and passes after the fix. Runs on Windows CI.