Skip to content

Commit 6aca6c3

Browse files
committed
Fix last_name edge cases with Solady library
1 parent f6413e6 commit 6aca6c3

File tree

5 files changed

+111
-6
lines changed

5 files changed

+111
-6
lines changed

slither/slithir/utils/ssa.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ def last_name(
345345
LocalIRVariable,
346346
],
347347
init_vars: Dict[str, LocalIRVariable],
348+
new_variables: List[Union[StateIRVariable, LocalIRVariable]],
348349
) -> Union[StateIRVariable, LocalIRVariable,]:
349350
candidates = []
350351
# Todo optimize by creating a variables_ssa_written attribute
@@ -357,9 +358,11 @@ def last_name(
357358
candidates.append(lvalue)
358359
if n.variable_declaration and n.variable_declaration.name == var.name:
359360
candidates.append(LocalIRVariable(n.variable_declaration))
360-
if n.type == NodeType.ENTRYPOINT:
361-
if var.name in init_vars:
362-
candidates.append(init_vars[var.name])
361+
if var.name in init_vars:
362+
candidates.append(init_vars[var.name])
363+
for v in new_variables:
364+
if v.name == var.name:
365+
candidates.append(v)
363366
assert candidates
364367
return max(candidates, key=lambda v: v.index)
365368

@@ -493,9 +496,25 @@ def fix_phi_rvalues_and_storage_ref(
493496
) -> None:
494497
for ir in node.irs_ssa:
495498
if isinstance(ir, (Phi)) and not ir.rvalues:
496-
variables = [
497-
last_name(dst, ir.lvalue, init_local_variables_instances) for dst in ir.nodes
498-
]
499+
# We need to order nodes so that a node with an Assignment operation is first
500+
# as the other node may use the variable assigned
501+
nodes = []
502+
for n in ir.nodes:
503+
assignment = False
504+
for irr in n.irs:
505+
if isinstance(irr, Assignment):
506+
nodes.insert(0, n)
507+
assignment = True
508+
break
509+
if not assignment:
510+
nodes.append(n)
511+
# Keep track of the new variables in this set of nodes. We need to pass it to last_name
512+
# in case a node has an Assignment operation and the following one uses the variable assigned
513+
variables = []
514+
for dst in nodes:
515+
variables.append(
516+
last_name(dst, ir.lvalue, init_local_variables_instances, variables)
517+
)
499518
ir.rvalues = variables
500519
if isinstance(ir, (Phi, PhiCallback)):
501520
if isinstance(ir.lvalue, LocalIRVariable):

tests/e2e/solc_parsing/test_ast_parsing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ def make_version(minor: int, patch_min: int, patch_max: int) -> List[str]:
476476
Test("scope/inherited_function_scope.sol", ["0.8.24"]),
477477
Test("using_for_global_user_defined_operator_1.sol", ["0.8.24"]),
478478
Test("require-error.sol", ["0.8.27"]),
479+
Test("yul-solady.sol", ["0.8.27"]),
479480
]
480481
# create the output folder if needed
481482
try:
Binary file not shown.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"C": {
3+
"_checkpointPushDiff(uint256,uint256,uint256,bool)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n1->2;\n2[label=\"Node Type: NEW VARIABLE 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: BEGIN_LOOP 4\n\"];\n4->6;\n5[label=\"Node Type: END_LOOP 5\n\"];\n5->25;\n6[label=\"Node Type: NEW VARIABLE 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: IF_LOOP 8\n\"];\n8->5[label=\"True\"];\n8->9[label=\"False\"];\n9[label=\"Node Type: IF 9\n\"];\n9->11[label=\"True\"];\n9->10[label=\"False\"];\n10[label=\"Node Type: END_IF 10\n\"];\n10->23;\n11[label=\"Node Type: IF 11\n\"];\n11->13[label=\"True\"];\n11->12[label=\"False\"];\n12[label=\"Node Type: END_IF 12\n\"];\n12->15;\n13[label=\"Node Type: EXPRESSION 13\n\"];\n13->14;\n14[label=\"Node Type: EXPRESSION 14\n\"];\n14->12;\n15[label=\"Node Type: EXPRESSION 15\n\"];\n15->16;\n16[label=\"Node Type: IF 16\n\"];\n16->18[label=\"True\"];\n16->17[label=\"False\"];\n17[label=\"Node Type: END_IF 17\n\"];\n17->20;\n18[label=\"Node Type: EXPRESSION 18\n\"];\n18->19;\n19[label=\"Node Type: BREAK 19\n\"];\n19->17;\n20[label=\"Node Type: EXPRESSION 20\n\"];\n20->21;\n21[label=\"Node Type: EXPRESSION 21\n\"];\n21->22;\n22[label=\"Node Type: BREAK 22\n\"];\n22->10;\n23[label=\"Node Type: NEW VARIABLE 23\n\"];\n23->24;\n24[label=\"Node Type: EXPRESSION 24\n\"];\n24->8;\n25[label=\"Node Type: END INLINE ASM 25\n\"];\n25->26;\n26[label=\"Node Type: RETURN 26\n\"];\n}\n"
4+
},
5+
"Initializable": {
6+
"_initializableSlot()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n",
7+
"initializer()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: INLINE ASM 2\n\"];\n2->3;\n3[label=\"Node Type: NEW VARIABLE 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: EXPRESSION 5\n\"];\n5->6;\n6[label=\"Node Type: IF 6\n\"];\n6->8[label=\"True\"];\n6->7[label=\"False\"];\n7[label=\"Node Type: END_IF 7\n\"];\n7->13;\n8[label=\"Node Type: IF 8\n\"];\n8->10[label=\"True\"];\n8->9[label=\"False\"];\n9[label=\"Node Type: END_IF 9\n\"];\n9->12;\n10[label=\"Node Type: EXPRESSION 10\n\"];\n10->11;\n11[label=\"Node Type: EXPRESSION 11\n\"];\n11->9;\n12[label=\"Node Type: EXPRESSION 12\n\"];\n12->7;\n13[label=\"Node Type: END INLINE ASM 13\n\"];\n13->14;\n14[label=\"Node Type: _ 14\n\"];\n14->15;\n15[label=\"Node Type: INLINE ASM 15\n\"];\n15->16;\n16[label=\"Node Type: IF 16\n\"];\n16->18[label=\"True\"];\n16->17[label=\"False\"];\n17[label=\"Node Type: END_IF 17\n\"];\n17->21;\n18[label=\"Node Type: EXPRESSION 18\n\"];\n18->19;\n19[label=\"Node Type: EXPRESSION 19\n\"];\n19->20;\n20[label=\"Node Type: EXPRESSION 20\n\"];\n20->17;\n21[label=\"Node Type: END INLINE ASM 21\n\"];\n}\n"
8+
}
9+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
contract C {
3+
// Snippet of the _checkpointPushDiff function that was making slither crashes
4+
// https://github.com/Vectorized/solady/blob/9298d096feb87de9a8873a704ff98f6892064c65/src/tokens/ERC20Votes.sol#L339-L361
5+
function _checkpointPushDiff(uint256 lengthSlot, uint256 key, uint256 amount, bool isAdd)
6+
private
7+
returns(uint256 newValue)
8+
{
9+
/// @solidity memory-safe-assembly
10+
assembly {
11+
let lengthSlotPacked := sload(lengthSlot)
12+
for { let n := shr(208, shl(160, lengthSlotPacked)) } 1 {} {
13+
if iszero(n) {
14+
if iszero(or(isAdd, iszero(amount))) {
15+
mstore(0x00, 0x5915f686) // `ERC5805CheckpointValueUnderflow()`.
16+
revert(0x1c, 0x04)
17+
}
18+
newValue := amount
19+
if iszero(or(eq(newValue, address()), shr(160, newValue))) {
20+
sstore(lengthSlot, or(or(key, shl(48, 1)), shl(96, newValue)))
21+
break
22+
}
23+
sstore(lengthSlot, or(or(key, shl(48, 1)), shl(96, address())))
24+
sstore(not(lengthSlot), newValue)
25+
break
26+
}
27+
let checkpointSlot := add(sub(n, 1), lengthSlot)
28+
}
29+
}
30+
}
31+
}
32+
33+
34+
// Snippet of the Initializable contract that was making slither crashes
35+
// https://github.com/Vectorized/solady/blob/9298d096feb87de9a8873a704ff98f6892064c65/src/utils/Initializable.sol#L7
36+
contract Initializable {
37+
bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
38+
0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
39+
bytes32 private constant _INITIALIZABLE_SLOT =
40+
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
41+
42+
43+
function _initializableSlot() internal pure virtual returns (bytes32) {
44+
return _INITIALIZABLE_SLOT;
45+
}
46+
47+
modifier initializer() virtual {
48+
bytes32 s = _initializableSlot();
49+
/// @solidity memory-safe-assembly
50+
assembly {
51+
let i := sload(s)
52+
// Set `initializing` to 1, `initializedVersion` to 1.
53+
sstore(s, 3)
54+
// If `!(initializing == 0 && initializedVersion == 0)`.
55+
if i {
56+
// If `!(address(this).code.length == 0 && initializedVersion == 1)`.
57+
if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
58+
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
59+
revert(0x1c, 0x04)
60+
}
61+
s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
62+
}
63+
}
64+
_;
65+
/// @solidity memory-safe-assembly
66+
assembly {
67+
if s {
68+
// Set `initializing` to 0, `initializedVersion` to 1.
69+
sstore(s, 2)
70+
// Emit the {Initialized} event.
71+
mstore(0x20, 1)
72+
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)