Skip to content

Commit 90f384e

Browse files
authored
Introduce utbot taint analysis (#1966)
1 parent c0c5fdc commit 90f384e

File tree

111 files changed

+3750
-567
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+3750
-567
lines changed

.github/workflows/framework-tests-matrix.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
{
88
"PART_NAME": "composite-part2",
9-
"TESTS_TO_RUN": "--tests \"org.utbot.engine.*\" --tests \"org.utbot.framework.*\" --tests \"org.utbot.sarif.*\""
9+
"TESTS_TO_RUN": "--tests \"org.utbot.engine.*\" --tests \"org.utbot.framework.*\" --tests \"org.utbot.sarif.*\" --tests \"org.utbot.examples.taint.*\""
1010
},
1111
{
1212
"PART_NAME": "composite-part3",

gradle.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ kryoVersion=5.4.0
6060
kryoSerializersVersion=0.45
6161
asmVersion=9.2
6262
testNgVersion=7.6.0
63-
kamlVersion = 0.51.0
64-
jacksonVersion = 2.12.3
63+
kamlVersion=0.51.0
64+
jacksonVersion=2.12.3
65+
kotlinxSerializationVersion=1.5.0
6566
slf4jVersion=1.7.36
6667
eclipseAetherVersion=1.1.0
6768
mavenWagonVersion=3.5.1

utbot-analytics/src/main/kotlin/org/utbot/features/UtExpressionStructureCounter.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ class UtExpressionStructureCounter(private val input: Iterable<UtExpression>) :
153153
return stat
154154
}
155155

156+
override fun visit(expr: UtBvNotExpression): NestStat {
157+
val stat = buildState(expr.variable.expr)
158+
stat.level++
159+
stat.nodes++
160+
return stat
161+
}
162+
156163
override fun visit(expr: UtCastExpression): NestStat {
157164
val stat = buildState(expr.variable.expr)
158165
stat.level++

utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,30 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
265265
DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS
266266
)
267267

268+
/**
269+
* Enable taint analysis or not.
270+
*/
271+
var useTaintAnalysis by getBooleanProperty(false)
272+
273+
/**
274+
* If it is true, [Traverser] forks the state and creates checks for each taint mark separately,
275+
* otherwise, it processes all available taint marks in one [Traverser.implicitlyThrowException] request.
276+
*
277+
* @see [org.utbot.engine.Traverser.processTaintSink]
278+
*/
279+
var throwTaintErrorForEachMarkSeparately = true
280+
281+
/**
282+
* How deep we should analyze the throwables.
283+
*/
284+
val exploreThrowableDepth: ExploreThrowableDepth
285+
get() =
286+
if (useTaintAnalysis) {
287+
ExploreThrowableDepth.EXPLORE_ALL_STATEMENTS
288+
} else {
289+
ExploreThrowableDepth.SKIP_ALL_STATEMENTS
290+
}
291+
268292
// region engine process debug
269293

270294
/**
@@ -680,3 +704,24 @@ enum class SummariesGenerationType {
680704
*/
681705
NONE,
682706
}
707+
708+
/**
709+
* Enum to describe how deep we should analyze the throwables.
710+
*/
711+
enum class ExploreThrowableDepth {
712+
713+
/**
714+
* Skip all statements between throwable's `new` and `<init>` statements.
715+
*/
716+
SKIP_ALL_STATEMENTS,
717+
718+
/**
719+
* Skip only throwable's <init> statement.
720+
*/
721+
SKIP_INIT_STATEMENT,
722+
723+
/**
724+
* Do not skip statements.
725+
*/
726+
EXPLORE_ALL_STATEMENTS
727+
}

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import org.utbot.framework.plugin.api.util.isSubtypeOf
6565
import org.utbot.framework.plugin.api.util.utContext
6666
import org.utbot.framework.process.OpenModulesContainer
6767
import soot.SootField
68+
import soot.SootMethod
6869

6970
const val SYMBOLIC_NULL_ADDR: Int = 0
7071

@@ -101,6 +102,17 @@ data class Step(
101102
}
102103
}
103104

105+
/**
106+
* One symbolic step.
107+
*
108+
* @see UtSymbolicExecution.symbolicSteps
109+
*/
110+
data class SymbolicStep(
111+
val method: SootMethod,
112+
val lineNumber: Int,
113+
val callDepth: Int,
114+
)
115+
104116

105117
/**
106118
* Traverse result.
@@ -150,7 +162,8 @@ class UtSymbolicExecution(
150162
coverage: Coverage? = null,
151163
summary: List<DocStatement>? = null,
152164
testMethodName: String? = null,
153-
displayName: String? = null
165+
displayName: String? = null,
166+
/** Convenient view of the full symbolic path */ val symbolicSteps: List<SymbolicStep> = listOf(),
154167
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName) {
155168
/**
156169
* By design the 'before' and 'after' states contain info about the same fields.
@@ -201,7 +214,8 @@ class UtSymbolicExecution(
201214
coverage,
202215
summary,
203216
testMethodName,
204-
displayName
217+
displayName,
218+
symbolicSteps,
205219
)
206220
}
207221
}

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/ArtificialErrors.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,17 @@ sealed class ArtificialError(message: String): Error(message)
1515
*
1616
* See [TraversalContext.intOverflowCheck] for more details.
1717
*/
18-
class OverflowDetectionError(message: String): ArtificialError(message)
18+
class OverflowDetectionError(message: String): ArtificialError(message)
19+
20+
/**
21+
* An artificial error that could be implicitly thrown by the symbolic engine during taint sink processing.
22+
*/
23+
class TaintAnalysisError(
24+
/** Sink method name: "${classId.simpleName}.${methodId.name}". */
25+
val sinkName: String,
26+
/** Some information about a tainted var, for example, its type. */
27+
val taintedVar: String,
28+
/** Name of the taint mark. */
29+
val taintMark: String,
30+
message: String = "'$taintedVar' marked '$taintMark' was passed into '$sinkName' method"
31+
) : ArtificialError(message)

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ data class UtOverflowFailure(
2323
override val exception: Throwable,
2424
) : UtExecutionFailure()
2525

26+
data class UtTaintAnalysisFailure(
27+
override val exception: Throwable
28+
) : UtExecutionFailure()
29+
2630
data class UtSandboxFailure(
2731
override val exception: Throwable
2832
) : UtExecutionFailure()

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,9 @@ val ExecutableId.humanReadableName: String
545545
return "$executableName($parameters)"
546546
}
547547

548+
val ExecutableId.simpleNameWithClass: String
549+
get() = "${classId.simpleName}.${name}"
550+
548551
val Constructor<*>.displayName: String
549552
get() = executableId.humanReadableName
550553

utbot-framework-api/src/main/kotlin/org/utbot/testcheckers/SettingsModificators.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ inline fun <reified T> withTreatingOverflowAsError(block: () -> T): T {
5959
}
6060
}
6161

62+
inline fun <reified T> withoutThrowTaintErrorForEachMarkSeparately(block: () -> T): T {
63+
val prev = UtSettings.throwTaintErrorForEachMarkSeparately
64+
UtSettings.throwTaintErrorForEachMarkSeparately = false
65+
try {
66+
return block()
67+
} finally {
68+
UtSettings.throwTaintErrorForEachMarkSeparately = prev
69+
}
70+
}
71+
6272
inline fun <reified T> withPushingStateFromPathSelectorForConcrete(block: () -> T): T {
6373
val prev = UtSettings.saveRemainingStatesForConcreteExecution
6474
UtSettings.saveRemainingStatesForConcreteExecution = true
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.utbot.examples.taint
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.plugin.api.TaintAnalysisError
5+
import org.utbot.taint.TaintConfigurationProviderResources
6+
import org.utbot.testcheckers.eq
7+
import org.utbot.testing.UtValueTestCaseCheckerForTaint
8+
import org.utbot.testing.isException
9+
10+
internal class TaintBranchingTest : UtValueTestCaseCheckerForTaint(
11+
testClass = TaintBranching::class,
12+
taintConfigurationProvider = TaintConfigurationProviderResources("taint/TaintBranchingConfig.yaml")
13+
) {
14+
@Test
15+
fun testTaintBad() {
16+
checkWithException(
17+
TaintBranching::bad,
18+
eq(3), // success (x2) & taint error
19+
{ cond, r -> cond == r.isException<TaintAnalysisError>() },
20+
)
21+
}
22+
23+
@Test
24+
fun testTaintGood() {
25+
checkWithException(
26+
TaintBranching::good,
27+
eq(2), // success in both cases
28+
{ _, r -> r.isSuccess },
29+
)
30+
}
31+
}

0 commit comments

Comments
 (0)