chore(tools): validate-before-allocate ceiling in frame-crosscheck lz4BlockDecompress (LAB-1202) - #55
chore(tools): validate-before-allocate ceiling in frame-crosscheck lz4BlockDecompress (LAB-1202)#5527Bslash6 wants to merge 1 commit into
Conversation
…4BlockDecompress (LAB-1202) Deferred follow-up from the LAB-903 crypto/protocol panel (protocol#47). lz4BlockDecompress allocated the output buffer straight from original_size — a value read off the wire — before any bound applied. Not exploitable in this repo (the driver matches original_size against the committed vector first), but this reader is the porting template for SDK envelope readers that WILL face attacker-controlled envelopes, and the ratified rule is validate-before-allocate. Ceiling choice (the one decision the ticket delegates): ratio-derived, not an absolute cap. The LZ4 block format cannot expand more than 255x — a match sequence costs at least 3 input bytes (token + 2-byte offset) and each match-length extension byte adds at most 255 output bytes, so per-sequence output L+19+255b <= 255*(3+a+L+b) input. expectedSize > 255*src.length is therefore impossible for well-formed input, self-scales with the fixture, and needs no revisiting when larger vectors land. Non-integer and negative sizes are refused by the same guard. tools/test-frame-crosscheck-guard.mjs pins the guard load-bearing per the LAB-903 mutation-evidence convention: baseline exit-0 on committed vectors, mutation no-op checks, and a synthetic vector declaring 64 MiB from 3 compressed bytes. Exit codes cannot discriminate guarded from unguarded (the ticket-acknowledged post-allocation op !== expectedSize check fires either way), so the suite discriminates by failure ORDER: guarded fails with the pre-allocation ceiling refusal; guard-stripped fails with the post-allocation size mismatch, proving the oversized allocation succeeded first — the exact pre-fix vulnerability. The suite runs before the tool in verify.yml, same cannot-silently-degrade rule as the version-floors suite. test-vectors/*.json byte-untouched; no normative spec text moved (panel gate not re-triggered, per the panel's own deferral scope).
|
Warning Review limit reachedNext included review available in 33 minutes. View limit detailsLimit details: You’ve used all 5 included reviews currently available. Your 57 included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Comment |
Kody Review CompleteGreat news! 🎉 Keep up the excellent work! 🚀 Kody Guide: Usage and ConfigurationInteracting with Kody
Current Kody ConfigurationReview OptionsThe following review options are enabled or disabled:
|
|
@coderabbitai review |
|
Closes LAB-1202 — deferred follow-up 2 of 2 from the LAB-903 crypto/protocol expert panel (#47).
What
lz4BlockDecompressintools/frame-crosscheck.mjsallocated its output buffer straight fromoriginal_size— a value read off the wire — before any bound applied. Not exploitable here (the driver cross-checksoriginal_sizeagainst the committed vector first), but this reader is the porting template for SDK envelope readers that will face attacker-controlled envelopes, and the ratified rule is validate-before-allocate. The guard now refuses an implausibleexpectedSizebeforenew Uint8Array(...)is reached, and refuses non-integer/negative sizes with it.The ceiling (the decision the ticket delegates)
Ratio-derived, option 1 from the ticket:
expectedSize <= 255 * src.length. Derived from the LZ4 block format, not guessed — a match sequence costs at least 3 input bytes (token + 2-byte offset) and each match-length extension byte adds at most 255 output bytes, so per-sequence outputL+19+255b <= 255*(3+a+L+b)input; no well-formed block expands more than 255× (the lz4 project's documented maximum compression ratio). Self-scaling: no revisiting when larger vectors land. Derivation lives in the source comment at the guard.Mutation evidence (LAB-903 convention)
New zero-dep
tools/test-frame-crosscheck-guard.mjs, wired intoverify.ymlbefore the tool run (same cannot-silently-degrade rule as the version-floors suite):original_sizefrom 3 compressed bytes exits non-zero with the pre-allocation ceiling refusalop !== expectedSizecheck the ticket acknowledges fires either way), so the suite discriminates by failure order — the stripped tool's failure is the post-allocation size mismatch, proving the 64 MiB allocation succeeded before any validation. That is the pre-fix vulnerability, pinned.All 6 cases green locally;
frame-crosscheck.mjsandpython-frame-reference.py verifygreen.Scope guarantees
test-vectors/*.jsonbyte-untouched (python-frame.jsonsha256-pin unmoved)wire-format-reference.pyuntouched (no LZ4 there, per ticket non-goals)