Skip to content

Commit 3b6cdc5

Browse files
committed
Add taint analysis
1 parent c7b2536 commit 3b6cdc5

File tree

77 files changed

+2208
-550
lines changed

Some content is hidden

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

77 files changed

+2208
-550
lines changed

gradle.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ kryoSerializersVersion=0.45
6060
asmVersion=9.2
6161
testNgVersion=7.6.0
6262
mockitoInlineVersion=4.0.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: 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
/**
@@ -680,3 +695,24 @@ enum class SummariesGenerationType {
680695
*/
681696
NONE,
682697
}
698+
699+
/**
700+
* Enum to describe how deep we should analyze the exceptions.
701+
*/
702+
enum class ExploreExceptionsDepth {
703+
704+
/**
705+
* Skip all statements between exception's `new` and `<init>` statements.
706+
*/
707+
SKIP_ALL_STATEMENTS,
708+
709+
/**
710+
* Skip only exception's <init> statement.
711+
*/
712+
SKIP_INIT_STATEMENT,
713+
714+
/**
715+
* Do not skip statements.
716+
*/
717+
EXPLORE_ALL_STATEMENTS
718+
}

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

Lines changed: 11 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,12 @@ data class Step(
101102
}
102103
}
103104

105+
data class SymbolicStep(
106+
val method: SootMethod,
107+
val lineNumber: Int,
108+
val callDepth: Int,
109+
)
110+
104111

105112
/**
106113
* Traverse result.
@@ -150,7 +157,8 @@ class UtSymbolicExecution(
150157
coverage: Coverage? = null,
151158
summary: List<DocStatement>? = null,
152159
testMethodName: String? = null,
153-
displayName: String? = null
160+
displayName: String? = null,
161+
val symbolicSteps: List<SymbolicStep> = listOf(),
154162
) : UtExecution(stateBefore, stateAfter, result, coverage, summary, testMethodName, displayName) {
155163
/**
156164
* By design the 'before' and 'after' states contain info about the same fields.
@@ -201,7 +209,8 @@ class UtSymbolicExecution(
201209
coverage,
202210
summary,
203211
testMethodName,
204-
displayName
212+
displayName,
213+
symbolicSteps,
205214
)
206215
}
207216
}

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()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.utbot.examples.taint
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.plugin.api.CodegenLanguage
5+
import org.utbot.framework.plugin.api.TaintAnalysisError
6+
import org.utbot.taint.TaintConfigurationProviderResources
7+
import org.utbot.testcheckers.eq
8+
import org.utbot.testcheckers.ge
9+
import org.utbot.testing.CodeGeneration
10+
import org.utbot.testing.UtValueTestCaseCheckerForTaint
11+
import org.utbot.testing.isException
12+
13+
internal class LongTaintPathTest : UtValueTestCaseCheckerForTaint(
14+
testClass = LongTaintPath::class,
15+
testCodeGeneration = true,
16+
pipelines = listOf(
17+
TestLastStage(CodegenLanguage.JAVA),
18+
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
19+
),
20+
taintConfigurationProvider = TaintConfigurationProviderResources("taint/LongTaintPathConfig.yaml")
21+
) {
22+
@Test
23+
fun testTaintBad() {
24+
checkWithException(
25+
LongTaintPath::bad,
26+
ge(2), // success & taint error
27+
{ r -> r.isException<TaintAnalysisError>() }
28+
)
29+
}
30+
31+
@Test
32+
fun testTaintGood() {
33+
check(
34+
LongTaintPath::good,
35+
eq(1), // only success
36+
)
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.utbot.examples.taint
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.plugin.api.CodegenLanguage
5+
import org.utbot.framework.plugin.api.TaintAnalysisError
6+
import org.utbot.taint.TaintConfigurationProviderResources
7+
import org.utbot.testcheckers.eq
8+
import org.utbot.testcheckers.ge
9+
import org.utbot.testing.CodeGeneration
10+
import org.utbot.testing.UtValueTestCaseCheckerForTaint
11+
import org.utbot.testing.isException
12+
13+
internal class SimpleTaintExampleTest : UtValueTestCaseCheckerForTaint(
14+
testClass = SimpleTaintExample::class,
15+
testCodeGeneration = true,
16+
pipelines = listOf(
17+
TestLastStage(CodegenLanguage.JAVA),
18+
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
19+
),
20+
taintConfigurationProvider = TaintConfigurationProviderResources("taint/SimpleExampleConfig.yaml")
21+
) {
22+
@Test
23+
fun testTaintBad() {
24+
checkWithException(
25+
SimpleTaintExample::bad,
26+
ge(2), // success & taint error
27+
{ r -> r.isException<TaintAnalysisError>() },
28+
)
29+
}
30+
31+
@Test
32+
fun testTaintGood() {
33+
check(
34+
SimpleTaintExample::good,
35+
eq(1), // only success
36+
)
37+
}
38+
}

utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ class SarifReportTest {
6969
Mockito.`when`(mockUtExecution.result).thenReturn(
7070
UtImplicitlyThrownException(NullPointerException(), false)
7171
)
72+
mockCoverage(mockUtExecution, 1337, "Main")
7273
Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf())
73-
Mockito.`when`(mockUtExecution.coverage?.coveredInstructions?.lastOrNull()?.lineNumber).thenReturn(1337)
74-
Mockito.`when`(mockUtExecution.coverage?.coveredInstructions?.lastOrNull()?.className).thenReturn("Main")
7574
Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException")
7675

7776
val report = sarifReportMain.createReport()
@@ -144,15 +143,15 @@ class SarifReportTest {
144143
fun testProcessSandboxFailure() {
145144
mockUtMethodNames()
146145

147-
val uncheckedException = Mockito.mock(java.security.AccessControlException::class.java)
146+
val uncheckedException = Mockito.mock(java.security.NoSuchAlgorithmException::class.java)
148147
Mockito.`when`(uncheckedException.stackTrace).thenReturn(arrayOf())
149148
Mockito.`when`(mockUtExecution.result).thenReturn(UtSandboxFailure(uncheckedException))
150149

151150
defaultMockCoverage(mockUtExecution)
152151

153152
val report = sarifReportMain.createReport()
154153
val result = report.runs.first().results.first()
155-
assert(result.message.text.contains("AccessControlException"))
154+
assert(result.message.text.contains("NoSuchAlgorithmException"))
156155
}
157156

158157
@Test
@@ -179,6 +178,7 @@ class SarifReportTest {
179178

180179
val classMainPath = "com/abc/Main"
181180
val classUtilPath = "com/cba/Util"
181+
Mockito.`when`(mockUtExecution.symbolicSteps).thenReturn(listOf())
182182
Mockito.`when`(mockUtExecution.coverage?.coveredInstructions).thenReturn(listOf(
183183
Instruction(classMainPath, "main(l)l", 3, 1),
184184
Instruction(classMainPath, "main(l)l", 4, 2),
@@ -313,10 +313,8 @@ class SarifReportTest {
313313
Mockito.`when`(mockUtExecution2.result).thenReturn(UtImplicitlyThrownException(NullPointerException(), false))
314314

315315
// different locations
316-
Mockito.`when`(mockUtExecution1.coverage?.coveredInstructions?.lastOrNull()?.lineNumber).thenReturn(11)
317-
Mockito.`when`(mockUtExecution1.coverage?.coveredInstructions?.lastOrNull()?.className).thenReturn("Main")
318-
Mockito.`when`(mockUtExecution2.coverage?.coveredInstructions?.lastOrNull()?.lineNumber).thenReturn(22)
319-
Mockito.`when`(mockUtExecution2.coverage?.coveredInstructions?.lastOrNull()?.className).thenReturn("Main")
316+
mockCoverage(mockUtExecution1, 11, "Main")
317+
mockCoverage(mockUtExecution2, 22, "Main")
320318

321319
val testSets = listOf(
322320
UtMethodTestSet(mockExecutableId, listOf(mockUtExecution1)),
@@ -383,9 +381,19 @@ class SarifReportTest {
383381
Mockito.`when`(mockExecutableId.classId.name).thenReturn("Main")
384382
}
385383

386-
private fun defaultMockCoverage(mockExecution: UtExecution) {
384+
private fun mockCoverage(mockExecution: UtExecution, lineNumber: Int, className: String) {
387385
Mockito.`when`(mockExecution.coverage?.coveredInstructions?.lastOrNull()?.lineNumber).thenReturn(1)
388386
Mockito.`when`(mockExecution.coverage?.coveredInstructions?.lastOrNull()?.className).thenReturn("Main")
387+
(mockExecution as? UtSymbolicExecution)?.let { mockSymbolicSteps(it, lineNumber, className) }
388+
}
389+
390+
private fun mockSymbolicSteps(mockExecution: UtSymbolicExecution, lineNumber: Int, className: String) {
391+
Mockito.`when`(mockExecution.symbolicSteps.lastOrNull()?.lineNumber).thenReturn(lineNumber)
392+
Mockito.`when`(mockExecution.symbolicSteps.lastOrNull()?.method?.declaringClass?.name).thenReturn(className)
393+
}
394+
395+
private fun defaultMockCoverage(mockExecution: UtExecution) {
396+
mockCoverage(mockExecution, 1, "Main")
389397
}
390398

391399
// constants

0 commit comments

Comments
 (0)