Fix divide-before-multiply false positive when the divided variable is reassigned#3040
Open
MarkLee131 wants to merge 2 commits into
Open
Fix divide-before-multiply false positive when the divided variable is reassigned#3040MarkLee131 wants to merge 2 commits into
MarkLee131 wants to merge 2 commits into
Conversation
…assigned The `_explore` worklist in `slither/detectors/statements/divide_before_multiply.py` tracks the set of currently-division-tainted IR variables in a `divisions` dict. The Assignment handler only propagates taint from rvalue to lvalue when the rvalue is already in `divisions`. There is no else branch, so when the rvalue is a clean (non-divided) value, the lvalue's previous taint is never cleared. A variable that was previously assigned a division result and is then fully overwritten with an unrelated value keeps its stale taint, and the next multiplication using it fires: uint256 a = x / y; // divisions[a] = [node15] a = z; // rvalue clean; divisions[a] never cleared return a * 100; // FP: `a` here is `z`, not x/y Clear the stale taint in an `else` branch on Assignment so the dataflow kill matches the join semantics of full overwrite. Adds tests/e2e/detectors/test_data/divide-before-multiply/0.8.20/divide_before_multiply_reassignment.sol covering two FP shapes (direct reassign, reassign through a temporary) plus a tripwire `genuineDivThenMul` that must still fire. After this change the snapshot contains only the tripwire; the two FPs are gone. All four existing DivideBeforeMultiply fixtures still pass with identical output. The change does not touch the multiplication-check itself, so the intentional-precision-loss firings (`a / b * c` in fixed-point math) are unchanged.
…signment Generated via: python tests/e2e/detectors/test_detectors.py --compile pytest -k DivideBeforeMultiply-0.8.20-divide_before_multiply_reassignment --insta update All 5 DivideBeforeMultiply test cases pass with the fix applied.
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.
Fixes #3039:
divide-before-multiplywas flagging multiplications whose operand had been previously assigned a division result but was then fully overwritten with an unrelated value.Root Cause
_exploreinslither/detectors/statements/divide_before_multiply.pytracks the set of currently-division-tainted IR variables in adivisions: defaultdict[LVALUE, list[Node]]and walks the CFG with a worklist DFS. TheAssignmenthandler only propagates taint:When
ir.rvalueis a clean (non-division-tainted) value,divisions[ir.lvalue]is never updated. So a variable that was previously assigned a division result and then fully overwritten by a clean rvalue keeps its stale division taint, and the next multiplication using it fires.This is a missing kill in the dataflow update.
Fix
Add an
elsebranch that clears the stale taint when the rvalue is clean:Tests
Added
tests/e2e/detectors/test_data/divide-before-multiply/0.8.20/divide_before_multiply_reassignment.sol. Three cases:safeReassign— divided then reassigned to a clean value. Was an FP; silent after the fix.safeChainedReassign— same shape via a temporary. Was an FP; silent after the fix.genuineDivThenMul— the real divide-then-multiply pattern (tripwire). Still fires after the fix.All four existing DivideBeforeMultiply fixtures (
divide_before_multiply.sol× 0.4.25 / 0.5.16 / 0.6.11 / 0.7.6) pass with identical output, so the fix does not narrow the detector for any existing test case.Scope
This fix narrowly addresses the missing kill on reassignment. It does not touch the over-approximation in the multiplication-check itself (the
a / b * cpattern in fixed-point math, which is intentional precision loss at the developer's discretion) — those firings remain.