Harden rule/binary parsers against malformed input - #21
Open
mnaza wants to merge 2 commits into
Open
Conversation
Rule YAML and binaries are untrusted input; these sites panicked or aborted whole-file analysis on malformed data: - rule parser: validate number//offset/ bitness suffix (was &suffix[1..] — slice panic on 'number/', silent truncation over u32); reject bare '/' or '/i' in RegexFeature::new (slice panic); topologically_order_rules returns MatchRuleNotFound instead of panicking on a missing dependency. - count(...): integer arm validated with u32::try_from (was *i as u32, wrapping -1 to u32::MAX); unbalanced 'count(mnemonic(mov' errors instead of silently parsing 'mo'; inline descriptions split on the first ' = ' only. - analysis: per-instruction feature extraction is best-effort — one malformed instruction is logged and skipped instead of aborting the whole file through the rayon loop. - extractors: read_bytes uses checked_sub for offset < base_addr; detect_ascii_len accepts a string ending exactly at EOF (no NUL) instead of erroring out the instruction; is_security_cookie and the self-XOR check guard single-operand formatting; the .NET extractor handles methods without a body and tokens with rid 0. 8 regression tests added (21 total, all passing).
mnaza
force-pushed
the
fix/malformed-input-robustness
branch
from
July 31, 2026 08:57
7ff1434 to
fdc5b74
Compare
Owner
|
conflicts :) |
…ut-robustness # Conflicts: # CHANGELOG.md
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes #20.
Rule YAML and binaries are untrusted input, but several sites panicked or aborted the whole analysis on malformed data. All fixes are small and local; 8 regression tests added (21 pass, was 13).
Rule YAML parser
number//offset/bitness suffix validated (src/rules/mod.rs): wasparse_int(&suffix[1..]) as u32— slice-out-of-bounds panic on an empty suffix, silent truncation overu32. Now a strict parse accepting the documentedx32/x64forms (capa-rulesdoc/format.md) plus bare numbers, erroring otherwise.string: ///irejected up front (src/rules/features.rs): passedStringFactory's starts/ends-with-/check, then&value[1..len-1]panicked inRegexFeature::new."//"(empty body) stays legal, matching Python.topologically_order_rulesreturnsMatchRuleNotFoundinstead of panicking on the map index when a dependency is absent from the input — the function is public and can be called on rule subsets.count(...)integer arm validated withu32::try_from:-1silently wrapped tou32::MAX(the string forms were hardened in 0.4.2 viaparse_count_u32; the integer arm was missed).count(mnemonic(mov)errors instead of silently dropping the last byte of the argument (mov→mo)." = "only (split_once), so a description containing the separator keeps its tail.Binary analysis
src/lib.rs): per-instruction feature-extraction errors are logged via the logger and skipped (best-effort) instead of propagating?through the rayon loop.read_bytesuseschecked_subforoffset < base_addr(matches the checked versiondetect_ascii_lenalready had).detect_ascii_lenaccepts a string ending exactly at end-of-buffer (no trailing NUL — common in truncated/packed binaries) instead of reporting a bogus buffer-overflow that aborted the instruction's features;read_bytesalready clamps.is_security_cookieand the self-XOR check guard single-operand formatting (operands.len() < 2).src/extractor/dnfile.rs):get_blocksno longer indexesinstructions[0]on methods without a body;resolve_dotnet_tokenuseschecked_subfor rid 0 (ECMA-335 "no reference").Tests
bitness_suffix_is_validated_instead_of_panicking,malformed_counts_error_instead_of_wrapping_or_mangling,inline_description_keeps_tail_after_first_separator,topological_order_missing_dependency_errors_instead_of_panicking(rules)regex_without_body_errors_instead_of_panicking(features)read_bytes_below_image_base_errors_instead_of_underflowing,read_string_at_exact_end_of_buffer(smda extractor)resolve_token_with_zero_rid_errors_instead_of_underflowing(dnfile extractor)Clippy clean,
cargo fmtapplied, CHANGELOG entry under[Unreleased]. Based on master; independent of #17 and #19 except for the trivial shared[Unreleased]CHANGELOG heading.