diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Producer.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Producer.kt
new file mode 100644
index 0000000000..d7f23111e1
--- /dev/null
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Producer.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2022 UnitTestBot contributors (utbot.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.usvm.dataflow.ifds
+
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
+
+interface Producer {
+ fun produce(event: T)
+ fun subscribe(consumer: Consumer)
+}
+
+fun interface Consumer {
+ fun consume(event: T)
+}
+
+class SyncProducer : Producer {
+ private val consumers: MutableList> = mutableListOf()
+ private val events: MutableList = mutableListOf()
+
+ @Synchronized
+ override fun produce(event: T) {
+ for (consumer in consumers) {
+ consumer.consume(event)
+ }
+ events.add(event)
+ }
+
+ @Synchronized
+ override fun subscribe(consumer: Consumer) {
+ for (event in events) {
+ consumer.consume(event)
+ }
+ consumers.add(consumer)
+ }
+}
+
+sealed interface ConsList : Iterable
+
+data object Nil : ConsList {
+ override fun iterator(): Iterator = object : Iterator {
+ override fun hasNext(): Boolean = false
+ override fun next(): Nothing = throw NoSuchElementException()
+ }
+}
+
+data class Cons(
+ val value: T,
+ val tail: ConsList,
+) : ConsList {
+ override fun iterator(): Iterator = Iter(this)
+
+ private class Iter(private var list: ConsList) : Iterator {
+ override fun hasNext(): Boolean = list !is Nil
+
+ override fun next(): T = when (val list = list) {
+ is Nil -> throw NoSuchElementException()
+ is Cons -> {
+ val value = list.value
+ this.list = list.tail
+ value
+ }
+ }
+ }
+}
+
+class NonBlockingQueue {
+ data class Node(
+ val value: T,
+ @Volatile var next: Node? = null,
+ )
+
+ var head: Node? = null
+ private set
+ val tail: AtomicReference> = AtomicReference(head)
+ val size: AtomicInteger = AtomicInteger(0)
+
+ fun add(element: T) {
+ val node = Node(element)
+ var currentTail: Node?
+ while (true) {
+ currentTail = tail.get()
+ if (tail.compareAndSet(currentTail, node)) break
+ }
+ if (currentTail != null) {
+ currentTail.next = node
+ } else {
+ head = node
+ }
+ size.incrementAndGet()
+ }
+}
+
+class ConcurrentProducer : Producer {
+ private var consumers: AtomicReference>> = AtomicReference(Nil)
+ private val events: NonBlockingQueue = NonBlockingQueue()
+
+ override fun produce(event: T) {
+ var currentConsumers: ConsList>
+ while (true) {
+ currentConsumers = consumers.get() ?: continue
+ if (consumers.compareAndSet(currentConsumers, null)) break
+ }
+
+ events.add(event)
+
+ try {
+ for (consumer in currentConsumers) {
+ consumer.consume(event)
+ }
+ } finally {
+ check(consumers.compareAndSet(null, currentConsumers))
+ }
+ }
+
+ override fun subscribe(consumer: Consumer) {
+ var last: NonBlockingQueue.Node? = null
+ while (true) {
+ val start = if (last != null) last.next else events.head
+ var current = start
+ while (current != null) {
+ last = current
+ consumer.consume(current.value)
+ current = current.next
+ }
+
+ val currentConsumers = consumers.get() ?: continue
+ if (!consumers.compareAndSet(currentConsumers, null)) continue
+ if (events.tail.get() === last) {
+ val newConsumers = Cons(consumer, currentConsumers)
+ check(consumers.compareAndSet(null, newConsumers))
+ break
+ } else {
+ check(consumers.compareAndSet(null, currentConsumers))
+ }
+ }
+ }
+}
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Runner.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Runner.kt
index c5281712eb..28496e996d 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Runner.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Runner.kt
@@ -114,6 +114,7 @@ class UniRunner(
// Add edge to worklist:
workList.trySend(edge).getOrThrow()
+ manager.handleControlEvent(queueIsNotEmpty)
return true
}
@@ -126,7 +127,6 @@ class UniRunner(
val edge = workList.tryReceive().getOrElse {
manager.handleControlEvent(queueIsEmpty)
val edge = workList.receive()
- manager.handleControlEvent(queueIsNotEmpty)
edge
}
tabulationAlgorithmStep(edge, this@coroutineScope)
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Summary.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Summary.kt
index 7db030a648..33b9802b9b 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Summary.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/ifds/Summary.kt
@@ -16,7 +16,6 @@
package org.usvm.dataflow.ifds
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import org.jacodb.api.common.CommonMethod
@@ -40,6 +39,7 @@ interface SummaryEdge : Summary : Summary {
+
val message: String
val sink: Vertex
@@ -50,60 +50,79 @@ interface Vulnerability : Summary> {
+class SummaryStorageWithFlows> {
+ private val summaries = ConcurrentHashMap>()
+ private val outFlows = ConcurrentHashMap>()
/**
- * A list of all methods for which summaries are not empty.
+ * @return a list with all methods for which there are some summaries.
*/
val knownMethods: List
+ get() = summaries.keys.toList()
+
+ private fun getFlow(method: CommonMethod): MutableSharedFlow {
+ return outFlows.computeIfAbsent(method) {
+ MutableSharedFlow(replay = Int.MAX_VALUE)
+ }
+ }
/**
- * Adds [summary] the summaries storage of its method.
+ * Adds a new [fact] to the storage.
*/
- fun add(summary: T)
+ fun add(fact: T) {
+ val isNew = summaries.computeIfAbsent(fact.method) { ConcurrentHashMap.newKeySet() }.add(fact)
+ if (isNew) {
+ val flow = getFlow(fact.method)
+ check(flow.tryEmit(fact))
+ }
+ }
/**
* @return a flow with all facts summarized for the given [method].
* Already received facts, along with the facts that will be sent to this storage later,
* will be emitted to the returned flow.
*/
- fun getFacts(method: CommonMethod): Flow
+ fun getFacts(method: CommonMethod): SharedFlow {
+ return getFlow(method)
+ }
/**
* @return a list will all facts summarized for the given [method] so far.
*/
- fun getCurrentFacts(method: CommonMethod): List
+ fun getCurrentFacts(method: CommonMethod): List {
+ return getFacts(method).replayCache
+ }
}
-class SummaryStorageImpl> : SummaryStorage {
-
+class SummaryStorageWithProducers>(
+ private val isConcurrent: Boolean = true,
+) {
private val summaries = ConcurrentHashMap>()
- private val outFlows = ConcurrentHashMap>()
-
- override val knownMethods: List
- get() = summaries.keys.toList()
-
- private fun getFlow(method: CommonMethod): MutableSharedFlow {
- return outFlows.computeIfAbsent(method) {
- MutableSharedFlow(replay = Int.MAX_VALUE)
+ private val producers = ConcurrentHashMap>()
+
+ val knownMethods: Collection
+ get() = summaries.keys
+
+ private fun getProducer(method: CommonMethod): Producer {
+ return producers.computeIfAbsent(method) {
+ if (isConcurrent) {
+ ConcurrentProducer()
+ } else {
+ SyncProducer()
+ }
}
}
- override fun add(summary: T) {
- val isNew = summaries.computeIfAbsent(summary.method) {
- ConcurrentHashMap.newKeySet()
- }.add(summary)
+ fun add(fact: T) {
+ val isNew = summaries.computeIfAbsent(fact.method) { ConcurrentHashMap.newKeySet() }.add(fact)
if (isNew) {
- val flow = getFlow(summary.method)
- check(flow.tryEmit(summary))
+ val producer = getProducer(fact.method)
+ producer.produce(fact)
}
}
- override fun getFacts(method: CommonMethod): SharedFlow {
- return getFlow(method)
- }
-
- override fun getCurrentFacts(method: CommonMethod): List {
- return getFacts(method).replayCache
+ fun subscribe(method: CommonMethod, handler: (T) -> Unit) {
+ val producer = getProducer(method)
+ producer.subscribe(handler)
}
}
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintAnalyzers.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintAnalyzers.kt
index 30cf52fefe..fbd552d403 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintAnalyzers.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintAnalyzers.kt
@@ -27,6 +27,7 @@ import org.usvm.dataflow.config.FactAwareConditionEvaluator
import org.usvm.dataflow.ifds.Analyzer
import org.usvm.dataflow.ifds.Edge
import org.usvm.dataflow.ifds.Reason
+import org.usvm.dataflow.ifds.UnitResolver
import org.usvm.dataflow.util.Traits
private val logger = object : KLogging() {}.logger
@@ -34,13 +35,14 @@ private val logger = object : KLogging() {}.logger
context(Traits)
class TaintAnalyzer(
private val graph: ApplicationGraph,
+ private val unitResolver: UnitResolver,
private val getConfigForMethod: (Method) -> List?,
) : Analyzer, Method, Statement>
where Method : CommonMethod,
Statement : CommonInst {
override val flowFunctions: ForwardTaintFlowFunctions by lazy {
- ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ ForwardTaintFlowFunctions(graph, unitResolver, getConfigForMethod)
}
private fun isExitPoint(statement: Statement): Boolean {
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintBidiRunner.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintBidiRunner.kt
index 4967c1f763..659d6a7d04 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintBidiRunner.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintBidiRunner.kt
@@ -20,6 +20,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
+import org.jacodb.api.common.CommonMethod
+import org.jacodb.api.common.analysis.ApplicationGraph
+import org.jacodb.api.common.cfg.CommonInst
import org.usvm.dataflow.ifds.ControlEvent
import org.usvm.dataflow.ifds.Edge
import org.usvm.dataflow.ifds.IfdsResult
@@ -28,9 +31,6 @@ import org.usvm.dataflow.ifds.QueueEmptinessChanged
import org.usvm.dataflow.ifds.Reason
import org.usvm.dataflow.ifds.UnitResolver
import org.usvm.dataflow.ifds.UnitType
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.common.cfg.CommonInst
class TaintBidiRunner(
val manager: TaintManager,
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintFlowFunctions.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintFlowFunctions.kt
index 657b1c0a45..69178b55d6 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintFlowFunctions.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintFlowFunctions.kt
@@ -45,6 +45,8 @@ import org.usvm.dataflow.config.TaintActionEvaluator
import org.usvm.dataflow.ifds.ElementAccessor
import org.usvm.dataflow.ifds.FlowFunction
import org.usvm.dataflow.ifds.FlowFunctions
+import org.usvm.dataflow.ifds.UnitResolver
+import org.usvm.dataflow.ifds.UnknownUnit
import org.usvm.dataflow.ifds.isOnHeap
import org.usvm.dataflow.ifds.isStatic
import org.usvm.dataflow.ifds.minus
@@ -57,6 +59,7 @@ private val logger = mu.KotlinLogging.logger {}
context(Traits)
class ForwardTaintFlowFunctions(
private val graph: ApplicationGraph,
+ private val unitResolver: UnitResolver,
val getConfigForMethod: (Method) -> List?,
) : FlowFunctions
where Method : CommonMethod,
@@ -321,7 +324,7 @@ class ForwardTaintFlowFunctions(
// to remove any marks from 'instance' and arguments.
// Currently, "analyzability" of the callee depends on the fact that the callee
// is "accessible" through the JcApplicationGraph::callees().
- if (callee in graph.callees(callStatement)) {
+ if (callee in graph.callees(callStatement) && unitResolver.resolve(callee) != UnknownUnit) {
if (fact.variable.isStatic) {
return@FlowFunction emptyList()
diff --git a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintManager.kt b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintManager.kt
index c5bc19f4c4..b0d301133b 100644
--- a/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintManager.kt
+++ b/usvm-dataflow/src/main/kotlin/org/usvm/dataflow/taint/TaintManager.kt
@@ -22,19 +22,22 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
+import org.jacodb.api.common.CommonMethod
+import org.jacodb.api.common.analysis.ApplicationGraph
+import org.jacodb.api.common.cfg.CommonInst
+import org.jacodb.taint.configuration.TaintConfigurationItem
import org.usvm.dataflow.graph.reversed
import org.usvm.dataflow.ifds.ControlEvent
import org.usvm.dataflow.ifds.IfdsResult
import org.usvm.dataflow.ifds.Manager
import org.usvm.dataflow.ifds.QueueEmptinessChanged
-import org.usvm.dataflow.ifds.SummaryStorageImpl
+import org.usvm.dataflow.ifds.SummaryStorageWithFlows
+import org.usvm.dataflow.ifds.SummaryStorageWithProducers
import org.usvm.dataflow.ifds.TraceGraph
import org.usvm.dataflow.ifds.UniRunner
import org.usvm.dataflow.ifds.UnitResolver
@@ -43,15 +46,10 @@ import org.usvm.dataflow.ifds.UnknownUnit
import org.usvm.dataflow.ifds.Vertex
import org.usvm.dataflow.util.Traits
import org.usvm.dataflow.util.getPathEdges
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.common.cfg.CommonInst
-import org.jacodb.taint.configuration.TaintConfigurationItem
import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
-import kotlin.time.ExperimentalTime
import kotlin.time.TimeSource
private val logger = mu.KotlinLogging.logger {}
@@ -70,8 +68,10 @@ open class TaintManager(
val runnerForUnit: MutableMap> = hashMapOf()
private val queueIsEmpty = ConcurrentHashMap()
- private val summaryEdgesStorage = SummaryStorageImpl>()
- private val vulnerabilitiesStorage = SummaryStorageImpl>()
+ // TODO private val summaryEdgesStorages = map
+
+ private val summaryEdgesStorage = SummaryStorageWithProducers>(isConcurrent = true)
+ private val vulnerabilitiesStorage = SummaryStorageWithFlows>()
private val stopRendezvous = Channel(Channel.RENDEZVOUS)
@@ -91,7 +91,7 @@ open class TaintManager(
unitResolver = unitResolver,
unit = unit,
{ manager ->
- val analyzer = TaintAnalyzer(graph, getConfigForMethod)
+ val analyzer = TaintAnalyzer(graph, unitResolver, getConfigForMethod)
UniRunner(
manager = manager,
graph = graph,
@@ -114,7 +114,7 @@ open class TaintManager(
}
)
} else {
- val analyzer = TaintAnalyzer(graph, getConfigForMethod)
+ val analyzer = TaintAnalyzer(graph, unitResolver, getConfigForMethod)
UniRunner(
manager = this@TaintManager,
graph = graph,
@@ -177,8 +177,8 @@ open class TaintManager(
// Start the runner:
launch(start = CoroutineStart.LAZY) {
- val methods = methodsForUnit[unit]!!.toList()
- runner.run(methods)
+ val methods = methodsForUnit[unit]!!
+ runner.run(startMethods.filter { it in methods })
}
}
@@ -288,10 +288,7 @@ open class TaintManager(
scope: CoroutineScope,
handler: (TaintEdge) -> Unit,
) {
- summaryEdgesStorage
- .getFacts(method)
- .onEach { handler(it.edge) }
- .launchIn(scope)
+ summaryEdgesStorage.subscribe(method) { handler(it.edge) }
}
fun vulnerabilityTraceGraph(
diff --git a/usvm-jvm-dataflow/build.gradle.kts b/usvm-jvm-dataflow/build.gradle.kts
index a6854f6ff1..b7309696c5 100644
--- a/usvm-jvm-dataflow/build.gradle.kts
+++ b/usvm-jvm-dataflow/build.gradle.kts
@@ -20,6 +20,7 @@ dependencies {
implementation(Libs.sarif4k)
+ testImplementation(Libs.logback)
testImplementation(Libs.mockk)
testImplementation(Libs.junit_jupiter_params)
diff --git a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeFlowFunctions.kt b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeFlowFunctions.kt
index 4fc99a1eb0..d6d50edda4 100644
--- a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeFlowFunctions.kt
+++ b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeFlowFunctions.kt
@@ -16,9 +16,6 @@
package org.usvm.dataflow.jvm.npe
-import org.jacodb.api.common.cfg.CommonAssignInst
-import org.jacodb.api.common.cfg.CommonExpr
-import org.jacodb.api.common.cfg.CommonValue
import org.jacodb.api.jvm.JcArrayType
import org.jacodb.api.jvm.JcClasspath
import org.jacodb.api.jvm.JcMethod
@@ -130,12 +127,9 @@ class ForwardNpeFlowFunctions(
private fun transmitTaintAssign(
fact: Tainted,
- from: CommonExpr,
- to: CommonValue,
+ from: JcExpr,
+ to: JcValue,
): Collection {
- from as JcExpr
- to as JcValue
-
val toPath = convertToPath(to)
val fromPath = convertToPathOrNull(from)
@@ -197,8 +191,8 @@ class ForwardNpeFlowFunctions(
private fun generates(
inst: JcInst,
): Collection = buildList {
- if (inst is CommonAssignInst) {
- val toPath = convertToPath(inst.lhv as JcValue)
+ if (inst is JcAssignInst) {
+ val toPath = convertToPath(inst.lhv)
val from = inst.rhv
if (from is JcNullConstant || (from is JcCallExpr && from.method.method.isNullable == true)) {
add(Tainted(toPath, TaintMark.NULLNESS))
diff --git a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeManager.kt b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeManager.kt
index f9c65c001e..53c331ba4f 100644
--- a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeManager.kt
+++ b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/npe/NpeManager.kt
@@ -58,14 +58,6 @@ class NpeManager(
runnerForUnit[unit] = runner
return runner
}
-
- override fun addStart(method: JcMethod) {
- logger.info { "Adding start method: $method" }
- val unit = unitResolver.resolve(method)
- if (unit == UnknownUnit) return
- methodsForUnit.getOrPut(unit) { hashSetOf() }.add(method)
- // Note: DO NOT add deps here!
- }
}
fun jcNpeManager(
diff --git a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/unused/UnusedVariableManager.kt b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/unused/UnusedVariableManager.kt
index 09c693e9de..9b9779269a 100644
--- a/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/unused/UnusedVariableManager.kt
+++ b/usvm-jvm-dataflow/src/main/kotlin/org/usvm/dataflow/jvm/unused/UnusedVariableManager.kt
@@ -29,12 +29,15 @@ import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
+import org.jacodb.api.common.CommonMethod
+import org.jacodb.api.common.analysis.ApplicationGraph
+import org.jacodb.api.common.cfg.CommonInst
import org.usvm.dataflow.ifds.ControlEvent
import org.usvm.dataflow.ifds.Edge
import org.usvm.dataflow.ifds.Manager
import org.usvm.dataflow.ifds.QueueEmptinessChanged
import org.usvm.dataflow.ifds.Runner
-import org.usvm.dataflow.ifds.SummaryStorageImpl
+import org.usvm.dataflow.ifds.SummaryStorageWithFlows
import org.usvm.dataflow.ifds.UniRunner
import org.usvm.dataflow.ifds.UnitResolver
import org.usvm.dataflow.ifds.UnitType
@@ -42,9 +45,6 @@ import org.usvm.dataflow.ifds.UnknownUnit
import org.usvm.dataflow.ifds.Vertex
import org.usvm.dataflow.util.Traits
import org.usvm.dataflow.util.getPathEdges
-import org.jacodb.api.common.CommonMethod
-import org.jacodb.api.common.analysis.ApplicationGraph
-import org.jacodb.api.common.cfg.CommonInst
import java.util.concurrent.ConcurrentHashMap
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@@ -65,7 +65,7 @@ class UnusedVariableManager(
private val runnerForUnit: MutableMap> = hashMapOf()
private val queueIsEmpty = ConcurrentHashMap()
- private val summaryEdgesStorage = SummaryStorageImpl>()
+ private val summaryEdgesStorage = SummaryStorageWithFlows>()
private val stopRendezvous = Channel(Channel.RENDEZVOUS)
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsNpeTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsNpeTest.kt
index 71836d4f14..d3d17f4cc6 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsNpeTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsNpeTest.kt
@@ -32,7 +32,7 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import org.usvm.dataflow.jvm.graph.JcApplicationGraphImpl
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.npe.jcNpeManager
import org.usvm.dataflow.taint.TaintVulnerability
import java.util.StringTokenizer
@@ -193,7 +193,7 @@ class IfdsNpeTest : BaseAnalysisTest() {
}
private fun findSinks(method: JcMethod): List> {
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcNpeManager(graph, unitResolver)
return manager.analyze(listOf(method), timeout = 30.seconds)
}
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsSqlTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsSqlTest.kt
index 2f10ed7f45..9b9a0db79a 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsSqlTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsSqlTest.kt
@@ -29,7 +29,7 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import org.usvm.dataflow.jvm.ifds.ClassUnitResolver
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.taint.jcTaintManager
import org.usvm.dataflow.jvm.util.JcTraits
import org.usvm.dataflow.sarif.sarifReportFromVulnerabilities
@@ -54,7 +54,7 @@ class IfdsSqlTest : BaseAnalysisTest() {
val methodName = "bad"
val method = cp.findClass().declaredMethods.single { it.name == methodName }
val methods = listOf(method)
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
val sinks = manager.analyze(methods, timeout = 30.seconds)
assertTrue(sinks.isNotEmpty())
@@ -68,7 +68,7 @@ class IfdsSqlTest : BaseAnalysisTest() {
@MethodSource("provideClassesForJuliet89")
fun `test on Juliet's CWE 89`(className: String) {
testSingleJulietClass(className) { method ->
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
manager.analyze(listOf(method), timeout = 30.seconds)
}
@@ -78,7 +78,7 @@ class IfdsSqlTest : BaseAnalysisTest() {
fun `test on specific Juliet instance`() {
val className = "juliet.testcases.CWE89_SQL_Injection.s01.CWE89_SQL_Injection__connect_tcp_execute_01"
testSingleJulietClass(className) { method ->
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
manager.analyze(listOf(method), timeout = 30.seconds)
}
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsTaintTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsTaintTest.kt
index a3bcce2114..ad695ae46f 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsTaintTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsTaintTest.kt
@@ -23,7 +23,7 @@ import org.jacodb.api.jvm.ext.findClass
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.taint.jcTaintManager
import org.usvm.dataflow.taint.TaintVulnerability
import kotlin.test.assertTrue
@@ -40,7 +40,7 @@ class IfdsTaintTest : BaseAnalysisTest() {
}
private fun findSinks(method: JcMethod): List> {
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
return manager.analyze(listOf(method), timeout = 3000.seconds)
}
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUntrustedLoopBoundTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUntrustedLoopBoundTest.kt
index e2b01e0499..49abc7c83a 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUntrustedLoopBoundTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUntrustedLoopBoundTest.kt
@@ -22,7 +22,7 @@ import org.jacodb.api.jvm.ext.findClass
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.taint.jcTaintManager
import org.usvm.dataflow.taint.TaintAnalysisOptions
import kotlin.test.assertTrue
@@ -31,7 +31,7 @@ import kotlin.time.Duration.Companion.seconds
private val logger = KotlinLogging.logger {}
@TestInstance(PER_CLASS)
-class Ifds2UpperBoundTest : BaseAnalysisTest(configFileName = "config_untrusted_loop_bound.json") {
+class IfdsUntrustedLoopBoundTest : BaseAnalysisTest(configFileName = "config_untrusted_loop_bound.json") {
@Test
fun `analyze untrusted upper bound`() {
@@ -41,7 +41,7 @@ class Ifds2UpperBoundTest : BaseAnalysisTest(configFileName = "config_untrusted_
private inline fun testOneMethod(methodName: String) {
val method = cp.findClass().declaredMethods.single { it.name == methodName }
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
val sinks = manager.analyze(listOf(method), timeout = 60.seconds)
logger.info { "Sinks: ${sinks.size}" }
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUnusedTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUnusedTest.kt
index 3770129a85..35bcef98d3 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUnusedTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/IfdsUnusedTest.kt
@@ -26,7 +26,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.unused.UnusedVariableManager
import org.usvm.dataflow.jvm.util.JcTraits
import java.util.stream.Stream
@@ -59,7 +59,7 @@ class IfdsUnusedTest : BaseAnalysisTest() {
@MethodSource("provideClassesForJuliet563")
fun `test on Juliet's CWE 563`(className: String) {
testSingleJulietClass(className) { method ->
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = with(JcTraits(cp)) {
UnusedVariableManager(graph, unitResolver)
}
@@ -73,7 +73,7 @@ class IfdsUnusedTest : BaseAnalysisTest() {
"juliet.testcases.CWE563_Unused_Variable.CWE563_Unused_Variable__unused_init_variable_StringBuilder_01"
val clazz = cp.findClass(className)
val badMethod = clazz.methods.single { it.name == "bad" }
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = with(JcTraits(cp)) {
UnusedVariableManager(graph, unitResolver)
}
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/JodaDateTimeAnalysisTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/JodaDateTimeAnalysisTest.kt
index 2615df0ea6..4c757647ae 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/JodaDateTimeAnalysisTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/JodaDateTimeAnalysisTest.kt
@@ -21,7 +21,7 @@ import org.joda.time.DateTime
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
-import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
+import org.usvm.dataflow.jvm.ifds.MethodUnitResolver
import org.usvm.dataflow.jvm.npe.jcNpeManager
import org.usvm.dataflow.jvm.taint.jcTaintManager
import org.usvm.dataflow.jvm.unused.UnusedVariableManager
@@ -37,7 +37,7 @@ class JodaDateTimeAnalysisTest : BaseAnalysisTest() {
fun `test taint analysis`() {
val clazz = cp.findClass()
val methods = clazz.declaredMethods
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcTaintManager(graph, unitResolver)
val sinks = manager.analyze(methods, timeout = 60.seconds)
logger.info { "Vulnerabilities found: ${sinks.size}" }
@@ -47,7 +47,7 @@ class JodaDateTimeAnalysisTest : BaseAnalysisTest() {
fun `test NPE analysis`() {
val clazz = cp.findClass()
val methods = clazz.declaredMethods
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = jcNpeManager(graph, unitResolver)
val sinks = manager.analyze(methods, timeout = 60.seconds)
logger.info { "Vulnerabilities found: ${sinks.size}" }
@@ -57,7 +57,7 @@ class JodaDateTimeAnalysisTest : BaseAnalysisTest() {
fun `test unused variables analysis`() {
val clazz = cp.findClass()
val methods = clazz.declaredMethods
- val unitResolver = SingletonUnitResolver
+ val unitResolver = MethodUnitResolver
val manager = with(JcTraits(cp)) {
UnusedVariableManager(graph, unitResolver)
}
diff --git a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/TaintFlowFunctionsTest.kt b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/TaintFlowFunctionsTest.kt
index c6e1c59622..db5b56870a 100644
--- a/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/TaintFlowFunctionsTest.kt
+++ b/usvm-jvm-dataflow/src/test/kotlin/org/usvm/dataflow/jvm/impl/TaintFlowFunctionsTest.kt
@@ -39,6 +39,7 @@ import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
+import org.usvm.dataflow.jvm.ifds.SingletonUnitResolver
import org.usvm.dataflow.jvm.util.JcTraits
import org.usvm.dataflow.jvm.util.callee
import org.usvm.dataflow.jvm.util.toPath
@@ -47,7 +48,7 @@ import org.usvm.dataflow.taint.TaintZeroFact
import org.usvm.dataflow.taint.Tainted
@TestInstance(PER_CLASS)
-open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_test.json") {
+class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_test.json") {
override val graph: JcApplicationGraph = mockk {
every { cp } returns this@TaintFlowFunctionsTest.cp
@@ -95,7 +96,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
@Test
fun `test obtain start facts`() {
with(JcTraits(cp)) {
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val facts = flowSpace.obtainPossibleStartFacts(testMethod).toList()
val arg0 = getArgument(testMethod.parameters[0])!!
val arg0Taint = Tainted(arg0.toPath(), TaintMark("EXAMPLE"))
@@ -110,7 +111,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
val x: JcLocal = JcLocalVar(1, "x", stringType)
val y: JcLocal = JcLocalVar(2, "y", stringType)
val inst = JcAssignInst(location = mockk(), lhv = x, rhv = y)
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainSequentFlowFunction(inst, next = mockk())
val yTaint = Tainted(y.toPath(), TaintMark("TAINT"))
val xTaint = Tainted(x.toPath(), TaintMark("TAINT"))
@@ -127,7 +128,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
val callStatement = JcAssignInst(location = mockk(), lhv = x, rhv = mockk {
every { callee } returns testMethod
})
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainCallToReturnSiteFlowFunction(callStatement, returnSite = mockk())
val xTaint = Tainted(x.toPath(), TaintMark("EXAMPLE"))
val facts = f.compute(TaintZeroFact).toList()
@@ -144,7 +145,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
every { callee } returns testMethod
every { args } returns listOf(x)
})
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainCallToReturnSiteFlowFunction(callStatement, returnSite = mockk())
val xTaint = Tainted(x.toPath(), TaintMark("REMOVE"))
val facts = f.compute(xTaint).toList()
@@ -162,7 +163,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
every { callee } returns testMethod
every { args } returns listOf(x)
})
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainCallToReturnSiteFlowFunction(callStatement, returnSite = mockk())
val xTaint = Tainted(x.toPath(), TaintMark("COPY"))
val yTaint = Tainted(y.toPath(), TaintMark("COPY"))
@@ -184,7 +185,7 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
every { callee } returns testMethod
every { args } returns listOf(x)
})
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainCallToStartFlowFunction(callStatement, calleeStart = mockk {
every { location } returns mockk {
every { method } returns testMethod
@@ -211,10 +212,13 @@ open class TaintFlowFunctionsTest : BaseAnalysisTest(configFileName = "config_te
every { callee } returns testMethod
})
val y: JcLocal = JcLocalVar(1, "y", stringType)
- val exitStatement = JcReturnInst(location = mockk {
- every { method } returns testMethod
- }, returnValue = y)
- val flowSpace = ForwardTaintFlowFunctions(graph, getConfigForMethod)
+ val exitStatement = JcReturnInst(
+ location = mockk {
+ every { method } returns testMethod
+ },
+ returnValue = y,
+ )
+ val flowSpace = ForwardTaintFlowFunctions(graph, SingletonUnitResolver, getConfigForMethod)
val f = flowSpace.obtainExitToReturnSiteFlowFunction(callStatement, returnSite = mockk(), exitStatement)
val yTaint = Tainted(y.toPath(), TaintMark("TAINT"))
val xTaint = Tainted(x.toPath(), TaintMark("TAINT"))