fix(Proxy): bound SendTo's SOCKS5 UDP relay against oversized payloads - #882
Merged
Merged
Conversation
CDatagramSocketProxy::SendTo prepended a 10-byte SOCKS5 UDP request header into the fixed 5120-byte m_buffer and then memcpy'd the full payload right after, with no upper-bound check. Any outbound UDP datagram larger than PROXY_BUFFER_SIZE - PROXY_UDP_OVERHEAD_IPV4 (5110 bytes) corrupted memory past the end of m_buffer. The sibling RecvFrom already carried the exact guard that was missing on send -- when the receive payload didn't fit, it allocated a per-call buffer and freed it after the copy. The asymmetry was the bug. Mirror that pattern on send: allocate a dynamic buffer for the oversized case, fall back to m_buffer otherwise, free if it was dynamically allocated. nBytes is outbound-controlled (assembled by aMule, not directly attacker-controlled), so reachability today is narrow -- but the SendTo interface places no upper bound on its payload, so any future caller that hands it a large datagram would have walked off the end. Fixes amule-project#881.
got3nks
added a commit
to got3nks/amule
that referenced
this pull request
Jun 7, 2026
…ule-project#912) Extends existing categories (preferring extensions over new lines): - Performance/Upload: amule-project#898 SlotAllocation default raised. - Networking & Discovery: wire-parser hardening list extended with amule-project#879/amule-project#882/amule-project#890/amule-project#886; new amuleweb security hardening bullet consolidating ngosang's amule-project#869-amule-project#874 triage (all landed in amule-project#875); amulegui list extended with amule-project#857; shared-folder watcher extended with amule-project#858. - Packaging: Windows installer i18n line extended with amule-project#899. - Internals & Refactoring: new docs-polish + code-quality bullets covering amule-project#851/amule-project#855/amule-project#862/amule-project#888/amule-project#900/amule-project#866/amule-project#867/amule-project#895 and amule-project#909/amule-project#910/amule-project#912. - Translations: new pre-release final-wave bullet covering amule-project#847/amule-project#856/ amule-project#891/amule-project#908/amule-project#860/amule-project#904/amule-project#859/amule-project#863/amule-project#861/amule-project#880/amule-project#911/amule-project#901/amule-project#902/amule-project#889/amule-project#868/amule-project#853. - Bug Fixes & Stability: amule-project#850/amule-project#854/amule-project#878/amule-project#906. - CI: ccache wiring (amule-project#892, amule-project#903) + CodeQL binutils-dev (amule-project#907). Contributors footer gains mifritscher and nguyenhoangminhhieu2004-gif (both first-time contributors). PR index extended through amule-project#912.
mrjimenez
pushed a commit
that referenced
this pull request
Jun 8, 2026
Extends existing categories (preferring extensions over new lines): - Performance/Upload: #898 SlotAllocation default raised. - Networking & Discovery: wire-parser hardening list extended with #879/#882/#890/#886; new amuleweb security hardening bullet consolidating ngosang's #869-#874 triage (all landed in #875); amulegui list extended with #857; shared-folder watcher extended with #858. - Packaging: Windows installer i18n line extended with #899. - Internals & Refactoring: new docs-polish + code-quality bullets covering #851/#855/#862/#888/#900/#866/#867/#895 and #909/#910/#912. - Translations: new pre-release final-wave bullet covering #847/#856/ #891/#908/#860/#904/#859/#863/#861/#880/#911/#901/#902/#889/#868/#853. - Bug Fixes & Stability: #850/#854/#878/#906. - CI: ccache wiring (#892, #903) + CodeQL binutils-dev (#907). Contributors footer gains mifritscher and nguyenhoangminhhieu2004-gif (both first-time contributors). PR index extended through #912.
got3nks
added a commit
to got3nks/amule
that referenced
this pull request
Aug 10, 2026
m_eventPending was a plain bool, set from the ASIO thread and cleared on the main thread, tested and set in separate steps. The damaging interleaving is the ASIO thread seeing the latch set and skipping its post while the main thread is about to clear it: the event it deferred to belongs to the previous delivery, so the bytes it just buffered end up with nothing left to announce them. Captured on a live amulegui/amuled pair with the connection dead and 4.6 kB still unread in the kernel (c = buffered, p = read pending, e = event pending): handleRead(10)[c=10 p=0 e=1] bytes land, latch already set postSkip(2) [c=10 p=0 e=1] post skipped block(8) [c=10 p=0 e=0] main consumes that event, latch clear leaving kernel_unread=4669 event_pending=0 read_pending=0 buffered=10, with no read armed, no event outstanding, and nothing that can recover. Make it atomic and use an acq_rel exchange to post. The exchange makes test-and-set a single step, so exactly one caller wins the right to notify. It also writes unconditionally, so even the skipping path releases the buffer and the cleared m_readPending; EventProcessed acquires that same value, without which the reader is still free to observe a stale read state and block on data that has already arrived. Check the wrapper before taking the latch rather than after. With no wrapper nothing delivers a notification, and EventProcessed only runs off a delivered one, so claiming the latch there would leave it set for the life of the socket and make every later post skip -- the same wedge from the other side. HandleRead reaches that state deliberately: it logs "wrapper gone" and carries on to post. Returning early also avoids the window a take-then-release would open, where a concurrent post on the other thread sees the latch set and drops itself.
got3nks
added a commit
to got3nks/amule
that referenced
this pull request
Aug 10, 2026
Rebased onto master with amule-project#882 in it. The instrumentation now reads the event latch through the same atomic operation that changes it: PostReadEvent labels post/postSkip from the exchange's return value, and EventProcessed counts ev_spurious from the value its exchange replaced. A separate read would race with the posting side and, worse, is free to disagree with the decision it claims to describe -- useless for the one race the trace exists to study. Also fix the [ecread] dead-socket report, which tested the pending flag alone and reloaded it after the branch. A completion landing in between left it clear with the buffer full, so healthy sockets were reported as dead; those false alarms outnumbered the real ones. Load the flag once, reuse it, and require both halves -- nothing buffered AND nothing on the way -- before calling a socket finished.
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 #881.
CDatagramSocketProxy::SendToprepended the 10-byte SOCKS5 UDP request header into the fixed 5120-bytem_bufferand thenmemcpy'd the full payload right after, with no upper-bound check. Any outbound UDP datagram larger thanPROXY_BUFFER_SIZE - PROXY_UDP_OVERHEAD_IPV4(5110 bytes) walked past the end ofm_buffer.The sibling
CDatagramSocketProxy::RecvFromalready carried the exact guard that was missing on send — when the payload didn't fit it allocated a per-call buffer and freed it after the copy. The asymmetry was the bug.Mirror that pattern on send: allocate a dynamic buffer for the oversized case, fall back to
m_bufferotherwise, free if it was dynamically allocated.nBytesis outbound-controlled (assembled by aMule, not directly attacker-controlled), so practical reachability today is narrow — but theSendTointerface places no upper bound on its payload, so any future caller that hands it a large datagram would have walked off the end.Tested: macOS local build (Apple Silicon, Homebrew) — clean.