Repository navigation
Expand file tree
/
Copy pathAssemblyAnalysis.ql
More file actions
139 lines (129 loc) · 3.94 KB
/
Copy pathAssemblyAnalysis.ql
File metadata and controls
139 lines (129 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* @name Assembly block analysis
* @description Inline assembly and Yul blocks with security classification.
* @id solidity/assembly-analysis
*/
import codeql.solidity.ast.internal.TreeSitter
/**
* Gets the contract name from a contract declaration.
*/
string getContractName(Solidity::ContractDeclaration contract) {
result = contract.getName().(Solidity::AstNode).getValue()
}
/**
* Gets the function name from a function definition.
*/
string getFunctionName(Solidity::FunctionDefinition func) {
result = func.getName().(Solidity::AstNode).getValue()
}
/**
* Represents an inline assembly block.
*/
class AssemblyBlock extends Solidity::AssemblyStatement {
/** Gets the enclosing function. */
Solidity::FunctionDefinition getEnclosingFunction() { this.getParent+() = result }
/** Gets the enclosing contract. */
Solidity::ContractDeclaration getEnclosingContract() { this.getParent+() = result }
}
/**
* Dangerous operations in assembly with risk levels.
*/
predicate dangerousOperation(string op, string risk) {
op = "delegatecall" and risk = "critical"
or
op = "selfdestruct" and risk = "critical"
or
op = "call" and risk = "high"
or
op = "create" and risk = "high"
or
op = "create2" and risk = "high"
or
op = "sstore" and risk = "high"
or
op = "staticcall" and risk = "medium"
or
op = "sload" and risk = "medium"
or
op = "extcodecopy" and risk = "medium"
or
op = "codecopy" and risk = "medium"
or
op = "mstore" and risk = "low"
or
op = "mload" and risk = "low"
or
op = "mstore8" and risk = "low"
or
op = "returndatacopy" and risk = "low"
or
op = "extcodesize" and risk = "low"
}
/**
* Holds if the assembly block contains a specific operation.
*/
predicate assemblyContainsOp(AssemblyBlock asm, string op) {
exists(Solidity::AstNode child |
child.getParent+() = asm and
(
child.getValue().toLowerCase() = op
or
child.toString().toLowerCase() = op
)
)
}
/**
* Gets the highest risk level from a set of operations.
*/
bindingset[ops]
string getHighestRisk(string ops) {
(ops.matches("%delegatecall%") or ops.matches("%selfdestruct%")) and result = "critical"
or
not (ops.matches("%delegatecall%") or ops.matches("%selfdestruct%")) and
(
ops.matches("%call%") or ops.matches("%create%") or ops.matches("%sstore%")
) and
result = "high"
or
not (ops.matches("%delegatecall%") or ops.matches("%selfdestruct%")) and
not (ops.matches("%call%") or ops.matches("%create%") or ops.matches("%sstore%")) and
(
ops.matches("%staticcall%") or ops.matches("%sload%") or ops.matches("%extcode%") or
ops.matches("%codecopy%")
) and
result = "medium"
or
not (ops.matches("%delegatecall%") or ops.matches("%selfdestruct%")) and
not (ops.matches("%call%") or ops.matches("%create%") or ops.matches("%sstore%")) and
not (
ops.matches("%staticcall%") or ops.matches("%sload%") or ops.matches("%extcode%") or
ops.matches("%codecopy%")
) and
result = "low"
}
/** One row per inline assembly block, with its risk-ranked operation set. */
query predicate assemblyBlocks(
string contract, string function, string operations, string risk, AssemblyBlock node
) {
contract = getContractName(node.getEnclosingContract()) and
function = getFunctionName(node.getEnclosingFunction()) and
operations =
concat(string op | dangerousOperation(op, _) and assemblyContainsOp(node, op) | op, ",") and
(
operations != "" and risk = getHighestRisk(operations)
or
operations = "" and risk = "low"
)
}
/**
* One row per (assembly block, dangerous operation) pair. Filter on `op` for the
* storage / call / create / selfdestruct subsets.
*/
query predicate assemblyOperations(
string contract, string function, string op, string risk, AssemblyBlock node
) {
contract = getContractName(node.getEnclosingContract()) and
function = getFunctionName(node.getEnclosingFunction()) and
dangerousOperation(op, risk) and
assemblyContainsOp(node, op)
}