{"content":{"title":"以太坊数据流分析技术","body":"# 以太坊数据流分析技术\r\n\r\n## 一、前置知识\r\n\r\n### 1. python第三方解析智能合约库：crytic-compile\r\n\r\ncrytic-compile是一个python智能合约审计框架下常用的第三方库，应用于slither、oyente等工具，其主要逻辑为通过solc生成合约的abi、bin、AST等信息，并进行一次封装，生成完整的json文件，同时支持多文件同时解析。\r\n\r\n如一个简单的合约：\r\n\r\n```solidity\r\npragma solidity ^0.5.0;\r\n\r\ncontract SimpleStorage {\r\n    uint public storedData;\r\n\r\n    function set(uint x) public {\r\n        storedData = x;\r\n    }\r\n\r\n    function get() public view returns (uint) {\r\n        return storedData;\r\n    }\r\n}\r\n```\r\n\r\n调用crytic-compile生成结果json：\r\n\r\n```python\r\nfrom crytic_compile import CryticCompile\r\nfrom crytic_compile.platform.standard import generate_standard_export\r\n\r\nif __name__ == '__main__':\r\n    compilation = CryticCompile(\"tests/test.sol\")\r\n    print(generate_standard_export(compilation))\r\n```\r\n\r\n结果如下：\r\n\r\n```\r\n{\r\n    'compilation_units': {\r\n        'test.sol': {\r\n            'compiler': {\r\n                'compiler': 'solc',\r\n                'version': '0.5.6',\r\n                'optimized': False\r\n            },\r\n            'asts': {\r\n                'D:\\\\PycharmProject\\\\ast_test\\\\test.sol': {\r\n                    'absolutePath': 'test.sol',\r\n                    'exportedSymbols': {\r\n                        'SimpleStorage': [22]\r\n                    },\r\n                    'id': 23,\r\n                    'nodeType': 'SourceUnit',\r\n                    'nodes': [{\r\n                            'id': 1,\r\n                            'literals': ['solidity', '^', '0.5', '.0'],\r\n                            'nodeType': 'PragmaDirective',\r\n                            'src': '0:23:0'\r\n                        },...],\r\n                    'src': '0:238:0'\r\n                }\r\n            },\r\n            'contracts': {\r\n                'test.sol': {\r\n                    'SimpleStorage': {\r\n                        'abi': [{\r\n                                'constant': True,\r\n                                'inputs': [],\r\n                                'name': 'storedData',\r\n                                'outputs': [{\r\n                                        'name': '',\r\n                                        'type': 'uint256'\r\n                                    }\r\n                                ],\r\n                                'payable': False,\r\n                                'stateMutability': 'view',\r\n                                'type': 'function'\r\n                            }...\r\n                        ],\r\n                        'bin': '6080......0029',\r\n                        'filenames': {\r\n                            'absolute': 'D:\\\\PycharmProject\\\\ast_test\\\\test.sol',\r\n                            'used': 'test.sol',\r\n                            'short': 'test.sol',\r\n                            'relative': 'test.sol'\r\n                        },\r\n                        'libraries': {},\r\n                        'is_dependency': False,\r\n                        'userdoc': {\r\n                            'methods': {},\r\n                            'notice': None\r\n                        },\r\n                        'devdoc': {\r\n                            'methods': {},\r\n                            'author': None,\r\n                            'details': None,\r\n                            'title': None\r\n                        }\r\n                    }\r\n                }\r\n            },\r\n            'filenames': [{\r\n                    'absolute': 'D:\\\\PycharmProject\\\\ast_test\\\\test.sol',\r\n                    'used': 'test.sol',\r\n                    'short': 'test.sol',\r\n                    'relative': 'test.sol'\r\n                }\r\n            ]\r\n        }\r\n    },\r\n    'package': None,\r\n    'working_dir': 'D:\\\\PycharmProject\\\\ast_test',\r\n    'type': 1,\r\n    'unit_tests': [],\r\n    'crytic_version': '0.0.1'\r\n}\r\n\r\n```\r\n\r\n由于篇幅限制，删除了部分信息，概括其中主体信息如下：\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/QM9EWWCn63a9e158468da.png)\r\n### 2. 智能合约AST中常见的NodeType\r\n\r\n智能合约的 AST（抽象语法树）是一种表示智能合约代码结构的数据结构。它通常由一些节点组成，每个节点都代表代码中的一个语法单元。\r\n\r\n一些常见的 AST 节点类型包括：\r\n\r\n- Program: 表示整个源代码文件。\r\n- BlockStatement: 表示代码块，也就是由花括号括起来的代码。\r\n- ExpressionStatement: 表示表达式，即在语句中进行计算或操作的代码。\r\n- Variable Declaration: 表示变量声明，即在代码中定义新变量的语句。\r\n- Function Declaration: 表示函数声明，即在代码中定义新函数的语句。\r\n- If Statement: 表示 if 语句，即根据某个条件来执行不同的代码块的语句。\r\n- For Loop: 表示 for 循环，即在代码中循环执行某段代码的语句。\r\n- Return Statement: 表示 return 语句，即从函数中返回结果的语句。\r\n- UserDefinedValueTypeDefinition：指在智能合约AST中定义的自定义值类型。\r\n\r\n例如，在某个智能合约中，我们可以定义一个自定义值类型Account，表示账户信息：\r\n\r\n```\r\nstruct Account {\r\n  uint256 balance;\r\n  string name;\r\n}\r\n```\r\n\r\n在AST中，这个自定义值类型的定义可能是这样的：\r\n\r\n```\r\n{\r\n  \"type\": \"UserDefinedValueTypeDefinition\",\r\n  \"name\": \"Account\",\r\n  \"fields\": [\r\n    {\r\n      \"name\": \"balance\",\r\n      \"type\": \"uint256\"\r\n    },\r\n    {\r\n      \"name\": \"name\",\r\n      \"type\": \"string\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n由于crytic-compile直接提供语法树，我们在数据流分析中可以基于其直接生成控制流，主要关注`ForStatement`、`IfStatement`等非顺序执行节点即可。\r\n\r\n### 3. 支配树算法\r\n\r\n在构造数据流时，我们需要知道每个变量的赋值受那些节点支配，即确定SOURCE与SINK之间是否有完整的支配关系，因此在生成控制流后应首先计算函数的支配树。\r\n\r\n#### 支配树\r\n\r\n支配树（dominator tree）用来表示支配信息，在树中，入口节点，并且每个节点只支配它在树中的后代节点。一种支配树的示例如下：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/4SVjYbrQ63a9e16686aea.png)\r\n\r\n#### 直接支配节点\r\n\r\n在支配树中，对于节点 **n** 来说，从根节点到节点 **n** 所在路径上的节点（不包括 **n** ）都严格支配节点 **n** ，例如上图中从根节点 **1 → 2 → 3** ，其中节点 **1** 和节点 **2** 都严格支配节点 **3** 。该路径上离节点 **n** 最近的节点叫做节点 **n** 的直接支配节点（immediate node），用 **IDom(n)** 表示，例如上图中 **IDom(6) = 2**。\r\n\r\n#### 支配边界\r\n\r\n在构造 SSA 过程中，还有另外一个概念很重要，就是支配边界（dominance frontier）。支配边界直观理解就是当前节点所能支配的边界（并不包括该边界），另一种等价描述“**Y** 是 **X** 的支配边界，当且仅当 **X** 支配 **Y** 的一个前驱节点（***CFG***）同时 X 并不严格支配 **Y**”。\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/W9B2kGa563a9e17379369.png)\r\n\r\n上面的图示直观的表示了支配边界的概念。下面的图给出了一个示例，给出了图中的支配结点以及支配边界关系。\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/JNoueokf63a9e17b709d1.png)\r\n\r\n上图中节点 **5** 支配边界是 **4、5、12、13**，也就是节点 **5** “刚好能力所不能及的地方”。\r\n\r\n支配边界的意义在于计算SSA时确定 **Φ函数** 插入位置，具体见**SSA**小节\r\n\r\n#### Dominator-tree Algorithm\r\n\r\n##### 计算支配树\r\n\r\n我们主要采用Dominator-tree Algorithm算法来计算支配树，该算法迭代过程如下：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/7fBQBJp363a9e18a0427a.png)\r\n\r\n其每次遍历控制流时，计算本节点与所有父节点的支配节点的交集，如最终所有节点的支配节点不发生变化，则完成支配树计算。算法伪代码如下：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/EIRe5DkI63a9e19522851.png)\r\n\r\n笔者用python简单实现一种计算方法用以验证：\r\n\r\n```python\r\nfrom typing import List\r\nimport json\r\n\r\nnodeDict = {}\r\nnode_json = \"\"\"\r\n{\r\n    \"0\":[\"1\", \"\"],\r\n    \"1\":[\"2,5\", \"0\"],\r\n    \"2\":[\"3\", \"1\"],\r\n    \"5\":[\"6,8\",\"1\"],\r\n    \"6\":[\"7\", \"5\"],\r\n    \"8\":[\"7\",\"5\"],\r\n    \"7\":[\"3\",\"6,8\"],\r\n    \"3\":[\"4\", \"2,7\"],\r\n    \"4\":[\"\", \"3\"]\r\n}\r\n\"\"\"\r\n\r\nclass Node:\r\n    def __init__(self, name, data):\r\n        self.name: str = name\r\n        self.data = data\r\n        self.fathers: List[Node] = []\r\n        self.children: List[Node] = []\r\n        self.dominators = set()\r\n\r\n    def set_children_fathers(self):\r\n        self.set_children(self.data[0].split(\",\"))\r\n        self.set_fathers(self.data[1].split(\",\"))\r\n\r\n\r\n    def set_children(self, name_list):\r\n        for n in name_list:\r\n            if n:\r\n                self.children.append(nodeDict[n])\r\n\r\n    def set_fathers(self, name_list):\r\n        for n in name_list:\r\n            if n:\r\n                self.fathers.append(nodeDict[n])\r\n\r\n    def print_dominators(self):\r\n        print(f\"{self.name}: {[d.name for d in self.dominators]}\")\r\n\r\n    def __str__(self):\r\n        return f\"{self.name}: {[c.name for c in self.children]}, {[f.name for f in self.fathers]}\"\r\n\r\ndef intersect(node: Node):\r\n    if node.fathers:\r\n        d = node.fathers[0].dominators\r\n        for f in node.fathers[1:]:\r\n            d = d.intersection(f.dominators)\r\n        return d\r\n    else:\r\n        return set()\r\n\r\n\r\ndef compute_dominators(node_list: List[Node]):\r\n    Change = True\r\n    while Change:\r\n        Change = False\r\n        for n in node_list:\r\n            new_dominators = intersect(n).union({n})\r\n            if new_dominators != n.dominators:\r\n                n.dominators = new_dominators\r\n                Change = True\r\n\r\n\r\ndef test():\r\n    node_list_str = json.loads(node_json)\r\n    node_list = []\r\n    for k,v in node_list_str.items():\r\n        n = Node(k, v)\r\n        nodeDict[k] = n\r\n        node_list.append(n)\r\n    for n in node_list:\r\n        n.set_children_fathers()\r\n    for n in node_list:\r\n        print(n)\r\n    print()\r\n    compute_dominators(node_list)\r\n    for n in node_list:\r\n        n.print_dominators()\r\n\r\n```\r\n\r\n模拟控制流图如下：\r\n\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/iNyz8BdJ63a9e19fb839a.png)\r\n\r\n生成结果：\r\n\r\n```\r\n0: ['0']\r\n1: ['0', '1']\r\n2: ['2', '0', '1']\r\n5: ['0', '1', '5']\r\n6: ['6', '0', '1', '5']\r\n8: ['8', '0', '1', '5']\r\n7: ['7', '0', '1', '5']\r\n3: ['3', '0', '1']\r\n4: ['3', '4', '0', '1']\r\n```\r\n\r\n符合支配树计算结果\r\n\r\n##### 计算直接支配节点\r\n\r\n将节点除自身外的的支配节点作为候选节点，遍历候选节点，每次将该节点的支配节点加入总节点列表中，最后找到在候选节点但不再总节点列表中即为直接支配节点，用python实现：\r\n\r\n```python\r\ndef compute_immediate_dominator(node_list: List[Node]):\r\n    for node in node_list:\r\n        idom_candidates = set(node.dominators)\r\n        idom_candidates.remove(node)\r\n        # 仅有一项那必为直接支配节点\r\n        if len(idom_candidates) == 1:\r\n            idom = idom_candidates.pop()\r\n            node.idom = idom\r\n            continue\r\n        all_dominators = set()\r\n        for d in idom_candidates:\r\n            if d in all_dominators:\r\n                continue\r\n            all_dominators |= d.dominators - {d}\r\n        idom_candidates = all_dominators.symmetric_difference(idom_candidates)\r\n        assert len(idom_candidates) <= 1\r\n        if idom_candidates:\r\n            idom = idom_candidates.pop()\r\n            node.idom = idom\r\n```\r\n\r\n最终结果：\r\n\r\n```\r\n0: \r\n1: 0\r\n2: 1\r\n5: 1\r\n6: 5\r\n8: 5\r\n7: 5\r\n3: 1\r\n4: 3\r\n```\r\n\r\n与直接支配者计算结果一致\r\n\r\n##### 计算支配边界\r\n\r\n遍历每个节点的父节点，如该父节点不是子节点的直接支配节点，则子节点作为父节点支配边界的一部分，pyhton实现：\r\n\r\n```python\r\ndef compute_dominator_frontier(node_list: List[Node]):\r\n    for node in node_list:\r\n        if len(node.fathers) >= 2:\r\n            for father in node.fathers:\r\n                if father != node.idom:\r\n                    father.dominator_frontier = father.dominator_frontier.union({node})\r\n```\r\n\r\n最终结果：\r\n\r\n```\r\n0: []\r\n1: []\r\n2: ['3']\r\n5: []\r\n6: ['7']\r\n8: ['7']\r\n7: ['3']\r\n3: []\r\n4: []\r\n```\r\n\r\n与支配边界计算结果一致\r\n\r\n### 4. 数据流分析\r\n\r\n在进行污点追踪时，我们希望能找到某个SINK的直接数据流来源，譬如：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/rKCb7hOs63a9e1af226bf.png)\r\n\r\n只有当transfer中的参数用户直接可控，这条污点链才是有意义的，因此我们在静态分析中最终目的是进行数据流分析，即分析数据是如何在程序执行路径上流动的。\r\n\r\n数据流分析的通用方法是在控制流图上定义一组方程并迭代求解，一般分为正向传播和逆向传播。正向传播就是沿着控制流路径，状态向前传递，前驱块的值传到后继块；逆向传播就是逆着控制流路径，后继块的值反向传给前驱块。\r\n\r\n### 5. 静态单赋值SSA\r\n\r\nSSA 是 static single assignment 的缩写，也就是静态单赋值形式。顾名思义，就是每个变量只有唯一的赋值。SSA范式可以用来简化各种基于数据流的分析。SSA范式之前，数据流分析的某个变量的定义是一个集合，SSA范式转换之后这些变量都变成了唯一定义；而且由于每个变量只有一次定义，相当于说每个变量都可以转换成常量（循环内定义的变量除外，每次循环迭代，变量都会被重新定义）。\r\n\r\n以下图为例，左图是原始代码，里面有分支， y 变量在不同路径中有不同赋值，最后打印 y 的值。右图是等价的 SSA 形式，y  变量在两个分支中被改写为 y1, y2，在控制流交汇处插入 Ф 函数，合并了来自不同边的 y1, y2 值, 赋给 y3, 最后打印的是 y3。\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/66rACP1p63a9e1c91880a.png)\r\n\r\n总结 SSA 形式的两个特征就是：\r\n\r\n1. 旧变量按照活动范围（从变量的一次定义到使用）分割，被重新命名为添加数字编号后缀的新变量，每个变量只定义一次。\r\n2. 控制流交汇处有 Ф 函数将来自不同路径的值合并。 Ф 函数表示一个 parallel 操作，根据运行的路径选择一个赋值。如果有多个 Ф 函数，它们是并发执行的。 \r\n\r\n这里引入两个名词 use-def chain 和 def-use chain。use-def chain 是一个数据结构，包含一个 def  变量，以及它的全部 use 的集合。相对的，def-use chain 包含一个 use 变量，以及它的全部 def  的集合。以图2左图为例，虚线就是 x 每处定义的 def-use chain. 传统代码因为变量不止一次定义，所以每个定义的 def-use  chain 非常复杂。再看右图，SSA 形式下没有同名变量，每个变量只定义一次，所以同名的 use 都是属于它的 def-use chain.  而且因为每个变量 use 前都只有一次 def, 所以 use-def chain 是一对一的。可见，SSA 形式下的 def-use  chain 与 use-def chain 都得到了简化。\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/RnQvLXFQ63a9e1cfee2fa.png)\r\n\r\nSSA 形式的优点不仅在于简化 def-use chain 与 use-def  chain，它提供了一种稀疏表示的数据结构，极大方便了数据流分析。如图3 所示，左边是传统的基于方程组的数据流分析，右边是基于 SSA  形式的数据流分析。前文讲过传统数据流分析是在基本块上沿控制流路径或逆向迭代传播，SSA 形式的 def-use chain 与 use-def  chain 直接给出了更多信息，数据流值传播不局限于控制流路径。可见 SSA 形式可以简化数据流分析。\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/dwJRTJ7f63a9e1d8bd044.png)\r\n\r\n\r\n## 二、Slither\r\n\r\nSlither是一个由Python 3编写的智能合约静态审计框架。提供如下功能：\r\n\r\n* 自动化漏洞检测。提供超30多项的漏洞检查模型\r\n* 自动优化检测。Slither可以检测编译器遗漏的代码优化项并给出优化建议。\r\n* 代码理解。Slither能够绘制合约的继承拓扑图，合约方法调用关系图等，帮助开发者理解代码。\r\n* 辅助代码审查。用户可以通过API与Slither进行交互\r\n\r\n工作流程如下：\r\n\r\n* 抽象语法树（AST）的生成：使用 crytic-compile第三方库生成直接生成智能合约的AST作为输入\r\n* 控制流的生成：经过遍历语法树节点，生成每个函数的控制流\r\n* 数据流的生成：通过将控制流转换为IR，并分析其中表达式构造静态单赋值，生成所有参数的def-use链\r\n* 漏洞检测：根据污点追踪等技术，采用不同模型判断漏洞是否存在并提供检测报告。\r\n\r\n作为一个完整的静态分析模型，其代码架构有助于我们理解智能合约中的数据流分析流程，笔者总结了简化版流程图：https://www.processon.com/v/63a9a358ebd78059fa588a61，文字分析如下，顺序基于工作流程。\r\n\r\n### 1. 抽象语法树的生成\r\n\r\n本部分直接通过第三方包 crytic-compile 即可完成，在入口函数`Slither()`中：\r\n\r\n```python\r\n87        try:\r\n88            # 如果是文件名，先进行编译\r\n89            if isinstance(target, CryticCompile):\r\n90                crytic_compile = target\r\n91            else:\r\n92                crytic_compile = CryticCompile(target, **kwargs)\r\n93            self._crytic_compile = crytic_compile\r\n```\r\n\r\n判断提供的参数是否为CryticCompile，若不是，则先进行一次封装，封装结果见`前置知识1`\r\n\r\n### 2. 控制流的生成\r\n\r\n首先介绍该过程用到的部分封装类，主要在`slither/core`与`slither/solc_parsing`文件夹下，前者为节点类，后者为解析器类，解析器其实就是节点的一个封装，首先保存该节点的所有信息，再提供某些方法供计算与解析节点。\r\n\r\n#### 解析器类\r\n\r\n##### SlitherCompilationUnit\r\n\r\n为编译单元解析器，负责维护每个编译单元（compilation_unit）中解析的内容，保存语法树中的顶层信息与编译单元中的合约信息\r\n\r\n```python\r\n    def __init__(self, core: \"SlitherCore\", crytic_compilation_unit: CompilationUnit):\r\n        super().__init__()\r\n\r\n        self._core = core\r\n        self._crytic_compile_compilation_unit = crytic_compilation_unit\r\n\r\n        # Top level object\r\n        self.contracts: List[Contract] = []\r\n        self._structures_top_level: List[StructureTopLevel] = []\r\n        self._enums_top_level: List[EnumTopLevel] = []\r\n        self._variables_top_level: List[TopLevelVariable] = []\r\n        self._functions_top_level: List[FunctionTopLevel] = []\r\n        self._pragma_directives: List[Pragma] = []\r\n        self._import_directives: List[Import] = []\r\n        self._custom_errors: List[CustomError] = []\r\n\r\n        self._all_functions: Set[Function] = set()\r\n        self._all_modifiers: Set[Modifier] = set()\r\n\r\n        # Memoize\r\n        self._all_state_variables: Optional[Set[StateVariable]] = None\r\n\r\n        self._storage_layouts: Dict[str, Dict[str, Tuple[int, int]]] = {}\r\n\r\n        self._contract_with_missing_inheritance = set()\r\n\r\n        self._source_units: Dict[int, str] = {}\r\n\r\n        self.counter_slithir_tuple = 0\r\n        self.counter_slithir_temporary = 0\r\n        self.counter_slithir_reference = 0\r\n\r\n        self.scopes: Dict[Filename, FileScope] = {}\r\n```\r\n\r\n##### ContractSolc\r\n\r\n合约解析器，维护对应合约的完整解析内容与其中的所有函数、修饰器、结构体解释器，\r\n\r\n```python\r\n    def __init__(self, slither_parser: \"SlitherCompilationUnitSolc\", contract: Contract, data):\r\n        # assert slitherSolc.solc_version.startswith('0.4')\r\n\r\n        self._contract = contract\r\n        self._slither_parser = slither_parser\r\n        self._data = data\r\n\r\n        self._functionsNotParsed: List[Dict] = []\r\n        self._modifiersNotParsed: List[Dict] = []\r\n        self._functions_no_params: List[FunctionSolc] = []\r\n        self._modifiers_no_params: List[ModifierSolc] = []\r\n        self._eventsNotParsed: List[Dict] = []\r\n        self._variablesNotParsed: List[Dict] = []\r\n        self._enumsNotParsed: List[Dict] = []\r\n        self._structuresNotParsed: List[Dict] = []\r\n        self._usingForNotParsed: List[Dict] = []\r\n        self._customErrorParsed: List[Dict] = []\r\n\r\n        self._functions_parser: List[FunctionSolc] = []\r\n        self._modifiers_parser: List[ModifierSolc] = []\r\n        self._structures_parser: List[StructureContractSolc] = []\r\n        self._custom_errors_parser: List[CustomErrorSolc] = []\r\n\r\n        self._is_analyzed: bool = False\r\n\r\n        # use to remap inheritance id\r\n        self._remapping: Dict[str, str] = {}\r\n\r\n        self.baseContracts: List[str] = []\r\n        self.baseConstructorContractsCalled: List[str] = []\r\n        self._linearized_base_contracts: List[int]\r\n\r\n        self._variables_parser: List[StateVariableSolc] = []\r\n\r\n        # Export info\r\n        if self.is_compact_ast:\r\n            self._contract.name = self._data[\"name\"]\r\n        else:\r\n            self._contract.name = self._data[\"attributes\"][self.get_key()]\r\n\r\n        self._contract.id = self._data[\"id\"]\r\n\r\n        # 解析合约\r\n        self._parse_contract_info()\r\n        self._parse_contract_items()\r\n```\r\n\r\n##### FunctionSolc\r\n\r\n函数解析器，维护对应的函数信息与其中的参数解释器\r\n\r\n```python\r\n    def __init__(\r\n        self,\r\n        function: Function,\r\n        function_data: Dict,\r\n        contract_parser: Optional[\"ContractSolc\"],\r\n        slither_parser: \"SlitherCompilationUnitSolc\",\r\n    ):\r\n        self._slither_parser: \"SlitherCompilationUnitSolc\" = slither_parser\r\n        self._contract_parser = contract_parser\r\n        self._function = function\r\n\r\n        # Only present if compact AST\r\n        if self.is_compact_ast:\r\n            self._function.name = function_data[\"name\"]\r\n            if \"id\" in function_data:\r\n                self._function.id = function_data[\"id\"]\r\n        else:\r\n            self._function.name = function_data[\"attributes\"][self.get_key()]\r\n        self._functionNotParsed = function_data\r\n        self._params_was_analyzed = False\r\n        self._content_was_analyzed = False\r\n\r\n        self._counter_scope_local_variables = 0\r\n        self._variables_renamed: Dict[\r\n            int, Union[LocalVariableSolc, LocalVariableInitFromTupleSolc]\r\n        ] = {}\r\n\r\n        self._analyze_type()\r\n\r\n        self._node_to_nodesolc: Dict[Node, NodeSolc] = {}\r\n        self._node_to_yulobject: Dict[Node, YulBlock] = {}\r\n\r\n        self._local_variables_parser: List[\r\n            Union[LocalVariableSolc, LocalVariableInitFromTupleSolc]\r\n        ] = []\r\n```\r\n\r\n##### NodeSolc\r\n\r\n封装节点的解析信息\r\n\r\n```\r\nclass NodeSolc:\r\n    def __init__(self, node: Node):\r\n        self._unparsed_expression: Optional[Dict] = None\r\n        self._node = node\r\n```\r\n\r\n#### 节点类\r\n\r\n##### FileScope\r\n\r\n对应文件的作用范围，保存AST对应的顶层节点\r\n\r\n```python\r\n    def __init__(self, filename: Filename):\r\n        self.filename = filename\r\n        self.accessible_scopes: List[FileScope] = []\r\n\r\n        self.contracts: Dict[str, Contract] = {}\r\n        # Custom error are a list instead of a dict\r\n        # Because we parse the function signature later on\r\n        # So we simplify the logic and have the scope fields all populated\r\n        self.custom_errors: Set[CustomErrorTopLevel] = set()\r\n        self.enums: Dict[str, EnumTopLevel] = {}\r\n        # Functions is a list instead of a dict\r\n        # Because we parse the function signature later on\r\n        # So we simplify the logic and have the scope fields all populated\r\n        self.functions: Set[FunctionTopLevel] = set()\r\n        self.imports: Set[Import] = set()\r\n        self.pragmas: Set[Pragma] = set()\r\n        self.structures: Dict[str, StructureTopLevel] = {}\r\n        self.variables: Dict[str, TopLevelVariable] = {}\r\n\r\n        # Renamed created by import\r\n        # import A as B\r\n        # local name -> original name (A -> B)\r\n        self.renaming: Dict[str, str] = {}\r\n\r\n        # User defined types\r\n        # Name -> type alias\r\n        self.user_defined_types: Dict[str, TypeAlias] = {}\r\n```\r\n\r\n##### TopLevelVariable\r\n\r\n维护顶层节点信息，负责后续进一步解析\r\n\r\n```python\r\n    def __init__(self, scope: \"FileScope\"):\r\n        super().__init__()\r\n        self._node_initialization: Optional[\"Node\"] = None\r\n        self.file_scope = scope\r\n```\r\n\r\n##### Contract\r\n\r\n维护合约中的所有信息，如父合约_linearizedBaseContracts，函数\\_functions，变量\\_variables，修饰器\\_modifiers等，\r\n\r\n```python\r\n    def __init__(self, compilation_unit: \"SlitherCompilationUnit\", scope: \"FileScope\"):\r\n        super().__init__()\r\n\r\n        self._name: Optional[str] = None\r\n        self._id: Optional[int] = None\r\n        self._inheritance: List[\"Contract\"] = []  # all contract inherited, c3 linearization\r\n        self._immediate_inheritance: List[\"Contract\"] = []  # immediate inheritance\r\n\r\n        # Constructors called on contract's definition\r\n        # contract B is A(1) { ..\r\n        self._explicit_base_constructor_calls: List[\"Contract\"] = []\r\n\r\n        self._enums: Dict[str, \"EnumContract\"] = {}\r\n        self._structures: Dict[str, \"StructureContract\"] = {}\r\n        self._events: Dict[str, \"Event\"] = {}\r\n        self._variables: Dict[str, \"StateVariable\"] = {}\r\n        self._variables_ordered: List[\"StateVariable\"] = []\r\n        self._modifiers: Dict[str, \"Modifier\"] = {}\r\n        self._functions: Dict[str, \"FunctionContract\"] = {}\r\n        self._linearizedBaseContracts: List[int] = []\r\n        self._custom_errors: Dict[str, \"CustomErrorContract\"] = {}\r\n\r\n        # The only str is \"*\"\r\n        self._using_for: Dict[Union[str, Type], List[Type]] = {}\r\n        self._kind: Optional[str] = None\r\n        self._is_interface: bool = False\r\n        self._is_library: bool = False\r\n\r\n        self._signatures: Optional[List[str]] = None\r\n        self._signatures_declared: Optional[List[str]] = None\r\n\r\n        self._is_upgradeable: Optional[bool] = None\r\n        self._is_upgradeable_proxy: Optional[bool] = None\r\n\r\n        self.is_top_level = False  # heavily used, so no @property\r\n\r\n        self._initial_state_variables: List[\"StateVariable\"] = []  # ssa\r\n\r\n        self._is_incorrectly_parsed: bool = False\r\n\r\n        self._available_functions_as_dict: Optional[Dict[str, \"Function\"]] = None\r\n        self._all_functions_called: Optional[List[\"InternalCallType\"]] = None\r\n\r\n        self.compilation_unit: \"SlitherCompilationUnit\" = compilation_unit\r\n        self.file_scope: \"FileScope\" = scope\r\n\r\n        # memoize\r\n        self._state_variables_used_in_reentrant_targets: Optional[\r\n            Dict[\"StateVariable\", Set[Union[\"StateVariable\", \"Function\"]]]\r\n        ] = None\r\n```\r\n\r\n##### Function\r\n\r\n维护函数的完整信息，包括其中节点、变量、参数等。\r\n\r\n```python\r\n    def __init__(self, compilation_unit: \"SlitherCompilationUnit\"):\r\n        super().__init__()\r\n        self._internal_scope: List[str] = []\r\n        self._name: Optional[str] = None\r\n        self._view: bool = False\r\n        self._pure: bool = False\r\n        self._payable: bool = False\r\n        self._visibility: Optional[str] = None\r\n\r\n        self._is_implemented: Optional[bool] = None\r\n        self._is_empty: Optional[bool] = None\r\n        self._entry_point: Optional[\"Node\"] = None\r\n        self._nodes: List[\"Node\"] = []\r\n        self._variables: Dict[str, \"LocalVariable\"] = {}\r\n        # slithir Temporary and references variables (but not SSA)\r\n        self._slithir_variables: Set[\"SlithIRVariable\"] = set()\r\n        self._parameters: List[\"LocalVariable\"] = []\r\n        self._parameters_ssa: List[\"LocalIRVariable\"] = []\r\n        self._parameters_src: SourceMapping = SourceMapping()\r\n        self._returns: List[\"LocalVariable\"] = []\r\n        self._returns_ssa: List[\"LocalIRVariable\"] = []\r\n        self._returns_src: SourceMapping = SourceMapping()\r\n        self._return_values: Optional[List[\"SlithIRVariable\"]] = None\r\n        self._return_values_ssa: Optional[List[\"SlithIRVariable\"]] = None\r\n        self._vars_read: List[\"Variable\"] = []\r\n        self._vars_written: List[\"Variable\"] = []\r\n        self._state_vars_read: List[\"StateVariable\"] = []\r\n        self._vars_read_or_written: List[\"Variable\"] = []\r\n        self._solidity_vars_read: List[\"SolidityVariable\"] = []\r\n        self._state_vars_written: List[\"StateVariable\"] = []\r\n        self._internal_calls: List[\"InternalCallType\"] = []\r\n        self._solidity_calls: List[\"SolidityFunction\"] = []\r\n        self._low_level_calls: List[\"LowLevelCallType\"] = []\r\n        self._high_level_calls: List[\"HighLevelCallType\"] = []\r\n        self._library_calls: List[\"LibraryCallType\"] = []\r\n        self._external_calls_as_expressions: List[\"Expression\"] = []\r\n        self._expression_vars_read: List[\"Expression\"] = []\r\n        self._expression_vars_written: List[\"Expression\"] = []\r\n        self._expression_calls: List[\"Expression\"] = []\r\n        # self._expression_modifiers: List[\"Expression\"] = []\r\n        self._modifiers: List[ModifierStatements] = []\r\n        self._explicit_base_constructor_calls: List[ModifierStatements] = []\r\n        self._contains_assembly: bool = False\r\n\r\n        self._expressions: Optional[List[\"Expression\"]] = None\r\n        self._slithir_operations: Optional[List[\"Operation\"]] = None\r\n        self._slithir_ssa_operations: Optional[List[\"Operation\"]] = None\r\n\r\n        self._all_expressions: Optional[List[\"Expression\"]] = None\r\n        self._all_slithir_operations: Optional[List[\"Operation\"]] = None\r\n        self._all_internals_calls: Optional[List[\"InternalCallType\"]] = None\r\n        self._all_high_level_calls: Optional[List[\"HighLevelCallType\"]] = None\r\n        self._all_library_calls: Optional[List[\"LibraryCallType\"]] = None\r\n        self._all_low_level_calls: Optional[List[\"LowLevelCallType\"]] = None\r\n        self._all_solidity_calls: Optional[List[\"SolidityFunction\"]] = None\r\n        self._all_state_variables_read: Optional[List[\"StateVariable\"]] = None\r\n        self._all_solidity_variables_read: Optional[List[\"SolidityVariable\"]] = None\r\n        self._all_state_variables_written: Optional[List[\"StateVariable\"]] = None\r\n        self._all_slithir_variables: Optional[List[\"SlithIRVariable\"]] = None\r\n        self._all_nodes: Optional[List[\"Node\"]] = None\r\n        self._all_conditional_state_variables_read: Optional[List[\"StateVariable\"]] = None\r\n        self._all_conditional_state_variables_read_with_loop: Optional[List[\"StateVariable\"]] = None\r\n        self._all_conditional_solidity_variables_read: Optional[List[\"SolidityVariable\"]] = None\r\n        self._all_conditional_solidity_variables_read_with_loop: Optional[\r\n            List[\"SolidityVariable\"]\r\n        ] = None\r\n        self._all_solidity_variables_used_as_args: Optional[List[\"SolidityVariable\"]] = None\r\n\r\n        self._is_shadowed: bool = False\r\n        self._shadows: bool = False\r\n\r\n        # set(ReacheableNode)\r\n        self._reachable_from_nodes: Set[ReacheableNode] = set()\r\n        self._reachable_from_functions: Set[Function] = set()\r\n        self._all_reachable_from_functions: Optional[Set[Function]] = None\r\n\r\n        # Constructor, fallback, State variable constructor\r\n        self._function_type: Optional[FunctionType] = None\r\n        self._is_constructor: Optional[bool] = None\r\n\r\n        # Computed on the fly, can be True of False\r\n        self._can_reenter: Optional[bool] = None\r\n        self._can_send_eth: Optional[bool] = None\r\n\r\n        self._nodes_ordered_dominators: Optional[List[\"Node\"]] = None\r\n\r\n        self._counter_nodes = 0\r\n\r\n        # Memoize parameters:\r\n        # TODO: identify all the memoize parameters and add a way to undo the memoization\r\n        self._full_name: Optional[str] = None\r\n        self._signature: Optional[Tuple[str, List[str], List[str]]] = None\r\n        self._solidity_signature: Optional[str] = None\r\n        self._signature_str: Optional[str] = None\r\n        self._canonical_name: Optional[str] = None\r\n        self._is_protected: Optional[bool] = None\r\n\r\n        self.compilation_unit: \"SlitherCompilationUnit\" = compilation_unit\r\n\r\n        # Assume we are analyzing Solidity by default\r\n        self.function_language: FunctionLanguage = FunctionLanguage.Solidity\r\n\r\n        self._id: Optional[str] = None\r\n```\r\n\r\n##### Variable\r\n\r\n维护变量的类型、可见性等信息\r\n\r\n```python\r\n    def __init__(self):\r\n        super().__init__()\r\n        self._name: Optional[str] = None\r\n        self._initial_expression: Optional[\"Expression\"] = None\r\n        self._type: Optional[Type] = None\r\n        self._initialized: Optional[bool] = None\r\n        self._visibility: Optional[str] = None\r\n        self._is_constant = False\r\n        self._is_immutable: bool = False\r\n        self._is_reentrant: bool = True\r\n        self._write_protection: Optional[List[str]] = None\r\n```\r\n\r\n##### Node\r\n\r\n维护控制流中的节点信息，如ir、Φ函数、读写变量等，主要用于控制流与数据流的生成中\r\n\r\n```python\r\nclass Node(SourceMapping, ChildFunction):  # pylint: disable=too-many-public-methods\r\n    \"\"\"\r\n    Node class\r\n\r\n    \"\"\"\r\n\r\n    def __init__(\r\n        self,\r\n        node_type: NodeType,\r\n        node_id: int,\r\n        scope: Union[\"Scope\", \"Function\"],\r\n        file_scope: \"FileScope\",\r\n    ):\r\n        super().__init__()\r\n        self._node_type = node_type\r\n\r\n        # TODO: rename to explicit CFG\r\n        self._sons: List[\"Node\"] = []\r\n        self._fathers: List[\"Node\"] = []\r\n\r\n        ## Dominators info\r\n        # Dominators nodes\r\n        self._dominators: Set[\"Node\"] = set()\r\n        self._immediate_dominator: Optional[\"Node\"] = None\r\n        ## Nodes of the dominators tree\r\n        # self._dom_predecessors = set()\r\n        self._dom_successors: Set[\"Node\"] = set()\r\n        # Dominance frontier\r\n        self._dominance_frontier: Set[\"Node\"] = set()\r\n        # Phi origin\r\n        # key are variable name\r\n        self._phi_origins_state_variables: Dict[str, Tuple[StateVariable, Set[\"Node\"]]] = {}\r\n        self._phi_origins_local_variables: Dict[str, Tuple[LocalVariable, Set[\"Node\"]]] = {}\r\n        # self._phi_origins_member_variables: Dict[str, Tuple[MemberVariable, Set[\"Node\"]]] = {}\r\n\r\n        self._expression: Optional[Expression] = None\r\n        self._variable_declaration: Optional[LocalVariable] = None\r\n        self._node_id: int = node_id\r\n\r\n        self._vars_written: List[Variable] = []\r\n        self._vars_read: List[Variable] = []\r\n\r\n        self._ssa_vars_written: List[\"SlithIRVariable\"] = []\r\n        self._ssa_vars_read: List[\"SlithIRVariable\"] = []\r\n\r\n        self._internal_calls: List[\"Function\"] = []\r\n        self._solidity_calls: List[SolidityFunction] = []\r\n        self._high_level_calls: List[\"HighLevelCallType\"] = []  # contains library calls\r\n        self._library_calls: List[\"LibraryCallType\"] = []\r\n        self._low_level_calls: List[\"LowLevelCallType\"] = []\r\n        self._external_calls_as_expressions: List[Expression] = []\r\n        self._internal_calls_as_expressions: List[Expression] = []\r\n        self._irs: List[Operation] = []\r\n        self._all_slithir_operations: Optional[List[Operation]] = None\r\n        self._irs_ssa: List[Operation] = []\r\n\r\n        self._state_vars_written: List[StateVariable] = []\r\n        self._state_vars_read: List[StateVariable] = []\r\n        self._solidity_vars_read: List[SolidityVariable] = []\r\n\r\n        self._ssa_state_vars_written: List[StateIRVariable] = []\r\n        self._ssa_state_vars_read: List[StateIRVariable] = []\r\n\r\n        self._local_vars_read: List[LocalVariable] = []\r\n        self._local_vars_written: List[LocalVariable] = []\r\n\r\n        self._slithir_vars: Set[\"SlithIRVariable\"] = set()  # non SSA\r\n\r\n        self._ssa_local_vars_read: List[LocalIRVariable] = []\r\n        self._ssa_local_vars_written: List[LocalIRVariable] = []\r\n\r\n        self._expression_vars_written: List[Expression] = []\r\n        self._expression_vars_read: List[Expression] = []\r\n        self._expression_calls: List[Expression] = []\r\n\r\n        # Computed on the fly, can be True of False\r\n        self._can_reenter: Optional[bool] = None\r\n        self._can_send_eth: Optional[bool] = None\r\n\r\n        self._asm_source_code: Optional[Union[str, Dict]] = None\r\n\r\n        self.scope: Union[\"Scope\", \"Function\"] = scope\r\n        self.file_scope: \"FileScope\" = file_scope\r\n```\r\n\r\n##### Expression\r\n\r\n各种语句的父类\r\n\r\n```python\r\n    def __init__(self) -> None:\r\n        super().__init__()\r\n        self._is_lvalue = False\r\n```\r\n\r\n##### Operation\r\n\r\n各种语句的封装类，继承于Expression，如二元语句：\r\n\r\n```python\r\nclass BinaryOperation(ExpressionTyped):\r\n    def __init__(\r\n        self,\r\n        left_expression: Expression,\r\n        right_expression: Expression,\r\n        expression_type: BinaryOperationType,\r\n    ) -> None:\r\n        assert isinstance(left_expression, Expression)\r\n        assert isinstance(right_expression, Expression)\r\n        super().__init__()\r\n        self._expressions = [left_expression, right_expression]\r\n        self._type: BinaryOperationType = expression_type\r\n```\r\n\r\n##### Context\r\n\r\n大部分节点类均继承该类，其中以键值对形式维护上下文中分析状态与漏洞结果等信息\r\n\r\n```python\r\n    def __init__(self) -> None:\r\n        super().__init__()\r\n        self._context: Dict = {\"MEMBERS\": defaultdict(None)}\r\n```\r\n\r\n#### 顶层节点解析：parse_top_level_from_loaded_json()\r\n\r\n在`Slither()`中调用，在生成语法树后首先解析顶层节点信息，即不解析节点内容，只创建顶层节点即合约、全局变量的解释器，保存节点名、id等信息。\r\n\r\n#### 解析合约：parse_contracts()\r\n\r\n解析合约中分为三步：\r\n\r\n##### _analyze_first_part()\r\n\r\n第一步创建结构体、变量、函数、合约的解析器\r\n\r\n函数中首先进行while循环，判断合约的所有依赖合约是否被解析，若未全部解析，则置于最后：\r\n\r\n```python\r\n\t\t while contracts_to_be_analyzed:\r\n            contract = contracts_to_be_analyzed[0]\r\n            contracts_to_be_analyzed = contracts_to_be_analyzed[1:]\r\n            all_father_analyzed = all(\r\n                self._underlying_contract_to_parser[father].is_analyzed\r\n                for father in contract.underlying_contract.inheritance\r\n            )\r\n            if not contract.underlying_contract.inheritance or all_father_analyzed:\r\n                self._parse_struct_var_modifiers_functions(contract)\r\n            else:\r\n                contracts_to_be_analyzed += [contract]\r\n```\r\n\r\n接着调用_parse_struct_var_modifiers_functions()方法，对所有结构体、函数等创建解析器：\r\n\r\n```python\r\n    def _parse_struct_var_modifiers_functions(self, contract: ContractSolc):\r\n        contract.parse_structs()  # struct can refer another struct\r\n        contract.parse_state_variables()\r\n        contract.parse_modifiers()\r\n        contract.parse_functions()\r\n        contract.parse_custom_errors()\r\n        contract.set_is_analyzed(True)\r\n```\r\n\r\n如parse_structs()最终创建结构体解析器：\r\n\r\n```python\r\n    def _parse_struct(self, struct: Dict):\r\n\r\n        st = StructureContract(self._contract.compilation_unit)\r\n        st.set_contract(self._contract)\r\n        st.set_offset(struct[\"src\"], self._contract.compilation_unit)\r\n\r\n        st_parser = StructureContractSolc(st, struct, self)\r\n        self._contract.structures_as_dict[st.name] = st\r\n        self._structures_parser.append(st_parser)\r\n```\r\n\r\n此处并未进一步解析\r\n\r\n##### _analyze_second_part()\r\n\r\n第二步解析结构体与事件\r\n\r\n同样先等待父合约解析完成：\r\n\r\n```python\r\n while contracts_to_be_analyzed:\r\n\r\n            contract = contracts_to_be_analyzed[0]\r\n\r\n            contracts_to_be_analyzed = contracts_to_be_analyzed[1:]\r\n            all_father_analyzed = all(\r\n                self._underlying_contract_to_parser[father].is_analyzed\r\n                for father in contract.underlying_contract.inheritance\r\n            )\r\n\r\n            if not contract.underlying_contract.inheritance or all_father_analyzed:\r\n                self._analyze_struct_events(contract)\r\n\r\n            else:\r\n                contracts_to_be_analyzed += [contract]\r\n```\r\n\r\n调用_analyze_struct_events()，进入结构体与事件解析：\r\n\r\n```python\r\n    def _analyze_struct_events(self, contract: ContractSolc):\r\n\r\n        contract.analyze_constant_state_variables()\r\n\r\n        # Struct can refer to enum, or state variables\r\n        contract.analyze_structs()\r\n        # Event can refer to struct\r\n        contract.analyze_events()\r\n\r\n        contract.analyze_using_for()\r\n        contract.analyze_custom_errors()\r\n\r\n        contract.set_is_analyzed(True)\r\n```\r\n\r\n如analyze_structs()最终调用StructureContractSolc.analyze()：\r\n\r\n```python\r\n    def analyze(self):\r\n        for elem_to_parse in self._elemsNotParsed:\r\n            elem = StructureVariable()\r\n            elem.set_structure(self._structure)\r\n            elem.set_offset(elem_to_parse[\"src\"], self._structure.contract.compilation_unit)\r\n\r\n            elem_parser = StructureVariableSolc(elem, elem_to_parse)\r\n            elem_parser.analyze(self._contract_parser)\r\n\r\n            self._structure.elems[elem.name] = elem\r\n            self._structure.add_elem_in_order(elem.name)\r\n        self._elemsNotParsed = []\r\n```\r\n\r\n将AST中的键值对进行完整解析赋值\r\n\r\n##### _analyze_third_part()\r\n\r\n第三步解析函数、修饰器与变量\r\n\r\n同样等待父合约解析完成：\r\n\r\n```python\r\n        while contracts_to_be_analyzed:\r\n\r\n            contract = contracts_to_be_analyzed[0]\r\n\r\n            contracts_to_be_analyzed = contracts_to_be_analyzed[1:]\r\n            all_father_analyzed = all(\r\n                self._underlying_contract_to_parser[father].is_analyzed\r\n                for father in contract.underlying_contract.inheritance\r\n            )\r\n\r\n            if not contract.underlying_contract.inheritance or all_father_analyzed:\r\n                self._analyze_variables_modifiers_functions(contract)\r\n\r\n            else:\r\n                contracts_to_be_analyzed += [contract]\r\n```\r\n\r\n调用_analyze_variables_modifiers_functions()，进入函数、修饰器与变量解析：\r\n\r\n```python\r\n    def _analyze_variables_modifiers_functions(self, contract: ContractSolc):\r\n        # State variables, modifiers and functions can refer to anything\r\n\r\n        contract.analyze_params_modifiers()\r\n        contract.analyze_params_functions()\r\n        self._analyze_params_top_level_function()\r\n        self._analyze_params_custom_error()\r\n\r\n        contract.analyze_state_variables()\r\n\r\n        contract.analyze_content_modifiers()\r\n        contract.analyze_content_functions()\r\n        self._analyze_content_top_level_function()\r\n\r\n        contract.set_is_analyzed(True)\r\n```\r\n\r\n如在analyze_content_functions()中，会调用_parse_cfg()最终生成控制流图：\r\n\r\n```python\r\n    def analyze_content(self):\r\n        if self._content_was_analyzed:\r\n            return\r\n\r\n        self._content_was_analyzed = True\r\n\r\n        if self.is_compact_ast:\r\n            body = self._functionNotParsed.get(\"body\", None)\r\n\r\n            if body and body[self.get_key()] == \"Block\":\r\n                self._function.is_implemented = True\r\n                # 生成控制流图\r\n                self._parse_cfg(body)\r\n\r\n            for modifier in self._functionNotParsed[\"modifiers\"]:\r\n                self._parse_modifier(modifier)\r\n```\r\n\r\n#### 生成控制流图：_parse_cfg()\r\n\r\n此时所有函数与参数都已解析完成，该函数主要调用`_parse_statement()`函数，根据不同表达式种类进行节点的父子连接：\r\n\r\n```python\r\n    def _parse_statement(\r\n        self, statement: Dict, node: NodeSolc, scope: Union[Scope, Function]\r\n    ) -> NodeSolc:\r\n        \"\"\"\r\n\r\n        Return:\r\n            node\r\n        \"\"\"\r\n        # Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |\r\n        #            ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |\r\n        #                          Throw | EmitStatement | SimpleStatement ) ';'\r\n        # SimpleStatement = VariableDefinition | ExpressionStatement\r\n\r\n        name = statement[self.get_key()]\r\n        # SimpleStatement = VariableDefinition | ExpressionStatement\r\n        if name == \"IfStatement\":\r\n            node = self._parse_if(statement, node)\r\n        elif name == \"WhileStatement\":\r\n            node = self._parse_while(statement, node)\r\n        elif name == \"ForStatement\":\r\n            node = self._parse_for(statement, node)\r\n        elif name == \"Block\":\r\n            node = self._parse_block(statement, node)\r\n        elif name == \"UncheckedBlock\":\r\n            node = self._parse_unchecked_block(statement, node)\r\n        ......\r\n```\r\n\r\n如`IfStatement`，进入`_parse_if()`函数：\r\n\r\n```python\r\n    def _parse_if(self, if_statement: Dict, node: NodeSolc) -> NodeSolc:\r\n        # IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?\r\n        falseStatement = None\r\n\r\n        if self.is_compact_ast:\r\n            condition = if_statement[\"condition\"]\r\n            # Note: check if the expression could be directly\r\n            # parsed here\r\n            condition_node = self._new_node(\r\n                NodeType.IF, condition[\"src\"], node.underlying_node.scope\r\n            )\r\n            condition_node.add_unparsed_expression(condition)\r\n            link_underlying_nodes(node, condition_node)\r\n            true_scope = Scope(\r\n                node.underlying_node.scope.is_checked, False, node.underlying_node.scope\r\n            )\r\n            trueStatement = self._parse_statement(\r\n                if_statement[\"trueBody\"], condition_node, true_scope\r\n            )\r\n            if \"falseBody\" in if_statement and if_statement[\"falseBody\"]:\r\n                false_scope = Scope(\r\n                    node.underlying_node.scope.is_checked, False, node.underlying_node.scope\r\n                )\r\n                falseStatement = self._parse_statement(\r\n                    if_statement[\"falseBody\"], condition_node, false_scope\r\n                )\r\n        else:\r\n            children = if_statement[self.get_children(\"children\")]\r\n            condition = children[0]\r\n            # Note: check if the expression could be directly\r\n            # parsed here\r\n            condition_node = self._new_node(\r\n                NodeType.IF, condition[\"src\"], node.underlying_node.scope\r\n            )\r\n            condition_node.add_unparsed_expression(condition)\r\n            link_underlying_nodes(node, condition_node)\r\n            true_scope = Scope(\r\n                node.underlying_node.scope.is_checked, False, node.underlying_node.scope\r\n            )\r\n            trueStatement = self._parse_statement(children[1], condition_node, true_scope)\r\n            if len(children) == 3:\r\n                false_scope = Scope(\r\n                    node.underlying_node.scope.is_checked, False, node.underlying_node.scope\r\n                )\r\n                falseStatement = self._parse_statement(children[2], condition_node, false_scope)\r\n\r\n        endIf_node = self._new_node(NodeType.ENDIF, if_statement[\"src\"], node.underlying_node.scope)\r\n        # 添加连接\r\n        link_underlying_nodes(trueStatement, endIf_node)\r\n\r\n        if falseStatement:\r\n            link_underlying_nodes(falseStatement, endIf_node)\r\n        else:\r\n            link_underlying_nodes(condition_node, endIf_node)\r\n        return endIf_node\r\n\r\n```\r\n\r\n其主要流程为创建`ENDIF`节点，将判断表达式转换为`CONDITION`节点，`TRUE`与`FALSE`作为单独节点，并创建连接关系：`FATHER`→`CONDITION`→`TRUE` `FALSE` → `ENDIF` ，最终返回`ENDIF`节点作为下次分析的父节点。\r\n\r\n### 3. 支配树的生成\r\n\r\n#### analyze_contracts()\r\n\r\n在`Slither()`函数中，解析完成合约后，会调用`analyze_contracts()`进行数据流的分析，其中会进行支配树的计算。\r\n\r\n##### compute_dominators()\r\n\r\n代码整体与前置知识中一致，采用Dominator-tree Algorithm算法，计算出支配者与直接支配者。\r\n\r\n```python\r\ndef compute_dominators(nodes: List[\"Node\"]):\r\n    \"\"\"\r\n    Naive implementation of Cooper, Harvey, Kennedy algo\r\n    See 'A Simple,Fast Dominance Algorithm'\r\n\r\n    Compute strict domniators\r\n    \"\"\"\r\n\r\n    for n in nodes:\r\n        n.dominators = set(nodes)\r\n\r\n    _compute_dominators(nodes)\r\n\r\n    # 计算直接支配者\r\n    _compute_immediate_dominators(nodes)\r\n```\r\n\r\n##### compute_dominance_frontier()\r\n\r\n采用Dominator-tree Algorithm算法，计算出支配边界。\r\n\r\n```python\r\ndef compute_dominance_frontier(nodes: List[\"Node\"]):\r\n    \"\"\"\r\n    Naive implementation of Cooper, Harvey, Kennedy algo\r\n    See 'A Simple,Fast Dominance Algorithm'\r\n\r\n    Compute dominance frontier\r\n    \"\"\"\r\n    for node in nodes:\r\n        if len(node.fathers) >= 2:\r\n            for father in node.fathers:\r\n                runner = father\r\n                # Corner case: if there is a if without else\r\n                # we need to add update the conditional node\r\n                if (\r\n                    runner == node.immediate_dominator\r\n                    and runner.type == NodeType.IF\r\n                    and node.type == NodeType.ENDIF\r\n                ):\r\n                    runner.dominance_frontier = runner.dominance_frontier.union({node})\r\n                while runner != node.immediate_dominator:\r\n                    runner.dominance_frontier = runner.dominance_frontier.union({node})\r\n                    runner = runner.immediate_dominator\r\n```\r\n\r\n此处对于 IF与ENDIF做出特别处理，原因是如果没有ELSE的IF语句，同样会产生两个分叉，因此支配边界也应当考虑。\r\n\r\n### 4. 数据流的生成\r\n\r\n#### 普通ir：generate_slithir_and_analyze()\r\n\r\n主要通过`convert_expression()`方法将节点中的表达式转换为ir形式：\r\n\r\n```python\r\ndef convert_expression(expression, node):\r\n    # handle standlone expression\r\n    # such as return true;\r\n    # 处理if语句与return\r\n    from slither.core.cfg.node import NodeType\r\n    ......\r\n    visitor = ExpressionToSlithIR(expression, node)\r\n    result = visitor.result()\r\n\t......\r\n    return result\r\n```\r\n\r\n在创建`ExpressionToSlithIR`时，进入转换函数，如二元表达式：\r\n\r\n```python\r\n    def _post_binary_operation(self, expression):\r\n        left = get(expression.expression_left)\r\n        right = get(expression.expression_right)\r\n        val = TemporaryVariable(self._node)\r\n\r\n        if expression.type in _signed_to_unsigned:\r\n            new_left = TemporaryVariable(self._node)\r\n            conv_left = TypeConversion(new_left, left, ElementaryType(\"int256\"))\r\n            new_left.set_type(ElementaryType(\"int256\"))\r\n            conv_left.set_expression(expression)\r\n            self._result.append(conv_left)\r\n\r\n            if expression.type != BinaryOperationType.RIGHT_SHIFT_ARITHMETIC:\r\n                new_right = TemporaryVariable(self._node)\r\n                conv_right = TypeConversion(new_right, right, ElementaryType(\"int256\"))\r\n                new_right.set_type(ElementaryType(\"int256\"))\r\n                conv_right.set_expression(expression)\r\n                self._result.append(conv_right)\r\n            else:\r\n                new_right = right\r\n\r\n            new_final = TemporaryVariable(self._node)\r\n            operation = Binary(new_final, new_left, new_right, _signed_to_unsigned[expression.type])\r\n            operation.set_expression(expression)\r\n            self._result.append(operation)\r\n\r\n            conv_final = TypeConversion(val, new_final, ElementaryType(\"uint256\"))\r\n            val.set_type(ElementaryType(\"uint256\"))\r\n            conv_final.set_expression(expression)\r\n            self._result.append(conv_final)\r\n        else:\r\n            operation = Binary(val, left, right, _binary_to_binary[expression.type])\r\n            operation.set_expression(expression)\r\n            self._result.append(operation)\r\n\r\n        set_val(expression, val)\r\n```\r\n\r\n如`d==1`会生成`TMP_1 = d==1`，即将每种表达式都转换为赋值表达式的形式\r\n\r\n#### ssa-ir：convert_expression_to_slithir_ssa\r\n\r\nssa主要目的是生成单一的赋值变量并添加函数传参与全局变量在数据流中的导向。\r\n\r\n首先将本合约与父合约的全局变量传入`_initial_state_variables`数组中：\r\n\r\n```python\r\n        for contract in self.inheritance:\r\n            for v in contract.state_variables_declared:\r\n                new_var = StateIRVariable(v)\r\n                all_ssa_state_variables_instances[v.canonical_name] = new_var\r\n                self._initial_state_variables.append(new_var)\r\n```\r\n\r\n将本合约的状态变量传入数组：\r\n\r\n```python\r\n        for v in self.variables:\r\n            if v.contract == self:\r\n                new_var = StateIRVariable(v)\r\n                all_ssa_state_variables_instances[v.canonical_name] = new_var\r\n                self._initial_state_variables.append(new_var)\r\n```\r\n\r\n生成函数的ssa-ir：\r\n\r\n```python\r\n        for func in self.functions + self.modifiers:\r\n            func.generate_slithir_ssa(all_ssa_state_variables_instances)\r\n```\r\n\r\n主要调用add_ssa_ir()函数：\r\n\r\n首先装载函数传参与returns的初始定义：\r\n\r\n```python\r\n    init_definition = {}\r\n    for v in function.parameters:\r\n        if v.name:\r\n            init_definition[v.name] = (v, function.entry_point)\r\n            function.entry_point.add_ssa_ir(Phi(LocalIRVariable(v), set()))\r\n\r\n    # 返回变量初始化\r\n    for v in function.returns:\r\n        # 有参数名才初始化\r\n        if v.name:\r\n            init_definition[v.name] = (v, function.entry_point)\r\n\r\n\r\n```\r\n\r\n接着在入口节点：ENTRYPOINT插入Φ函数：\r\n\r\n```python\r\n    for (_, variable_instance) in all_state_variables_instances.items():\r\n        if is_used_later(function.entry_point, variable_instance):\r\n            # rvalues are fixed in solc_parsing.declaration.function, 在entrypoint添加\r\n            function.entry_point.add_ssa_ir(Phi(StateIRVariable(variable_instance), set()))\r\n\r\n    # 添加phi函数\r\n    add_phi_origins(function.entry_point, init_definition, {})\r\n\r\n\r\n```\r\n\r\n`add_phi_origins()`函数为循环所有节点插入Φ变量：\r\n\r\n```python\r\ndef add_phi_origins(node, local_variables_definition, state_variables_definition):\r\n\r\n    # Add new key to local_variables_definition\r\n    # The key is the variable_name\r\n    # The value is (variable_instance, the node where its written)\r\n    # We keep the instance as we want to avoid to add __hash__ on v.name in Variable\r\n    # That might work for this used, but could create collision for other uses\r\n    local_variables_definition = dict(\r\n        local_variables_definition,\r\n        **{v.name: (v, node) for v in node.local_variables_written},\r\n    )\r\n    state_variables_definition = dict(\r\n        state_variables_definition,\r\n        **{v.canonical_name: (v, node) for v in node.state_variables_written},\r\n    )\r\n\r\n    # For unini variable declaration\r\n    if (\r\n        node.variable_declaration\r\n        and not node.variable_declaration.name in local_variables_definition\r\n    ):\r\n        local_variables_definition[node.variable_declaration.name] = (\r\n            node.variable_declaration,\r\n            node,\r\n        )\r\n\r\n    # filter length of successors because we have node with one successor\r\n    # while most of the ssa textbook would represent following nodes as one\r\n    # 添加新的 phi变量\r\n    if node.dominance_frontier and len(node.dominator_successors) != 1:\r\n        for phi_node in node.dominance_frontier:\r\n            for _, (variable, n) in local_variables_definition.items():\r\n                phi_node.add_phi_origin_local_variable(variable, n)\r\n            for _, (variable, n) in state_variables_definition.items():\r\n                phi_node.add_phi_origin_state_variable(variable, n)\r\n\r\n    if not node.dominator_successors:\r\n        return\r\n    for succ in node.dominator_successors:\r\n        add_phi_origins(succ, local_variables_definition, state_variables_definition)\r\n\r\n```\r\n\r\n我们知道每个节点对于其支配者Φ函数值是固定的，然而在支配边界以外，Φ函数值不可控，因此需要插入新的Φ变量。\r\n\r\n随后遍历判断是否需新添加Φ函数：\r\n\r\n```python\r\n    for node in function.nodes:\r\n        for (variable, nodes) in node.phi_origins_local_variables.values():\r\n            if len(nodes) < 2:\r\n                continue\r\n            if not is_used_later(node, variable):\r\n                continue\r\n            node.add_ssa_ir(Phi(LocalIRVariable(variable), nodes))\r\n        for (variable, nodes) in node.phi_origins_state_variables.values():\r\n            if len(nodes) < 2:\r\n                continue\r\n            # if not is_used_later(node, variable.name, []):\r\n            #    continue\r\n            node.add_ssa_ir(Phi(StateIRVariable(variable), nodes))\r\n```\r\n\r\n只有当某个节点对一个变量受到两个不同的Φ变量控制时，需要插入新的Φ函数，如前置知识中提到的：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/rqSyFqDq63a9e2103a7f8.png)\r\n\r\n接着调用`generate_ssa_irs()`函数生成ssa-ir：\r\n\r\n```python\r\n    generate_ssa_irs(\r\n        function.entry_point,\r\n        dict(init_local_variables_instances),\r\n        all_init_local_variables_instances,\r\n        dict(init_state_variables_instances),\r\n        all_state_variables_instances,\r\n        init_local_variables_instances,\r\n        [],\r\n    )\r\n```\r\n\r\n主要逻辑就是递增左值下标，并替换右值中变量为最新变量下标：\r\n\r\n```python\r\n        new_ir = copy_ir(\r\n            ir,\r\n            local_variables_instances,\r\n            state_variables_instances,\r\n            temporary_variables_instances,\r\n            reference_variables_instances,\r\n            tuple_variables_instances,\r\n            all_local_variables_instances,\r\n        )\r\n\r\n        new_ir.set_expression(ir.expression)\r\n        new_ir.set_node(ir.node)\r\n\r\n        update_lvalue(\r\n            new_ir,\r\n            node,\r\n            local_variables_instances,\r\n            all_local_variables_instances,\r\n            state_variables_instances,\r\n            all_state_variables_instances,\r\n        )\r\n```\r\n\r\n`update_lvalue()`中进行递增操作\r\n\r\n```python\r\n                new_var = StateIRVariable(lvalue)\r\n                new_var.index = all_state_variables_instances[lvalue.canonical_name].index + 1\r\n                all_state_variables_instances[lvalue.canonical_name] = new_var\r\n                state_variables_instances[lvalue.canonical_name] = new_var\r\n```\r\n\r\n最后更新φ函数中全局变量的依赖，由于函数调用中可能更新全局变量的值，因此一个依赖于全局变量的节点同时依赖于新的下标的全局变量：\r\n\r\n```python\r\n    def fix_phi(self):\r\n        last_state_variables_instances = {}\r\n        initial_state_variables_instances = {}\r\n        for v in self._initial_state_variables:\r\n            last_state_variables_instances[v.canonical_name] = []\r\n            initial_state_variables_instances[v.canonical_name] = v\r\n\r\n        # 更新了一下函数中的调用造成的状态变量的改变\r\n        for func in self.functions + self.modifiers:\r\n            result = func.get_last_ssa_state_variables_instances()\r\n            for variable_name, instances in result.items():\r\n                last_state_variables_instances[variable_name] += instances\r\n\r\n        for func in self.functions + self.modifiers:\r\n            # 替换phi函数中参数\r\n            func.fix_phi(last_state_variables_instances, initial_state_variables_instances)\r\n```\r\n\r\n\r\n\r\n#### 计算依赖：compute_dependency()\r\n\r\n主要调用`compute_dependency_function()`计算每个函数的参数依赖：\r\n\r\n```python\r\ndef compute_dependency_function(function: Function) -> None:\r\n    if KEY_SSA in function.context:\r\n        return\r\n\r\n    function.context[KEY_SSA] = {}\r\n    function.context[KEY_SSA_UNPROTECTED] = {}\r\n\r\n    is_protected = function.is_protected()\r\n    for node in function.nodes:\r\n        for ir in node.irs_ssa:\r\n            if isinstance(ir, OperationWithLValue) and ir.lvalue:\r\n                if isinstance(ir.lvalue, LocalIRVariable) and ir.lvalue.is_storage:\r\n                    continue\r\n                if isinstance(ir.lvalue, ReferenceVariable):\r\n                    lvalue = ir.lvalue.points_to\r\n                    if lvalue:\r\n                        add_dependency(lvalue, function, ir, is_protected)\r\n                add_dependency(ir.lvalue, function, ir, is_protected)\r\n\r\n    # 转化为非ssa版本\r\n    function.context[KEY_NON_SSA] = convert_to_non_ssa(function.context[KEY_SSA])\r\n    function.context[KEY_NON_SSA_UNPROTECTED] = convert_to_non_ssa(\r\n        function.context[KEY_SSA_UNPROTECTED]\r\n    )\r\n```\r\n\r\n最终依赖以键值对形式保存在函数的上下文中，其中\r\n\r\n* KEY_SSA为SSA形式依赖：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/hYrx7Yh563a9e1f6c1b5d.png)\r\n\r\n* KEY_NON_SSA为普通形式依赖：\r\n\r\n\r\n![图片.png](https://img.learnblockchain.cn/attachments/2022/12/dTKaz5JM63a9e1fc8cce3.png)\r\n\r\n两种形式都为 def-use 链，即根据使用的参数能像上回溯找到流经的参数\r\n\r\n在计算完函数以来后会调用`propagate_function()`，整合所有依赖关系：\r\n\r\n```python\r\ndef transitive_close_dependencies(\r\n    context: Context_types, context_key: str, context_key_non_ssa: str\r\n) -> None:\r\n    # transitive closure\r\n    changed = True\r\n    keys = context.context[context_key].keys()\r\n    while changed:\r\n        changed = False\r\n        to_add = defaultdict(set)\r\n        # &: 计算交集，取出每个依赖的变量的依赖，追溯回最初的依赖\r\n        [  # pylint: disable=expression-not-assigned\r\n            [\r\n                to_add[key].update(context.context[context_key][item] - {key} - items)\r\n                # print(key, \":\", item,[ a.__str__() for a in context.context[context_key][item]],key, [ a.__str__() for a in items], context.context[context_key][item]-{key}-items)\r\n                for item in items & keys\r\n            ]\r\n            for key, items in context.context[context_key].items()\r\n        ]\r\n        for k, v in to_add.items():\r\n            # Because we dont have any check on the update operation\r\n            # We might update an empty set with an empty set\r\n            if v:\r\n                changed = True\r\n                context.context[context_key][k] |= v\r\n    context.context[context_key_non_ssa] = convert_to_non_ssa(context.context[context_key])\r\n\r\n```\r\n\r\n如SSA依赖：\r\n\r\n```\r\nb_1 : ['b_2', 'b_0']\r\nTMP_0 : ['d_1']\r\na_1 : []\r\nTUPLE_0 : ['add_1']\r\na_2 : ['a_2', 'a_1']\r\nb_2 : ['b_2', 'b_1']\r\nTMP_2 : ['c_0']\r\nc_1 : ['TMP_2']\r\nTMP_3 : ['a_2', 'b_2']\r\nc_2 : ['TMP_3']\r\n```\r\n\r\n第一次循环后to_add为：\r\n\r\n```\r\nb_1 []\r\na_2 []\r\nb_2 ['b_0']\r\nc_1 ['c_0']\r\nTMP_3 ['a_1', 'b_1']\r\nc_2 ['a_2', 'b_2']\r\n```\r\n\r\n整体依赖变为：\r\n\r\n```\r\nb_1 ['b_2', 'b_0']\r\nTMP_0 ['d_1']\r\na_1 []\r\nTUPLE_0 ['add_1']\r\na_2 ['a_2', 'a_1']\r\nb_2 ['b_2', 'b_0', 'b_1']\r\nTMP_2 ['c_0']\r\nc_1 ['c_0', 'TMP_2']\r\nTMP_3 ['a_1', 'a_2', 'b_2', 'b_1']\r\nc_2 ['a_2', 'TMP_3', 'b_2']\r\n```\r\n\r\n第二次循环后to_add为：\r\n\r\n```\r\nb_1 []\r\na_2 []\r\nb_2 []\r\nc_1 []\r\nTMP_3 ['b_0']\r\nc_2 ['b_0', 'a_1', 'b_1']\r\n```\r\n\r\n整体依赖变为：\r\n\r\n```\r\nb_1 ['b_2', 'b_0']\r\nTMP_0 ['d_1']\r\na_1 []\r\nTUPLE_0 ['add_1']\r\na_2 ['a_2', 'a_1']\r\nb_2 ['b_2', 'b_0', 'b_1']\r\nTMP_2 ['c_0']\r\nc_1 ['c_0', 'TMP_2']\r\nTMP_3 ['b_2', 'b_0', 'a_1', 'a_2', 'b_1']\r\nc_2 ['TMP_3', 'b_2', 'b_0', 'a_1', 'a_2', 'b_1']\r\n```\r\n\r\n第三次循环后to_add变为：\r\n\r\n```\r\nb_1 []\r\na_2 []\r\nb_2 []\r\nc_1 []\r\nTMP_3 []\r\nc_2 []\r\n```\r\n\r\n整体依赖不变，结束循环，此时非SSA依赖变为：\r\n\r\n```\r\nb ['b']\r\nTMP_0 ['d']\r\na ['a']\r\nTUPLE_0 ['add']\r\nTMP_2 ['c']\r\nc ['TMP_2', 'b', 'a', 'c', 'TMP_3']\r\nTMP_3 ['b', 'a']\r\n```\r\n\r\n将与全局变量或函数传参的依赖添加进依赖关系中\r\n\r\n### 5. 漏洞检测\r\n\r\n#### 整合所有漏洞检测器：_run_all_detectors()\r\n\r\n在调用`run_detectors()`检测前，需先对所有检测器进行注册：\r\n\r\n```python\r\ndef _run_all_detectors(slither: Slither):\r\n    detectors = [getattr(all_detectors, name) for name in dir(all_detectors)]\r\n    detectors = [d for d in detectors if inspect.isclass(d) and issubclass(d, AbstractDetector)]\r\n\r\n    for detector in detectors:\r\n        slither.register_detector(detector)\r\n\r\n    slither.run_detectors()\r\n```\r\n\r\n共有81种检测器：\r\n\r\n```\r\nABIEncoderV2Array\r\nArbitrarySendErc20NoPermit\r\nArbitrarySendErc20Permit\r\nArbitrarySendEth\r\nArrayByReference\r\nArrayLengthAssignment\r\nAssembly\r\nAssertStateChange\r\nBackdoor\r\nBadPRNG\r\nBooleanConstantMisuse\r\nBooleanEquality\r\nBuiltinSymbolShadowing\r\nConstCandidateStateVars\r\nConstantFunctionsAsm\r\nConstantFunctionsState\r\nConstantPragma\r\nControlledDelegateCall\r\nCostlyOperationsInLoop\r\nDeadCode\r\nDelegatecallInLoop\r\nDeprecatedStandards\r\nDivideBeforeMultiply\r\nDomainSeparatorCollision\r\nEnumConversion\r\nExternalFunction\r\nFunctionInitializedState\r\nIncorrectERC20InterfaceDetection\r\nIncorrectERC721InterfaceDetection\r\nIncorrectSolc\r\nIncorrectStrictEquality\r\nIncorrectUnaryExpressionDetection\r\nLocalShadowing\r\nLockedEther\r\nLowLevelCalls\r\nMappingDeletionDetection\r\nMissingEventsAccessControl\r\nMissingEventsArithmetic\r\nMissingInheritance\r\nMissingZeroAddressValidation\r\nModifierDefaultDetection\r\nMsgValueInLoop\r\nMultipleCallsInLoop\r\nMultipleConstructorSchemes\r\nNameReused\r\nNamingConvention\r\nPredeclarationUsageLocal\r\nProtectedVariables\r\nPublicMappingNested\r\nRedundantStatements\r\nReentrancyBenign\r\nReentrancyEth\r\nReentrancyEvent\r\nReentrancyNoGas\r\nReentrancyReadBeforeWritten\r\nReusedBaseConstructor\r\nRightToLeftOverride\r\nShadowingAbstractDetection\r\nShiftParameterMixup\r\nSimilarVarsDetection\r\nStateShadowing\r\nStorageSignedIntegerArray\r\nSuicidal\r\nTimestamp\r\nTooManyDigits\r\nTxOrigin\r\nTypeBasedTautology\r\nUncheckedLowLevel\r\nUncheckedSend\r\nUncheckedTransfer\r\nUnimplementedFunctionDetection\r\nUnindexedERC20EventParameters\r\nUninitializedFunctionPtrsConstructor\r\nUninitializedLocalVars\r\nUninitializedStateVarsDetection\r\nUninitializedStorageVars\r\nUnprotectedUpgradeable\r\nUnusedReturnValues\r\nUnusedStateVars\r\nVoidConstructor\r\nWriteAfterWrite\r\n```\r\n\r\n以重入漏洞ReentrancyEth演示检测过程：\r\n\r\n各重入型漏洞均继承于Reentrancy，初始化时首先调用父类的`_explore()`方法：\r\n\r\n```python\r\n    def _explore(self, node: Optional[Node], skip_father: Optional[Node] = None) -> None:        \r\n    \tif node is None:\r\n            return\r\n\r\n        fathers_context = AbstractState()\r\n        fathers_context.merge_fathers(node, skip_father, self)\r\n\r\n        # Exclude path that dont bring further information\r\n        if node in self.visited_all_paths:\r\n            if self.visited_all_paths[node].does_not_bring_new_info(fathers_context):\r\n                return\r\n        else:\r\n            self.visited_all_paths[node] = AbstractState()\r\n\r\n        # 合并\r\n        self.visited_all_paths[node].add(fathers_context)\r\n\r\n        node.context[self.KEY] = fathers_context\r\n\r\n        contains_call = fathers_context.analyze_node(node, self)\r\n        node.context[self.KEY] = fathers_context\r\n\r\n        sons = node.sons\r\n        if contains_call and node.type in [NodeType.IF, NodeType.IFLOOP]:\r\n            if _filter_if(node):\r\n                son = sons[0]\r\n                self._explore(son, skip_father=node)\r\n                sons = sons[1:]\r\n            else:\r\n                son = sons[1]\r\n                self._explore(son, skip_father=node)\r\n                sons = [sons[0]]\r\n\r\n        for son in sons:\r\n            self._explore(son)\r\n```\r\n\r\n主要作用为记录节点的读写变量、是否有函数调用、是否发送以太坊信息，其中读变量、是否有函数调用、是否发送以太坊均继承于父节点，这是合理的，因为这些操作同样会影响子节点。\r\n\r\n接着返回子类，继续检测逻辑：\r\n\r\n```python\r\n\t\treentrancies = self.find_reentrancies()\r\n```\r\n\r\n查找可能存在重入的节点：\r\n\r\n```python\r\n    def find_reentrancies(self) -> Dict[FindingKey, Set[FindingValue]]:\r\n        result: Dict[FindingKey, Set[FindingValue]] = defaultdict(set)\r\n        for contract in self.contracts:  # pylint: disable=too-many-nested-blocks\r\n            variables_used_in_reentrancy = contract.state_variables_used_in_reentrant_targets\r\n            for f in contract.functions_and_modifiers_declared:\r\n                for node in f.nodes:\r\n                    # dead code\r\n                    if not self.KEY in node.context:\r\n                        continue\r\n                    if node.context[self.KEY].calls and node.context[self.KEY].send_eth:\r\n                        # 在调用节点以后的节点\r\n                        if not any(n != node for n in node.context[self.KEY].send_eth):\r\n                            continue\r\n                        read_then_written = set()\r\n                        for c in node.context[self.KEY].calls:\r\n                            if c == node:\r\n                                continue\r\n                            read_then_written |= {\r\n                                FindingValue(\r\n                                    v,\r\n                                    node,\r\n                                    tuple(sorted(nodes, key=lambda x: x.node_id)),\r\n                                    tuple(\r\n                                        sorted(\r\n                                            variables_used_in_reentrancy[v], key=lambda x: str(x)\r\n                                        )\r\n                                    ),\r\n                                )\r\n                                for (v, nodes) in node.context[self.KEY].written.items()\r\n                                # 判断该变量在调用函数时是否用到\r\n                                if v in node.context[self.KEY].reads_prior_calls[c]\r\n                                and (f.is_reentrant or v in variables_used_in_reentrancy)\r\n                            }\r\n\r\n                        if read_then_written:\r\n                            # calls are ordered\r\n                            finding_key = FindingKey(\r\n                                function=node.function,\r\n                                calls=to_hashable(node.context[self.KEY].calls),\r\n                                send_eth=to_hashable(node.context[self.KEY].send_eth),\r\n                            )\r\n\r\n                            result[finding_key] |= set(read_then_written)\r\n        return result\r\n```\r\n\r\n即在满足以下情况时认为存在重入漏洞：\r\n\r\n* 存在外部调用\r\n* 外部调用读取某变量\r\n* 该变量在外部调用以后的语句中被重写\r\n\r\n但此处明显看出存在不严谨处，如：\r\n\r\n```solidity\r\ncontract A{\r\n    uint c = 100;\r\n\r\n    function g(uint d, address payable add) internal returns(uint){\r\n        if(d > 0){\r\n            add.call.value(100)(\"\");\r\n            c = c - 100;\r\n        }\r\n        return c;\r\n    }\r\n}\r\n```\r\n\r\n此时由于写入变量与调用时使用的变量不一致，工具无法检测出漏洞。\r\n\r\n> 此处为降低误报率而作出的折中选择，如直接检测外部调用后是否存在状态变量的写入，则容易导致误报率大大升高。"},"author":{"user":"https://learnblockchain.cn/people/12719","address":"0x9f58ee280a6b75d9d1d35ba734030b48bd57fe32"},"history":null,"timestamp":1672077928,"version":1}