-
-
Notifications
You must be signed in to change notification settings - Fork 878
feat[venom]: variable range analysis #4790
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
base: master
Are you sure you want to change the base?
Conversation
wip wip wip more non zero case more tests refactor tests tests wip improve precition rename ValueInfo to VariableRangeAnalysis and update references lint rename worklist refactor optimization Revert "optimization" This reverts commit 0c1cd77f6f3f1008f655926ddbd4c963c133ae07. refactor ValueRange refactor bytes_range add comment fancy more asserts split out assert elimination move test lint widening rename test file comments convert `is_` methods to properties more comments udpate sigs dispatch with dict lint split out evaluators refactor lint private cleanup redundant check cleanups post fix multi-output update reduntant back to TOP BOTTOM
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #4790 +/- ##
==========================================
- Coverage 93.26% 92.54% -0.72%
==========================================
Files 148 153 +5
Lines 20531 21259 +728
Branches 3566 3755 +189
==========================================
+ Hits 19148 19674 +526
- Misses 926 1053 +127
- Partials 457 532 +75 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Known bugs in variable range analysis: Miscompiles (assert elimination): - lt false branch excludes negatives from signed ranges - gt true branch excludes negatives from signed ranges - iszero false branch intersects with [1, MAX], excluding negatives - signextend BOTTOM propagates through phi, losing branch values Additional bugs: - lt with negative constant gives wrong result (signed vs unsigned) - signextend produces BOTTOM for out-of-range input - and with signed range gives narrow upper bound - unsigned comparison narrowing doesn't handle signed ranges Root cause: analysis uses single signed range but doesn't account for signed/unsigned duality in EVM (negative values are large unsigned). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
|
||
| # If the range is growing, widen to top | ||
| if new_range.lo < old_range.lo or new_range.hi > old_range.hi: | ||
| return ValueRange.top() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just putting a note here that there should be a possibility to be more precise in the widening. You would need to have the set of some values that would be considered for possible ends for a range after widening (and for that set to be finite) to be able to get fixed point. But that should be outside of the scope of this PR
| if self.is_top: | ||
| lo = SIGNED_MIN if lo is None else max(SIGNED_MIN, lo) | ||
| hi = UNSIGNED_MAX if hi is None else min(UNSIGNED_MAX, hi) | ||
| return ValueRange((lo, hi)) if lo <= hi else ValueRange.empty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this ternary necessary shouldn't it be handled by __post_init__
| assert self.bounds is not None | ||
| new_lo = self.bounds[0] if lo is None else max(self.bounds[0], lo) | ||
| new_hi = self.bounds[1] if hi is None else min(self.bounds[1], hi) | ||
| return ValueRange((new_lo, new_hi)) if new_lo <= new_hi else ValueRange.empty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
| index = _get_uint_literal(index_op) | ||
| if index is not None and index >= 32: | ||
| return ValueRange.constant(0) | ||
| return ValueRange.bytes_range() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is kinda imprecise for case like
main:
%x = calldataload 0
%cond = lt %x, 10
jnz %cond, @then, @else
then:
%b = byte 0, %x ; here the result could be only in range [0, 10] but this would set it to [0, 255]
sink %b
else:
sink %x
What I did
Add flow-sensitive range analysis and assert pruning
How I did it
How to verify it
Commit message
Description for the changelog
Cute Animal Picture