1616import it .unive .lisa .checks .semantic .SemanticCheck ;
1717import it .unive .lisa .program .cfg .CFG ;
1818import it .unive .lisa .program .cfg .statement .Statement ;
19+ import java .util .HashMap ;
1920import java .util .HashSet ;
21+ import java .util .Map ;
2022import java .util .Set ;
2123import org .apache .logging .log4j .LogManager ;
2224import 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