Skip to content

Commit 9bc0d96

Browse files
committed
Implementing checker for the detection of cross-channel invocation issues
1 parent ad56283 commit 9bc0d96

File tree

3 files changed

+115
-15
lines changed

3 files changed

+115
-15
lines changed

go-lisa/src/main/java/it/unive/golisa/GoLiSA.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import it.unive.golisa.checker.IntegrityNIChecker;
1212
import it.unive.golisa.checker.NumericalOverflowOfVariablesChecker;
1313
import it.unive.golisa.checker.TaintChecker;
14+
import it.unive.golisa.checker.hf.CrossChannelInvocationsIssuesChecker;
1415
import it.unive.golisa.checker.hf.DifferentCrossChannelInvocationsChecker;
1516
import it.unive.golisa.checker.hf.UnhandledErrorsChecker;
1617
import it.unive.golisa.checker.hf.readwrite.ReadWritePairChecker;
@@ -177,11 +178,11 @@ public static void main(String[] args) throws AnalysisSetupException {
177178
new ValueEnvironment<>(new TaintDomain()), new TypeEnvironment<>(new InferredTypes()));
178179
conf.semanticChecks.add(new TaintChecker("Possible untrusted cross-contract invocation."));
179180
break;
180-
case "dcci":
181+
case "cchi":
181182
conf.openCallPolicy = RelaxedOpenCallPolicy.INSTANCE;
182183
conf.abstractState = new SimpleAbstractState<>(new PointBasedHeap(), new ValueEnvironment<>(new Tarsis()),
183184
new TypeEnvironment<>(new InferredTypes()));
184-
conf.semanticChecks.add(new DifferentCrossChannelInvocationsChecker());
185+
conf.semanticChecks.add(new CrossChannelInvocationsIssuesChecker());
185186
break;
186187
case "read-write":
187188

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package it.unive.golisa.checker.hf;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
import java.util.SortedSet;
6+
7+
import it.unive.lisa.analysis.string.tarsis.RegexAutomaton;
8+
import it.unive.lisa.util.datastructures.automaton.State;
9+
import it.unive.lisa.util.datastructures.automaton.Transition;
10+
import it.unive.lisa.util.datastructures.regex.RegularExpression;
11+
import it.unive.lisa.util.datastructures.regex.TopAtom;
12+
13+
public class AutomatonUtils {
14+
15+
public static boolean containsTopTransaction(RegexAutomaton automaton) {
16+
for(Transition<RegularExpression> t : automaton.getTransitions())
17+
if(t.getSymbol().equals(TopAtom.INSTANCE))
18+
return true;
19+
return false;
20+
}
21+
22+
public static boolean hasCycle(RegexAutomaton automaton) {
23+
24+
Set<State> initStates = automaton.getInitialStates();
25+
26+
for(State i : initStates) {
27+
Set<State> seen = new HashSet<State>();
28+
return recursiveDFS(i,automaton,seen);
29+
}
30+
return false;
31+
32+
}
33+
34+
private static boolean recursiveDFS(State s, RegexAutomaton automaton, Set<State> seen) {
35+
if(seen.add(s)) {
36+
for(Transition<RegularExpression> t : automaton.getOutgoingTransitionsFrom(s)) {
37+
if(seen.contains(t.getDestination()))
38+
return true;
39+
else
40+
recursiveDFS(t.getDestination(), automaton, Set.copyOf(seen));
41+
}
42+
return false;
43+
44+
} else
45+
return true;
46+
}
47+
48+
49+
50+
}

go-lisa/src/main/java/it/unive/golisa/checker/hf/DifferentCrossChannelInvocationsChecker.java renamed to go-lisa/src/main/java/it/unive/golisa/checker/hf/CrossChannelInvocationsIssuesChecker.java

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,36 +33,68 @@
3333
import java.util.Map;
3434
import java.util.Set;
3535

36+
import org.apache.commons.lang3.tuple.Pair;
37+
3638
/**
3739
* A Go Checker for the detection of different cross-channel invocations in
3840
* Hyperledger Fabric.
3941
*
4042
* @author <a href="mailto:[email protected]">Luca Olivieri</a>
4143
*/
42-
public class DifferentCrossChannelInvocationsChecker implements
44+
public class CrossChannelInvocationsIssuesChecker implements
4345
SemanticCheck<
4446
SimpleAbstractState<PointBasedHeap, ValueEnvironment<Tarsis>, TypeEnvironment<InferredTypes>>> {
4547

46-
private Map<Statement, Set<Tarsis>> crossChannelInvocations;
48+
private Map<Statement, Set<Tarsis>> crossContractInvocations;
49+
50+
private boolean intraChaincode = true;
4751

52+
public CrossChannelInvocationsIssuesChecker() {
53+
}
54+
55+
public CrossChannelInvocationsIssuesChecker(boolean intraChaincode) {
56+
this.intraChaincode = intraChaincode;
57+
}
4858
@Override
4959
public void beforeExecution(CheckToolWithAnalysisResults<
5060
SimpleAbstractState<PointBasedHeap, ValueEnvironment<Tarsis>, TypeEnvironment<InferredTypes>>> tool) {
51-
crossChannelInvocations = new HashMap<>();
61+
crossContractInvocations = new HashMap<>();
5262
}
5363

5464
@Override
5565
public void afterExecution(
5666
CheckToolWithAnalysisResults<
5767
SimpleAbstractState<PointBasedHeap, ValueEnvironment<Tarsis>,
5868
TypeEnvironment<InferredTypes>>> tool) {
59-
60-
Statement[] invocations = crossChannelInvocations.keySet().toArray(new Statement[] {});
69+
70+
Set<Statement> singleCrossChannelInvocations = new HashSet<>();
71+
72+
//CASE 1: single CCIs with arbitrary channel
73+
Statement[] invocations = crossContractInvocations.keySet().toArray(new Statement[] {});
74+
for (int i = 0; i < invocations.length; i++) {
75+
Set<Tarsis> stringApproximations = crossContractInvocations.get(invocations[i]);
76+
for(Tarsis t : stringApproximations) {
77+
if(mayCrossChannel(t)) {
78+
singleCrossChannelInvocations.add(invocations[i]);
79+
break;
80+
}
81+
}
82+
}
83+
84+
for(Statement cch : singleCrossChannelInvocations) {
85+
tool.warnOn(cch, "Detected possible cross-channel invocation. It may lead to a lack of transparency because no new transactions are created during the invocation.");
86+
if(intraChaincode)
87+
tool.warnOn(cch, "Detected possible cross-channel invocation. It may lead to uncommited write operations during the execution of callee chaincode.");
88+
}
89+
90+
//CASE 2: CCIs with different channels
91+
Set<Pair<Statement,Statement>> multipleCrossChannelInvocations = new HashSet<>();
92+
6193
boolean isDiff = false;
6294
for (int i = 0; i < invocations.length - 1; i++) {
6395
for (int j = i + 1; j < invocations.length; j++) {
64-
Set<Tarsis> t1Set = crossChannelInvocations.get(invocations[i]);
65-
Set<Tarsis> t2Set = crossChannelInvocations.get(invocations[j]);
96+
Set<Tarsis> t1Set = crossContractInvocations.get(invocations[i]);
97+
Set<Tarsis> t2Set = crossContractInvocations.get(invocations[j]);
6698
for (Tarsis t1 : t1Set) {
6799
for (Tarsis t2 : t2Set) {
68100
if (t1.isTop() || t2.isTop()
@@ -79,13 +111,30 @@ public void afterExecution(
79111
break;
80112
}
81113

82-
if (isDiff)
83-
tool.warnOn(invocations[i],
84-
"Detected cross-channel invocations on different channels. The other invocation: "
85-
+ invocations[j].getLocation());
114+
if (isDiff) {
115+
if(!multipleCrossChannelInvocations.contains(Pair.of(invocations[j], invocations[i])))
116+
multipleCrossChannelInvocations.add(Pair.of(invocations[i], invocations[j]));
117+
}
118+
86119
}
87120
}
121+
122+
for( Pair<Statement, Statement> cchs : multipleCrossChannelInvocations) {
123+
tool.warnOn(cchs.getLeft(),
124+
"Detected cross-channel invocations on different channels. The other invocation: "
125+
+ cchs.getRight().getLocation() + ". They may lead to a lack of transparency because no new transactions are created during the invocation.");
126+
if(intraChaincode)
127+
tool.warnOn(cchs.getLeft(),
128+
"Detected cross-channel invocations on different channels. The other invocation: "
129+
+ cchs.getRight().getLocation() + ". They may lead to uncommited write operations during the execution of callee chaincode.");
130+
}
131+
132+
}
88133

134+
private boolean mayCrossChannel(Tarsis t) {
135+
return t.isTop() || t.getAutomaton().getFinalStates().size() > 2
136+
|| AutomatonUtils.containsTopTransaction(t.getAutomaton())
137+
|| AutomatonUtils.hasCycle(t.getAutomaton());
89138
}
90139

91140
@Override
@@ -142,9 +191,9 @@ public boolean visit(
142191
channelValues = extractChannelValues(call, call.getParameters().length, node, result);
143192
}
144193

145-
crossChannelInvocations.putIfAbsent(call, new HashSet<>());
194+
crossContractInvocations.putIfAbsent(call, new HashSet<>());
146195
if (channelValues != null)
147-
crossChannelInvocations.get(call).addAll(channelValues);
196+
crossContractInvocations.get(call).addAll(channelValues);
148197
}
149198

150199
} catch (SemanticException e) {

0 commit comments

Comments
 (0)