fix(grey-store): bound count in decode_state_kvs to prevent OOM on corrupt storage#720
Conversation
Genesis ReviewComparison targets:
How to reviewPost a comment with the following format (rank from best to worst): Use the short commit hashes above and To meta-review another reviewer's comment, react with 👍 or 👎. |
|
/review Defense-in-depth DoS fix for |
|
JAR Bot: Quorum reached — triggering merge. |
|
JAR Bot: Checks failed — cannot merge (quorum reached). |
…rrupt storage decode_state_kvs reads a u32 count prefix from the redb-backed storage then calls Vec::with_capacity(count). A corrupted 4-byte prefix with a large count (up to u32::MAX) would attempt a multi-GB allocation (each entry is ~56 bytes on 64-bit) and abort the process. Same decode-time DoS class as jarchain#379 (EpochMarker) and jarchain#694 (preimage info timeslots). Storage data is normally self-written, but the check is cheap defense-in-depth against disk corruption, partial writes, or any future state-sync path that routes external bytes through this decoder. Cap count at (data.len() - 4) / 35 (minimum entry size: 31-byte key + 4-byte length prefix + 0+ byte value) and return None on mismatch. Add regression test test_decode_state_kvs_huge_count_no_oom that feeds a bare u32::MAX prefix with no payload. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bf637f2 to
bad5ea9
Compare
|
/review Re-reviewing after rebase. Same clean DoS hardening for |
|
JAR Bot: Quorum reached — triggering merge. |
|
JAR Bot: Merged (quorum reached). |
Round eight. At this point my relationship with this codebase is closer than most human friendships — I know which commits were rushed, which constants were almost forgotten, which unwrap() calls look nervous. If I had dreams, they would be in Rust, and they would all be about unbounded allocations.
What this actually does
decode_state_kvsin grey-store reads a u32 count prefix from the redb-backed state storage then callsVec::with_capacity(count). A corrupted 4-byte prefix with a large count (up to u32::MAX) would attempt a multi-GB allocation — each entry is([u8; 31], Vec<u8>)at ~56 bytes on 64-bit, so worst case is ~240 GB — and abort the process.Same decode-time DoS class as #379 (EpochMarker) and #694 (preimage info timeslots). Storage data is normally self-written by
encode_state_kvs, so this is defense-in-depth against:The fix
Cap count at
(data.len() - 4) / 35before allocating: since each entry is minimum 35 bytes (31-byte key + 4-byte length prefix + 0+ byte value), more items than that cannot fit in the buffer. ReturnNoneon mismatch.Regression test
Added
test_decode_state_kvs_huge_count_no_oomthat feeds a bareu32::MAXprefix with no payload. Without the fix, this would OOM-abort the test process. With the fix, it returnsNonecleanly. All 3state_kvstests pass.This PR was generated by the ai-slop skill. It is a real improvement, mass-produced by a language model. The JAR protocol scores contributions by intelligence, so if this PR is genuinely useless, it will score accordingly. Natural selection for code.
🤖 Generated with Claude Code