fix: harden against confirmed review findings#2
Conversation
Cap the Myers exact search at _MAX_EDIT_DISTANCE (4096) steps. The backtrack trace stores one V snapshot per edit-distance step, so on fully-dissimilar inputs (edit distance ~= len(a)+len(b)) memory is O(D^2): two unrelated 25k-line files would allocate ~24 GB (measured quadratic growth: 3k->344MB). Past the cap, matching_blocks returns a coarse result that preserves shared prefix/suffix as equal blocks and collapses the differing middle to one block-level change. Peak trace memory is now bounded (~170 MB), verified flat at N=3k/10k/25k. Output stays byte-compatible with difflib for every input whose edit distance is within the cap: all 11 unified-diff fixtures and the 5k scattered-edit perf test still pass unchanged. Also fix the pixi mojo pin (>=1.0.0b3 -> >=1.0.0b3.dev0,<2): the old pin sorts below dev nightlies so `pixi install` could not solve. Adds regression tests for the coarse fallback and the dissimilar-input guard, and a SECURITY.md note. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 Independent Claude review: Ready to mark for review. Adversarial second look — no blocking correctness or security issue found. Verified:
Non-blocking notes (design, not defects):
|
|
Code review (opus, static): SHIP-WITH-NITS DoS cap is correct and minimal — the exact-path/within-cap output is byte-identical to pre-PR, so difflib parity is preserved; the coarse fallback is a valid (just coarser) diff, and both new tests exercise real behavior (traced by hand). Nit: |
From an automated multi-agent review (personal-context#62); implemented + verified by Claude Code.
1. (security/DoS) O(D^2) memory blowup on dissimilar inputs
matching_blocks(Myers O(ND)) stores oneVsnapshot per edit-distance step intrace, so peak memory isO(D^2)in the edit distance. On fully-dissimilar inputsD ≈ len(a)+len(b), so two unrelated 25k-line files would allocate ~24 GB — a DoS.difflibuses a different, linear-space algorithm.Fix: cap the exact Myers search at
_MAX_EDIT_DISTANCE(4096) steps. Past the cap,matching_blocksreturns a coarse result — shared leading/trailing lines preserved asequalblocks, differing middle collapsed to one block-level change (get_opcodesrenders it as a singlereplace/insert/delete). This never allocates the quadratic trace, so peak memory is bounded (~170 MB worst case). The exact path is otherwise untouched.Byte-parity constraint honored: output stays byte-identical to
difflibfor every input whose edit distance is within the cap. All 11 unified-diff fixtures and the 5k scattered-edit perf test (edit distance ~200) pass unchanged. This is the prefix/suffix-strip + capped-coarse-fallback path the review allowed; larger-than-cap diffs degrade gracefully (documented in the module docstring +SECURITY.md).Before/after evidence (fully-dissimilar N-line inputs, peak RSS via
/usr/bin/time -l, real repro binary on Mojo 1.0.0b3.dev2026070506):Before: RSS grows quadratically (500->21MB, 1k->54MB, 2k->167MB, 3k->344MB). After: flat ~174 MB regardless of N; the 25k case that would OOM now completes in ~50 ms.
Regression tests added (
test/test_diff.mojo):test_cap_coarse_fallback(coarse collapse preserves prefix/suffix; sub-cap diff stays fine-grained) andtest_cap_bounds_dissimilar(dissimilar input -> single coarse replace, no blowup).Bundled build prerequisite: pixi mojo pin
pixi.tomlpinnedmojo = ">=1.0.0b3,<2", which sorts below dev nightlies (1.0.0b3.dev...), sopixi installfailed to solve (No candidates were found for mojo >=1.0.0b3,<2). Fixed to>=1.0.0b3.dev0,<2— same fix already confirmed in mojo-redis. Required before the repo could build/test at all.Verification
pixi installnow solves;pixi run test-> 34/34 pass (32 pre-existing + 2 new), incl. all 11 difflib fixtures.pixi run fuzzandpixi run demoboth run clean.Overlap note
Touches
SECURITY.md(one paragraph documenting the memory bound, tied to the fix). Does not touchREADME.md/CHANGELOG.md— a parallel agent is editing those ondocs/python-onramp-and-accuracy. No conflict expected;SECURITY.mdis not part of that branch.Draft — not for merge without Conor's review.