Skip to content

Commit 1702a95

Browse files
committed
Add taint analysis
1 parent 79d90f6 commit 1702a95

File tree

45 files changed

+1230
-37
lines changed

Some content is hidden

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

45 files changed

+1230
-37
lines changed

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ testNgVersion=7.6.0
6262
mockitoInlineVersion=4.0.0
6363
kamlVersion = 0.51.0
6464
jacksonVersion = 2.12.3
65+
kotlinxSerializationVersion=1.5.0
6566
javasmtSolverZ3Version=4.8.9-sosy1
6667
slf4jVersion=1.7.36
6768
eclipseAetherVersion=1.1.0

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: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,21 @@ 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+
* How deep we should analyze the exceptions.
275+
*/
276+
val exploreExceptionsDepth: ExploreExceptionsDepth
277+
get() =
278+
if (useTaintAnalysis)
279+
ExploreExceptionsDepth.EXPLORE_ALL_STATEMENTS
280+
else
281+
ExploreExceptionsDepth.SKIP_ALL_STATEMENTS
282+
268283
// region engine process debug
269284

270285
/**
@@ -645,3 +660,24 @@ enum class SummariesGenerationType {
645660
*/
646661
NONE,
647662
}
663+
664+
/**
665+
* Enum to describe how deep we should analyze the exceptions.
666+
*/
667+
enum class ExploreExceptionsDepth {
668+
669+
/**
670+
* Skip all statements between exception's `new` and `<init>` statements.
671+
*/
672+
SKIP_ALL_STATEMENTS,
673+
674+
/**
675+
* Skip only exception's <init> statement.
676+
*/
677+
SKIP_INIT_STATEMENT,
678+
679+
/**
680+
* Do not skip statements.
681+
*/
682+
EXPLORE_ALL_STATEMENTS
683+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@ 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+
sinkFqn: String,
25+
taintedVar: String,
26+
taintMark: String,
27+
message: String = "'$taintedVar' marked '$taintMark' was passed into '$sinkFqn' method"
28+
) : 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()
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,27 @@ import org.utbot.taint.parser.constants.*
88
import org.utbot.taint.parser.model.*
99
import org.utbot.taint.parser.yaml.TaintParseError
1010

11-
class TaintAnalysisConfigurationParserTest {
11+
class TaintYamlParserTest {
1212

1313
@Test
1414
fun `parse should parse correct yaml`() {
15-
val actualConfiguration = TaintAnalysisConfigurationParser.parse(yamlInput)
15+
val actualConfiguration = TaintYamlParser.parse(yamlInput)
1616
assertEquals(expectedConfiguration, actualConfiguration)
1717
}
1818

1919
@Test
2020
fun `parse should throw exception on malformed yaml`() {
2121
val malformedYamlInput = yamlInput.replace("{", "")
2222
assertThrows<YamlException> {
23-
TaintAnalysisConfigurationParser.parse(malformedYamlInput)
23+
TaintYamlParser.parse(malformedYamlInput)
2424
}
2525
}
2626

2727
@Test
2828
fun `parse should throw exception on incorrect yaml`() {
2929
val incorrectYamlInput = yamlInput.replace(k_not, "net")
3030
assertThrows<TaintParseError> {
31-
TaintAnalysisConfigurationParser.parse(incorrectYamlInput)
31+
TaintYamlParser.parse(incorrectYamlInput)
3232
}
3333
}
3434

utbot-framework/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ configurations {
88
z3native
99
}
1010

11+
plugins {
12+
id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20'
13+
}
14+
1115
dependencies {
1216

1317
api project(':utbot-fuzzers')
@@ -30,6 +34,7 @@ dependencies {
3034
implementation group: 'com.charleskorn.kaml', name: 'kaml', version: kamlVersion
3135
implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: jacksonVersion
3236
implementation group: 'org.sosy-lab', name: 'javasmt-solver-z3', version: javasmtSolverZ3Version
37+
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-serialization-cbor', version: kotlinxSerializationVersion
3338
implementation group: 'com.github.curious-odd-man', name: 'rgxgen', version: rgxgenVersion
3439
implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: log4j2Version
3540
implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion

utbot-framework/src/main/kotlin/org/utbot/engine/Extensions.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ import soot.SootField
4545
import soot.SootMethod
4646
import soot.Type
4747
import soot.Value
48-
import soot.jimple.Expr
49-
import soot.jimple.InvokeExpr
50-
import soot.jimple.JimpleBody
51-
import soot.jimple.StaticFieldRef
52-
import soot.jimple.Stmt
5348
import soot.jimple.internal.JDynamicInvokeExpr
5449
import soot.jimple.internal.JIdentityStmt
5550
import soot.jimple.internal.JInterfaceInvokeExpr
@@ -69,6 +64,7 @@ import kotlinx.collections.immutable.PersistentMap
6964
import kotlinx.collections.immutable.persistentHashMapOf
7065
import org.utbot.engine.types.OBJECT_TYPE
7166
import org.utbot.framework.plugin.api.util.enumConstants
67+
import soot.jimple.*
7268

7369
val JIdentityStmt.lines: String
7470
get() = tags.joinToString { "$it" }
@@ -85,6 +81,15 @@ fun Expr.isInvokeExpr() = this is JDynamicInvokeExpr
8581
|| this is JVirtualInvokeExpr
8682
|| this is JSpecialInvokeExpr
8783

84+
fun InvokeExpr.baseOrNull(): Value? =
85+
when (this) {
86+
is StaticInvokeExpr -> null
87+
is SpecialInvokeExpr -> base
88+
is VirtualInvokeExpr -> base
89+
is InterfaceInvokeExpr -> base
90+
else -> null
91+
}
92+
8893
val SootMethod.pureJavaSignature
8994
get() = bytecodeSignature.substringAfter(' ').dropLast(1)
9095

utbot-framework/src/main/kotlin/org/utbot/engine/Memory.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import org.utbot.engine.pc.UtExpression
1414
import org.utbot.engine.pc.UtFalse
1515
import org.utbot.engine.pc.UtInt32Sort
1616
import org.utbot.engine.pc.UtIntSort
17+
import org.utbot.engine.pc.UtLongSort
1718
import org.utbot.engine.pc.UtMkArrayExpression
1819
import org.utbot.engine.pc.UtMkTermArrayExpression
1920
import org.utbot.engine.pc.UtSeqSort
2021
import org.utbot.engine.pc.UtSort
2122
import org.utbot.engine.pc.UtTrue
2223
import org.utbot.engine.pc.mkArrayConst
2324
import org.utbot.engine.pc.mkInt
25+
import org.utbot.engine.pc.mkLong
2426
import org.utbot.engine.pc.select
2527
import org.utbot.engine.pc.store
2628
import org.utbot.engine.pc.toSort
@@ -109,6 +111,12 @@ data class Memory( // TODO: split purely symbolic memory and information about s
109111
UtFalse,
110112
UtArraySort(UtAddrSort, UtBoolSort)
111113
),
114+
// const array here is because even in the situation when MUT is marked as a `PassThrough` we will
115+
// process this information in a current state, not initial one
116+
private var taintArray: UtArrayExpressionBase = UtConstArrayExpression(
117+
mkLong(value = 0L),
118+
UtArraySort(indexSort = UtAddrSort, itemSort = UtLongSort)
119+
),
112120
private val symbolicEnumValues: PersistentList<ObjectValue> = persistentListOf()
113121
) {
114122
val chunkIds: Set<ChunkId>
@@ -163,6 +171,8 @@ data class Memory( // TODO: split purely symbolic memory and information about s
163171
*/
164172
fun isSpeculativelyNotNull(addr: UtAddrExpression): UtArraySelectExpression = speculativelyNotNullAddresses.select(addr)
165173

174+
fun taintVector(addr: UtAddrExpression): UtArraySelectExpression = taintArray.select(addr)
175+
166176
/**
167177
* @return ImmutableCollection of the initial values for all the arrays we touched during the execution
168178
*/
@@ -268,6 +278,17 @@ data class Memory( // TODO: split purely symbolic memory and information about s
268278
acc.store(addr, UtTrue)
269279
}
270280

281+
val updTaintArray = update.taintArrayUpdate
282+
.groupBy { (addr, _) ->
283+
addr
284+
}.map { (addr, listUpdates) ->
285+
addr to listUpdates.fold(mkLong(0).toLongValue()) { acc, (_, taintVector) ->
286+
Or(acc, taintVector.toLongValue()).toLongValue()
287+
}
288+
}.fold(taintArray) { acc, (addr, taintVector) ->
289+
acc.store(addr, taintVector.expr)
290+
}
291+
271292
// We have a list with updates for generic type info, and we want to apply
272293
// them in such way that only updates with more precise type information
273294
// should be applied.
@@ -304,6 +325,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
304325
touchedAddresses = updTouchedAddresses,
305326
instanceFieldReadOperations = instanceFieldReadOperations.addAll(update.instanceFieldReads),
306327
speculativelyNotNullAddresses = updSpeculativelyNotNullAddresses,
328+
taintArray = updTaintArray,
307329
symbolicEnumValues = symbolicEnumValues.addAll(update.symbolicEnumValues)
308330
)
309331
}
@@ -406,6 +428,7 @@ data class MemoryUpdate(
406428
val classIdToClearStatics: ClassId? = null,
407429
val instanceFieldReads: PersistentSet<InstanceFieldReadOperation> = persistentHashSetOf(),
408430
val speculativelyNotNullAddresses: PersistentList<UtAddrExpression> = persistentListOf(),
431+
val taintArrayUpdate: PersistentList<Pair<UtAddrExpression, UtExpression>> = persistentListOf(),
409432
val symbolicEnumValues: PersistentList<ObjectValue> = persistentListOf()
410433
) {
411434
operator fun plus(other: MemoryUpdate) =
@@ -427,6 +450,7 @@ data class MemoryUpdate(
427450
classIdToClearStatics = other.classIdToClearStatics,
428451
instanceFieldReads = instanceFieldReads.addAll(other.instanceFieldReads),
429452
speculativelyNotNullAddresses = speculativelyNotNullAddresses.addAll(other.speculativelyNotNullAddresses),
453+
taintArrayUpdate = taintArrayUpdate.addAll(other.taintArrayUpdate),
430454
symbolicEnumValues = symbolicEnumValues.addAll(other.symbolicEnumValues),
431455
)
432456

utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import org.utbot.framework.plugin.api.UtOverflowFailure
5555
import org.utbot.framework.plugin.api.UtPrimitiveModel
5656
import org.utbot.framework.plugin.api.UtSandboxFailure
5757
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
58+
import org.utbot.framework.plugin.api.UtTaintAnalysisFailure
5859
import org.utbot.framework.plugin.api.UtVoidModel
5960
import org.utbot.framework.plugin.api.classId
6061
import org.utbot.framework.plugin.api.id
@@ -91,6 +92,7 @@ import kotlin.math.min
9192
import kotlinx.collections.immutable.persistentListOf
9293
import kotlinx.collections.immutable.persistentSetOf
9394
import org.utbot.framework.plugin.api.OverflowDetectionError
95+
import org.utbot.framework.plugin.api.TaintAnalysisError
9496
import org.utbot.engine.types.CLASS_REF_CLASSNAME
9597
import org.utbot.engine.types.CLASS_REF_CLASS_ID
9698
import org.utbot.engine.types.CLASS_REF_NUM_DIMENSIONS_DESCRIPTOR
@@ -397,6 +399,7 @@ class Resolver(
397399
when (exception) {
398400
is OverflowDetectionError -> UtOverflowFailure(exception)
399401
is AccessControlException -> UtSandboxFailure(exception)
402+
is TaintAnalysisError -> UtTaintAnalysisFailure(exception)
400403
else -> UtImplicitlyThrownException(exception, inNestedMethod)
401404
}
402405
}

utbot-framework/src/main/kotlin/org/utbot/engine/SymbolicValue.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ val SymbolicValue.addr
166166
is PrimitiveValue -> error("PrimitiveValue $this doesn't have an address")
167167
}
168168

169+
val SymbolicValue.addrOrNull
170+
get() = when (this) {
171+
is ReferenceValue -> addr
172+
is PrimitiveValue -> null
173+
}
174+
169175
val SymbolicValue.isConcrete: Boolean
170176
get() = when (this) {
171177
is PrimitiveValue -> this.expr.isConcrete

0 commit comments

Comments
 (0)