Close the data connection when a transfer command fails - #80
Open
christhomas wants to merge 1 commit into
Open
Conversation
Fixes secsy#46. prepareDataConn opens the data connection before the transfer command is sent — it has to, because active mode needs the port in hand to tell the server about. Nothing closes it until the getter has handed it over, and the deferred close is only set up after that. A command the server refuses returns in between. The connection is then open with no reference left to close it: one leaked descriptor per failed transfer, on both ends of the wire. 25 failed retrieves leaked exactly 25 descriptors against both test servers. prepareDataConn now returns an abort alongside the getter, and the two internal callers defer it. The abort is a no-op once the getter has run, since the caller owns the connection from then on. The exported RawConn.PrepareDataConn keeps its signature. Widening it would break every implementation of that interface, and a caller driving the protocol by hand already owns the connection either way — that is now said in a comment where someone would look for it. file_system.go's dataStringList had the same shape and the same leak, found while fixing the reported one. Red before green: the leak is measured by counting /proc/self/fd across 25 failed transfers, which is exact rather than approximate — 25 leaked before, none after. Full suite green three consecutive runs.
This was referenced Aug 26, 2026
Open
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #46.
prepareDataConnopens the data connection before the transfer command is sent — it has to, because active mode needs the port in hand to tell the server about. Nothing closes it until the getter has handed it over, and the deferreddc.Close()is only set up after that.A command the server refuses returns in between, exactly as @yehaoyu-e pointed out:
Measured rather than estimated
Counting
/proc/self/fdacross 25 failed retrieves:One per failed transfer, on both ends of the wire.
The fix
prepareDataConnreturns an abort alongside the getter, and the internal callers defer it. It is a no-op once the getter has run, since the caller owns the connection from then on. Both the active-mode listener and the passive-mode connection are covered.RawConn.PrepareDataConnkeeps its signature. Widening it would break every implementation of that interface, and a caller driving the protocol by hand owns the connection either way — that is now said in a comment where someone would look for it.dataStringListinfile_system.gohad the same shape and the same leak. Found while fixing the reported one, and fixed with it.One note on running the test: it counts descriptors via
/proc/self/fd, so it skips on anything but Linux. This branch is offmaster, soTestMainstill needs./build_test_server.sh; I verified via the container harness in #73.