java-topology/defects/root-cern-0001/SCAN-NOTES.md
russell@unturf.com c13562b619 root-cern+hexchat: 5-MOAD scan; 2 defects + hexchat CLEAN
root-cern-0001: TTreeCache::FillBuffer potentialVetoes std::vector
  O(N²) per basket/branch — replace with std::unordered_set, 9.8x speedup
  (MOAD-0001 CWE-407, tree/tree/src/TTreeCache.cxx)

root-cern-0002: TWebFile::GetFromWeb10 logs full HTTP request including
  Authorization: Basic base64(user:password) at gDebug > 0 (HIGH)
  Also affects TS3WebFile — exposes AWS access key + signature
  (MOAD-0004 CWE-312, net/net/src/TWebFile.cxx)

hexchat: all 5 MOADs CLEAN — binary tree user lookup, single-threaded
  event loop, raw log is ephemeral in-memory widget only
2026-03-31 21:49:50 -04:00

2.5 KiB
Raw Permalink Blame History

root-cern-0001 — TTreeCache::FillBuffer potentialVetoes O(N²) MOAD-0001

Target

ROOT (CERN data analysis framework) — https://github.com/root-project/root

File

tree/tree/src/TTreeCache.cxx

Pattern

CWE-407: Algorithmic complexity — quadratic basket scan in TTreeCache prefetch planning.

Location

TTreeCache::FillBuffer → inner lambda CollectBaskets, lines ~13811511.

Structure:

for (Int_t i = 0; i < fNbranches; ++i) {        // outer: branches
    potentialVetoes.clear();
    b->fCacheInfo.GetUnused(potentialVetoes);     // fills vector with unused basket indices
    ...
    for (Int_t j = ...; j < nb; ++j) {           // inner: baskets per branch
        if (std::find(begin(potentialVetoes), end(potentialVetoes), j) ...)

potentialVetoes is std::vector<Int_t>. std::find on it is O(V) where V = number of unused baskets. With B branches each having N baskets, total cost is O(B × N²) in our worst case where all baskets are unused.

Impact

TTreeCache::FillBuffer is called at the start of each cluster read, which happens every few thousand entries during a TTree scan. For physics analysis with many branches and many baskets (common in CMS/ATLAS use), this becomes a significant overhead.

Example: 100 branches × 1000 baskets each → std::find scans up to 1000 elements per basket × 1000 baskets = 1,000,000 comparisons per branch per cluster reload. With 100 branches: 100,000,000 comparisons per cluster. Hash set makes this 100,000.

Severity

MEDIUM — basket count V is bounded per branch but can reach thousands in large physics files. Affects every TTree cache-prefetch cycle.

Fix

Replace std::vector<Int_t> potentialVetoes with std::unordered_set<Int_t>.

  • Membership check: O(1) average vs O(V)
  • Insert: O(1) average (from GetUnused() output)
  • Clear: O(size) same
  • The debug iteration loop for(auto v : potentialVetoes) still works

Speedup

O(B × N²) → O(B × N). At N=1000 baskets per branch: 1000x fewer comparisons in the inner scan.

MOADs checked

  • MOAD-0001 (CWE-407): CONFIRMED — see above
  • MOAD-0002 (Intertangle): gROOT god-object is intentional ROOT architecture, not a defect we can fix
  • MOAD-0003 (Leaked Context): TDirectory uses thread_local for gDirectory correctly — CLEAN
  • MOAD-0004 (CWE-312): TWebFile::GetFromWeb10 logs full HTTP request including Authorization: Basic at gDebug > 0 — see root-cern-0002
  • MOAD-0005 (Thundering Herd): TClass::BuildRealData has gInterpreterMutex + double-check inside — acceptable pattern

Date

2026-03-31