Skip to content

Commit

Permalink
Reentrancy warning raised just on the last SSTORE opcode of a basic
Browse files Browse the repository at this point in the history
block
  • Loading branch information
VincenzoArceri committed Nov 4, 2024
1 parent d43a455 commit 7f39359
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 73 deletions.
86 changes: 60 additions & 26 deletions src/main/java/it/unipr/cfg/EVMCFG.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package it.unipr.cfg;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

import it.unipr.analysis.Number;
import it.unipr.cfg.push.Push;
import it.unive.lisa.analysis.AbstractState;
Expand All @@ -25,14 +34,6 @@
import it.unive.lisa.util.datastructures.graph.algorithms.Fixpoint;
import it.unive.lisa.util.datastructures.graph.algorithms.FixpointException;
import it.unive.lisa.util.datastructures.graph.code.NodeList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

public class EVMCFG extends CFG {

Expand Down Expand Up @@ -77,8 +78,8 @@ public Set<Number> getAllJumpdestLocations() {
getAllJumpdest();
if (jumpDestsNodesLocations == null)
return jumpDestsNodesLocations = this.jumpDestsNodes.stream()
.map(j -> new Number(((ProgramCounterLocation) j.getLocation()).getPc()))
.collect(Collectors.toSet());
.map(j -> new Number(((ProgramCounterLocation) j.getLocation()).getPc()))
.collect(Collectors.toSet());
else
return jumpDestsNodesLocations;

Expand Down Expand Up @@ -142,7 +143,7 @@ public <A extends AbstractState<A>> AnalyzedCFG<A> fixpoint(
boolean isOptimized = conf.optimize && conf.descendingPhaseType == DescendingPhaseType.NONE;
Fixpoint<CFG, Statement, Edge, CompoundState<A>> fix = isOptimized
? new OptimizedFixpoint<>(this, false, conf.hotspots)
: new Fixpoint<>(this, false);
: new Fixpoint<>(this, false);
EVMAscendingFixpoint<A> asc = new EVMAscendingFixpoint<>(this, interprocedural,
conf.wideningThreshold);

Expand All @@ -158,14 +159,14 @@ public <A extends AbstractState<A>> AnalyzedCFG<A> fixpoint(
Map<Statement, CompoundState<A>> descending;
switch (conf.descendingPhaseType) {
case GLB:
// DescendingGLBFixpoint<A> dg = new DescendingGLBFixpoint<>(this, conf.glbThreshold,
// interprocedural);
// descending = fix.fixpoint(starting, ws, dg, ascending);
// break;
// DescendingGLBFixpoint<A> dg = new DescendingGLBFixpoint<>(this, conf.glbThreshold,
// interprocedural);
// descending = fix.fixpoint(starting, ws, dg, ascending);
// break;
case NARROWING:
// DescendingNarrowingFixpoint<A> dn = new DescendingNarrowingFixpoint<>(this, interprocedural);
// descending = fix.fixpoint(starting, ws, dn, ascending);
// break;
// DescendingNarrowingFixpoint<A> dn = new DescendingNarrowingFixpoint<>(this, interprocedural);
// descending = fix.fixpoint(starting, ws, dn, ascending);
// break;
case NONE:
default:
// should never happen
Expand All @@ -177,13 +178,13 @@ public <A extends AbstractState<A>> AnalyzedCFG<A> fixpoint(
}

private <V extends ValueDomain<V>,
T extends TypeDomain<T>,
A extends AbstractState<A>,
H extends HeapDomain<H>> AnalyzedCFG<A> flatten(
boolean isOptimized, AnalysisState<A> singleton,
Map<Statement, AnalysisState<A>> startingPoints,
InterproceduralAnalysis<A> interprocedural, ScopeId id,
Map<Statement, CompoundState<A>> fixpointResults) {
T extends TypeDomain<T>,
A extends AbstractState<A>,
H extends HeapDomain<H>> AnalyzedCFG<A> flatten(
boolean isOptimized, AnalysisState<A> singleton,
Map<Statement, AnalysisState<A>> startingPoints,
InterproceduralAnalysis<A> interprocedural, ScopeId id,
Map<Statement, CompoundState<A>> fixpointResults) {
Map<Statement, AnalysisState<A>> finalResults = new HashMap<>(fixpointResults.size());
for (Entry<Statement, CompoundState<A>> e : fixpointResults.entrySet()) {
finalResults.put(e.getKey(), e.getValue().postState);
Expand All @@ -194,7 +195,7 @@ H extends HeapDomain<H>> AnalyzedCFG<A> flatten(
return isOptimized
? new OptimizedAnalyzedCFG<A>(this, id, singleton, startingPoints, finalResults,
interprocedural)
: new AnalyzedCFG<>(this, id, singleton, startingPoints, finalResults);
: new AnalyzedCFG<>(this, id, singleton, startingPoints, finalResults);
}

@Override
Expand Down Expand Up @@ -244,4 +245,37 @@ private boolean dfs(Statement start, Statement target, Set<Statement> visited) {
return false;
}

public boolean reachableFromSequentially(Statement start, Statement target) {
return dfsSequential(start, target, new HashSet<>());
}


private boolean dfsSequential(Statement start, Statement target, Set<Statement> visited) {
Stack<Statement> stack = new Stack<>();
stack.push(start);

while (!stack.isEmpty()) {
Statement current = stack.pop();

if (current.equals(target))
return true;

if (!visited.contains(current)) {
visited.add(current);

Collection<Edge> outgoingEdges = list.getOutgoingEdges(current);

for (Edge edge : outgoingEdges) {
if (edge.getSource() instanceof Jump || edge.getSource() instanceof Jumpi)
continue;
Statement next = edge.getDestination();
if (!visited.contains(next))
stack.push(next);
}
}
}

return false;
}

}
171 changes: 124 additions & 47 deletions src/main/java/it/unipr/checker/ReentrancyChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@ public boolean visit(

if (node instanceof Call) {
EVMCFG cfg = ((EVMCFG) graph);
Call call = (Call) node;
ProgramCounterLocation callLoc = (ProgramCounterLocation) call.getLocation();


for (AnalyzedCFG<SimpleAbstractState<MonolithicHeap, EVMAbstractState,
TypeEnvironment<InferredTypes>>> result : tool.getResultOf(cfg)) {
AnalysisState<SimpleAbstractState<MonolithicHeap, EVMAbstractState,
TypeEnvironment<InferredTypes>>> analysisResult = null;

try {
analysisResult = result.getAnalysisStateBefore(node);
analysisResult = result.getAnalysisStateBefore(call);
} catch (SemanticException e1) {
log.error(e1.getMessage());
e1.printStackTrace();
}

// Retrieve the symbolic stack from the analysis result
Expand All @@ -60,55 +63,129 @@ public boolean visit(
if (valueState.isBottom()) {
// Nothing to do
continue;
} else if (valueState.isTop()) {
for (Statement stmt : ns) {

Pair<Object, Object> myPair = new ImmutablePair<>(node, stmt);
String warn = "Reentrancy attack at " + ((ProgramCounterLocation) stmt.getLocation()).getPc();

if (MyCache.getInstance().existsStmtReachableFrom(myPair)) {
log.debug("[ReentrancyChecker] Value cached.");
if (MyCache.getInstance().isStmtReachableFrom(myPair)) {
tool.warn(warn);
// TODO to optimize, temp solution
UniqueItemCollector.getInstance().add(warn);
}
} else {
if (cfg.reachableFrom(node, stmt)) {
MyCache.getInstance().setStmtReachableFrom(myPair, true);
tool.warn(warn);
// TODO to optimize, temp solution
UniqueItemCollector.getInstance().add(warn);
} else {
if (valueState.isTop()) {
for (Statement sstore : ns) {
Pair<Object, Object> myPair = new ImmutablePair<>(call, sstore);



ProgramCounterLocation sstoreLoc = (ProgramCounterLocation) sstore.getLocation();
if (MyCache.getInstance().existsStmtReachableFrom(myPair)) {
if (MyCache.getInstance().isStmtReachableFrom(myPair)) {

for (Statement otherSstore : ns)
if (!otherSstore.equals(sstore))
if (otherSstore.getLocation().compareTo(sstoreLoc) > 0 && cfg.reachableFromSequentially(sstore, otherSstore))
sstoreLoc = (ProgramCounterLocation) otherSstore.getLocation();

log.debug("Reentrancy attack at "
+ sstoreLoc.getPc() + "at line no. "
+ sstoreLoc.getSourceCodeLine()
+ "coming from line "
+ callLoc.getSourceCodeLine());
String warn = "Reentrancy attack at "
+ sstoreLoc.getPc();
tool.warn(warn);
UniqueItemCollector.getInstance().add(warn); // TODO
// to
// optimize,
// temp
// solution
}
} else {
MyCache.getInstance().setStmtReachableFrom(myPair, false);
if (cfg.reachableFrom(call, sstore)) {


MyCache.getInstance().setStmtReachableFrom(myPair, true);


for (Statement otherSstore : ns)
if (!otherSstore.equals(sstore))
if (otherSstore.getLocation().compareTo(sstoreLoc) > 0 && cfg.reachableFromSequentially(sstore, otherSstore))
sstoreLoc = (ProgramCounterLocation) otherSstore.getLocation();


log.debug("Reentrancy attack at "
+ sstoreLoc.getPc() + "at line no. "
+ sstoreLoc.getSourceCodeLine()
+ "coming from line "
+ callLoc.getSourceCodeLine());
String warn = "Reentrancy attack at "
+ sstoreLoc.getPc();
tool.warn(warn);
UniqueItemCollector.getInstance().add(warn); // TODO
// to
// optimize,
// temp
// solution

} else {
MyCache.getInstance().setStmtReachableFrom(myPair, false);
}
}
}
}
} else {
AbstractStackSet stacks = valueState.getStacks();
for (AbstractStack stack : stacks) {
StackElement topStack = stack.getSecondElement();

if (topStack.isTop() || topStack.isTopNotJumpdest()) {
for (Statement stmt : ns) {

Pair<Object, Object> myPair = new ImmutablePair<>(node, stmt);
String warn = "Reentrancy attack at " + ((ProgramCounterLocation) stmt.getLocation()).getPc();

if (MyCache.getInstance().existsStmtReachableFrom(myPair)) {
log.debug("[ReentrancyChecker] Value cached.");
if (MyCache.getInstance().isStmtReachableFrom(myPair)) {
tool.warn(warn);
// TODO to optimize, temp solution
UniqueItemCollector.getInstance().add(warn);
}
} else {
if (cfg.reachableFrom(node, stmt)) {
MyCache.getInstance().setStmtReachableFrom(myPair, true);
tool.warn(warn);
UniqueItemCollector.getInstance().add(warn);
} else {
AbstractStackSet stacks = valueState.getStacks();
for (AbstractStack stack : stacks) {
StackElement topStack = stack.getSecondElement();

if (topStack.isTop() || topStack.isTopNotJumpdest()) {
for (Statement sstore : ns) {

Pair<Object, Object> myPair = new ImmutablePair<>(call, sstore);

ProgramCounterLocation sstoreLoc = (ProgramCounterLocation) sstore.getLocation();
if (MyCache.getInstance().existsStmtReachableFrom(myPair)) {
if (MyCache.getInstance().isStmtReachableFrom(myPair)) {

for (Statement otherSstore : ns)
if (!otherSstore.equals(sstore))
if (otherSstore.getLocation().compareTo(sstoreLoc) > 0 && cfg.reachableFromSequentially(sstore, otherSstore))
sstoreLoc = (ProgramCounterLocation) otherSstore.getLocation();

log.debug("Reentrancy attack at "
+ sstoreLoc.getPc() + "at line no. "
+ sstoreLoc.getSourceCodeLine()
+ "coming from line "
+ callLoc.getSourceCodeLine());
String warn = "Reentrancy attack at "
+ sstoreLoc.getPc();
tool.warn(warn);
UniqueItemCollector.getInstance().add(warn); // TODO
// to
// optimize,
// temp
// solution
}
} else {
MyCache.getInstance().setStmtReachableFrom(myPair, false);
if (cfg.reachableFrom(call, sstore)) {


for (Statement otherSstore : ns)
if (!otherSstore.equals(sstore))
if (otherSstore.compareTo(sstore) > 0 && cfg.reachableFromSequentially(sstore, otherSstore))
sstoreLoc = (ProgramCounterLocation) otherSstore.getLocation();


MyCache.getInstance().setStmtReachableFrom(myPair, true);
log.debug("Reentrancy attack at "
+ sstoreLoc.getPc() + "at line no. "
+ sstoreLoc.getSourceCodeLine()
+ "coming from line "
+ callLoc.getSourceCodeLine());
String warn = "Reentrancy attack at "
+ sstoreLoc.getPc();
tool.warn(warn);
UniqueItemCollector.getInstance().add(warn); // TODO
// to
// optimize,
// temp
// solution

} else {
MyCache.getInstance().setStmtReachableFrom(myPair, false);
}
}
}
}
Expand Down

0 comments on commit 7f39359

Please sign in to comment.