Skip to content

Commit a071cc2

Browse files
committed
Enhanced AccessControlIncompletenessChecker to track sanitized program points
1 parent a1f1710 commit a071cc2

File tree

1 file changed

+88
-52
lines changed

1 file changed

+88
-52
lines changed

src/main/java/it/unipr/crosschain/checker/AccessControlIncompletenessChecker.java

Lines changed: 88 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import it.unive.lisa.checks.semantic.SemanticCheck;
1717
import it.unive.lisa.program.cfg.CFG;
1818
import it.unive.lisa.program.cfg.statement.Statement;
19+
import java.util.HashMap;
1920
import java.util.HashSet;
21+
import java.util.Map;
2022
import java.util.Set;
2123
import org.apache.logging.log4j.LogManager;
2224
import org.apache.logging.log4j.Logger;
@@ -34,6 +36,7 @@ public class AccessControlIncompletenessChecker implements
3436
private static final Logger log = LogManager.getLogger(AccessControlIncompletenessChecker.class);
3537

3638
private Set<Statement> taintedJumpi = new HashSet<>();
39+
private Map<Statement, Set<Integer>> jumpiProgramPoints = new HashMap<>();
3740
private SmartContract contract;
3841

3942
/**
@@ -82,17 +85,35 @@ public boolean visit(
8285
analysisResult = result.getAnalysisStateBefore(node);
8386
} catch (SemanticException e1) {
8487
log.error("(AccessControlIncompletenessChecker): {}", e1.getMessage());
88+
continue;
8589
}
8690

8791
RelationalTaintAbstractDomain taintedStack = analysisResult.getState().getValueState();
8892

8993
if (taintedStack.isBottom() || taintedStack.isTop())
9094
continue;
9195

92-
if (RelationalTaintElement.isAtLeastOneTainted(
93-
taintedStack.getElementAtPosition(1),
94-
taintedStack.getElementAtPosition(2)))
96+
RelationalTaintElement elem1 = taintedStack.getElementAtPosition(1);
97+
RelationalTaintElement elem2 = taintedStack.getElementAtPosition(2);
98+
99+
if (RelationalTaintElement.isAtLeastOneTainted(elem1, elem2)) {
95100
taintedJumpi.add(node);
101+
// Track program points sanitized by this specific Jumpi
102+
Set<Integer> jumpiPps = new HashSet<>();
103+
if (elem1.isTaint()) {
104+
log.debug("[SANITIZER] Tainted Jumpi at pc {} - elem1 sanitizes program points: {}",
105+
((ProgramCounterLocation) node.getLocation()).getPc(),
106+
elem1.getProgramPoints());
107+
jumpiPps.addAll(elem1.getProgramPoints());
108+
}
109+
if (elem2.isTaint()) {
110+
log.debug("[SANITIZER] Tainted Jumpi at pc {} - elem2 sanitizes program points: {}",
111+
((ProgramCounterLocation) node.getLocation()).getPc(),
112+
elem2.getProgramPoints());
113+
jumpiPps.addAll(elem2.getProgramPoints());
114+
}
115+
jumpiProgramPoints.put(node, jumpiPps);
116+
}
96117
}
97118
return true;
98119
}
@@ -127,19 +148,14 @@ public boolean visit(
127148

128149
int numArgs = getNumberOfArgs(node);
129150
boolean isAtLeastOneTainted = false;
130-
boolean isAtLeastOneTop = false;
131151

132152
for (int argIndex = 1; argIndex <= numArgs; argIndex++) {
133153
isAtLeastOneTainted |= RelationalTaintElement.isAtLeastOneTainted(
134154
taintedStack.getElementAtPosition(argIndex));
135-
isAtLeastOneTop |= RelationalTaintElement.isAtLeastOneTop(
136-
taintedStack.getElementAtPosition(argIndex));
137155
}
138156

139157
if (isAtLeastOneTainted)
140-
checkForAccessControlIncompleteness(tool, cfg, node, false);
141-
else if (isAtLeastOneTop)
142-
checkForAccessControlIncompleteness(tool, cfg, node, true);
158+
checkForAccessControlIncompleteness(tool, cfg, node);
143159
}
144160
}
145161
return true;
@@ -177,7 +193,7 @@ private int getNumberOfArgs(Statement node) {
177193
private void checkForAccessControlIncompleteness(CheckToolWithAnalysisResults<
178194
SimpleAbstractState<MonolithicHeap, RelationalTaintAbstractDomain, TypeEnvironment<InferredTypes>>> tool,
179195
EVMCFG cfg,
180-
Statement sink, boolean isTop) {
196+
Statement sink) {
181197

182198
Set<Statement> sources = cfg.getAllStatementsByClass(
183199
Calldataload.class,
@@ -188,52 +204,72 @@ private void checkForAccessControlIncompleteness(CheckToolWithAnalysisResults<
188204
Calldatasize.class);
189205

190206
for (Statement source : sources) {
191-
if (cfg.reachableFromWithoutStatements(source, sink, taintedJumpi)) {
192-
String functionSignatureByStatement = contract.getFunctionSignatureByStatement(source);
193-
194-
// It means that this vulnerability is inside a private function
195-
if (functionSignatureByStatement.equals("no-function-found"))
196-
continue;
207+
// Check if source can reach sink
208+
if (cfg.reachableFromWithoutStatements(source, sink, new HashSet<>())) {
209+
int sourcePC = ((ProgramCounterLocation) source.getLocation()).getPc();
210+
211+
// Get all tainted jumpi on the path from source to sink
212+
Set<Statement> jumpiBetweenSourceAndSink = cfg.getStatementsInAPathWithTypes(source, sink, Jumpi.class);
213+
214+
// Check if at least one jumpi sanitizes this source's program
215+
// point
216+
boolean isSanitized = false;
217+
for (Statement jumpi : jumpiBetweenSourceAndSink) {
218+
Set<Integer> sanitizedPps = jumpiProgramPoints.get(jumpi);
219+
if (sanitizedPps == null)
220+
continue;
221+
if (sanitizedPps.contains(sourcePC)) {
222+
isSanitized = true;
223+
break;
224+
}
225+
}
197226

198-
if (isTop) {
199-
// log.warn(
200-
// "[POSSIBLE] Access Control Incompleteness vulnerability at pc {} (line {}) coming from pc {} (line {}).",
201-
// ((ProgramCounterLocation) sink.getLocation()).getPc(),
202-
// ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine(),
203-
// ((ProgramCounterLocation) source.getLocation()).getPc(),
204-
// ((ProgramCounterLocation) source.getLocation()).getSourceCodeLine());
205-
206-
String warn = "[POSSIBLE] Access Control Incompleteness vulnerability at "
207-
+ ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine();
208-
tool.warn(warn);
209-
MyCache.getInstance().addPossibleAccessControlIncompletenessWarning(cfg.hashCode(), warn);
210-
211-
// warn = "[POSSIBLE] Access Control Incompleteness vulnerability in " + contract.getName() + " at "
212-
// + functionSignatureByStatement
213-
// + " (pc: " + ((ProgramCounterLocation) sink.getLocation()).getPc() + ", "
214-
// + "line: " + ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine() + ")";
215-
// MyCache.getInstance().addVulnerabilityPerFunction(cfg.hashCode(), warn);
216-
} else {
217-
log.warn(
218-
"[DEFINITE] Access Control Incompleteness vulnerability at pc {} (line {}) coming from pc {} (line {}).",
219-
((ProgramCounterLocation) sink.getLocation()).getPc(),
220-
((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine(),
221-
((ProgramCounterLocation) source.getLocation()).getPc(),
222-
((ProgramCounterLocation) source.getLocation()).getSourceCodeLine());
223-
224-
String warn = "[DEFINITE] Access Control Incompleteness vulnerability at "
225-
+ ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine();
226-
tool.warn(warn);
227-
MyCache.getInstance().addAccessControlIncompletenessWarning(cfg.hashCode(), warn);
228-
229-
warn = "[DEFINITE] Access Control Incompleteness vulnerability in " + contract.getName() + " at "
230-
+ functionSignatureByStatement
231-
+ " (pc: " + ((ProgramCounterLocation) sink.getLocation()).getPc() + ", "
232-
+ "line: " + ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine() + ")";
233-
MyCache.getInstance().addVulnerabilityPerFunction(cfg.hashCode(), warn);
227+
// If not sanitized by any jumpi on the path, it's a
228+
// vulnerability
229+
if (!isSanitized) {
230+
reportVulnerability(tool, cfg, source, sink);
234231
}
235232
}
236233
}
237234
}
238235

236+
/**
237+
* Reports a vulnerability found by the checker.
238+
*
239+
* @param tool the analysis tool to report warnings on
240+
* @param cfg the CFG under inspection
241+
* @param source the untrusted source statement
242+
* @param sink the sensitive sink statement
243+
*/
244+
private void reportVulnerability(CheckToolWithAnalysisResults<
245+
SimpleAbstractState<MonolithicHeap, RelationalTaintAbstractDomain, TypeEnvironment<InferredTypes>>> tool,
246+
EVMCFG cfg,
247+
Statement source,
248+
Statement sink) {
249+
String functionSignatureByStatement = contract.getFunctionSignatureByStatement(source);
250+
251+
// It means that this vulnerability is inside a private function
252+
if (functionSignatureByStatement.equals("no-function-found"))
253+
return;
254+
255+
log.warn(
256+
"[DEFINITE] Access Control Incompleteness vulnerability at pc {} (line {}) coming from pc {} (line {}).",
257+
((ProgramCounterLocation) sink.getLocation()).getPc(),
258+
((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine(),
259+
((ProgramCounterLocation) source.getLocation()).getPc(),
260+
((ProgramCounterLocation) source.getLocation()).getSourceCodeLine());
261+
262+
String warn = "[DEFINITE] Access Control Incompleteness vulnerability at "
263+
+ ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine();
264+
tool.warn(warn);
265+
MyCache.getInstance().addAccessControlIncompletenessWarning(cfg.hashCode(), warn);
266+
267+
warn = "[DEFINITE] Access Control Incompleteness vulnerability in " + contract.getName() + " at "
268+
+ functionSignatureByStatement
269+
+ " (pc: " + ((ProgramCounterLocation) sink.getLocation()).getPc() + ", "
270+
+ "line: " + ((ProgramCounterLocation) sink.getLocation()).getSourceCodeLine() + ")";
271+
MyCache.getInstance().addVulnerabilityPerFunction(cfg.hashCode(), warn);
272+
273+
}
274+
239275
}

0 commit comments

Comments
 (0)