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"))