Skip to content

Commit 6dc926e

Browse files
committed
Refactored EVMLiSAExecutor to support owner-based executors and removed shutdown calls from various classes to avoid bugs
1 parent c0ed7a7 commit 6dc926e

File tree

6 files changed

+167
-131
lines changed

6 files changed

+167
-131
lines changed

src/main/java/it/unipr/EVMLiSA.java

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ else if (cmd.hasOption("bytecode")) {
252252

253253
EVMLiSA.analyzeContract(contract);
254254
System.err.println(contract);
255-
EVMLiSAExecutor.shutdown();
256255
}
257256

258257
/**
@@ -261,19 +260,9 @@ else if (cmd.hasOption("bytecode")) {
261260
* @param filePath the path to the file containing contract addresses
262261
*/
263262
public static void analyzeSetOfContracts(Path filePath) {
264-
analyzeSetOfContracts(filePath, true);
265-
}
266-
267-
/**
268-
* Analyzes a set of smart contracts from a given file.
269-
*
270-
* @param filePath the path to the file containing contract addresses
271-
* @param shutdown whether to shut down the executor after the analysis
272-
*/
273-
public static void analyzeSetOfContracts(Path filePath, boolean shutdown) {
274263
log.info("Building contracts.");
275264
List<SmartContract> contracts = buildContractsFromFile(filePath);
276-
analyzeSetOfContracts(contracts, shutdown);
265+
analyzeSetOfContracts(contracts);
277266
}
278267

279268
/**
@@ -282,22 +271,12 @@ public static void analyzeSetOfContracts(Path filePath, boolean shutdown) {
282271
* @param contracts the list of {@link SmartContract} to be analyzed
283272
*/
284273
public static void analyzeSetOfContracts(List<SmartContract> contracts) {
285-
analyzeSetOfContracts(contracts, true);
286-
}
287-
288-
/**
289-
* Analyzes a set of smart contracts from a list of {@link SmartContract}.
290-
*
291-
* @param contracts the list of {@link SmartContract} to be analyzed
292-
* @param shutdown whether to shut down the executor after the analysis
293-
*/
294-
public static void analyzeSetOfContracts(List<SmartContract> contracts, boolean shutdown) {
295274
log.info("Analyzing {} contracts.", contracts.size());
296275

297276
List<Future<?>> futures = new ArrayList<>();
298277

299278
for (SmartContract contract : contracts)
300-
futures.add(EVMLiSAExecutor.submit(() -> analyzeContract(contract)));
279+
futures.add(EVMLiSAExecutor.submit(EVMLiSA.class, () -> analyzeContract(contract)));
301280

302281
log.debug("{} contracts submitted to Thread pool with {} workers.", contracts.size(),
303282
EVMLiSAExecutor.getCoresAvailable());
@@ -319,8 +298,7 @@ public static void analyzeSetOfContracts(List<SmartContract> contracts, boolean
319298
System.exit(1);
320299
}
321300

322-
if (shutdown)
323-
EVMLiSAExecutor.shutdown();
301+
EVMLiSAExecutor.shutdown(EVMLiSA.class);
324302
}
325303

326304
/**

src/main/java/it/unipr/crosschain/xEVMLiSA.java

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ public static void runAnalysis(Path bytecodeDirectoryPath, Path abiDirectoryPath
5858

5959
printVulnerabilities(bridge);
6060
printVulnerabilitiesPerFunction(bridge);
61-
62-
EVMLiSAExecutor.shutdown();
6361
}
6462

6563
/**
@@ -72,7 +70,7 @@ public static void runAnalysis(Path bytecodeDirectoryPath, Path abiDirectoryPath
7270
public static void analyzeBridge(Bridge bridge) {
7371
log.info("Number of contracts to be analyzed: {}.", bridge.getSmartContracts().size());
7472

75-
EVMLiSA.analyzeSetOfContracts(bridge.getSmartContracts(), false);
73+
EVMLiSA.analyzeSetOfContracts(bridge.getSmartContracts());
7674
bridge.buildPartialXCFG();
7775
bridge.addEdges(
7876
getCrossChainEdgesUsingEventsAndFunctionsEntrypoint(bridge));
@@ -245,21 +243,29 @@ public static void runCrossChainCheckers(Bridge bridge) {
245243

246244
List<Future<?>> futures = new ArrayList<>();
247245
for (SmartContract contract : bridge) {
248-
futures.add(EVMLiSAExecutor.submit(() -> runEventOrderChecker(bridge, contract)));
249-
futures.add(EVMLiSAExecutor.submit(() -> runUncheckedExternalCallChecker(bridge, contract)));
250-
futures.add(EVMLiSAExecutor.submit(() -> runUncheckedExternalInfluenceChecker(bridge, contract)));
251-
futures.add(EVMLiSAExecutor.submit(() -> runMissingEventNotificationChecker(contract)));
246+
futures.add(
247+
EVMLiSAExecutor.submit(xEVMLiSA.class, () -> runEventOrderChecker(bridge, contract)));
248+
futures.add(
249+
EVMLiSAExecutor.submit(xEVMLiSA.class, () -> runUncheckedExternalCallChecker(bridge, contract)));
250+
futures.add(
251+
EVMLiSAExecutor.submit(xEVMLiSA.class,
252+
() -> runUncheckedExternalInfluenceChecker(bridge, contract)));
253+
futures.add(
254+
EVMLiSAExecutor.submit(xEVMLiSA.class, () -> runMissingEventNotificationChecker(contract)));
255+
futures.add(
256+
EVMLiSAExecutor.submit(xEVMLiSA.class, () -> runAccessControlIncompleteness(bridge, contract)));
252257
}
253-
futures.add(EVMLiSAExecutor.submit(() -> runLocalDependencyCheckers(bridge)));
258+
futures.add(
259+
EVMLiSAExecutor.submit(xEVMLiSA.class, () -> runLocalDependencyCheckers(bridge)));
254260
EVMLiSAExecutor.awaitCompletionFutures(futures);
255261

256262
log.info("Saving cross-chain checkers results.");
257-
for (SmartContract contract : bridge) {
263+
for (SmartContract contract : bridge)
258264
contract.setVulnerabilities(
259265
VulnerabilitiesObject.buildFromCFG(
260266
contract.getCFG()));
261-
}
262267

268+
EVMLiSAExecutor.shutdown(xEVMLiSA.class);
263269
log.info("[OUT] Cross-chain checkers results saved.");
264270
}
265271

@@ -280,18 +286,24 @@ public static void runLocalDependencyCheckers(Bridge bridge) {
280286

281287
log.info("[LocalDependencyChecker] Computing vulnerable LOGs.");
282288
for (SmartContract contract : bridge)
283-
futures.add(EVMLiSAExecutor.submit(() -> computeVulnerablesLOGsForLocalDependencyChecker(contract)));
289+
futures.add(
290+
EVMLiSAExecutor.submit(xEVMLiSA.class,
291+
() -> computeVulnerablesLOGsForLocalDependencyChecker(contract)));
284292
EVMLiSAExecutor.awaitCompletionFutures(futures);
285293
log.info("[LocalDependencyChecker] Vulnerable LOGs computed.");
286294

287295
log.info("[LocalDependencyChecker] Computing tainted Call Data.");
288296
for (SmartContract contract : bridge)
289-
futures.add(EVMLiSAExecutor.submit(() -> computeTaintedCallDataForLocalDependencyChecker(contract)));
297+
futures.add(
298+
EVMLiSAExecutor.submit(xEVMLiSA.class,
299+
() -> computeTaintedCallDataForLocalDependencyChecker(contract)));
290300
EVMLiSAExecutor.awaitCompletionFutures(futures);
291301
log.info("[LocalDependencyChecker] Tainted Call Data computed.");
292302

293303
for (SmartContract contract : bridge)
294-
futures.add(EVMLiSAExecutor.submit(() -> runLocalDependencyChecker(contract)));
304+
futures.add(
305+
EVMLiSAExecutor.submit(xEVMLiSA.class,
306+
() -> runLocalDependencyChecker(contract)));
295307
EVMLiSAExecutor.awaitCompletionFutures(futures);
296308

297309
log.info("[OUT] Local Dependency checker ended.");
@@ -331,6 +343,27 @@ public static void runUncheckedExternalInfluenceChecker(Bridge bridge, SmartCont
331343
MyCache.getInstance().getPossibleUncheckedExternalInfluenceWarnings(contract.getCFG().hashCode()));
332344
}
333345

346+
public static void runAccessControlIncompleteness(Bridge bridge, SmartContract contract) {
347+
log.info("[IN] Running Access Control Incompleteness checker on {}.", contract.getName());
348+
349+
// Setup configuration
350+
Program program = new Program(new EVMLiSAFeatures(), new EVMLiSATypeSystem());
351+
program.addCodeMember(contract.getCFG());
352+
LiSAConfiguration conf = LiSAConfigurationManager.createConfiguration(contract);
353+
LiSA lisa = new LiSA(conf);
354+
355+
AccessControlIncompletenessChecker checker = new AccessControlIncompletenessChecker();
356+
conf.semanticChecks.add(checker);
357+
conf.abstractState = new SimpleAbstractState<>(new MonolithicHeap(),
358+
new AccessControlIncompletenessAbstractDomain(),
359+
new TypeEnvironment<>(new InferredTypes()));
360+
lisa.run(program);
361+
362+
log.info("[OUT] Access Control Incompleteness checker ended on {}, with {} vulnerabilities found.",
363+
contract.getName(),
364+
MyCache.getInstance().getUncheckedExternalCallWarnings(contract.getCFG().hashCode()));
365+
}
366+
334367
/**
335368
* Runs the Unchecked External Call Checker on a single contract. This
336369
* configures and invokes LiSA with the UncheckedExternalCallChecker to

src/main/java/it/unipr/utils/EVMLiSAExecutor.java

Lines changed: 90 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,74 +11,12 @@ public class EVMLiSAExecutor {
1111
private static int CORES = (Runtime.getRuntime().availableProcessors() > 1)
1212
? Runtime.getRuntime().availableProcessors() - 1
1313
: 1;
14-
private static ExecutorService _executor = Executors.newFixedThreadPool(CORES);
14+
private static final ConcurrentMap<Class<?>, ExecutorService> ownerExecutors = new ConcurrentHashMap<>();
1515

1616
private static long tasksInQueue = 0;
17-
@SuppressWarnings("unused")
1817
private static long tasksExecuted = 0;
1918
private static long tasksTimedOut = 0;
2019

21-
/**
22-
* Submits a task for execution in the thread pool.
23-
*
24-
* @param task the task to be executed
25-
*
26-
* @return a Future representing the pending result of the task
27-
*/
28-
public static Future<?> submit(Runnable task) {
29-
return _executor.submit(new EVMLiSAExecutorTask(task));
30-
}
31-
32-
/**
33-
* Submits a list of tasks for execution in the thread pool.
34-
*
35-
* @param tasks the list of tasks to be executed
36-
*
37-
* @return a list of Future objects representing the pending results of the
38-
* tasks
39-
*/
40-
public static List<Future<?>> submitAll(List<Runnable> tasks) {
41-
List<Future<?>> futures = new ArrayList<>();
42-
43-
for (Runnable task : tasks)
44-
futures.add(submit(new EVMLiSAExecutorTask(task)));
45-
46-
return futures;
47-
}
48-
49-
/**
50-
* Executes a main task asynchronously and schedules additional tasks upon
51-
* its completion.
52-
*
53-
* @param mainTask the main task to execute
54-
* @param secondaryTasks additional tasks to execute after the main task
55-
*
56-
* @return a CompletableFuture that completes when all tasks are finished
57-
*/
58-
public static CompletableFuture<Void> runAsync(Runnable mainTask, Runnable... secondaryTasks) {
59-
return runAsync(mainTask, Set.of(secondaryTasks));
60-
}
61-
62-
/**
63-
* Executes a main task asynchronously and schedules additional tasks upon
64-
* its completion.
65-
*
66-
* @param mainTask the main task to execute
67-
* @param secondaryTasks additional tasks to execute after the main task
68-
*
69-
* @return a CompletableFuture that completes when all tasks are finished
70-
*/
71-
public static CompletableFuture<Void> runAsync(Runnable mainTask, Set<Runnable> secondaryTasks) {
72-
return CompletableFuture.runAsync(new EVMLiSAExecutorTask(mainTask), _executor)
73-
.thenCompose(ignored -> {
74-
List<CompletableFuture<Void>> checkerFutures = new ArrayList<>();
75-
for (Runnable secondaryTask : secondaryTasks)
76-
checkerFutures
77-
.add(CompletableFuture.runAsync(new EVMLiSAExecutorTask(secondaryTask), _executor));
78-
return CompletableFuture.allOf(checkerFutures.toArray(new CompletableFuture[0]));
79-
});
80-
}
81-
8220
/**
8321
* Waits for the completion of all provided CompletableFutures.
8422
*
@@ -124,7 +62,6 @@ public static void awaitCompletionFutures(List<Future<?>> futures) {
12462
future.get();
12563
} catch (ExecutionException e) {
12664
System.err.println(JSONManager.throwNewError("Error during task execution: " + e.getMessage()));
127-
e.printStackTrace();
12865
System.exit(1);
12966
} catch (InterruptedException ie) {
13067
System.err.println(JSONManager.throwNewError("Interrupted during task execution: " + ie.getMessage()));
@@ -159,25 +96,108 @@ public static void awaitCompletionFutures(List<Future<?>> futures, long timeout,
15996
}
16097

16198
/**
162-
* Shuts down the executor service, preventing new tasks from being
163-
* submitted.
99+
* Creates (if absent) and returns an ExecutorService associated with the
100+
* given owner class. The created executor will have {@code threads} worker
101+
* threads. If an executor is already present for the owner, it is returned
102+
* unchanged.
103+
*
104+
* @param owner the class that will own the executor
105+
* @param threads number of threads for the executor (min 1)
106+
*
107+
* @return the ExecutorService associated with the owner
108+
*/
109+
public static ExecutorService createExecutorFor(Class<?> owner, int threads) {
110+
int t = Math.max(1, threads);
111+
return ownerExecutors.computeIfAbsent(owner, k -> Executors.newFixedThreadPool(t));
112+
}
113+
114+
/**
115+
* Returns the ExecutorService associated with the owner class, creating a
116+
* default single-threaded executor if none exists.
117+
*
118+
* @param owner the class that will own the executor
119+
*
120+
* @return the ExecutorService associated with the owner
121+
*/
122+
public static ExecutorService getExecutorFor(Class<?> owner) {
123+
return ownerExecutors.computeIfAbsent(owner, k -> Executors.newFixedThreadPool(CORES));
124+
}
125+
126+
/**
127+
* Submits a task to the executor associated with the given owner class. If
128+
* no executor exists for the owner, a default one is created.
129+
*
130+
* @param owner the class owning the executor
131+
* @param task the runnable task to submit
132+
*
133+
* @return a Future representing pending completion of the task
134+
*/
135+
public static Future<?> submit(Class<?> owner, Runnable task) {
136+
ExecutorService ex = getExecutorFor(owner);
137+
return ex.submit(new EVMLiSAExecutorTask(task));
138+
}
139+
140+
/**
141+
* Shuts down the executor associated with the given owner class. This
142+
* removes the executor from the internal map and calls shutdown on it. If
143+
* no executor is present, this is a no-op.
144+
*
145+
* @param owner the class whose executor should be shutdown
146+
*/
147+
public static void shutdown(Class<?> owner) {
148+
ExecutorService ex = ownerExecutors.remove(owner);
149+
if (ex != null)
150+
ex.shutdown();
151+
}
152+
153+
/**
154+
* Shuts down all owner-bound executors.
155+
*/
156+
public static void shutdownAllOwners() {
157+
for (Map.Entry<Class<?>, ExecutorService> e : ownerExecutors.entrySet()) {
158+
executorShutdownQuiet(e.getValue());
159+
}
160+
ownerExecutors.clear();
161+
}
162+
163+
/**
164+
* Attempts to shut down the provided executor quietly, ignoring any
165+
* exceptions. This is a private helper used when shutting down multiple
166+
* executors.
167+
*
168+
* @param ex the executor service to shut down
164169
*/
165-
public static void shutdown() {
166-
_executor.shutdown();
170+
private static void executorShutdownQuiet(ExecutorService ex) {
171+
try {
172+
ex.shutdown();
173+
} catch (Exception ignored) {
174+
}
167175
}
168176

177+
/**
178+
* Updates the number of cores available for per-owner executors. If the
179+
* provided value differs from the current configuration, all owner-bound
180+
* executors are shutdown and the internal core count is updated.
181+
*
182+
* @param cores desired number of cores (will be clamped to a minimum of 1
183+
* and at most the number of available processors - 1)
184+
*/
169185
public static void setCoresAvailable(int cores) {
170186
if (cores > Runtime.getRuntime().availableProcessors())
171187
cores = Runtime.getRuntime().availableProcessors() - 1;
172188

173189
if (CORES == cores)
174190
return;
175191

176-
shutdown();
192+
shutdownAllOwners();
177193
CORES = Math.max(cores, 1);
178-
_executor = Executors.newFixedThreadPool(CORES);
179194
}
180195

196+
/**
197+
* Gets the current number of cores configured for owner executors.
198+
*
199+
* @return the configured core count
200+
*/
181201
public static int getCoresAvailable() {
182202
return CORES;
183203
}

0 commit comments

Comments
 (0)