From a21e7b4cd1e4716894c7aee9dcbd9d73e5afde06 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 15 May 2023 22:14:22 +0200 Subject: [PATCH 01/14] Fix yul function calls --- slither/solc_parsing/yul/parse_yul.py | 28 ++++++++++++++++-- tests/e2e/solc_parsing/test_ast_parsing.py | 4 +++ .../test_data/assembly-functions.sol | 11 +++++++ .../assembly-functions.sol-0.6.9-compact.zip | Bin 0 -> 1537 bytes .../assembly-functions.sol-0.6.9-legacy.zip | Bin 0 -> 1403 bytes .../assembly-functions.sol-0.7.6-compact.zip | Bin 0 -> 1510 bytes .../assembly-functions.sol-0.7.6-legacy.zip | Bin 0 -> 1378 bytes .../assembly-functions.sol-0.8.16-compact.zip | Bin 0 -> 1528 bytes .../assembly-functions.sol-0.6.9-compact.json | 9 ++++++ .../assembly-functions.sol-0.6.9-legacy.json | 5 ++++ .../assembly-functions.sol-0.7.6-compact.json | 9 ++++++ .../assembly-functions.sol-0.7.6-legacy.json | 5 ++++ ...assembly-functions.sol-0.8.16-compact.json | 9 ++++++ 13 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/solc_parsing/test_data/assembly-functions.sol create mode 100644 tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-legacy.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-legacy.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json create mode 100644 tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json create mode 100644 tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index 35d5cdd9d..978859a53 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/slither/solc_parsing/yul/parse_yul.py @@ -181,7 +181,7 @@ class YulScope(metaclass=abc.ABCMeta): def add_yul_local_function(self, func: "YulFunction") -> None: self._yul_local_functions.append(func) - def get_yul_local_function_from_name(self, func_name: str) -> Optional["YulLocalVariable"]: + def get_yul_local_function_from_name(self, func_name: str) -> Optional["YulFunction"]: return next( (v for v in self._yul_local_functions if v.underlying.name == func_name), None, @@ -252,6 +252,10 @@ class YulFunction(YulScope): def function(self) -> Function: return self._function + @property + def root(self) -> YulScope: + return self._root + def convert_body(self) -> None: node = self.new_node(NodeType.ENTRYPOINT, self._ast["src"]) link_underlying_nodes(self._entrypoint, node) @@ -271,6 +275,9 @@ class YulFunction(YulScope): def parse_body(self) -> None: for node in self._nodes: node.analyze_expressions() + for f in self._yul_local_functions: + if f != self: + f.parse_body() def new_node(self, node_type: NodeType, src: str) -> YulNode: if self._function: @@ -325,7 +332,10 @@ class YulBlock(YulScope): return yul_node def convert(self, ast: Dict) -> YulNode: - return convert_yul(self, self._entrypoint, ast, self.node_scope) + yul_node = convert_yul(self, self._entrypoint, ast, self.node_scope) + for f in self._yul_local_functions: + f.parse_body() + return yul_node def analyze_expressions(self) -> None: for node in self._nodes: @@ -390,7 +400,6 @@ def convert_yul_function_definition( root.add_yul_local_function(yul_function) yul_function.convert_body() - yul_function.parse_body() return parent @@ -809,6 +818,19 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ if func: return Identifier(func.underlying) + # check yul-block scoped function + if isinstance(root, YulFunction): + yul_block = root.root + + # Iterate until we get to the YulBlock scope + while not isinstance(yul_block, YulBlock): + if isinstance(yul_block, YulFunction): + yul_block = yul_block.root + + func = yul_block.get_yul_local_function_from_name(name) + if func: + return Identifier(func.underlying) + magic_suffix = _parse_yul_magic_suffixes(name, root) if magic_suffix: return magic_suffix diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index a561343de..ebdd04d9a 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -453,6 +453,10 @@ ALL_TESTS = [ Test("complex_imports/import_aliases_issue_1319/test.sol", ["0.5.12"]), Test("yul-state-constant-access.sol", ["0.8.16"]), Test("negate-unary-element.sol", ["0.8.16"]), + Test( + "assembly-functions.sol", + ["0.6.9", "0.7.6", "0.8.16"], + ), ] # create the output folder if needed try: diff --git a/tests/e2e/solc_parsing/test_data/assembly-functions.sol b/tests/e2e/solc_parsing/test_data/assembly-functions.sol new file mode 100644 index 000000000..c7b1c17d3 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/assembly-functions.sol @@ -0,0 +1,11 @@ +contract A { + function foo() public { + assembly { + function f() { function z() { function x() { g() } x() } z() } + function g() { + f() + } + g() + } + } +} diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..10b6ee248058585c51cf6277ed5477a09f86838b GIT binary patch literal 1537 zcma*nX*|;n00;2@Vn#Wl#*;Z09Ue!hJnsA4M2faOq{SLFTedMW%q^<9a+H!)dI}-o zVeX@Oh{D>e!^e?pJ?{G`kDm9>=kxr2FMh9nZ@=yc5m8HE4^g}|^~I9?0s=#Ex>qSAqJ$_I@Bsh@ z0GKBy;|;eBgA4n6t4*cyJBWdrO)3euJrpfe`IH8E{S{k_JD*@!*|G6I7x#HRWUe?V zE|u$f%;x4eJ=-tfQiC55#T$FtgA)j>itJL*prS>rzbR1&?SuQOj}Md-cAX5q_e+}1 zfgT_)9;g~`GmlyjdufNrC(D_{D1C9v<)Q=DQh8rGsnW_8a`JmyHcla|HCmR-2-5-w zr>tk8Ixi&MkcsikKm@^_87JFpfyvYzX?sAOx8~qP*cT{VEp9kCumGL zOB-JKI@<29IK3dY6#tU7JdWU~tUTqk7^%ZuJ3V99kcJuhZXXqpA;F)J)L7AI%ie_d zJAP2*d;2K4m6(b3)o#NBGJH`r*yN7fRXx+tJR$&5Xf0s*LkOy>Y0o*>L6PBkhifo; zk+{kW~X2#hj^29l*Nu3_3_pv#)N&MV#7IZ+FO(M_L38PHC9#6G456bkNo8D>eG zVk0$NYOV66(%e#}*&aomJ8Vrv+DTw5B*poLcOdaePa}$gP1-N*w%SE(znMd;$eTD{_a2K`5!w3p4pg2c89R8`|ZlHOU+EB_{2?HNPBIL7t5-5&lM-;HZR4cHmD(Xk*?^ zd&Jqe7Opgxyl2mr2FXs&K*B9OOjB#BdRXg>3sPgB|8Ogk(G6`cRvb8g)7e9`{!9jU z_3#AcIGkwqwpjFY3fw0(RU;F5V4ysmE8EWa>cp5bP0o;f6{(kyI8#%D=03vIMQMT4 z3Bnjz23WKsbuXN5VZ5;dtM13s)p)O0KgFCtbSZV&Q_9|3fhPDZ57x38X}G6};a@BL z9cn9I(^lajboJsD;#`d zg5eEh!LpJe!XF_*TSrkVP;=*FjJkJJJm=WR;^;x~Fiujsf(Zz^sfiv0@hY_iGmzD2 zXj!YoHF4qwILHU2s((CI%d7456Lu!Iq+s#t^2MTaW64`@q=lZBD>4%HZW=t^$mbV@ zIn|F3@5g9cuB^D1CCP@3ye4CL3S>c#itWDRccSAI;f8np#yXui@Mk$Wr4#f`J z@?mrrvLLJb`ts6vIa6%(D{DNH#QLs)*L%#1+Ueuu*1ID>dn`r%8|7V>{|y85bN)Zk U?g+5h&j)C?sNK!ng&qL@0g`UfH~;_u literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-legacy.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-legacy.zip new file mode 100644 index 0000000000000000000000000000000000000000..b429bc3e35436fff28f2da46365ac4367ed774e7 GIT binary patch literal 1403 zcma)+YdF&j0LK5zZEl&{Fqg@3iQJ+ilFg9YRI9P5+@@jJmJP8KVI@R*oP!>8aa=+S z2SY+9=Dtl1%{Am+ZmlTd)cJm%=e+NS-}8QWzy2_2Az>tN05}M+nN_&qcii?4VE~Yj z0{{a60Q_k*0tp`$V-y%k4u~L9$TVXbCCrx)eU(Cu@b#w#1QVkO#vwEcSzK5I@CJZ* z0I*3;4l>_04}UT^P;V`r*Fh%gv}!Q!orfXdBeWLi(a-jX^f6U`+1WYPrX;Q)#$`=b()9EVfglsj5ua#{~&a*?x!hFA-$MHkU7 zw$hin8Dy7(Df$YpwYJwdq_?19ZLg~(@D`;BYBSY;uyGdATY`4`_K`bEkE1rD)(DYq zFUkeHgp;`lF3pxBLJvLCAhlJbuNZl!-W92-qTBY_O18tv#Ww30rVJ7ujvm>`j2bRq zU|2fdZdI;LD2moWuj7%YPsOTnqmG(`7T_0}l0I^ys;{Ovj;b27?D83KA$9~)6?MA9 zEPi|-7qdJc!yz??i|;}L_59>Ls=!J?5%==Ms%EG@o+C57ZJ+qQ>-sT#Q(}Mqr9oNd zr;H1iv81*as98e8g1*%Lj^k8d1FAKvbWIwZQFGWp(-Y5i@V1(Fnrsl+NVy!OC=pmP zJnpqV5=iQ@C$-(JSUyxHJgtto)ZErH+aSYH95dB8XH63XgDqWeH(JTB>(y&Cxo9Nd znHvKal8Ae(;bcEo?6fa*7n*0s$Iq_VzBJn|0yWSG#P9R#AG{7Mi9-3R?h>K?G?^?A z6=$+mvGllBTzKPaabl|eGXoprXiS23!`GD~7iCzK@3&d+t8WRFYrP(KqATQN#lGV^ zEgI>XJ4pkjGJ~+q8uQMnM)+2Y*YWf&vDm4t#mWa8ug(!tH-n(4{ozcoN>qxaL~Z^; zz&_1wc!B)k0!j)v$-}|= zI$R6zy}9^_Q{FyF)}nB%vuAyr@%IlB$`$;u!^BcdWMmQJ5)VFF6b2OhndWU+S)-#~ zF81kdNlms@ou#CI@pLHL#Dq0D&E73NiFR+(q)r4Y$T(Y<5B1sF3)-=gG5MCEqK~T* z!4~0bI&lNBN`7wph?L-qHpn`=n&4eI4;f#?THg@|^VOz= z@3W83m(^b literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..300b4bf39f0421ea4e2f730dcc8d585c358cc42e GIT binary patch literal 1510 zcma*nYdF&j00!{CYfLmG8FHya7`bFIQz$e^NJx{*G?_7zYvkIP!?dR4&N<61EJ7Wd zyG%2kP_%?N8@W{&(Z$jEexB#N?}zu(`}OCH6cM!ogaH|VMXz`7Kx_;diUGh`MF2Po z0Dv!nfDZA)#^?v$!1+f6h2jWMLMYY;9UT@L9^vB~?vDwIL_@C=LUEF!V!#yuNCW`O z^z>`S-;MEQLxas0(uKXapu_D4(z0A22(?iHbKfz64I=xUvajq6@9Bz^dofvH4_T!f zxGd-0x32)d9f`mf;UwBPJ@_T2YuL{fkQEP z+HQn;^E9aF3lZn~BvTz^(pP-J*T8jdLGfGtIRiGXF?^062ogu z3c0VJEKpopB^v(dxGVvKIY{x{-j{6Dg#Nh4WzTDbI`577JB>pEAp-qKOqF`R)-z>e zeraY@59@sbF@WI34C2i$F*8DZF4N#q1i!}%vc(+j@3MeX>@@u~EUj?=-YA9}UjjBRRp+^#{!tnbc$l3=x*3VC zJIeKRgFA)D++5c?$~^c{Zf)cn?Ns{-tAeu%115i!8{c`k3IlJQeW-AOM(uCwB%4gw z~YW;x~R!=e6~ruX-UJM+TZBeGt}iG+d<*Uio8CF*mC z=ll5kz}1z`Z}I-CP1LuA0>*h^NW3fVbkN@2lV^QkfWuA&y2`=@)ow+ z?X8TWqev}xrAdENwLC)(^>ES8L@eLi&EbQ>hF-)cfk>;;0)4h|&u`T>`~9`6Fq0k1 zY)lj#EAtG524G-auY}ibZ2eNo5TdQUS&LhDNG0f6%b#OYtXRMB00BqIM=Aa}!j$}I zP#$j08aVk0G^&zOCemRme25-aIRG^iiYq=he0TTj){D|pyegKmDK9pW;)t+$Y4DqWf9fOyg~5*T7?^zJGtx&hiB zWCl4))Ubf(Xy^DxNfP0?pgdIbA!8anWwmog2a%QL!YmtfzVbkeO{-NKI1X~11qVBj z#BBwH>V{}tSBiMyq@4D{-G}4qxDf-y{5FEN<4-~7{ti52h0wLNC*HPf*C@7IxMtG9?Z{ z*#WX((m8>JjVZI$`fB`hH3|yg|K5u!FDzXbOPouBV_awT4l18+Il-t6i__>4_si%8 z4}S%anxYJDLN)KjMVK761SM literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-legacy.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-legacy.zip new file mode 100644 index 0000000000000000000000000000000000000000..916e32eb2547685b6e0825b6413fdbcb1d3213ec GIT binary patch literal 1378 zcma*nYdF&j00!{CHn%XhXhv>P$ukOB$7MLgkPHzO3z=DLW`trSw20EF$hNY%bR$uO zT*6$Al9=Z(moAtg#nx~p+0prap69&phxgO_^@sKZfgONt00iiLRvnl^|ng%E<%)Xn{HN~FAS+SLT=GKxZ;e=AJ-05o#r(N6$0*EUqQjX zKQs6JR#b?3pu!!FCou?et^I6LRy3>TPL=*=~grxgxkEW1?b%pcV_+poSqmFB^&b93wLAQ1Yw7g zN#%-2{rV}tg)~&JCNKQ#MaFSDy!bL|vKr~+i8HC2I2|;dbN117ib0nqaPa5zp$!2_ za}#zymiBZRCl_MJ`eOHPn^M2)?YvY_dq)swkK;#1+A`~wx6dj{@*@Iz1mRdUp6(tp z>bJtp8Nl6ZE}Di~Y*Hybpw(e=P*mxoGj%R}V|MuQ(EiKacY1XeL8x_420ipR>z^l0l{Ad-dt=rfOJ8odMHEB2&}N+~d1b>i0_pS6P&7cf4jX`$5o{r_b`quUp37Ol$t$)+6&A>P(qJ z!Y}o?tDU^frZRMLQ;V)_j^;CxyAt#zH$8r7dosujJagz?w%OsS{V4xrrUIA8Nt{ zVXY67?o1`ntRbf|GAj47_%Gl;v(r%^x+iPsXp|1n>b>bx%gl{(8)eQ|6R=xF+s(2yZ_hDdJfJe1E z=B8UeJq#DdLfy9m%FUs=59LJrEVxYwEWM(LvDR6^t=KgUyV$x%x?-(w!}#EdtP6jx zg~f0Tu+1bto)ZmmbWp1sEwA7G)e6aG$vyyAT`e8wWDYG%!mOB0+s77^ubG_QXK3~C zf)k^_n;67*IyTkd^0|)}x0oQhXc`FqiYPcvtmX&P)ii#mnV&$<_H8 zxf1X+&jt+FJ#qNPnvo^6Hn7cWdUj*uWHS#phA5}M!i;ntpk%)EVJ@KS9|sY17$r4LRtv0=09aHHDrn4*LB%OG4e} zLpi7s1=p_fCcYrS*C}f(SSe=QT=x{;*29n1c@FY-eKLFVu?7gXmb~Vt5QPoaDM&SW zVSuJRu{O4q9#kn~YPcR*WjiDZse;{JTMc%aSxngdHhh+9HdiP~ zsSZ=2kn;oc5<8wj(hy>;!0>dYJd6nV z)abJXUjBO|gNqURe@Vk=v?rKS^6=>Bau3(u8#zGI1B7D-%47CktMx*ed31C1+A`ts z@{Tl|xWY$LD>3eX2^`LlCWglF?dO6^CGYKSmZ3eRwmE?QTfHs#{!O~nulfH5qCKT$ Qem$hN8o0HhElC039~*>+yZ`_I literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..9a88417ba3a962eddb6396cdbd698545008b2b95 GIT binary patch literal 1528 zcma)+YdF&j0LK49lw6v-2x*pTp<&0ct-@S#FSkrKmxyi6hO|SI$(^LwqN9<{aV^5+ zHkWL2SrJmyArf=xL5hf@^Zh)}dEXDePw$8K%g@^})0Dz-V0txuAXlPIb(VrSZB2rFJNMRQPuU;XAQ!nDe{ewdy15aG0kcfN4!~rY- z&;h_ADd`ey6Gmna2cx}4o@M82nT z>$H3yz+wQkpE@jJmv&cL+UJLZ5OM17G0Z`)5bL@AZ_s_V0lbDTR~wLcGjH%QxhK6wD=2( zkmjeKYDI!A&F3E^v1o2K&dHPz6C>%+Ak>a|&?fU>a&bde5?_m0VS!^CX%xs8{vN zl`-|6G<}xDWY<-`>QmO}tmjmjwrRn=6bae%>jBB7C_ScJ{q6+{J=^u?LsaT zupRb$)7?<|{TL4S=Fw`6Yy1cOxOe5T8bf2JV9%2?t_NzHdY);Nr>UE2<$7e+Ybf3} zi?Qi-v=mP?E_NXT6!{*16zK3orl$M#pANaHuRBG|rnBcHmm?U4*KUXB)eJxn{DxaE z7m|kP&sx{(Qt|G4ntl3j8MZv~82JLz)``fBXa^lu53j~Y&Ho&3CM=9*6!CHmN;b8} zgA<%$bK<-~DKX4Lb%_(4wY2oJ!g8m8v_WUNWxJ&jl_7-0cTk6^!njt!@s}XMWV15W zWwu+V%U3f984z%k`PRBv%JE1n+%u6hHGm|9Jgl$x`3~CGaZ1s4Zus{Zf@C5^ssTbS zQVkQ#<0CnJjonOh0D6}d)L`kC4~p8T;F+rY-8fv9G!ykucK}rsh^Xkmo%~_NqzOXnaFmmM1ueIS1{)1ALbLwwqkzC(d6yyRXRk$E|@hUx8(&< zJxNs~ST?;}Lbu-N>NF{v&XbfEdROXc^969>kL6@Wl#*NecsTq=WWNWijIsHFXkxNklGcRX!IAXQOT(kKC?4LX&~|Innellu+pC5_XHJQA1_i$Am!y zT&s%}zhZ}l#FHJ?rq5N#lt^Q?_wnliXM2%dDAE6R@s5uF4PE3H{=b=>?ZtQhT8Qk_ MbB8HAeg}Ym030d55dZ)H literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json new file mode 100644 index 000000000..ea4faf796 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json @@ -0,0 +1,9 @@ +{ + "A": { + "foo()": "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: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json new file mode 100644 index 000000000..09c0a51f7 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-legacy.json @@ -0,0 +1,5 @@ +{ + "A": { + "foo()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json new file mode 100644 index 000000000..ea4faf796 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json @@ -0,0 +1,9 @@ +{ + "A": { + "foo()": "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: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json new file mode 100644 index 000000000..09c0a51f7 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-legacy.json @@ -0,0 +1,5 @@ +{ + "A": { + "foo()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: INLINE ASM 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json new file mode 100644 index 000000000..ea4faf796 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json @@ -0,0 +1,9 @@ +{ + "A": { + "foo()": "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: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" + } +} \ No newline at end of file From 413a50a1d2561fbec306c1823079e6b1579b0f54 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 15 May 2023 22:57:40 +0200 Subject: [PATCH 02/14] Fix call to a function in an inner scope --- slither/solc_parsing/yul/parse_yul.py | 7 ++++++- .../test_data/assembly-functions.sol | 1 + .../assembly-functions.sol-0.6.9-compact.zip | Bin 1537 -> 1607 bytes .../assembly-functions.sol-0.6.9-legacy.zip | Bin 1403 -> 1433 bytes .../assembly-functions.sol-0.7.6-compact.zip | Bin 1510 -> 1588 bytes .../assembly-functions.sol-0.7.6-legacy.zip | Bin 1378 -> 1414 bytes .../assembly-functions.sol-0.8.16-compact.zip | Bin 1528 -> 1605 bytes .../assembly-functions.sol-0.6.9-compact.json | 3 +++ .../assembly-functions.sol-0.7.6-compact.json | 3 +++ ...assembly-functions.sol-0.8.16-compact.json | 3 +++ 10 files changed, 16 insertions(+), 1 deletion(-) diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index 978859a53..8657947ea 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/slither/solc_parsing/yul/parse_yul.py @@ -787,6 +787,7 @@ def _parse_yul_magic_suffixes(name: str, root: YulScope) -> Optional[Expression] return None +# pylint: disable=too-many-branches def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: name = ast["name"] @@ -822,8 +823,12 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ if isinstance(root, YulFunction): yul_block = root.root - # Iterate until we get to the YulBlock scope + # Iterate until we searched in all the scopes until the YulBlock scope while not isinstance(yul_block, YulBlock): + func = yul_block.get_yul_local_function_from_name(name) + if func: + return Identifier(func.underlying) + if isinstance(yul_block, YulFunction): yul_block = yul_block.root diff --git a/tests/e2e/solc_parsing/test_data/assembly-functions.sol b/tests/e2e/solc_parsing/test_data/assembly-functions.sol index c7b1c17d3..224e16bab 100644 --- a/tests/e2e/solc_parsing/test_data/assembly-functions.sol +++ b/tests/e2e/solc_parsing/test_data/assembly-functions.sol @@ -2,6 +2,7 @@ contract A { function foo() public { assembly { function f() { function z() { function x() { g() } x() } z() } + function w() { function a() {} function b() { a() } b() } function g() { f() } diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.6.9-compact.zip index 10b6ee248058585c51cf6277ed5477a09f86838b..8389eb6f593dc3d0adfaf206f8ba933852ff32db 100644 GIT binary patch delta 1135 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 delta 1056 zcmV+*1mF9#4CV_PP)h>@KL7#%4gkoouU5c5_tG#0002rC001hJ@dYihNM!+kS^tQ* zz*RdDUu(x4b(wR3NuRSGk0%!$Ejg(m11qzM3$_crFw#j!?&6c(%A6D$+WUJV4UWi! z4>dGG@ex`m(Kc{LA&57Muq)TvSyVhuZ4P|)EGoSt=@RtR`=vp@IX|0970fiyqBD`t z^EeRtN~RT2rIg9O!H+cFLzo$Vcy&LGTK92|9x%j7DbiIU$V|EUrF=c5aY6azemdYr zs=IBSY#hipSvte7WxU_bb~{}g_=gf);zl^%k&0QpT;@pW3Jyt@XWDSiXx<2`yXarV zVeQKuONpo4h`iim)MblCLbY{XuDmJgS7gY%KcC`S%atlTONR6aP=aoMOm-a_IyW+# zkEBFsSaz=&PKsVG)2`^~EPyS|hfl}m77(haP}y-~^R<-Oob|j<0vdf&Zq>y0>~oLn zIU9uHjwfiaB|JDOHr_&q?Ng_ezqv8}tfwf+{gRtP$!vO7X!KY3j(R%#Qa*?J zmKD5C+gL9A>|!(pnp{*#;T88R zbno>8yBX`S=d%xtszVrKD5+@Qz#GeGcd%>@xe8?fI1npZe$`aVEufOpKEA)UCfcqf z9BF5_y4c{zdSY^9_01Vy8GXh+HIJ;$6Ffr5%s}r7*BptV1HeRo0wl0-sLU=m0ehZJ z(~}I9>Ta1!rF6IG`fKF{;_j7_jhxir5Ss(FQ4{QoK&2fKIfv25n&-ZOr|7A8Cydkf7MjuZ}Mw$>yR}KsF+DUw8YeAZ8b96V2XIMF3D*(8cK&DJ3NpmkYjfVhdh-Em|nIdGo`SD z^TFdOK8=M^y`j!fU704!rL7;#HVRYc5Nk<_21NC9s0D^rpNoZk{CrVJ%}uG)0W zhnl`~C`|tK!5F}Cy!U@1T$DJ5rA-&kY%FM0pU%P|AJY&#QnP`Se7z8KYjQL{<(@Cz z!g8Q)GO{Y@nqJLE+?5=#p(s{<6^q)O!LAp*2qZ4cWDyXGP4zoNHNCq+V133Pl3oDj zqs3^VoS^n>1%T**JZMwrEF&95w>iA6aC;%d24acB5YzYyzNmxTr!VAQE~y(LaFBa# z!DPJpZ*cJ69T`@0WwIK8kn)uL@b{~Ol*~Bed8`DECkg)Y2V77~0Rle*KL7#%4gkoo auU5c5_tG#0002rCli&qT26zPk0002MruXpx diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-legacy.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.7.6-legacy.zip index 916e32eb2547685b6e0825b6413fdbcb1d3213ec..c1285733d81f638c7c279f1709d3547f7ab2bc15 100644 GIT binary patch delta 775 zcmV+i1Ni*n3Wf_CP)h>@KL7#%4ghhsuT}?yUb@f(006lX001hJ9|bM3NE`xxoGV&c zNQzLkntiT4sxbO%@AgGWA@uTHtLyc;eoT)tJ)TLce&ui)usM!pqTpf*JtlO)j~H@y zN=92@*wtNL@3|3cD+V@Q7t3KrHYw{k!6_^C;uQ1Sxf551bj-U$&BqH>vP`_;xJx z;+a;|<#3GRc_p!2TfDy-fTOH|Fla8(O!%YGk{4R;Up(!FKW`elABo0V?<`5QRco?7 z^R9{s@npNX-NC0s^XjIs@sdBEHJgo+6{Ko19edb{b|uTfy2?1)rG6iO0x5B2%?Mu< zRp(lAe3oarftqhAD@U)Y3Cj)+Q*mApkoG)A;YFV|$Y|>2Kkh4n>Q;Q^6{A5R)$V>r z$_K706I5?_FCi==Wv}mXVjB5{Skib|Q~h~}2t!FMiB9&g3YmFb{U)Jh_OmqOqp^v0 zyx)1j^j2?f?->q@eLYWqQ*)4XGjmc71iTxf65&&Sz=MSlSU~P=dpyAFf_k!i@LPeq z5`8Kd>aW=2WzhINnlzZ^=lubVxACU92BF37&WBK+gAe}Z-@RGJS&GmHs;+0H1Nw84kbPhd}b zwOD>7nhh9k_rlHyT(%Jh$7dgjL59gdIw6ZK0WSpW_8dt*-Y||Pv+31lZN~*ZkZ(_Q z8QjQ9BW$7weo+ajxuaZyLIp9lvaJqr1B8)yVmwS-`nj9XgU!4=MXo&?GS4-FY(@>wNWjnD*XD{&)YAIc=9th{(AOiEt3x3q--O`>DyF z1F-l1vLkgjxS#D$|H5yK!ca>A0zU&k00ICG0CBdjRtJP$y3hmw0J##A4h2pI7zF?T F005F0ZcYFI delta 732 zcmV<20weu~3*rhGP)h>@KL7#%4gkoouT~Jaj(MyE008?Du^3DOe;WNZ#L;y9+no{r z;Mv1aW*EbrqrFGq34ZQ|hl@Kz8obvIecXq<*>}Ql-mzoxV!o(&mg04rHV#{Yf~+T^ z=d>;Th7-p#KZti|4m@FHUv3Y=i%6uRE1Ts+$Qk+xxlhpK7i@^r6rlib1!q4}z`pOj z{71lMbv5nJJ+=Aoe+D;FhMY!6Pz3U=%uxZRwD~CB&+zeTNWnmhfHtl;E%I;}NeTcD zykQ=w2=pj2*}6AooTiwa_rs>yrXAxNd%h+GruWMHB{5fpddc@*x2yL**#A_&v73B^4c(^Z6}PfO-L?E#?v3age`OlK$~d! zrcwX0FaIUN8u341aop0{YQbw=qyNx6lB7^e0Rle*KL7#%4gkoouT~Jaj(MyE008?D OlQIQP2J{2~0000P>ts{_ diff --git a/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip b/tests/e2e/solc_parsing/test_data/compile/assembly-functions.sol-0.8.16-compact.zip index 9a88417ba3a962eddb6396cdbd698545008b2b95..a2b78d7b0698f3f623f5a2a37a95314adaa14197 100644 GIT binary patch delta 1124 zcmV-q1e^Q#3&ji=P)h>@KL7#%4ghhsuU3)0TTYJ!006@ou^6fWe<-GIdm<|qgPLus z)qD0-))LC{XRyf6q?%Mw;DE6v;AqFDBD`g$GJmK~n2I)dBUew?9u4ZEj7{G%%dMvC zo|A`23WLAw0RdeA6Z&w+9r1m-`FX5inpPjU`fOkXCGOWJJrLO}|2c-9CbgMBz%yQr zHonDr0KAwxvjbT)e|oHxZ4EKT@r@7f5ERW0532P3>YF##3f|8HlVGq2%zjYp7yxv? z(xU@Y1+` zly7+DlOysBqqlr%RC3e(rD}2#qZ_ayE7DyGqxW#w+f#}cB=`|jIk})i(;$25F1K)0 z6&Yr?8emN&$Gz6bZ}I5CxZkU)d@m3r=ygM7N(C5{VEeLA@W~M25=Z;kO;rO0gdI44 zDx#^~+5B0ef76BDay4+tZ_f_q!l2apS7Z(3OMEp!bKe|s|J!!ng!0phm3N?DgR+NptX zjCIi9Rm)Xk`a`D$jC3ZK50W}RJUZ2r{mKSlO*v#Z> zVu^%LDM#LfQ^(vmbK<}IQnVbS^RxGnN~N>o$aE#r61-zVd|@p|0~LJhD5RfG|DpIG z0)$ajSC{oT?Rs~ue)@0cbUW$utT#_QOX22J9*ty1!4~dhA*WcOT?I(RcFmqTHQ;kVx;2 zesF5Lb874TrHjk2AzlnAk6h~i;t=6I$GVvFFeKrYv0H9`-4yUg4Xn)9J9>n{n| zM603pkkEAnI|_e6@+Z`FB<*Y9N~Rp!JGo1uYYCKsv1IzmK_QWc05d)!Ap-p{0T8Rx zj9SRrF9sS+*$Y@oWrhjh`rJ5TiSvd*H)qcXe}X!0m%jW0Z8FzRZ`i00rNoIw;Q#rI qNZ3$I0Rle*KL7#%4ghhsuU3)0TTYJ!006@olcWVs2G<1u00012$s;ZR delta 1046 zcmV+x1nK+54EPHeP)h>@KL7#%4gkoouT~F7x9CCz001Nwu^6fWf7~3)j4zo7Y3O#k)O_+bY(3~EzSS~ zvgtt6<`p}AwuHNM2(`Wg5$!0)NnYrtpr+^&Z}t!Qd15St zG=PL^QW)dC`|auLf6uLA+9sy71^!c%?A-Z4jRRL(gl*z-pJB>EY1K{tY<#?xYk-}7 zx4(uevK@NIpwVI4tbQHU+(9$MiH?6|Avst>yAyjHIUtu;kGLHbk~@7!%uYlFhc%^9 zZUA!=%VR$hB;k)AR)76Xmy6TI0XycC>j?67gfV)Oa+|c$e=Qq0Vfn1;Z`FXg!1=a| zVpa*gT+xs*zNS~$?=vAIb&qwz5+odQvSN4a{nov0?c0u{#+NXu{2}0Ig-?E#fm#xZ zeV8J)hvB#QjEzj{tWRW&)KC!eO7Ykj)j9|F62mzZ;2@tY&l1k#y%%&*>B=L>ULR&Y zWMnFs*+!)ce@-ZVH&};n(+eeZ3u^R3%Vz9qZw?@>|%De z&AZB&JOC|~nP$2~V4Mn<~7~i|rtBB}#q$bioqh&g+!(lS|MVNHe_2=ExPs}tY z6oiAJPB%?z&oJ3F&Tw99(ELvWvZY!YT|n_z7s!p{f5(AIwC-cC!e|(J`E8*UDZney zZp$4S#wp}R!O$nc9-(wRz?+QRM6)wl0TZr!SpGk@J;geCdv1XHCuzSX9PwtimL~P? zHk00^cZ>M6ZENkh4tBy_2ZdvQ&@-6l`aI$CNd#btd2#=f^?)Z7N@$tb5dK*w88UPl zZA84!fAK)Tg2=@-tLB>s6Y5&7BiumfR5Ww9MI-L}W8N{>%oczvGrSy2Sz%yMnZ6V1 z^dam~#s^a9Z5H#?1T7&|F;2_b!;r1Rj=ou#+a)n=TstMkMuR;;7nN2wDS};G2r@r= zKLo7nDK8(FQ}LsIV#cArJ~Im*H#Sc3OlyW$ecmTM{*CqPS?)6@0N=6nddvzdezenaX;F+@69ex|kNET%|-j1rxjK^YPBF0*Q zY1_2F4hq7L+Gj_Do15}V)yMX+Tw9{)caki2|FS514lQ6$doNq5bAUjpp}sjZx~_rZ zh15DXAjp}6^GaPOgKqL1x^Nv1poj50L}&Uh5!Hn diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json index ea4faf796..a48faa23d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.6.9-compact.json @@ -4,6 +4,9 @@ "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json index ea4faf796..a48faa23d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.7.6-compact.json @@ -4,6 +4,9 @@ "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json index ea4faf796..a48faa23d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/assembly-functions.sol-0.8.16-compact.json @@ -4,6 +4,9 @@ "foo.asm_0.f()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.f.z.x()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", + "foo.asm_0.w.a()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n}\n", + "foo.asm_0.w.b()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n", "foo.asm_0.g()": "digraph{\n0[label=\"Node Type: INLINE ASM 0\n\"];\n0->1;\n1[label=\"Node Type: ENTRY_POINT 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n}\n" } } \ No newline at end of file From a82683139d46fca135299bc7c751aa48ea2c58b0 Mon Sep 17 00:00:00 2001 From: 0xGusMcCrae <0xGusMcCrae@protonmail.com> Date: Fri, 2 Jun 2023 16:12:24 -0400 Subject: [PATCH 03/14] initial optimization --- slither/detectors/variables/similar_variables.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/slither/detectors/variables/similar_variables.py b/slither/detectors/variables/similar_variables.py index 465e1ce01..5db955241 100644 --- a/slither/detectors/variables/similar_variables.py +++ b/slither/detectors/variables/similar_variables.py @@ -47,9 +47,7 @@ class SimilarVarsDetection(AbstractDetector): Returns: bool: true if names are similar """ - if len(seq1) != len(seq2): - return False - val = difflib.SequenceMatcher(a=seq1.lower(), b=seq2.lower()).ratio() + val = difflib.SequenceMatcher(a=seq1, b=seq2).ratio() ret = val > 0.90 return ret @@ -69,11 +67,15 @@ class SimilarVarsDetection(AbstractDetector): ret = [] for v1 in all_var: + _len_v1 = len(v1.name) for v2 in all_var: - if v1.name.lower() != v2.name.lower(): - if SimilarVarsDetection.similar(v1.name, v2.name): - if (v2, v1) not in ret: - ret.append((v1, v2)) + if _len_v1 != len(v2.name): + continue + _v1_name_lower = v1.name.lower() + _v2_name_lower = v2.name.lower() + if _v1_name_lower != _v2_name_lower: + if SimilarVarsDetection.similar(_v1_name_lower, _v2_name_lower): + ret.append((v1, v2)) return set(ret) From 1b4c0b913727ff6306db9e6f7bb9a4cf0ff900c3 Mon Sep 17 00:00:00 2001 From: 0xGusMcCrae <0xGusMcCrae@protonmail.com> Date: Fri, 2 Jun 2023 18:07:45 -0400 Subject: [PATCH 04/14] reduced num iterations in inner loop --- slither/detectors/variables/similar_variables.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/slither/detectors/variables/similar_variables.py b/slither/detectors/variables/similar_variables.py index 5db955241..8bd5b93d7 100644 --- a/slither/detectors/variables/similar_variables.py +++ b/slither/detectors/variables/similar_variables.py @@ -63,12 +63,14 @@ class SimilarVarsDetection(AbstractDetector): contract_var = contract.variables - all_var = set(all_var + contract_var) + all_var = list(set(all_var + contract_var)) ret = [] - for v1 in all_var: + for i in range(len(all_var)): + v1 = all_var[i] _len_v1 = len(v1.name) - for v2 in all_var: + for j in range(i,len(all_var)): + v2 = all_var[j] if _len_v1 != len(v2.name): continue _v1_name_lower = v1.name.lower() From 15e8ce9e330df30090a5ce82c1fbef0491ee6f1d Mon Sep 17 00:00:00 2001 From: 0xGusMcCrae <0xGusMcCrae@protonmail.com> Date: Sat, 3 Jun 2023 07:15:57 -0400 Subject: [PATCH 05/14] remove changes to how similar() works --- .../detectors/variables/similar_variables.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/slither/detectors/variables/similar_variables.py b/slither/detectors/variables/similar_variables.py index 8bd5b93d7..7cb10cbd5 100644 --- a/slither/detectors/variables/similar_variables.py +++ b/slither/detectors/variables/similar_variables.py @@ -47,7 +47,9 @@ class SimilarVarsDetection(AbstractDetector): Returns: bool: true if names are similar """ - val = difflib.SequenceMatcher(a=seq1, b=seq2).ratio() + if len(seq1) != len(seq2): + return False + val = difflib.SequenceMatcher(a=seq1.lower(), b=seq2.lower()).ratio() ret = val > 0.90 return ret @@ -68,16 +70,13 @@ class SimilarVarsDetection(AbstractDetector): ret = [] for i in range(len(all_var)): v1 = all_var[i] - _len_v1 = len(v1.name) - for j in range(i,len(all_var)): + _v1_name_lower = v1.name.lower() + for j in range(i,len(all_var)): v2 = all_var[j] - if _len_v1 != len(v2.name): - continue - _v1_name_lower = v1.name.lower() - _v2_name_lower = v2.name.lower() - if _v1_name_lower != _v2_name_lower: - if SimilarVarsDetection.similar(_v1_name_lower, _v2_name_lower): - ret.append((v1, v2)) + if _v1_name_lower != v2.name.lower(): + if SimilarVarsDetection.similar(v1.name, v2.name): + if (v2, v1) not in ret: + ret.append((v1, v2)) return set(ret) From 173698d8e0b598ad2a79e5eab20db48bf601d898 Mon Sep 17 00:00:00 2001 From: 0xGusMcCrae <0xGusMcCrae@protonmail.com> Date: Sat, 3 Jun 2023 11:55:54 -0400 Subject: [PATCH 06/14] linting --- slither/detectors/variables/similar_variables.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slither/detectors/variables/similar_variables.py b/slither/detectors/variables/similar_variables.py index 7cb10cbd5..9f8eaaa2d 100644 --- a/slither/detectors/variables/similar_variables.py +++ b/slither/detectors/variables/similar_variables.py @@ -68,10 +68,11 @@ class SimilarVarsDetection(AbstractDetector): all_var = list(set(all_var + contract_var)) ret = [] + # pylint: disable=consider-using-enumerate for i in range(len(all_var)): v1 = all_var[i] _v1_name_lower = v1.name.lower() - for j in range(i,len(all_var)): + for j in range(i, len(all_var)): v2 = all_var[j] if _v1_name_lower != v2.name.lower(): if SimilarVarsDetection.similar(v1.name, v2.name): From 0c27b000aa6d1ffd88439078af4086ee0cd12542 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 5 Jun 2023 09:22:27 -0500 Subject: [PATCH 07/14] add support for prevrando (solc 0.8.18) --- CONTRIBUTING.md | 2 +- slither/core/declarations/solidity_variables.py | 4 +++- slither/solc_parsing/yul/evm_functions.py | 3 +++ tests/e2e/solc_parsing/test_ast_parsing.py | 1 + ...lobal_variables-0.8.18.sol-0.8.18-compact.zip | Bin 0 -> 2023 bytes ...obal_variables-0.8.18.sol-0.8.18-compact.json | 6 ++++++ .../test_data/global_variables-0.8.18.sol | 11 +++++++++++ 7 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/solc_parsing/test_data/compile/global_variables-0.8.18.sol-0.8.18-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/expected/global_variables-0.8.18.sol-0.8.18-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/global_variables-0.8.18.sol diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1d1a9497f..c00fda8aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -106,7 +106,7 @@ For each new detector, at least one regression tests must be present. > - To run tests for a specific test case, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k user_defined_value_type` (the filename is the argument). > - To run tests for a specific version, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k 0.8.12`. > - To run tests for a specific compiler json format, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k legacy` (can be legacy or compact). -> - The IDs of tests can be inspected using ``pytest tests/e2e/solc_parsing/test_ast_parsing.py --collect-only`. +> - The IDs of tests can be inspected using `pytest tests/e2e/solc_parsing/test_ast_parsing.py --collect-only`. ### Synchronization with crytic-compile diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index 552cf9e7f..f6a0f0839 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -21,10 +21,11 @@ SOLIDITY_VARIABLES_COMPOSED = { "block.basefee": "uint", "block.coinbase": "address", "block.difficulty": "uint256", + "block.prevrandao": "uint256", "block.gaslimit": "uint256", "block.number": "uint256", "block.timestamp": "uint256", - "block.blockhash": "uint256", # alias for blockhash. It's a call + "block.blockhash": "bytes32", # alias for blockhash. It's a call "block.chainid": "uint256", "msg.data": "bytes", "msg.gas": "uint256", @@ -60,6 +61,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = { "log2(bytes32,bytes32,bytes32)": [], "log3(bytes32,bytes32,bytes32,bytes32)": [], "blockhash(uint256)": ["bytes32"], + "prevrandao()": ["uint256"], # the following need a special handling # as they are recognized as a SolidityVariableComposed # and converted to a SolidityFunction by SlithIR diff --git a/slither/solc_parsing/yul/evm_functions.py b/slither/solc_parsing/yul/evm_functions.py index dfb52a244..28ea70e93 100644 --- a/slither/solc_parsing/yul/evm_functions.py +++ b/slither/solc_parsing/yul/evm_functions.py @@ -51,6 +51,7 @@ evm_opcodes = [ "TIMESTAMP", "NUMBER", "DIFFICULTY", + "PREVRANDAO", "GASLIMIT", "CHAINID", "SELFBALANCE", @@ -168,6 +169,7 @@ builtins = [ ) ] + yul_funcs +# "identifier": [input_count, output_count] function_args = { "byte": [2, 1], "addmod": [3, 1], @@ -221,6 +223,7 @@ function_args = { "timestamp": [0, 1], "number": [0, 1], "difficulty": [0, 1], + "prevrandao": [0, 1], "gaslimit": [0, 1], } diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index a561343de..316f2cc38 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -308,6 +308,7 @@ ALL_TESTS = [ Test("units_and_global_variables-0.8.0.sol", VERSIONS_08), Test("units_and_global_variables-0.8.4.sol", make_version(8, 4, 6)), Test("units_and_global_variables-0.8.7.sol", make_version(8, 7, 9)), + Test("global_variables-0.8.18.sol", make_version(8, 18, 18)), Test( "push-all.sol", ALL_VERSIONS, diff --git a/tests/e2e/solc_parsing/test_data/compile/global_variables-0.8.18.sol-0.8.18-compact.zip b/tests/e2e/solc_parsing/test_data/compile/global_variables-0.8.18.sol-0.8.18-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..04dde0da9c01b3b5d61bed184e30c285e572dcdd GIT binary patch literal 2023 zcmb8wX&}=N0|)TmHb-TKa>TIY$dxk;WgCAL!rVfHEk_t8k!0nXE1L)*=fs~mQYngM z2uUqD|BhUV_qT3X>BoU*#)5@iApwf*vWXe?QYMHI;wy3OjdMbYv>Z>Dkr+TYY)y<) zAk3sKpyNePyfpvVb}eGHa>5v`q12ajpHwS;^jMNqUftu7ddNoas;#2#7Y(5^jMTTxyUTEcEN+Es#yzL4wJppOt-=qZ$3zdOOa`L6Yc5*c1?V* zLBc|XbaRS3Xirw9NbF2ouyokFw5pW+nkSU?aR(~Acv3|g2=6m+!q6b3i1?a8tV=RZ zE%erv%eYa&!e;EeY5vKwQ~{mdLDXL82V2%S+4MGeVOA!k{sm~+E-pLDjAQ+9-SG(d zK(^|I>U}%=_#}V(cVk}*&CAz}dfy%Hoj%pV8877Y!7SG&G!oiv*Dc-3@nSzR@*9=k zHhpWLFj(;oD{gu_Hj?x!x5 zuURqDb6Sn)nnoaD?l;a|XgcP`*Bon;?OC*I7_W9PX(Sd?jS@Rhf4HUZgs7K^hhFq>7Rqk`rpOB*nw!pU zMCrKJdJ2>ZyDwl=DqqG%V0wI5;i6$`L#9)19B}dVXXILroN0J^YFU)e3x?0v2c0^W zxgdyx*4R>7#7Xta#b0c?NFZ}q5@W3j?Kt^MXnlaLmiF^5_0ED!_w!6f{(jx_gRHgu zY`s07izK%en`PG0;0VflZ~3i%fh|~SYJ`3$-GyNo>y)~g-5S788>8pwGuB`p1_8-4 z@7~-xlCZ*c!P+_FN=?=5ha;f0GZkTWqkG8ZVqEQkJni531=1m7h)_{`3w3n#q5C(d z;v@qDL~U$A{ozgQ z`(*~NjvVYdXf3xPn>JezeM`@nXp)`b?5vbKLt|{Q35GTe_k+fK!}hNV(@!snq|Wb{ z?AXn`#TmY`n9BDIrh^-}33~lsvxj`SHI=>UYZ=4!#dCE{p0nPUjnUPGsC}m?Ui&ii zw*Dy!iMeOBd%TqU9bQB$f8jXYVbYrT%ro%lg!zwPXVr{<ump%om zej+p!arKaqBqHQy(R&dEUA2lOm;n-WR*!3L{W*S0?*j(B!~YPRVsxedvYwW95sL>8 zC-+nBE*d=&Al}wm4CYVFI6tZ+&#q1T?3$25T(LfJZS%|bCriy+P1k=7eF)8YVW~Ce zi?}W{V8kGOFYguuxq3WL98~DT7EiFQxwkom#?NQJX>tF#RkVgG>$8kBq<}cr83RK% zskZmu(?n^?3D&M~j~Tke=XbRVNQKXXx4p05G%-geiyPy-j0~5d3e9b!(pM8c;U$IX zk8@G~2!ey=>cAz*AuNj+-Aw-`n1z!=?%P)(R`4q2b{J32%5kRHMoE=*^E!-D@-QUu zx5x+63&QI>tVSl9Q;}_;B+1Wncd}UPTYJnUoUhIllmP~`1_)HwN2=6unZZ1z%X|^? zVs2;y@e_DLkr-P+(F5^oxgT+Zuewxyue3Mq1}61w5KlMx_2g#f`~eDF47*)02Q@md z_43V75y9WS>f&5(qFhIfI^Me<#H`KJ5^rm*mayCrhZM}#ox1cP@8<=qHQ}&~K9q8_ zCMq&akR`BK2@5xgVmza;pr4qpW5Q>jtcA9mJBmeYrayAGDVd=;Z}eET&8|>dqR*}x zOH@l^TMO(YF$CAR(()_keKWh_@*BI3W89G6@;Z>~c~(R4EBL2kP9A8%%Ek(`Uo)F` z$~o^r@+(h$c}Hp7ooiRyRWxx?PXVwtl1;\n1[label=\"Node Type: RETURN 1\n\"];\n}\n", + "g()": "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: EXPRESSION 2\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/global_variables-0.8.18.sol b/tests/e2e/solc_parsing/test_data/global_variables-0.8.18.sol new file mode 100644 index 000000000..f21ae5d8f --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/global_variables-0.8.18.sol @@ -0,0 +1,11 @@ +contract C { + function f() public view returns (uint256) { + return block.prevrandao; + } + + function g() public view returns (uint256 ret) { + assembly { + ret := prevrandao() + } + } +} \ No newline at end of file From 7cb7cb94ad00df6fe78775b656189643d896a993 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 15 Jun 2023 17:21:44 -0500 Subject: [PATCH 08/14] inform user if inheritance cannot be resolved (#1956) * inform user if inheritance cannot be resolved * lint * update explanation --- .../slither_compilation_unit_solc.py | 11 ++++++++++- .../contract_with_duplicate_names.sol | 2 ++ .../inheritance_resolution_error/import.sol | 1 + tests/unit/core/test_error_messages.py | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/unit/core/test_data/inheritance_resolution_error/contract_with_duplicate_names.sol create mode 100644 tests/unit/core/test_data/inheritance_resolution_error/import.sol create mode 100644 tests/unit/core/test_error_messages.py diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index f4258cd41..a739c6f97 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -33,6 +33,10 @@ logger = logging.getLogger("SlitherSolcParsing") logger.setLevel(logging.INFO) +class InheritanceResolutionError(SlitherException): + pass + + def _handle_import_aliases( symbol_aliases: Dict, import_directive: Import, scope: FileScope ) -> None: @@ -432,7 +436,12 @@ Please rename it, this name is reserved for Slither's internals""" target = contract_parser.underlying_contract.file_scope.get_contract_from_name( contract_name ) - assert target + if target == contract_parser.underlying_contract: + raise InheritanceResolutionError( + "Could not resolve contract inheritance. This is likely caused by an import renaming that collides with existing names (see https://github.com/crytic/slither/issues/1758)." + f"\n Try changing `contract {target}` ({target.source_mapping}) to a unique name." + ) + assert target, f"Contract {contract_name} not found" ancestors.append(target) elif i in self._contracts_by_id: ancestors.append(self._contracts_by_id[i]) diff --git a/tests/unit/core/test_data/inheritance_resolution_error/contract_with_duplicate_names.sol b/tests/unit/core/test_data/inheritance_resolution_error/contract_with_duplicate_names.sol new file mode 100644 index 000000000..d5c6816e2 --- /dev/null +++ b/tests/unit/core/test_data/inheritance_resolution_error/contract_with_duplicate_names.sol @@ -0,0 +1,2 @@ +import {ERC20 as ERC20_1} from "./import.sol"; +contract ERC20 is ERC20_1 {} \ No newline at end of file diff --git a/tests/unit/core/test_data/inheritance_resolution_error/import.sol b/tests/unit/core/test_data/inheritance_resolution_error/import.sol new file mode 100644 index 000000000..e3221165a --- /dev/null +++ b/tests/unit/core/test_data/inheritance_resolution_error/import.sol @@ -0,0 +1 @@ +contract ERC20 {} \ No newline at end of file diff --git a/tests/unit/core/test_error_messages.py b/tests/unit/core/test_error_messages.py new file mode 100644 index 000000000..d0d915d56 --- /dev/null +++ b/tests/unit/core/test_error_messages.py @@ -0,0 +1,18 @@ +from pathlib import Path +import pytest + + +from slither import Slither +from slither.solc_parsing.slither_compilation_unit_solc import InheritanceResolutionError + +TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" +INHERITANCE_ERROR_ROOT = Path(TEST_DATA_DIR, "inheritance_resolution_error") + + +def test_inheritance_resolution_error(solc_binary_path) -> None: + with pytest.raises(InheritanceResolutionError): + solc_path = solc_binary_path("0.8.0") + Slither( + Path(INHERITANCE_ERROR_ROOT, "contract_with_duplicate_names.sol").as_posix(), + solc=solc_path, + ) From ccad54263de5802c9893bd6eb151aa68d1a2cb95 Mon Sep 17 00:00:00 2001 From: Simone <79767264+smonicas@users.noreply.github.com> Date: Fri, 16 Jun 2023 00:21:59 +0200 Subject: [PATCH 09/14] Handle if crytic-compile returns an empty ast (#1961) --- slither/solc_parsing/slither_compilation_unit_solc.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index a739c6f97..00ac3a519 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -198,6 +198,13 @@ class SlitherCompilationUnitSolc(CallerContextExpression): # pylint: disable=too-many-branches,too-many-statements,too-many-locals def parse_top_level_from_loaded_json(self, data_loaded: Dict, filename: str) -> None: + if not data_loaded or data_loaded is None: + logger.error( + "crytic-compile returned an empty AST. " + "If you are trying to analyze a contract from etherscan or similar make sure it has source code available." + ) + return + if "nodeType" in data_loaded: self._is_compact_ast = True From 8a5aab62c9a0a4b9dde91eb9d0ffd847b8987bdb Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 20 Jun 2023 11:03:42 -0500 Subject: [PATCH 10/14] reduce false positives on modifying storage array by value detector (#1962) Check the respective parameter's storage location for each argument --------- Co-authored-by: bossjoker1 <1397157763@qq.com> --- .../compiler_bugs/array_by_reference.py | 13 ++++++----- ...rence_0_4_25_array_by_reference_sol__0.txt | 14 +++++++----- ...rence_0_5_16_array_by_reference_sol__0.txt | 14 +++++++----- ...rence_0_6_11_array_by_reference_sol__0.txt | 14 +++++++----- ...erence_0_7_6_array_by_reference_sol__0.txt | 14 +++++++----- .../0.4.25/array_by_reference.sol | 21 ++++++++++++++++++ .../0.4.25/array_by_reference.sol-0.4.25.zip | Bin 4879 -> 6184 bytes .../0.5.16/array_by_reference.sol | 21 ++++++++++++++++++ .../0.5.16/array_by_reference.sol-0.5.16.zip | Bin 4925 -> 6194 bytes .../0.6.11/array_by_reference.sol | 21 ++++++++++++++++++ .../0.6.11/array_by_reference.sol-0.6.11.zip | Bin 4841 -> 6086 bytes .../0.7.6/array_by_reference.sol | 21 ++++++++++++++++++ .../0.7.6/array_by_reference.sol-0.7.6.zip | Bin 4741 -> 5972 bytes 13 files changed, 124 insertions(+), 29 deletions(-) diff --git a/slither/detectors/compiler_bugs/array_by_reference.py b/slither/detectors/compiler_bugs/array_by_reference.py index 04dfe085a..47e2af581 100644 --- a/slither/detectors/compiler_bugs/array_by_reference.py +++ b/slither/detectors/compiler_bugs/array_by_reference.py @@ -133,7 +133,7 @@ As a result, Bob's usage of the contract is incorrect.""" continue # Verify one of these parameters is an array in storage. - for arg in ir.arguments: + for (param, arg) in zip(ir.function.parameters, ir.arguments): # Verify this argument is a variable that is an array type. if not isinstance(arg, (StateVariable, LocalVariable)): continue @@ -141,8 +141,11 @@ As a result, Bob's usage of the contract is incorrect.""" continue # If it is a state variable OR a local variable referencing storage, we add it to the list. - if isinstance(arg, StateVariable) or ( - isinstance(arg, LocalVariable) and arg.location == "storage" + if ( + isinstance(arg, StateVariable) + or (isinstance(arg, LocalVariable) and arg.location == "storage") + ) and ( + isinstance(param.type, ArrayType) and param.location != "storage" ): results.append((node, arg, ir.function)) return results @@ -165,9 +168,9 @@ As a result, Bob's usage of the contract is incorrect.""" calling_node.function, " passes array ", affected_argument, - "by reference to ", + " by reference to ", invoked_function, - "which only takes arrays by value\n", + " which only takes arrays by value\n", ] res = self.generate_result(info) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt index f056bea10..5cb8add39 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt index 4264c809a..6e97d8cc2 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt index e71930b51..39574b5f5 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt index 7c0f9ccd9..74ea36a0c 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt @@ -1,12 +1,14 @@ -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value -D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value +D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39) by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28) which only takes arrays by value -C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value +C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11) by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23) which only takes arrays by value + +E.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#57-61) passes array E.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#54) by reference to E.setByValue(uint256[1],uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#63-66) which only takes arrays by value diff --git a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol index 304af6a48..c2707601a 100644 --- a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol +++ b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol @@ -48,4 +48,25 @@ contract D { } +} + +contract E { + uint[1] public x; // storage + uint[1] public y; // storage + + function f() public { + uint[1] memory temp; + setByValue(temp, x); // can set temp, but cannot set x + setByRef(temp, y); // can set temp and y + } + + function setByValue(uint[1] memory arr, uint[1] memory arr2) internal { + arr[0] = 1; + arr2[0] = 2; + } + + function setByRef(uint[1] memory arr, uint[1] storage arr2) internal { + arr[0] = 2; + arr2[0] = 3; + } } \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol-0.4.25.zip b/tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol-0.4.25.zip index d9daf8f4d7dc27c4f371b018a31cc9736180c278..96bdaa0f85c484feec1e0f47ea8e3409f0cd1dc9 100644 GIT binary patch delta 5938 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 G0001;R*ZlE From cec07db510e6064dea17a349839880d98adc4121 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 20 Jun 2023 11:12:06 -0500 Subject: [PATCH 11/14] feat: add `--unstructured` to slither-read-storage; retrieve custom storage layouts (#1963) Retrieve storage values of constant arguments passed to SLOAD. If a call to keccack is made, the expression is evaluated and treated as a constant. --------- Co-authored-by: webthethird --- slither/printers/guidance/echidna.py | 17 +- .../expressions/expression_parsing.py | 2 + slither/tools/read_storage/__main__.py | 7 + slither/tools/read_storage/read_storage.py | 315 ++++++++++++++++-- slither/tools/read_storage/utils/utils.py | 2 + slither/utils/integer_conversion.py | 4 +- .../visitors/expression/constants_folding.py | 71 ++-- tests/conftest.py | 6 +- .../tools/{check-erc => check_erc}/erc20.sol | 0 .../tools/{check-erc => check_erc}/test_1.txt | 0 .../safeAdd/safeAdd.sol | 0 .../safeAdd/spec.md | 0 .../{check-kspec => check_kspec}/test_1.txt | 0 .../contractV1.sol | 0 .../contractV1_struct.sol | 0 .../contractV2.sol | 0 .../contractV2_bug.sol | 0 .../contractV2_bug2.sol | 0 .../contractV2_struct.sol | 0 .../contractV2_struct_bug.sol | 0 .../contract_initialization.sol | 0 .../contract_v1_var_init.sol | 0 .../contract_v2_constant.sol | 0 .../proxy.sol | 0 .../test_1.txt | 0 .../test_10.txt | 0 .../test_11.txt | 0 .../test_12.txt | 0 .../test_13.txt | 0 .../test_2.txt | 0 .../test_3.txt | 0 .../test_4.txt | 0 .../test_5.txt | 0 .../test_6.txt | 0 .../test_7.txt | 0 .../test_8.txt | 0 .../test_9.txt | 0 tests/tools/read-storage/conftest.py | 56 ++++ ...ge_layout-0.8.10.sol => StorageLayout.sol} | 3 +- .../test_data/TEST_unstructured_storage.json | 56 ++++ .../test_data/UnstructuredStorageLayout.abi | 1 + .../test_data/UnstructuredStorageLayout.bin | 1 + .../test_data/UnstructuredStorageLayout.sol | 141 ++++++++ tests/tools/read-storage/test_read_storage.py | 66 +--- tests/unit/core/test_constant_folding.py | 59 ++-- .../constant_folding_binop.sol | 2 + 46 files changed, 680 insertions(+), 129 deletions(-) rename tests/tools/{check-erc => check_erc}/erc20.sol (100%) rename tests/tools/{check-erc => check_erc}/test_1.txt (100%) rename tests/tools/{check-kspec => check_kspec}/safeAdd/safeAdd.sol (100%) rename tests/tools/{check-kspec => check_kspec}/safeAdd/spec.md (100%) rename tests/tools/{check-kspec => check_kspec}/test_1.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV1.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV1_struct.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV2.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV2_bug.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV2_bug2.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV2_struct.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contractV2_struct_bug.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contract_initialization.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contract_v1_var_init.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/contract_v2_constant.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/proxy.sol (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_1.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_10.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_11.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_12.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_13.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_2.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_3.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_4.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_5.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_6.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_7.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_8.txt (100%) rename tests/tools/{check-upgradeability => check_upgradeability}/test_9.txt (100%) create mode 100644 tests/tools/read-storage/conftest.py rename tests/tools/read-storage/test_data/{storage_layout-0.8.10.sol => StorageLayout.sol} (95%) create mode 100644 tests/tools/read-storage/test_data/TEST_unstructured_storage.json create mode 100644 tests/tools/read-storage/test_data/UnstructuredStorageLayout.abi create mode 100644 tests/tools/read-storage/test_data/UnstructuredStorageLayout.bin create mode 100644 tests/tools/read-storage/test_data/UnstructuredStorageLayout.sol diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index acbf5b015..25e0968cd 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -32,7 +32,7 @@ from slither.slithir.operations import ( from slither.slithir.operations.binary import Binary from slither.slithir.variables import Constant from slither.utils.output import Output -from slither.visitors.expression.constants_folding import ConstantFolding +from slither.visitors.expression.constants_folding import ConstantFolding, NotConstant def _get_name(f: Union[Function, Variable]) -> str: @@ -178,11 +178,16 @@ def _extract_constants_from_irs( # pylint: disable=too-many-branches,too-many-n all_cst_used_in_binary[str(ir.type)].append( ConstantValue(str(r.value), str(r.type)) ) - if isinstance(ir.variable_left, Constant) and isinstance(ir.variable_right, Constant): - if ir.lvalue: - type_ = ir.lvalue.type - cst = ConstantFolding(ir.expression, type_).result() - all_cst_used.append(ConstantValue(str(cst.value), str(type_))) + if isinstance(ir.variable_left, Constant) or isinstance( + ir.variable_right, Constant + ): + if ir.lvalue: + try: + type_ = ir.lvalue.type + cst = ConstantFolding(ir.expression, type_).result() + all_cst_used.append(ConstantValue(str(cst.value), str(type_))) + except NotConstant: + pass if isinstance(ir, TypeConversion): if isinstance(ir.variable, Constant): if isinstance(ir.type, TypeAlias): diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 945a60b8f..4d2cfc00f 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -433,6 +433,8 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression) type_candidate = ElementaryType("uint256") else: type_candidate = ElementaryType("string") + elif type_candidate.startswith("rational_const "): + type_candidate = ElementaryType("uint256") elif type_candidate.startswith("int_const "): type_candidate = ElementaryType("uint256") elif type_candidate.startswith("bool"): diff --git a/slither/tools/read_storage/__main__.py b/slither/tools/read_storage/__main__.py index f6635ab4b..8415ae185 100644 --- a/slither/tools/read_storage/__main__.py +++ b/slither/tools/read_storage/__main__.py @@ -104,6 +104,12 @@ def parse_args() -> argparse.Namespace: default="latest", ) + parser.add_argument( + "--unstructured", + action="store_true", + help="Include unstructured storage slots", + ) + cryticparser.init(parser) return parser.parse_args() @@ -133,6 +139,7 @@ def main() -> None: rpc_info = RpcInfo(args.rpc_url, block) srs = SlitherReadStorage(contracts, args.max_depth, rpc_info) + srs.unstructured = bool(args.unstructured) # Remove target prefix e.g. rinkeby:0x0 -> 0x0. address = target[target.find(":") + 1 :] # Default to implementation address unless a storage address is given. diff --git a/slither/tools/read_storage/read_storage.py b/slither/tools/read_storage/read_storage.py index 72331f66a..8c0cf515d 100644 --- a/slither/tools/read_storage/read_storage.py +++ b/slither/tools/read_storage/read_storage.py @@ -15,9 +15,21 @@ from web3.middleware import geth_poa_middleware from slither.core.declarations import Contract, Structure from slither.core.solidity_types import ArrayType, ElementaryType, MappingType, UserDefinedType from slither.core.solidity_types.type import Type +from slither.core.cfg.node import NodeType from slither.core.variables.state_variable import StateVariable from slither.core.variables.structure_variable import StructureVariable +from slither.core.expressions import ( + AssignmentOperation, + Literal, + Identifier, + BinaryOperation, + UnaryOperation, + TupleExpression, + TypeConversion, + CallExpression, +) from slither.utils.myprettytable import MyPrettyTable +from slither.visitors.expression.constants_folding import ConstantFolding, NotConstant from .utils import coerce_type, get_offset_value, get_storage_data @@ -72,7 +84,7 @@ class RpcInfo: return self._block -# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes,too-many-public-methods class SlitherReadStorage: def __init__(self, contracts: List[Contract], max_depth: int, rpc_info: RpcInfo = None) -> None: self._checksum_address: Optional[ChecksumAddress] = None @@ -81,9 +93,11 @@ class SlitherReadStorage: self._max_depth: int = max_depth self._slot_info: Dict[str, SlotInfo] = {} self._target_variables: List[Tuple[Contract, StateVariable]] = [] + self._constant_storage_slots: List[Tuple[Contract, StateVariable]] = [] self.rpc_info: Optional[RpcInfo] = rpc_info self.storage_address: Optional[str] = None self.table: Optional[MyPrettyTable] = None + self.unstructured: bool = False @property def contracts(self) -> List[Contract]: @@ -114,6 +128,11 @@ class SlitherReadStorage: """Storage variables (not constant or immutable) and their associated contract.""" return self._target_variables + @property + def constant_slots(self) -> List[Tuple[Contract, StateVariable]]: + """Constant bytes32 variables and their associated contract.""" + return self._constant_storage_slots + @property def slot_info(self) -> Dict[str, SlotInfo]: """Contains the location, type, size, offset, and value of contract slots.""" @@ -133,9 +152,48 @@ class SlitherReadStorage: elif isinstance(type_, ArrayType): elems = self._all_array_slots(var, contract, type_, info.slot) tmp[var.name].elems = elems - + if self.unstructured: + tmp.update(self.get_unstructured_layout()) self._slot_info = tmp + def get_unstructured_layout(self) -> Dict[str, SlotInfo]: + tmp: Dict[str, SlotInfo] = {} + for _, var in self.constant_slots: + var_name = var.name + try: + exp = var.expression + if isinstance( + exp, + ( + BinaryOperation, + UnaryOperation, + Identifier, + TupleExpression, + TypeConversion, + CallExpression, + ), + ): + exp = ConstantFolding(exp, "bytes32").result() + if isinstance(exp, Literal): + slot = coerce_type("int", exp.value) + else: + continue + offset = 0 + type_string, size = self.find_constant_slot_storage_type(var) + if type_string: + tmp[var.name] = SlotInfo( + name=var_name, type_string=type_string, slot=slot, size=size, offset=offset + ) + self.log += ( + f"\nSlot Name: {var_name}\nType: bytes32" + f"\nStorage Type: {type_string}\nSlot: {str(exp)}\n" + ) + logger.info(self.log) + self.log = "" + except NotConstant: + continue + return tmp + # TODO: remove this pylint exception (montyly) # pylint: disable=too-many-locals def get_storage_slot( @@ -144,7 +202,8 @@ class SlitherReadStorage: contract: Contract, **kwargs: Any, ) -> Union[SlotInfo, None]: - """Finds the storage slot of a variable in a given contract. + """ + Finds the storage slot of a variable in a given contract. Args: target_variable (`StateVariable`): The variable to retrieve the slot for. contracts (`Contract`): The contract that contains the given state variable. @@ -230,6 +289,78 @@ class SlitherReadStorage: if slot_info: self._slot_info[f"{contract.name}.{var.name}"] = slot_info + def find_constant_slot_storage_type( + self, var: StateVariable + ) -> Tuple[Optional[str], Optional[int]]: + """ + Given a constant bytes32 StateVariable, tries to determine which variable type is stored there, using the + heuristic that if a function reads from the slot and returns a value, it probably stores that type of value. + Also uses the StorageSlot library as a heuristic when a function has no return but uses the library's getters. + Args: + var (StateVariable): The constant bytes32 storage slot. + + Returns: + type (str): The type of value stored in the slot. + size (int): The type's size in bits. + """ + assert var.is_constant and var.type == ElementaryType("bytes32") + storage_type = None + size = None + funcs = [] + for c in self.contracts: + c_funcs = c.get_functions_reading_from_variable(var) + c_funcs.extend( + f + for f in c.functions + if any(str(v.expression) == str(var.expression) for v in f.variables) + ) + c_funcs = list(set(c_funcs)) + funcs.extend(c_funcs) + fallback = [f for f in var.contract.functions if f.is_fallback] + funcs += fallback + for func in funcs: + rets = func.return_type if func.return_type is not None else [] + for ret in rets: + size, _ = ret.storage_size + if size <= 32: + return str(ret), size * 8 + for node in func.all_nodes(): + exp = node.expression + # Look for use of the common OpenZeppelin StorageSlot library + if f"getAddressSlot({var.name})" in str(exp): + return "address", 160 + if f"getBooleanSlot({var.name})" in str(exp): + return "bool", 1 + if f"getBytes32Slot({var.name})" in str(exp): + return "bytes32", 256 + if f"getUint256Slot({var.name})" in str(exp): + return "uint256", 256 + # Look for variable assignment in assembly loaded from a hardcoded slot + if ( + isinstance(exp, AssignmentOperation) + and isinstance(exp.expression_left, Identifier) + and isinstance(exp.expression_right, CallExpression) + and "sload" in str(exp.expression_right.called) + and str(exp.expression_right.arguments[0]) == str(var.expression) + ): + if func.is_fallback: + return "address", 160 + storage_type = exp.expression_left.value.type.name + size, _ = exp.expression_left.value.type.storage_size + return storage_type, size * 8 + # Look for variable storage in assembly stored to a hardcoded slot + if ( + isinstance(exp, CallExpression) + and "sstore" in str(exp.called) + and isinstance(exp.arguments[0], Identifier) + and isinstance(exp.arguments[1], Identifier) + and str(exp.arguments[0].value.expression) == str(var.expression) + ): + storage_type = exp.arguments[1].value.type.name + size, _ = exp.arguments[1].value.type.storage_size + return storage_type, size * 8 + return storage_type, size + def walk_slot_info(self, func: Callable) -> None: stack = list(self.slot_info.values()) while stack: @@ -242,7 +373,8 @@ class SlitherReadStorage: func(slot_info) def get_slot_values(self, slot_info: SlotInfo) -> None: - """Fetches the slot value of `SlotInfo` object + """ + Fetches the slot value of `SlotInfo` object :param slot_info: """ assert self.rpc_info is not None @@ -257,25 +389,162 @@ class SlitherReadStorage: ) logger.info(f"\nValue: {slot_info.value}\n") - def get_all_storage_variables(self, func: Callable = None) -> None: - """Fetches all storage variables from a list of contracts. + def get_all_storage_variables(self, func: Callable = lambda x: x) -> None: + """ + Fetches all storage variables from a list of contracts. kwargs: func (Callable, optional): A criteria to filter functions e.g. name. """ for contract in self.contracts: - self._target_variables.extend( - filter( - func, - [ - (contract, var) - for var in contract.state_variables_ordered - if not var.is_constant and not var.is_immutable - ], - ) + for var in contract.state_variables_ordered: + if func(var): + if not var.is_constant and not var.is_immutable: + self._target_variables.append((contract, var)) + elif ( + self.unstructured + and var.is_constant + and var.type == ElementaryType("bytes32") + ): + self._constant_storage_slots.append((contract, var)) + if self.unstructured: + hardcoded_slot = self.find_hardcoded_slot_in_fallback(contract) + if hardcoded_slot is not None: + self._constant_storage_slots.append((contract, hardcoded_slot)) + + def find_hardcoded_slot_in_fallback(self, contract: Contract) -> Optional[StateVariable]: + """ + Searches the contract's fallback function for a sload from a literal storage slot, i.e., + `let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7)`. + + Args: + contract: a Contract object, which should have a fallback function. + + Returns: + A newly created StateVariable representing the Literal bytes32 slot, if one is found, otherwise None. + """ + fallback = None + for func in contract.functions_entry_points: + if func.is_fallback: + fallback = func + break + if fallback is None: + return None + queue = [fallback.entry_point] + visited = [] + while len(queue) > 0: + node = queue.pop(0) + visited.append(node) + queue.extend(son for son in node.sons if son not in visited) + if node.type == NodeType.ASSEMBLY and isinstance(node.inline_asm, str): + return SlitherReadStorage.find_hardcoded_slot_in_asm_str(node.inline_asm, contract) + if node.type == NodeType.EXPRESSION: + sv = self.find_hardcoded_slot_in_exp(node.expression, contract) + if sv is not None: + return sv + return None + + @staticmethod + def find_hardcoded_slot_in_asm_str( + inline_asm: str, contract: Contract + ) -> Optional[StateVariable]: + """ + Searches a block of assembly code (given as a string) for a sload from a literal storage slot. + Does not work if the argument passed to sload does not start with "0x", i.e., `sload(add(1,1))` + or `and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)`. + + Args: + inline_asm: a string containing all the code in an assembly node (node.inline_asm for solc < 0.6.0). + + Returns: + A newly created StateVariable representing the Literal bytes32 slot, if one is found, otherwise None. + """ + asm_split = inline_asm.split("\n") + for asm in asm_split: + if "sload(" in asm: # Only handle literals + arg = asm.split("sload(")[1].split(")")[0] + if arg.startswith("0x"): + exp = Literal(arg, ElementaryType("bytes32")) + sv = StateVariable() + sv.name = "fallback_sload_hardcoded" + sv.expression = exp + sv.is_constant = True + sv.type = exp.type + sv.set_contract(contract) + return sv + return None + + def find_hardcoded_slot_in_exp( + self, exp: "Expression", contract: Contract + ) -> Optional[StateVariable]: + """ + Parses an expression to see if it contains a sload from a literal storage slot, + unrolling nested expressions if necessary to determine which slot it loads from. + Args: + exp: an Expression object to search. + contract: the Contract containing exp. + + Returns: + A newly created StateVariable representing the Literal bytes32 slot, if one is found, otherwise None. + """ + if isinstance(exp, AssignmentOperation): + exp = exp.expression_right + while isinstance(exp, BinaryOperation): + exp = next( + (e for e in exp.expressions if isinstance(e, (CallExpression, BinaryOperation))), + exp.expression_left, ) + while isinstance(exp, CallExpression) and len(exp.arguments) > 0: + called = exp.called + exp = exp.arguments[0] + if "sload" in str(called): + break + if isinstance( + exp, + ( + BinaryOperation, + UnaryOperation, + Identifier, + TupleExpression, + TypeConversion, + CallExpression, + ), + ): + try: + exp = ConstantFolding(exp, "bytes32").result() + except NotConstant: + return None + if ( + isinstance(exp, Literal) + and isinstance(exp.type, ElementaryType) + and exp.type.name in ["bytes32", "uint256"] + ): + sv = StateVariable() + sv.name = "fallback_sload_hardcoded" + value = exp.value + str_value = str(value) + if str_value.isdecimal(): + value = int(value) + if isinstance(value, (int, bytes)): + if isinstance(value, bytes): + str_value = "0x" + value.hex() + value = int(str_value, 16) + exp = Literal(str_value, ElementaryType("bytes32")) + state_var_slots = [ + self.get_variable_info(contract, var)[0] + for contract, var in self.target_variables + ] + if value in state_var_slots: + return None + sv.expression = exp + sv.is_constant = True + sv.type = ElementaryType("bytes32") + sv.set_contract(contract) + return sv + return None def convert_slot_info_to_rows(self, slot_info: SlotInfo) -> None: - """Convert and append slot info to table. Create table if it + """ + Convert and append slot info to table. Create table if it does not yet exist :param slot_info: """ @@ -293,7 +562,8 @@ class SlitherReadStorage: def _find_struct_var_slot( elems: List[StructureVariable], slot_as_bytes: bytes, struct_var: str ) -> Tuple[str, str, bytes, int, int]: - """Finds the slot of a structure variable. + """ + Finds the slot of a structure variable. Args: elems (List[StructureVariable]): Ordered list of structure variables. slot_as_bytes (bytes): The slot of the struct to begin searching at. @@ -335,7 +605,8 @@ class SlitherReadStorage: deep_key: int = None, struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: - """Finds the slot of array's index. + """ + Finds the slot of array's index. Args: target_variable (`StateVariable`): The array that contains the target variable. slot (bytes): The starting slot of the array. @@ -438,7 +709,8 @@ class SlitherReadStorage: deep_key: Union[int, str] = None, struct_var: str = None, ) -> Tuple[str, str, bytes, int, int]: - """Finds the data slot of a target variable within a mapping. + """ + Finds the data slot of a target variable within a mapping. target_variable (`StateVariable`): The mapping that contains the target variable. slot (bytes): The starting slot of the mapping. key (Union[int, str]): The key the variable is stored at. @@ -509,7 +781,7 @@ class SlitherReadStorage: ) info += info_tmp - # TODO: suppory mapping with dynamic arrays + # TODO: support mapping with dynamic arrays # mapping(elem => elem) elif isinstance(target_variable_type.type_to, ElementaryType): @@ -615,7 +887,8 @@ class SlitherReadStorage: return elems def _get_array_length(self, type_: Type, slot: int) -> int: - """Gets the length of dynamic and fixed arrays. + """ + Gets the length of dynamic and fixed arrays. Args: type_ (`AbstractType`): The array type. slot (int): Slot a dynamic array's length is stored at. diff --git a/slither/tools/read_storage/utils/utils.py b/slither/tools/read_storage/utils/utils.py index 4a04a5b6d..20e7c1372 100644 --- a/slither/tools/read_storage/utils/utils.py +++ b/slither/tools/read_storage/utils/utils.py @@ -37,6 +37,8 @@ def coerce_type( (Union[int, bool, str, ChecksumAddress, hex]): The type representation of the value. """ if "int" in solidity_type: + if str(value).startswith("0x"): + return to_int(hexstr=value) return to_int(value) if "bool" in solidity_type: return bool(to_int(value)) diff --git a/slither/utils/integer_conversion.py b/slither/utils/integer_conversion.py index e4dff333c..99064f564 100644 --- a/slither/utils/integer_conversion.py +++ b/slither/utils/integer_conversion.py @@ -4,7 +4,9 @@ from typing import Union from slither.exceptions import SlitherError -def convert_string_to_fraction(val: Union[str, int]) -> Fraction: +def convert_string_to_fraction(val: Union[str, bytes, int]) -> Fraction: + if isinstance(val, bytes): + return int.from_bytes(val, byteorder="big") if isinstance(val, int): return Fraction(val) if val.startswith(("0x", "0X")): diff --git a/slither/visitors/expression/constants_folding.py b/slither/visitors/expression/constants_folding.py index 12eb6be9d..b1fa570c6 100644 --- a/slither/visitors/expression/constants_folding.py +++ b/slither/visitors/expression/constants_folding.py @@ -1,5 +1,6 @@ from fractions import Fraction from typing import Union +from Crypto.Hash import keccak from slither.core import expressions from slither.core.expressions import ( @@ -11,9 +12,9 @@ from slither.core.expressions import ( UnaryOperation, TupleExpression, TypeConversion, + CallExpression, ) from slither.core.variables import Variable - from slither.utils.integer_conversion import convert_string_to_fraction, convert_string_to_int from slither.visitors.expression.expression import ExpressionVisitor from slither.core.solidity_types.elementary_type import ElementaryType @@ -65,23 +66,31 @@ class ConstantFolding(ExpressionVisitor): value = value & (2**256 - 1) return Literal(value, self._type) + # pylint: disable=import-outside-toplevel def _post_identifier(self, expression: Identifier) -> None: - if not isinstance(expression.value, Variable): - return - if not expression.value.is_constant: + from slither.core.declarations.solidity_variables import SolidityFunction + + if isinstance(expression.value, Variable): + if expression.value.is_constant: + expr = expression.value.expression + # assumption that we won't have infinite loop + # Everything outside of literal + if isinstance( + expr, + (BinaryOperation, UnaryOperation, Identifier, TupleExpression, TypeConversion), + ): + cf = ConstantFolding(expr, self._type) + expr = cf.result() + assert isinstance(expr, Literal) + set_val(expression, convert_string_to_int(expr.converted_value)) + else: + raise NotConstant + elif isinstance(expression.value, SolidityFunction): + set_val(expression, expression.value) + else: raise NotConstant - expr = expression.value.expression - # assumption that we won't have infinite loop - # Everything outside of literal - if isinstance( - expr, (BinaryOperation, UnaryOperation, Identifier, TupleExpression, TypeConversion) - ): - cf = ConstantFolding(expr, self._type) - expr = cf.result() - assert isinstance(expr, Literal) - set_val(expression, convert_string_to_int(expr.converted_value)) - # pylint: disable=too-many-branches + # pylint: disable=too-many-branches,too-many-statements def _post_binary_operation(self, expression: BinaryOperation) -> None: expression_left = expression.expression_left expression_right = expression.expression_right @@ -95,7 +104,6 @@ class ConstantFolding(ExpressionVisitor): (Literal, BinaryOperation, UnaryOperation, Identifier, TupleExpression, TypeConversion), ): raise NotConstant - left = get_val(expression_left) right = get_val(expression_right) @@ -183,7 +191,9 @@ class ConstantFolding(ExpressionVisitor): raise NotConstant def _post_literal(self, expression: Literal) -> None: - if expression.converted_value in ["true", "false"]: + if str(expression.type) == "bool": + set_val(expression, expression.converted_value) + elif str(expression.type) == "string": set_val(expression, expression.converted_value) else: try: @@ -195,7 +205,14 @@ class ConstantFolding(ExpressionVisitor): raise NotConstant def _post_call_expression(self, expression: expressions.CallExpression) -> None: - raise NotConstant + called = get_val(expression.called) + args = [get_val(arg) for arg in expression.arguments] + if called.name == "keccak256(bytes)": + digest = keccak.new(digest_bits=256) + digest.update(str(args[0]).encode("utf-8")) + set_val(expression, digest.digest()) + else: + raise NotConstant def _post_conditional_expression(self, expression: expressions.ConditionalExpression) -> None: raise NotConstant @@ -247,10 +264,24 @@ class ConstantFolding(ExpressionVisitor): expr = expression.expression if not isinstance( expr, - (Literal, BinaryOperation, UnaryOperation, Identifier, TupleExpression, TypeConversion), + ( + Literal, + BinaryOperation, + UnaryOperation, + Identifier, + TupleExpression, + TypeConversion, + CallExpression, + ), ): raise NotConstant cf = ConstantFolding(expr, self._type) expr = cf.result() assert isinstance(expr, Literal) - set_val(expression, convert_string_to_fraction(expr.converted_value)) + if str(expression.type).startswith("uint") and isinstance(expr.value, bytes): + value = int.from_bytes(expr.value, "big") + elif str(expression.type).startswith("byte") and isinstance(expr.value, int): + value = int.to_bytes(expr.value, 32, "big") + else: + value = convert_string_to_fraction(expr.converted_value) + set_val(expression, value) diff --git a/tests/conftest.py b/tests/conftest.py index 5ea228fd3..63fccfa12 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,12 @@ # pylint: disable=redefined-outer-name import os -from pathlib import Path -import tempfile import shutil +import tempfile +from pathlib import Path from contextlib import contextmanager -import pytest from filelock import FileLock from solc_select import solc_select +import pytest from slither import Slither diff --git a/tests/tools/check-erc/erc20.sol b/tests/tools/check_erc/erc20.sol similarity index 100% rename from tests/tools/check-erc/erc20.sol rename to tests/tools/check_erc/erc20.sol diff --git a/tests/tools/check-erc/test_1.txt b/tests/tools/check_erc/test_1.txt similarity index 100% rename from tests/tools/check-erc/test_1.txt rename to tests/tools/check_erc/test_1.txt diff --git a/tests/tools/check-kspec/safeAdd/safeAdd.sol b/tests/tools/check_kspec/safeAdd/safeAdd.sol similarity index 100% rename from tests/tools/check-kspec/safeAdd/safeAdd.sol rename to tests/tools/check_kspec/safeAdd/safeAdd.sol diff --git a/tests/tools/check-kspec/safeAdd/spec.md b/tests/tools/check_kspec/safeAdd/spec.md similarity index 100% rename from tests/tools/check-kspec/safeAdd/spec.md rename to tests/tools/check_kspec/safeAdd/spec.md diff --git a/tests/tools/check-kspec/test_1.txt b/tests/tools/check_kspec/test_1.txt similarity index 100% rename from tests/tools/check-kspec/test_1.txt rename to tests/tools/check_kspec/test_1.txt diff --git a/tests/tools/check-upgradeability/contractV1.sol b/tests/tools/check_upgradeability/contractV1.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV1.sol rename to tests/tools/check_upgradeability/contractV1.sol diff --git a/tests/tools/check-upgradeability/contractV1_struct.sol b/tests/tools/check_upgradeability/contractV1_struct.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV1_struct.sol rename to tests/tools/check_upgradeability/contractV1_struct.sol diff --git a/tests/tools/check-upgradeability/contractV2.sol b/tests/tools/check_upgradeability/contractV2.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV2.sol rename to tests/tools/check_upgradeability/contractV2.sol diff --git a/tests/tools/check-upgradeability/contractV2_bug.sol b/tests/tools/check_upgradeability/contractV2_bug.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV2_bug.sol rename to tests/tools/check_upgradeability/contractV2_bug.sol diff --git a/tests/tools/check-upgradeability/contractV2_bug2.sol b/tests/tools/check_upgradeability/contractV2_bug2.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV2_bug2.sol rename to tests/tools/check_upgradeability/contractV2_bug2.sol diff --git a/tests/tools/check-upgradeability/contractV2_struct.sol b/tests/tools/check_upgradeability/contractV2_struct.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV2_struct.sol rename to tests/tools/check_upgradeability/contractV2_struct.sol diff --git a/tests/tools/check-upgradeability/contractV2_struct_bug.sol b/tests/tools/check_upgradeability/contractV2_struct_bug.sol similarity index 100% rename from tests/tools/check-upgradeability/contractV2_struct_bug.sol rename to tests/tools/check_upgradeability/contractV2_struct_bug.sol diff --git a/tests/tools/check-upgradeability/contract_initialization.sol b/tests/tools/check_upgradeability/contract_initialization.sol similarity index 100% rename from tests/tools/check-upgradeability/contract_initialization.sol rename to tests/tools/check_upgradeability/contract_initialization.sol diff --git a/tests/tools/check-upgradeability/contract_v1_var_init.sol b/tests/tools/check_upgradeability/contract_v1_var_init.sol similarity index 100% rename from tests/tools/check-upgradeability/contract_v1_var_init.sol rename to tests/tools/check_upgradeability/contract_v1_var_init.sol diff --git a/tests/tools/check-upgradeability/contract_v2_constant.sol b/tests/tools/check_upgradeability/contract_v2_constant.sol similarity index 100% rename from tests/tools/check-upgradeability/contract_v2_constant.sol rename to tests/tools/check_upgradeability/contract_v2_constant.sol diff --git a/tests/tools/check-upgradeability/proxy.sol b/tests/tools/check_upgradeability/proxy.sol similarity index 100% rename from tests/tools/check-upgradeability/proxy.sol rename to tests/tools/check_upgradeability/proxy.sol diff --git a/tests/tools/check-upgradeability/test_1.txt b/tests/tools/check_upgradeability/test_1.txt similarity index 100% rename from tests/tools/check-upgradeability/test_1.txt rename to tests/tools/check_upgradeability/test_1.txt diff --git a/tests/tools/check-upgradeability/test_10.txt b/tests/tools/check_upgradeability/test_10.txt similarity index 100% rename from tests/tools/check-upgradeability/test_10.txt rename to tests/tools/check_upgradeability/test_10.txt diff --git a/tests/tools/check-upgradeability/test_11.txt b/tests/tools/check_upgradeability/test_11.txt similarity index 100% rename from tests/tools/check-upgradeability/test_11.txt rename to tests/tools/check_upgradeability/test_11.txt diff --git a/tests/tools/check-upgradeability/test_12.txt b/tests/tools/check_upgradeability/test_12.txt similarity index 100% rename from tests/tools/check-upgradeability/test_12.txt rename to tests/tools/check_upgradeability/test_12.txt diff --git a/tests/tools/check-upgradeability/test_13.txt b/tests/tools/check_upgradeability/test_13.txt similarity index 100% rename from tests/tools/check-upgradeability/test_13.txt rename to tests/tools/check_upgradeability/test_13.txt diff --git a/tests/tools/check-upgradeability/test_2.txt b/tests/tools/check_upgradeability/test_2.txt similarity index 100% rename from tests/tools/check-upgradeability/test_2.txt rename to tests/tools/check_upgradeability/test_2.txt diff --git a/tests/tools/check-upgradeability/test_3.txt b/tests/tools/check_upgradeability/test_3.txt similarity index 100% rename from tests/tools/check-upgradeability/test_3.txt rename to tests/tools/check_upgradeability/test_3.txt diff --git a/tests/tools/check-upgradeability/test_4.txt b/tests/tools/check_upgradeability/test_4.txt similarity index 100% rename from tests/tools/check-upgradeability/test_4.txt rename to tests/tools/check_upgradeability/test_4.txt diff --git a/tests/tools/check-upgradeability/test_5.txt b/tests/tools/check_upgradeability/test_5.txt similarity index 100% rename from tests/tools/check-upgradeability/test_5.txt rename to tests/tools/check_upgradeability/test_5.txt diff --git a/tests/tools/check-upgradeability/test_6.txt b/tests/tools/check_upgradeability/test_6.txt similarity index 100% rename from tests/tools/check-upgradeability/test_6.txt rename to tests/tools/check_upgradeability/test_6.txt diff --git a/tests/tools/check-upgradeability/test_7.txt b/tests/tools/check_upgradeability/test_7.txt similarity index 100% rename from tests/tools/check-upgradeability/test_7.txt rename to tests/tools/check_upgradeability/test_7.txt diff --git a/tests/tools/check-upgradeability/test_8.txt b/tests/tools/check_upgradeability/test_8.txt similarity index 100% rename from tests/tools/check-upgradeability/test_8.txt rename to tests/tools/check_upgradeability/test_8.txt diff --git a/tests/tools/check-upgradeability/test_9.txt b/tests/tools/check_upgradeability/test_9.txt similarity index 100% rename from tests/tools/check-upgradeability/test_9.txt rename to tests/tools/check_upgradeability/test_9.txt diff --git a/tests/tools/read-storage/conftest.py b/tests/tools/read-storage/conftest.py new file mode 100644 index 000000000..3e2cfb400 --- /dev/null +++ b/tests/tools/read-storage/conftest.py @@ -0,0 +1,56 @@ +""" +Testing utilities for the read-storage tool +""" + +import shutil +import subprocess +from time import sleep +from typing import Generator +from dataclasses import dataclass +from web3 import Web3 +import pytest + + +@dataclass +class GanacheInstance: + def __init__(self, provider: str, eth_address: str, eth_privkey: str): + self.provider = provider + self.eth_address = eth_address + self.eth_privkey = eth_privkey + + +@pytest.fixture(scope="module", name="ganache") +def fixture_ganache() -> Generator[GanacheInstance, None, None]: + """Fixture that runs ganache""" + if not shutil.which("ganache"): + raise Exception( + "ganache was not found in PATH, you can install it with `npm install -g ganache`" + ) + + # Address #1 when ganache is run with `--wallet.seed test`, it starts with 1000 ETH + eth_address = "0xae17D2dD99e07CA3bF2571CCAcEAA9e2Aefc2Dc6" + eth_privkey = "0xe48ba530a63326818e116be262fd39ae6dcddd89da4b1f578be8afd4e8894b8d" + eth = int(1e18 * 1e6) + port = 8545 + with subprocess.Popen( + f"""ganache + --port {port} + --chain.networkId 1 + --chain.chainId 1 + --account {eth_privkey},{eth} + """.replace( + "\n", " " + ), + shell=True, + ) as p: + + sleep(3) + yield GanacheInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) + p.kill() + p.wait() + + +@pytest.fixture(scope="module", name="web3") +def fixture_web3(ganache: GanacheInstance): + w3 = Web3(Web3.HTTPProvider(ganache.provider, request_kwargs={"timeout": 30})) + return w3 diff --git a/tests/tools/read-storage/test_data/storage_layout-0.8.10.sol b/tests/tools/read-storage/test_data/StorageLayout.sol similarity index 95% rename from tests/tools/read-storage/test_data/storage_layout-0.8.10.sol rename to tests/tools/read-storage/test_data/StorageLayout.sol index 28d1428eb..0940b6769 100644 --- a/tests/tools/read-storage/test_data/storage_layout-0.8.10.sol +++ b/tests/tools/read-storage/test_data/StorageLayout.sol @@ -1,5 +1,6 @@ +pragma solidity 0.8.10; // overwrite abi and bin: -// solc tests/storage-layout/storage_layout-0.8.10.sol --abi --bin -o tests/storage-layout --overwrite +// solc StorageLayout.sol --abi --bin --overwrite contract StorageLayout { uint248 packedUint = 1; bool packedBool = true; diff --git a/tests/tools/read-storage/test_data/TEST_unstructured_storage.json b/tests/tools/read-storage/test_data/TEST_unstructured_storage.json new file mode 100644 index 000000000..6dbdbddca --- /dev/null +++ b/tests/tools/read-storage/test_data/TEST_unstructured_storage.json @@ -0,0 +1,56 @@ +{ + "masterCopy": { + "name": "masterCopy", + "type_string": "address", + "slot": 0, + "size": 160, + "offset": 0, + "value": "0x0000000000000000000000000000000000000000", + "elems": {} + }, + "ADMIN_SLOT": { + "name": "ADMIN_SLOT", + "type_string": "address", + "slot": 7616251639890160809447714111544359812065171195189364993079081710756264753419, + "size": 160, + "offset": 0, + "value": "0xae17D2dD99e07CA3bF2571CCAcEAA9e2Aefc2Dc6", + "elems": {} + }, + "IMPLEMENTATION_SLOT": { + "name": "IMPLEMENTATION_SLOT", + "type_string": "address", + "slot": 24440054405305269366569402256811496959409073762505157381672968839269610695612, + "size": 160, + "offset": 0, + "value": "0x54006763154c764da4AF42a8c3cfc25Ea29765D5", + "elems": {} + }, + "ROLLBACK_SLOT": { + "name": "ROLLBACK_SLOT", + "type_string": "bool", + "slot": 33048860383849004559742813297059419343339852917517107368639918720169455489347, + "size": 1, + "offset": 0, + "value": true, + "elems": {} + }, + "BEACON_SLOT": { + "name": "BEACON_SLOT", + "type_string": "address", + "slot": 74152234768234802001998023604048924213078445070507226371336425913862612794704, + "size": 160, + "offset": 0, + "value": "0x54006763154c764da4AF42a8c3cfc25Ea29765D5", + "elems": {} + }, + "fallback_sload_hardcoded": { + "name": "fallback_sload_hardcoded", + "type_string": "address", + "slot": 89532207833283453166981358064394884954800891875771469636219037672473505217783, + "size": 160, + "offset": 0, + "value": "0x54006763154c764da4AF42a8c3cfc25Ea29765D5", + "elems": {} + } +} \ No newline at end of file diff --git a/tests/tools/read-storage/test_data/UnstructuredStorageLayout.abi b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.abi new file mode 100644 index 000000000..9b579f254 --- /dev/null +++ b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.abi @@ -0,0 +1 @@ +[{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"store","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/tests/tools/read-storage/test_data/UnstructuredStorageLayout.bin b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.bin new file mode 100644 index 000000000..9f20de74c --- /dev/null +++ b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5061030b806100206000396000f3fe608060405234801561001057600080fd5b506004361061002f5760003560e01c8063975057e71461009757610030565b5b600180035473ffffffffffffffffffffffffffffffffffffffff600054167fc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7543660008037600080366000845af43d806000803e816000811461009257816000f35b816000fd5b61009f6100a1565b005b60006100ab6101a8565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146100e657600080fd5b60007f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b9050600033905080825560007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b905060007354006763154c764da4af42a8c3cfc25ea29765d59050808255807fc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf75561018460016101d6565b6101a17354006763154c764da4af42a8c3cfc25ea29765d5610220565b5050505050565b6000807f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b9050805491505090565b806102037f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd914360001b61025e565b60000160006101000a81548160ff02191690831515021790555050565b600060017fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5160001c61025291906102a1565b60001b90508181555050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102ac82610268565b91506102b783610268565b9250828210156102ca576102c9610272565b5b82820390509291505056fea2646970667358221220f079473c1b94744ac2818f521ccef06187a433d996633e61e51a86dfb60cc6ff64736f6c634300080a0033 \ No newline at end of file diff --git a/tests/tools/read-storage/test_data/UnstructuredStorageLayout.sol b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.sol new file mode 100644 index 000000000..81b14a119 --- /dev/null +++ b/tests/tools/read-storage/test_data/UnstructuredStorageLayout.sol @@ -0,0 +1,141 @@ +pragma solidity 0.8.10; +// overwrite abi and bin: +// solc UnstructuredStorageLayout.sol --abi --bin --overwrite + +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } +} + +contract UnstructuredStorageLayout { + + bytes32 constant ADMIN_SLOT = keccak256("org.zeppelinos.proxy.admin"); + // This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. + bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + bytes32 constant BEACON_SLOT = bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1); + + address internal masterCopy; + + function _admin() internal view returns (address admin) { + bytes32 slot = ADMIN_SLOT; + assembly { + admin := sload(slot) + } + } + + function _implementation() internal view returns (address) { + address _impl; + bytes32 slot = IMPLEMENTATION_SLOT; + assembly { + _impl := sload(slot) + } + return _impl; + } + + function _set_rollback(bool _rollback) internal { + StorageSlot.getBooleanSlot(ROLLBACK_SLOT).value = _rollback; + } + + function _set_beacon(address _beacon) internal { + bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1); + assembly { + sstore(slot, _beacon) + } + } + + function store() external { + address admin = _admin(); + require(admin == address(0)); + + bytes32 admin_slot = ADMIN_SLOT; + address sender = msg.sender; + assembly { + sstore(admin_slot, sender) + } + + bytes32 impl_slot = IMPLEMENTATION_SLOT; + address _impl = address(0x0054006763154c764da4af42a8c3cfc25ea29765d5); + assembly { + sstore(impl_slot, _impl) + sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, _impl) + } + + _set_rollback(true); + _set_beacon(address(0x0054006763154c764da4af42a8c3cfc25ea29765d5)); + } + + // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" + fallback() external { + assembly { // solium-disable-line + let nonsense := sload(sub(1,1)) + let _masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff) + let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) + calldatacopy(0x0, 0x0, calldatasize()) + let success := delegatecall(gas(), contractLogic, 0x0, calldatasize(), 0, 0) + let retSz := returndatasize() + returndatacopy(0, 0, retSz) + switch success + case 0 { + revert(0, retSz) + } + default { + return(0, retSz) + } + } + } +} diff --git a/tests/tools/read-storage/test_read_storage.py b/tests/tools/read-storage/test_read_storage.py index ea04a91fe..b056ad056 100644 --- a/tests/tools/read-storage/test_read_storage.py +++ b/tests/tools/read-storage/test_read_storage.py @@ -1,14 +1,9 @@ -import json import re -import shutil -import subprocess -from time import sleep +import json from pathlib import Path -from typing import Generator import pytest from deepdiff import DeepDiff -from web3 import Web3 from web3.contract import Contract from slither import Slither @@ -16,50 +11,6 @@ from slither.tools.read_storage import SlitherReadStorage, RpcInfo TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" -# pylint: disable=too-few-public-methods -class GanacheInstance: - def __init__(self, provider: str, eth_address: str, eth_privkey: str): - self.provider = provider - self.eth_address = eth_address - self.eth_privkey = eth_privkey - - -@pytest.fixture(scope="module", name="web3") -def fixture_web3(ganache: GanacheInstance): - w3 = Web3(Web3.HTTPProvider(ganache.provider, request_kwargs={"timeout": 30})) - return w3 - - -@pytest.fixture(scope="module", name="ganache") -def fixture_ganache() -> Generator[GanacheInstance, None, None]: - """Fixture that runs ganache""" - if not shutil.which("ganache"): - raise Exception( - "ganache was not found in PATH, you can install it with `npm install -g ganache`" - ) - - # Address #1 when ganache is run with `--wallet.seed test`, it starts with 1000 ETH - eth_address = "0xae17D2dD99e07CA3bF2571CCAcEAA9e2Aefc2Dc6" - eth_privkey = "0xe48ba530a63326818e116be262fd39ae6dcddd89da4b1f578be8afd4e8894b8d" - eth = int(1e18 * 1e6) - port = 8545 - with subprocess.Popen( - f"""ganache - --port {port} - --chain.networkId 1 - --chain.chainId 1 - --account {eth_privkey},{eth} - """.replace( - "\n", " " - ), - shell=True, - ) as p: - - sleep(3) - yield GanacheInstance(f"http://127.0.0.1:{port}", eth_address, eth_privkey) - p.kill() - p.wait() - def get_source_file(file_path) -> str: with open(file_path, "r", encoding="utf8") as f: @@ -89,24 +40,29 @@ def deploy_contract(w3, ganache, contract_bin, contract_abi) -> Contract: # pylint: disable=too-many-locals +@pytest.mark.parametrize( + "test_contract, storage_file", + [("StorageLayout", "storage_layout"), ("UnstructuredStorageLayout", "unstructured_storage")], +) @pytest.mark.usefixtures("web3", "ganache") -def test_read_storage(web3, ganache, solc_binary_path) -> None: +def test_read_storage(test_contract, storage_file, web3, ganache, solc_binary_path) -> None: solc_path = solc_binary_path(version="0.8.10") assert web3.is_connected() - bin_path = Path(TEST_DATA_DIR, "StorageLayout.bin").as_posix() - abi_path = Path(TEST_DATA_DIR, "StorageLayout.abi").as_posix() + bin_path = Path(TEST_DATA_DIR, f"{test_contract}.bin").as_posix() + abi_path = Path(TEST_DATA_DIR, f"{test_contract}.abi").as_posix() bytecode = get_source_file(bin_path) abi = get_source_file(abi_path) contract = deploy_contract(web3, ganache, bytecode, abi) contract.functions.store().transact({"from": ganache.eth_address}) address = contract.address - sl = Slither(Path(TEST_DATA_DIR, "storage_layout-0.8.10.sol").as_posix(), solc=solc_path) + sl = Slither(Path(TEST_DATA_DIR, f"{test_contract}.sol").as_posix(), solc=solc_path) contracts = sl.contracts rpc_info: RpcInfo = RpcInfo(ganache.provider) srs = SlitherReadStorage(contracts, 100, rpc_info) + srs.unstructured = True srs.storage_address = address srs.get_all_storage_variables() srs.get_storage_layout() @@ -116,7 +72,7 @@ def test_read_storage(web3, ganache, solc_binary_path) -> None: slot_infos_json = srs.to_json() json.dump(slot_infos_json, file, indent=4) - expected_file = Path(TEST_DATA_DIR, "TEST_storage_layout.json").as_posix() + expected_file = Path(TEST_DATA_DIR, f"TEST_{storage_file}.json").as_posix() with open(expected_file, "r", encoding="utf8") as f: expected = json.load(f) diff --git a/tests/unit/core/test_constant_folding.py b/tests/unit/core/test_constant_folding.py index 489b4e0ec..6c0cc8295 100644 --- a/tests/unit/core/test_constant_folding.py +++ b/tests/unit/core/test_constant_folding.py @@ -21,39 +21,40 @@ def test_constant_folding_rational(solc_binary_path): variable_a = contract.get_state_variable_from_name("a") assert str(variable_a.type) == "uint256" - assert str(ConstantFolding(variable_a.expression, "uint256").result()) == "10" + assert ConstantFolding(variable_a.expression, "uint256").result().value == 10 variable_b = contract.get_state_variable_from_name("b") assert str(variable_b.type) == "int128" - assert str(ConstantFolding(variable_b.expression, "int128").result()) == "2" + assert ConstantFolding(variable_b.expression, "int128").result().value == 2 variable_c = contract.get_state_variable_from_name("c") assert str(variable_c.type) == "int64" - assert str(ConstantFolding(variable_c.expression, "int64").result()) == "3" + assert ConstantFolding(variable_c.expression, "int64").result().value == 3 variable_d = contract.get_state_variable_from_name("d") assert str(variable_d.type) == "int256" - assert str(ConstantFolding(variable_d.expression, "int256").result()) == "1500" + assert ConstantFolding(variable_d.expression, "int256").result().value == 1500 variable_e = contract.get_state_variable_from_name("e") assert str(variable_e.type) == "uint256" assert ( - str(ConstantFolding(variable_e.expression, "uint256").result()) - == "57896044618658097711785492504343953926634992332820282019728792003956564819968" + ConstantFolding(variable_e.expression, "uint256").result().value + == 57896044618658097711785492504343953926634992332820282019728792003956564819968 ) variable_f = contract.get_state_variable_from_name("f") assert str(variable_f.type) == "uint256" assert ( - str(ConstantFolding(variable_f.expression, "uint256").result()) - == "115792089237316195423570985008687907853269984665640564039457584007913129639935" + ConstantFolding(variable_f.expression, "uint256").result().value + == 115792089237316195423570985008687907853269984665640564039457584007913129639935 ) variable_g = contract.get_state_variable_from_name("g") assert str(variable_g.type) == "int64" - assert str(ConstantFolding(variable_g.expression, "int64").result()) == "-7" + assert ConstantFolding(variable_g.expression, "int64").result().value == -7 +# pylint: disable=too-many-locals def test_constant_folding_binary_expressions(solc_binary_path): sl = Slither( Path(CONSTANT_FOLDING_TEST_ROOT, "constant_folding_binop.sol").as_posix(), @@ -63,51 +64,65 @@ def test_constant_folding_binary_expressions(solc_binary_path): variable_a = contract.get_state_variable_from_name("a") assert str(variable_a.type) == "uint256" - assert str(ConstantFolding(variable_a.expression, "uint256").result()) == "0" + assert ConstantFolding(variable_a.expression, "uint256").result().value == 0 variable_b = contract.get_state_variable_from_name("b") assert str(variable_b.type) == "uint256" - assert str(ConstantFolding(variable_b.expression, "uint256").result()) == "3" + assert ConstantFolding(variable_b.expression, "uint256").result().value == 3 variable_c = contract.get_state_variable_from_name("c") assert str(variable_c.type) == "uint256" - assert str(ConstantFolding(variable_c.expression, "uint256").result()) == "3" + assert ConstantFolding(variable_c.expression, "uint256").result().value == 3 variable_d = contract.get_state_variable_from_name("d") assert str(variable_d.type) == "bool" - assert str(ConstantFolding(variable_d.expression, "bool").result()) == "False" + assert ConstantFolding(variable_d.expression, "bool").result().value is False variable_e = contract.get_state_variable_from_name("e") assert str(variable_e.type) == "bool" - assert str(ConstantFolding(variable_e.expression, "bool").result()) == "False" + assert ConstantFolding(variable_e.expression, "bool").result().value is False variable_f = contract.get_state_variable_from_name("f") assert str(variable_f.type) == "bool" - assert str(ConstantFolding(variable_f.expression, "bool").result()) == "True" + assert ConstantFolding(variable_f.expression, "bool").result().value is True variable_g = contract.get_state_variable_from_name("g") assert str(variable_g.type) == "bool" - assert str(ConstantFolding(variable_g.expression, "bool").result()) == "False" + assert ConstantFolding(variable_g.expression, "bool").result().value is False variable_h = contract.get_state_variable_from_name("h") assert str(variable_h.type) == "bool" - assert str(ConstantFolding(variable_h.expression, "bool").result()) == "False" + assert ConstantFolding(variable_h.expression, "bool").result().value is False variable_i = contract.get_state_variable_from_name("i") assert str(variable_i.type) == "bool" - assert str(ConstantFolding(variable_i.expression, "bool").result()) == "True" + assert ConstantFolding(variable_i.expression, "bool").result().value is True variable_j = contract.get_state_variable_from_name("j") assert str(variable_j.type) == "bool" - assert str(ConstantFolding(variable_j.expression, "bool").result()) == "False" + assert ConstantFolding(variable_j.expression, "bool").result().value is False variable_k = contract.get_state_variable_from_name("k") assert str(variable_k.type) == "bool" - assert str(ConstantFolding(variable_k.expression, "bool").result()) == "True" + assert ConstantFolding(variable_k.expression, "bool").result().value is True variable_l = contract.get_state_variable_from_name("l") assert str(variable_l.type) == "uint256" assert ( - str(ConstantFolding(variable_l.expression, "uint256").result()) - == "115792089237316195423570985008687907853269984665640564039457584007913129639935" + ConstantFolding(variable_l.expression, "uint256").result().value + == 115792089237316195423570985008687907853269984665640564039457584007913129639935 ) + + IMPLEMENTATION_SLOT = contract.get_state_variable_from_name("IMPLEMENTATION_SLOT") + assert str(IMPLEMENTATION_SLOT.type) == "bytes32" + assert ( + int.from_bytes( + ConstantFolding(IMPLEMENTATION_SLOT.expression, "bytes32").result().value, + byteorder="big", + ) + == 24440054405305269366569402256811496959409073762505157381672968839269610695612 + ) + + variable_m = contract.get_state_variable_from_name("m") + assert str(variable_m.type) == "bytes2" + assert ConstantFolding(variable_m.expression, "bytes2").result().value == "ab" diff --git a/tests/unit/core/test_data/constant_folding/constant_folding_binop.sol b/tests/unit/core/test_data/constant_folding/constant_folding_binop.sol index 923418ce7..3935585b8 100644 --- a/tests/unit/core/test_data/constant_folding/constant_folding_binop.sol +++ b/tests/unit/core/test_data/constant_folding/constant_folding_binop.sol @@ -11,4 +11,6 @@ contract BinOp { bool j = true && false; bool k = true || false; uint l = uint(1) - uint(2); + bytes32 IMPLEMENTATION_SLOT = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); + bytes2 m = "ab"; } \ No newline at end of file From eacbf5c224fb85a5f70263d32937700468ee28ff Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 20 Jun 2023 11:35:47 -0500 Subject: [PATCH 12/14] Create dependabot.yml (#1972) --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..dd3015cbc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "dev" + schedule: + interval: "weekly" From d56f66338051eb07b2f280a1dd2f355ab6f95394 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 20 Jun 2023 11:36:16 -0500 Subject: [PATCH 13/14] create release action with sigstore (#1957) --- .github/workflows/publish.yml | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..529752f80 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +name: Publish to PyPI + +on: + release: + types: [published] + +jobs: + build-release: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Build distributions + run: | + python -m pip install --upgrade pip + python -m pip install build + python -m build + - name: Upload distributions + uses: actions/upload-artifact@v3 + with: + name: slither-dists + path: dist/ + + publish: + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write # For trusted publishing + codesigning. + contents: write # For attaching signing artifacts to the release. + needs: + - build-release + steps: + - name: fetch dists + uses: actions/download-artifact@v3 + with: + name: slither-dists + path: dist/ + + - name: publish + uses: pypa/gh-action-pypi-publish@v1.8.6 + + - name: sign + uses: sigstore/gh-action-sigstore-python@v1.2.3 + with: + inputs: ./dist/*.tar.gz ./dist/*.whl + release-signing-artifacts: true + bundle-only: true From 7f7ff770d7c558916fee1d530f6d5a0ec7e36e38 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Tue, 20 Jun 2023 21:56:44 +0300 Subject: [PATCH 14/14] docs: update recommendation for msg.value-inside-a-loop (#1971) --- slither/detectors/statements/msg_value_in_loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slither/detectors/statements/msg_value_in_loop.py b/slither/detectors/statements/msg_value_in_loop.py index 55bd9bfc2..83c5658ca 100644 --- a/slither/detectors/statements/msg_value_in_loop.py +++ b/slither/detectors/statements/msg_value_in_loop.py @@ -79,7 +79,7 @@ contract MsgValueInLoop{ # endregion wiki_exploit_scenario WIKI_RECOMMENDATION = """ -Track msg.value through a local variable and decrease its amount on every iteration/usage. +Provide an explicit array of amounts alongside the receivers array, and check that the sum of all amounts matches `msg.value`. """ def _detect(self) -> List[Output]: