Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions slither/detectors/statements/divide_before_multiply.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ def _explore(
divisions[ir.lvalue] = divisions[ir.rvalue] + [node] # type: ignore
else:
divisions[ir.lvalue] = divisions[ir.rvalue] # type: ignore
else:
# rvalue is not division-tainted, so the lvalue is being
# fully overwritten. Clear any stale taint left over from
# a previous division assignment to the same lvalue.
divisions.pop(ir.lvalue, None) # type: ignore

if is_division(ir):
divisions[ir.lvalue] = [node] # type: ignore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DivideBeforeMultiplyTest.genuineDivThenMul(uint256,uint256,uint256) (tests/e2e/detectors/test_data/divide-before-multiply/0.8.20/divide_before_multiply_reassignment.sol#32-35) performs a multiplication on the result of a division:
- a = x / y (tests/e2e/detectors/test_data/divide-before-multiply/0.8.20/divide_before_multiply_reassignment.sol#33)
- a * z (tests/e2e/detectors/test_data/divide-before-multiply/0.8.20/divide_before_multiply_reassignment.sol#34)

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/// Regression coverage for the `divide-before-multiply` reassignment-kill fix.
/// `_explore`'s Assignment handler in `divide_before_multiply.py` propagates
/// taint from `rvalue` to `lvalue` when `rvalue` is itself division-tainted,
/// but never CLEARS `divisions[lvalue]` when the rvalue is a clean value.
/// So a variable that was previously the result of a division and is then
/// fully overwritten with an unrelated value still keeps its division taint,
/// and the next multiplication using it fires.
///
/// Expected output after fix: exactly one finding, for `genuineDivThenMul`
/// (the tripwire confirming the detector still catches the real bug).

contract DivideBeforeMultiplyTest {
// FP class (fix): variable reassigned to a clean value before being multiplied.
function safeReassign(uint256 x, uint256 y, uint256 z) public pure returns (uint256) {
uint256 a = x / y;
a = z; // `a` no longer holds x/y
return a * 100; // multiplies z, not a division result
}

// FP class (fix): chained reassignment through a temporary.
function safeChainedReassign(uint256 x, uint256 y, uint256 z) public pure returns (uint256) {
uint256 a = x / y;
uint256 t = z;
a = t; // taint should not propagate from a clean rvalue
return a * 100;
}

// Tripwire — the real divide-then-multiply pattern. Must still fire.
function genuineDivThenMul(uint256 x, uint256 y, uint256 z) public pure returns (uint256) {
uint256 a = x / y;
return a * z; // genuine precision loss path
}
}
Binary file not shown.
5 changes: 5 additions & 0 deletions tests/e2e/detectors/test_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,11 @@ def id_test(test_item: Test):
"divide_before_multiply.sol",
"0.7.6",
),
Test(
all_detectors.DivideBeforeMultiply,
"divide_before_multiply_reassignment.sol",
"0.8.20",
),
Test(
all_detectors.TypeBasedTautology,
"type_based_tautology.sol",
Expand Down