cleanup: wxASSERT -> wxCHECK_MSG on array-access guards in Logger / UserEvents - #772
Merged
mrjimenez merged 1 commit intoMay 30, 2026
Merged
Conversation
Logger / UserEvents wxASSERT is a no-op in release builds, so the existing "wxASSERT(precond); use_value()" pattern in CLogger::GetDebugCategory and in CUserEvents' s_EventList accessors would read past the array in release if an out-of-range index ever reached them (where in debug it would have asserted + aborted). clang-tidy surfaces all of these as clang-analyzer-security.ArrayBound on the release-build code path. Switch to wxCHECK_MSG / wxCHECK_RET on the same precondition: debug builds still assert + abort on first mis-use, release builds now log the failure and either return a safe sentinel (entry [0] of the array, which is always present and well-defined) or short-circuit the void path. UserEvents.cpp's CheckIndex was previously guarded behind #ifdef __WXDEBUG__ because wxASSERT doesn't evaluate its argument; wxCHECK_* does, so the function is now always-defined. It is a trivial one-liner and adds no measurable cost in release. Out of scope for this commit: the MD4Hash.h:202 warning that appeared in the same cluster on lint output is not actually a wxASSERT issue -- the analyzer can't track the per-byte writes through RawPokeUInt64 in CMD4Hash::SetHash, so it concludes m_hash is partially uninitialised when EncodeSTL reads it. The code is correct; the analyzer is symbolically wrong on that one. Verified the warnings disappear: a re-run of clang-tidy with the .clang-tidy baseline from amule-project#770 over the two changed files emits zero ArrayBound hits, down from 12 on the pre-fix tree.
Closed
mrjimenez
pushed a commit
that referenced
this pull request
Jun 1, 2026
mifritscher2 hit a debug-build assert switching ed2k servers:
/home/x/code/amule/src/EncryptedStreamSocket.cpp(437): assert
"m_nReceiveBytesWanted > 0" failed in Negotiate().
bt: CServerSocket::OnReceive -> CEMSocket::OnReceive
-> CEncryptedStreamSocket::Read -> Negotiate -> wxASSERT
CEncryptedStreamSocket::Read() unconditionally calls Negotiate()
whenever the socket sits in ECS_NEGOTIATING and bytes arrive. The
negotiation state machine consumes bytes in chunks of
m_nReceiveBytesWanted; when that counter hits 0 the inner loop exits
and the function returns either via ONS_COMPLETE (advancing to
ECS_ENCRYPTING) or via a state transition that resets the counter to
a fresh positive expectation.
There is a window where the socket can stay in ECS_NEGOTIATING with
m_nReceiveBytesWanted == 0 -- the obvious trigger is the user
switching servers while the kernel still has buffered bytes for the
previous half-negotiated connection. The next OnReceive lands in
Read(), Read() dispatches to Negotiate(), and we hit the precondition
assert.
The asserted precondition is a real bug shape: walking into the while
loop with m_nReceiveBytesWanted == 0 lets std::min(nLen - nRead, 0)
return 0 forever (infinite loop bails via ONS_COMPLETE check, but the
state math is bogus). wxASSERT compiles to a no-op in release builds,
so the production path is undefined behaviour rather than a clean
abort. Same shape as the wxASSERT-as-precondition cluster the
clang-tidy baseline (#770) and #772 already converted to wxCHECK_*
elsewhere -- just on a site #772 did not reach.
Switch this site to wxCHECK_MSG with a -1 sentinel return. Both
callers in Read() already check for nRead == (uint32_t)(-1) and
short-circuit to "encryption read error" -> abort the connection.
Debug builds still abort (wxCHECK_MSG asserts in debug); release
builds now drop the connection cleanly instead of UB'ing forward.
1 task
got3nks
added a commit
to got3nks/amule
that referenced
this pull request
Jun 4, 2026
…ndex Adds 55+ merged PRs to the 3.0.0 changelog since the last update (amule-project#747, 2026-05-27). Narrative additions cover: - Packaging: expanded the top list to include the macOS per-arch .app bundles and the Windows NSIS installer alongside the existing AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785 (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796 (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths). - Bug Fixes & Stability: post-amule-project#744 fixes including EC notification leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728), amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760), PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788, amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756), Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833, amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774). - Translations: late-cycle wave covering French/Turkish manpages (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for date+version drift (amule-project#802). - Contributors: added ngosang for UX feedback on the late-3.0 cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the user-facing manual at amule-org.github.io. - Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
got3nks
added a commit
to got3nks/amule
that referenced
this pull request
Jun 4, 2026
…ndex Adds 55+ merged PRs to the 3.0.0 changelog since the last update (amule-project#747, 2026-05-27). Narrative additions cover: - Packaging: expanded the top list to include the macOS per-arch .app bundles and the Windows NSIS installer alongside the existing AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785 (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796 (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths). - Bug Fixes & Stability: post-amule-project#744 fixes including EC notification leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728), amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760), PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788, amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756), Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833, amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774). - Translations: late-cycle wave covering French/Turkish manpages (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for date+version drift (amule-project#802). - Contributors: added ngosang for UX feedback on the late-3.0 cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the user-facing manual at amule-org.github.io. - Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
mrjimenez
pushed a commit
that referenced
this pull request
Jun 4, 2026
Adds 55+ merged PRs to the 3.0.0 changelog since the last update (#747, 2026-05-27). Narrative additions cover: - Packaging: expanded the top list to include the macOS per-arch .app bundles and the Windows NSIS installer alongside the existing AppImage / Flatpak / .dmg / .zip entries. New bullets for #785 (alc/alcc/cas/wxcas everywhere + Windows amuleweb), #794 (.dmg amuleweb path), #789 (<OS>-<arch> artifact naming), #780 / #796 (Windows DPI + comctl32 manifest), #784 (FHS share/amule paths). - Bug Fixes & Stability: post-#744 fixes including EC notification leak (#797), big-library scaling (#736, #840 superseding #728), amulegui ghost entries (#810, #819, #841, #824, #830, #760), PartFile early hash (#762), server protocol fixes (#835, #788, #721, #787), crypto stream UB (#779), UAF prevention (#756), Kad rotation (#795, #799/#805), GTK warning silencing (#833, #826/#836), and the clang-tidy worklist (#770, #772-#774). - Translations: late-cycle wave covering French/Turkish manpages (#753/#754/#776), Galician (#763), Slovenian (#771), pt-BR (#768/#775/#812), French (#811), plus man-page tooling for date+version drift (#802). - Contributors: added ngosang for UX feedback on the late-3.0 cycle (#817/#818/#821/#828/#844) and ongoing work on the user-facing manual at amule-org.github.io. - Merged PRs flat index: extended with #746-#845 + #841.
mrjimenez
pushed a commit
to mrjimenez/amule
that referenced
this pull request
Aug 4, 2026
…roject#772) * perf(ec): build a client's tags only for fields that changed The client list is rebuilt in full on every EC poll. Each field went through `AddTag(CECTag(name, value), valuemap)`, which constructs the tag first -- calling the getter, copying the string, allocating the tag -- and only then asks the value map whether it was needed, discarding it if not. So the diff suppressed the field from the wire but not the work, and that work is identical whether a peer is idle or saturating the link. CValueMap already had the other form. `CreateTag` compares the raw value against a typed cache and constructs a CECTag only when it differs. It was simply unused: all 56 value-map call sites in ECSpecialCoreTags.cpp took the build-then-discard path, none took this one. It also stops caching whole CECTag objects per field in `m_map_tag`, which held every field's data buffer for every peer. Field measurements on a live daemon put this loop at 55% of the whole GET_UPDATE handler -- roughly 34us per peer, scaling linearly with peer count while the shared-file walk stayed flat at a constant 1204 files. It is the only phase that grows with anything a user can change. `AddDiffTag` wraps the choice because `valuemap` is NULL for callers not doing an incremental update, where every tag is emitted unconditionally. Two details that would otherwise change the wire format: bool and double get their own CreateTag overloads. CECTag has explicit constructors for both, so without them a bool argument is ambiguous across the integer overloads and resolving it with a cast would silently change the tag's type for every client reading it. EC_TAG_CLIENT_UPLOAD_FILE deliberately stays on the old path. It is written from two branches, one a file ECID and the other a CECIntTag zero, and the two forms keep separate caches -- converting one would leave neither seeing the other's last value, so a transition between the branches could be suppressed and the field would freeze in the GUI. ForgetTag only clears `m_map_tag` and so would not reach the typed caches, but it has no callers anywhere in the tree. * perf(ec): pass value-map values by reference, not by copy CreateTagT, the wxString/CMD4Hash/CUInt128 CreateTag overloads and the new AddDiffTag all took their value by copy. The client getters hand back a const reference (`const wxString &GetUserName() const`), so each string field paid a wxString copy into AddDiffTag and a second into CreateTag -- where the original build-then-discard form paid one, into the tag. The previous commit therefore improved the scalar fields and pessimised the string ones. Microbenchmark of the client tag shape, 50 peers x 300 polls, 3 of 37 fields changing: build-then-discard 3.88 us/peer/poll compare-first, by copy 1.10 us/peer/poll compare-first, by ref 0.55 us/peer/poll and with 10 of 37 changing, 3.31 / 1.42 / 0.89. So half the remaining cost was copies this file introduced. Left the integer overloads by value: a reference to a uint8 buys nothing and costs an indirection. * perf(ec): one map probe per field, and close the literal-to-bool trap Two follow-ups from review, plus the test that should have come with the original change. CreateTagT walked the tree twice on the unchanged path -- count() then operator[] -- and three times when a value did change. That path is the common one and the whole reason the function exists. One lower_bound now serves as both the probe and the insertion hint. A string literal reaching AddDiffTag would have emitted the wrong tag type. CreateTag had no pointer overload, and `const char*` -> bool is a standard conversion that beats the user-defined one to wxString, so a literal bound to the bool overload through the value map while the plain CECTag path -- which has its own const char* and const wxChar* constructors -- built a string tag. The same call site would then carry a different wire type on an incremental update than on a full request. No caller passes one today, which is exactly why nothing caught it; the overloads forward to wxString so none ever can. CValueMapTest pins the contract. CValueMap needs no app, daemon or database -- the EC tag types are the whole dependency -- and its failure mode is silence: a field wrongly judged unchanged stops updating in the GUI with no crash and no log line. Twelve cases, including the two branches of AddDiffTag agreeing on the tag they produce, which is where the bug above actually lived, and the one-name-two-types hazard that keeps EC_TAG_CLIENT_UPLOAD_FILE on the old path. Each new assertion was checked by mutation rather than assumed: folding double through the integer cache fails DoubleKeepsItsFractionalPart, breaking the value-map-absent branch fails both AddDiffTag equivalence cases. Note the bool overload is required to resolve ambiguity, not to protect the wire format: CECTag(name, bool) calls InitInt() exactly as CECTag(name, uint8) does, so folding it into an integer overload would produce an identical tag. The earlier claim that it guarded the wire type was wrong. double is a real value concern -- routed through an integer it loses its fractional part -- and that is what its test asserts. The test target pulls in LoggerConsole.cpp for the no-op DoECLogLine and theLogger symbols that libec.a's Debug build references unconditionally. Release preprocesses those calls to `do {} while(0)` via ECLog.h, so the symbols are unused there and only the Debug matrix entries fail to link -- which is how this reached CI green locally and red there. RefresherTest pulls the same TU in for the same reason. Benchmark, one run at this commit, 50 peers x 300 polls: 3 of 37 fields changing 3.42 -> 0.48 us/peer/poll 10 of 37 fields changing 3.25 -> 0.78 us/peer/poll
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.
Summary
Switches the
wxASSERT(precondition); use_value()pattern inCLogger::GetDebugCategoryand acrossCUserEvents'ss_EventListaccessors towxCHECK_MSG/wxCHECK_RET.wxASSERTcompiles to a no-op in release, so the existing pattern would read past the array in release on a bad index (where a debug build would assert and abort). Surfaced byclang-analyzer-security.ArrayBoundin the baseline config from #770 — this was the most-actionable cluster in that worklist (the only category that genuinely UB's in release builds on bad input).wxCHECK_*keeps the debug-build behaviour identical (assert + abort on first mis-use) and on release substitutes a safe sentinel return (s_EventList[0]/g_debugcats[0]) or short-circuits void paths, plus emits a runtime log line so the misuse is still observable.Mechanical change
CUserEvents'sCheckIndexwas previously wrapped in#ifdef __WXDEBUG__becausewxASSERTdoesn't evaluate its argument.wxCHECK_*does, so the function is now always-defined. It's a one-liner (return idx < itemsof(s_EventList);), no measurable cost in release.Out of scope
MD4Hash.h:202appeared in the same cluster on lint output but isn't awxASSERTissue — the analyzer can't track the per-byte writes throughRawPokeUInt64insideCMD4Hash::SetHashand concludesm_hashis partially uninitialised whenEncodeSTLreads it. The code is correct; the analyzer is symbolically wrong on that one. Left alone.Test plan
ArrayBoundwarnings on the two changed files (was 12 pre-fix)wxCHECK_*short-circuits identically to the previous code path's happy case)