bip158: regression test of malformed filter counts
What changed, and why it matters
This commit only adds new regression tests for malformed BIP158 compact-size filter counts. It does not change any production code, so it cannot introduce or fix a runtime vulnerability by itself. The tests verify that truncated and non-minimal CompactSize prefixes are rejected with parse errors in the GCS filter reader's match_any and match_all methods.
No action required; treat as routine test coverage. If auditing, verify that the underlying production code already rejects these malformed inputs as the test assumes, but the commit itself is not a security patch.
Security signals we found
Regression test for malformed input handling
Coverage of truncated CompactSize prefix
Coverage of non-minimal CompactSize prefix
No production code changes
Evidence from the diff
The diff adds a single unit test, malformed_filter_count_errors, in bitcoin/src/bip158.rs. It constructs a GcsFilterReader and feeds malformed CompactSize prefixes: a single 0xfd byte (truncated, should yield MissingData) and 0xfd 0xfc 0x00 (non-minimal, should yield NonMinimalCompactSize). It asserts both match_any and match_all return Error::InvalidCompactSize wrapping the corresponding consensus parse error. No library logic is modified.
Changed components
bitcoin/src/bip158.rs (test module only)Inspect captured patch +31 / −0
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 6c855945..d71b81c0 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -697,6 +697,37 @@ mod test {
}
}
+ #[test]
+ fn malformed_filter_count_errors() {
+ use crate::consensus::Error::Parse as ConsensusParse;
+ use crate::consensus::ParseError::{MissingData, NonMinimalCompactSize};
+
+ let query = [hex!("000000")];
+ let reader = GcsFilterReader::new(0, 0, M, P);
+
+ let mut bytes = &[0xfd][..];
+ let result = reader.match_any(&mut bytes, query.iter().map(|v| v.as_slice()));
+ assert!(matches!(result, Err(Error::InvalidCompactSize(ConsensusParse(MissingData)))));
+
+ let mut bytes = &[0xfd][..];
+ let result = reader.match_all(&mut bytes, query.iter().map(|v| v.as_slice()));
+ assert!(matches!(result, Err(Error::InvalidCompactSize(ConsensusParse(MissingData)))));
+
+ let mut bytes = &[0xfd, 0xfc, 0x00][..];
+ let result = reader.match_any(&mut bytes, query.iter().map(|v| v.as_slice()));
+ assert!(matches!(
+ result,
+ Err(Error::InvalidCompactSize(ConsensusParse(NonMinimalCompactSize)))
+ ));
+
+ let mut bytes = &[0xfd, 0xfc, 0x00][..];
+ let result = reader.match_all(&mut bytes, query.iter().map(|v| v.as_slice()));
+ assert!(matches!(
+ result,
+ Err(Error::InvalidCompactSize(ConsensusParse(NonMinimalCompactSize)))
+ ));
+ }
+
#[test]
fn bit_stream() {
let mut out = Vec::new();
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.