Optimize Some Statement Evaluation and Fix Correctness Mismatches#3083
Optimize Some Statement Evaluation and Fix Correctness Mismatches#3083mike-hunhoff wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Please add bug fixes, new features, breaking changes and anything else you think is worthwhile mentioning to the master (unreleased) section of CHANGELOG.md. If no CHANGELOG update is needed add the following to the PR description: [x] No CHANGELOG update needed
CHANGELOG updated or no update needed, thanks! 😄
There was a problem hiding this comment.
Code Review
This pull request introduces an optimization in the evaluate method of the Some class within the capa engine. It now checks if the satisfaction threshold is already met (e.g., when count is 0) before iterating through children, allowing for immediate short-circuiting. Corresponding unit tests were added to verify the correctness of Some(0, ...) and ensure that children are not evaluated unnecessarily. I have no feedback to provide.
| assert bool(Some(0, [Number(1)]).evaluate({Number(0): {ADDR1}})) is True | ||
| assert bool(Some(1, [Number(1)]).evaluate({Number(0): {ADDR1}})) is False | ||
|
|
||
| # Test Some(0, []) correctness (should evaluate to True in both short-circuit modes) |
There was a problem hiding this comment.
imho this isn't a reasonable structure for a rule, so this test is noise.
| assert Some(0, []).evaluate({Number(0): {ADDR1}}, short_circuit=False).success is True | ||
|
|
||
| # Test Some(0, [Child]) optimization (child must not be evaluated when short-circuit is True) | ||
| class TrackingFeature(Number): |
There was a problem hiding this comment.
i find this to be an overly clever way to test the behavior.
i'm not sure that the behavior is worth the code maintenance investment for this test. i don't think the the behavior will likely regress. therefore i'd propose to remove these tests, too, but i'm open to discussion.
thoughts?
| results = [] | ||
| results: list[Result] = [] | ||
| satisfied_children_count = 0 | ||
| if satisfied_children_count >= self.count: |
There was a problem hiding this comment.
it doesn't make sense for count to be less than 0, so this condition is better expressed as if self.count == 0. if we don't have validation that count is at least 0, then we should add that in the rule loading code, not try to handle the case here.
This PR resolves a performance leak and a correctness mismatch inside
capa's logical AST engine'sSomestatement class.1. Overview of the Problems
A. Performance Bug (Unnecessary Child Evaluation in Optional Blocks)
In
caparules,optional:blocks (which are extremely common, used in over 199 rules incapa-rules) are compiled internally as aSomestatement with a threshold count of0:Some(count=0, children=[Child1, Child2, ...])During the fast matching pass (
short_circuit=True),capashould stop evaluating children as soon as a statement's threshold is met. Sincecount=0is met by default before evaluating any child (0 >= 0), the statement should instantly succeed.However, because the threshold check was placed inside the loop after the first child's evaluation,
capawas forced to evaluate at least the first child feature of every single optional block in the ruleset. If that child was a slow regular expression or substring search, it wasted significant CPU cycles on features that had zero impact on the rule matching outcome.B. Correctness Bug (
Some(0, [])Mismatch)An empty optional statement
Some(0, [])evaluated toFalsein short-circuit mode (because the loop didn't run and fell through), but evaluated toTruein non-short-circuit mode (because0 >= 0isTrue). This created a silent correctness mismatch between fast and slow matching passes.2. The Fix
We perform the threshold validation before entering the evaluation loop when short-circuiting is enabled:
This guarantees:
Some(0, [Children])returnsTrueimmediately in microseconds without evaluating any child features during the fast pass.Some(0, [])now correctly and consistently evaluates toTruein both matching passes.3. Verification & Performance Results
A. Unit Tests
Added two new targeted unit test cases in
tests/test_engine.py:Some(0, [])evaluates toTruein both short-circuit modes.Some(0, [Child])does not evaluate the child feature undershort_circuit=True.All 19 engine unit tests passed cleanly:
B. Real-World Performance Impact (
mimikatz.exe_)We profile-audited
capa's execution on the largemimikatz.exe_binary before and after this change to measure the exact real-world performance impact:Someevaluations: 25,112 callscumtime)1.003seconds0.956secondsImpact:
mimikatz.exe_, offering a 5% direct speedup forSomestatement processing.Checklist