diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index 9ce031499..225644cb3 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -71,6 +71,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = { "abi.encodeWithSelector()": ["bytes"], "abi.encodeWithSignature()": ["bytes"], "bytes.concat()": ["bytes"], + "string.concat()": ["string"], # abi.decode returns an a list arbitrary types "abi.decode()": [], "type(address)": [], diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index a1ebef063..91c7394e9 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -944,6 +944,18 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis s.set_expression(ins.expression) return s + if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant( + "concat" + ): + s = SolidityCall( + SolidityFunction("string.concat()"), + ins.nbr_arguments, + ins.lvalue, + ins.type_call, + ) + s.set_expression(ins.expression) + return s + msgcall = HighLevelCall( ins.ori.variable_left, ins.ori.variable_right, diff --git a/slither/slithir/operations/member.py b/slither/slithir/operations/member.py index c835f58e0..7dca0cf79 100644 --- a/slither/slithir/operations/member.py +++ b/slither/slithir/operations/member.py @@ -22,7 +22,7 @@ class Member(OperationWithLValue): # f.h(1); # } # } - # Can be an ElementaryType because of bytes.concat + # Can be an ElementaryType because of bytes.concat, string.concat assert is_valid_rvalue(variable_left) or isinstance( variable_left, (Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType), diff --git a/tests/ast-parsing/compile/units_and_global_variables-0.8.12-compact.zip b/tests/ast-parsing/compile/units_and_global_variables-0.8.12-compact.zip index 53f9c93b5..0026bf0dc 100644 Binary files a/tests/ast-parsing/compile/units_and_global_variables-0.8.12-compact.zip and b/tests/ast-parsing/compile/units_and_global_variables-0.8.12-compact.zip differ diff --git a/tests/ast-parsing/expected/units_and_global_variables-0.8.12-compact.json b/tests/ast-parsing/expected/units_and_global_variables-0.8.12-compact.json index f1a57c142..74226bae7 100644 --- a/tests/ast-parsing/expected/units_and_global_variables-0.8.12-compact.json +++ b/tests/ast-parsing/expected/units_and_global_variables-0.8.12-compact.json @@ -4,9 +4,8 @@ "Test": { "ether_unit()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "time_unit()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: EXPRESSION 5\n\"];\n}\n", - "block_and_transactions()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 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: EXPRESSION 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: EXPRESSION 8\n\"];\n8->9;\n9[label=\"Node Type: EXPRESSION 9\n\"];\n9->10;\n10[label=\"Node Type: EXPRESSION 10\n\"];\n10->11;\n11[label=\"Node Type: EXPRESSION 11\n\"];\n11->12;\n12[label=\"Node Type: EXPRESSION 12\n\"];\n12->13;\n13[label=\"Node Type: EXPRESSION 13\n\"];\n13->14;\n14[label=\"Node Type: EXPRESSION 14\n\"];\n14->15;\n15[label=\"Node Type: EXPRESSION 15\n\"];\n15->16;\n16[label=\"Node Type: EXPRESSION 16\n\"];\n}\n", + "block_and_transactions()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 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: EXPRESSION 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: EXPRESSION 8\n\"];\n8->9;\n9[label=\"Node Type: EXPRESSION 9\n\"];\n9->10;\n10[label=\"Node Type: EXPRESSION 10\n\"];\n10->11;\n11[label=\"Node Type: EXPRESSION 11\n\"];\n11->12;\n12[label=\"Node Type: EXPRESSION 12\n\"];\n12->13;\n13[label=\"Node Type: EXPRESSION 13\n\"];\n13->14;\n14[label=\"Node Type: EXPRESSION 14\n\"];\n14->15;\n15[label=\"Node Type: EXPRESSION 15\n\"];\n}\n", "abi_encode()": "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: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: NEW VARIABLE 5\n\"];\n5->6;\n6[label=\"Node Type: EXPRESSION 6\n\"];\n6->7;\n7[label=\"Node Type: NEW VARIABLE 7\n\"];\n7->8;\n8[label=\"Node Type: EXPRESSION 8\n\"];\n}\n", - "member()": "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: NEW VARIABLE 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n}\n", "error_handling()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: EXPRESSION 5\n\"];\n}\n", "math_and_crypto()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 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: NEW VARIABLE 6\n\"];\n6->7;\n7[label=\"Node Type: NEW VARIABLE 7\n\"];\n7->8;\n8[label=\"Node Type: NEW VARIABLE 8\n\"];\n8->9;\n9[label=\"Node Type: NEW VARIABLE 9\n\"];\n9->10;\n10[label=\"Node Type: EXPRESSION 10\n\"];\n}\n", "address_related()": "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: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 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: EXPRESSION 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: EXPRESSION 8\n\"];\n8->9;\n9[label=\"Node Type: EXPRESSION 9\n\"];\n}\n", diff --git a/tests/ast-parsing/units_and_global_variables-0.8.12.sol b/tests/ast-parsing/units_and_global_variables-0.8.12.sol new file mode 100644 index 000000000..11a467304 --- /dev/null +++ b/tests/ast-parsing/units_and_global_variables-0.8.12.sol @@ -0,0 +1,119 @@ +pragma experimental ABIEncoderV2; + +contract A{} + +interface I{} + +contract Test{ + + function ether_unit() public{ + 1 wei; + 1 ether; + } + + function time_unit() public{ + 1 seconds; + 1 minutes; + 1 hours; + 1 days; + 1 weeks; + } + + function block_and_transactions() payable public{ + blockhash(0); + block.basefee; + block.chainid; + block.coinbase; + block.difficulty; + block.gaslimit; + block.number; + block.timestamp; + gasleft(); + msg.data; + msg.sender; + msg.sig; + msg.value; + block.timestamp; + tx.gasprice; + tx.origin; + } + + function abi_encode() public{ + bytes memory m; + abi.decode(m, (uint, uint)); + abi.encode(10); + abi.encodePacked(uint(10)); + bytes4 selector; + abi.encodeWithSelector(selector, 10); + string memory signature; + abi.encodeWithSignature(signature, 10); + } + + function member() public{ + bytes1 b1; + bytes32 b32; + bytes.concat(b1, b32); + string.concat("", ""); + } + + function error_handling() public{ + assert(true); + require(true); + require(true, "something"); + revert(); + revert("something"); + } + + function math_and_crypto() public{ + addmod(0, 0, 1); + mulmod(0, 0, 1); + keccak256(""); + sha256(""); + ripemd160(""); + bytes32 hash; + uint8 v; + bytes32 r; + bytes32 s; + ecrecover(hash,v,r,s); + } + + function address_related() public{ + address payable a; + a.balance; + a.code; + a.codehash; + a.send(0); + a.transfer(0); + a.call(""); + a.delegatecall(""); + a.staticcall(""); + } + + + function return_addr() internal returns(address){} + function address_edge_case() public{ + // For now slithIR loss precision on this edge case + // And create a Ref variable instead of a Temporary one + return_addr().balance; + return_addr().code; + return_addr().codehash; + } + + + function contract_related() public{ + this; + address payable a; + selfdestruct(a); + } + + function type_related() public{ + type(A).name; + type(A).creationCode; + type(A).runtimeCode; + type(I).interfaceId; + type(uint256).min; + type(uint256).min; + } + + +}