Repository navigation
perf(byte-storage): decode the envelope in one bounded pass on retrieve (LAB-12100) #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
23447e5
perf(byte-storage): decode the envelope store writes in one pass on r…
27Bslash6 74eb9fa
perf(byte-storage): read every format width in one pass; share the di…
27Bslash6 4f636a7
fix: address coderabbit review — one-pass scope wording, owned decode…
27Bslash6 2ab6a0f
Merge remote-tracking branch 'origin/main' into lab-12100-one-pass-en…
27Bslash6 478bb80
test(byte-storage): assert store's own output takes the one-pass deco…
27Bslash6 a963911
docs(byte-storage): state the one-pass decode's encodings and the pre…
27Bslash6 75a0f84
perf(byte-storage): keep extract_payload out of line and the full dec…
27Bslash6 8345087
fix: address coderabbit review — pre-scan error scope, size cap in th…
27Bslash6 f3c7e12
Merge origin/main into the one-pass envelope decode
27Bslash6 476c637
docs(security): state that decode_envelope pre-scans only before rmp_…
27Bslash6 6c9d897
Merge remote-tracking branch 'origin/main' into lab-12100-one-pass-en…
27Bslash6 f40f8ee
docs(byte-storage): scope the fallback and size-cap wording; pin the …
27Bslash6 98035c3
docs(bench): restate the retrieve_data LTO cost for the one-pass decode
27Bslash6 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,34 +1,39 @@ | ||
| #![no_main] | ||
|
|
||
| use cachekit_core::byte_storage::{assert_one_pass_matches_full_decode, StorageEnvelope}; | ||
| use cachekit_core::ByteStorage; | ||
| use libfuzzer_sys::fuzz_target; | ||
| use cachekit_core::byte_storage::StorageEnvelope; | ||
|
|
||
| // Attack: malformed and corrupted envelopes through the envelope decodes. | ||
| // Validates: fail-closed rejection without panics, and that the one-pass decode agrees with the full decode. | ||
|
|
||
| fuzz_target!(|data: &[u8]| { | ||
| // Attack: Attempt to deserialize arbitrary bytes as StorageEnvelope | ||
| // Validates: Safe rejection of corrupted MessagePack serialization without panics | ||
| // The input as envelope bytes, through `retrieve`'s decodes and through | ||
| // the public owned `StorageEnvelope`, which has no pre-scan of its own. | ||
| assert_one_pass_matches_full_decode(data); | ||
| if let Ok(envelope) = rmp_serde::from_slice::<StorageEnvelope>(data) { | ||
| let _ = envelope.extract(); | ||
| } | ||
|
|
||
| // Try to deserialize fuzz input as MessagePack StorageEnvelope | ||
| match rmp_serde::from_slice::<StorageEnvelope>(data) { | ||
| Ok(envelope) => { | ||
| // Successfully deserialized - now test extract() validation | ||
| // extract() should either succeed (valid envelope) or reject safely (invalid) | ||
| match envelope.extract() { | ||
| Ok(_decompressed) => { | ||
| // Valid envelope and decompression succeeded - this is acceptable | ||
| // (rare but possible if fuzzer generates valid structure) | ||
| } | ||
| Err(_) => { | ||
| // Envelope validation or decompression failed - expected for most malformed input | ||
| // This is the fail-closed behavior we want | ||
| } | ||
| } | ||
| } | ||
| Err(_) => { | ||
| // Deserialization failed - expected for invalid MessagePack | ||
| // This is also safe fail-closed behavior | ||
| // The input as an envelope `store` writes, then corrupted, which reaches | ||
| // the one-pass decode's boundaries far more often than raw bytes do. The | ||
| // first byte picks up to three 3-byte edits (a position and a byte) and | ||
| // what they do; the rest is the stored value. | ||
| let Some((&plan, rest)) = data.split_first() else { | ||
| return; | ||
| }; | ||
| let Some((edits, value)) = rest.split_at_checked(usize::from(plan % 4) * 3) else { | ||
| return; | ||
| }; | ||
| let mut envelope = ByteStorage::new(None).store(value, None).unwrap(); | ||
| for edit in edits.chunks_exact(3) { | ||
| // Up to one past the end, so an edit can also append. | ||
| let pos = usize::from(u16::from_be_bytes([edit[0], edit[1]])) % (envelope.len() + 1); | ||
| match plan >> 2 & 3 { | ||
| 0 if pos < envelope.len() => envelope[pos] = edit[2], | ||
| 1 => envelope.truncate(pos), | ||
| _ => envelope.insert(pos, edit[2]), | ||
| } | ||
| } | ||
|
|
||
| // Success: No panics occurred, all paths handled safely | ||
| // Invariant: Malformed MessagePack must be rejected without crashes | ||
| assert_one_pass_matches_full_decode(&envelope); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.