Skip to content

Commit bf3f6b4

Browse files
Refactored Taint Analysis (#216)
Co-authored-by: Sergey Pospelov <[email protected]>
1 parent 4499986 commit bf3f6b4

File tree

110 files changed

+7667
-4096
lines changed

Some content is hidden

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

110 files changed

+7667
-4096
lines changed

buildSrc/src/main/kotlin/Dependencies.kt

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ object Versions {
2929
const val kotlinx_metadata = "0.5.0"
3030
const val kotlinx_serialization = "1.4.1"
3131
const val licenser = "0.6.1"
32-
const val mockito = "4.8.0"
32+
const val mockk = "1.13.3"
33+
const val sarif4k = "0.5.0"
3334
const val shadow = "8.1.1"
3435
const val slf4j = "1.7.36"
3536
const val soot_utbot_fork = "4.4.0-FORK-2"
@@ -134,6 +135,11 @@ object Libs {
134135
)
135136

136137
// https://github.com/Kotlin/kotlinx.serialization
138+
val kotlinx_serialization_core = dep(
139+
group = "org.jetbrains.kotlinx",
140+
name = "kotlinx-serialization-core",
141+
version = Versions.kotlinx_serialization
142+
)
137143
val kotlinx_serialization_json = dep(
138144
group = "org.jetbrains.kotlinx",
139145
name = "kotlinx-serialization-json",
@@ -199,11 +205,11 @@ object Libs {
199205
version = Versions.jdot
200206
)
201207

202-
// https://github.com/mockito/mockito
203-
val mockito_core = dep(
204-
group = "org.mockito",
205-
name = "mockito-core",
206-
version = Versions.mockito
208+
// https://github.com/mockk/mockk
209+
val mockk = dep(
210+
group = "io.mockk",
211+
name = "mockk",
212+
version = Versions.mockk
207213
)
208214

209215
// https://github.com/JetBrains/java-annotations
@@ -275,6 +281,13 @@ object Libs {
275281
name = "cwe${cweNum}",
276282
version = Versions.juliet
277283
)
284+
285+
// https://github.com/detekt/sarif4k
286+
val sarif4k = dep(
287+
group = "io.github.detekt.sarif4k",
288+
name = "sarif4k",
289+
version = Versions.sarif4k
290+
)
278291
}
279292

280293
object Plugins {
@@ -320,4 +333,4 @@ object Plugins {
320333

321334
fun PluginDependenciesSpec.id(plugin: Plugins.ProjectPlugin) {
322335
id(plugin.id).version(plugin.version)
323-
}
336+
}

jacodb-analysis/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ plugins {
66
dependencies {
77
api(project(":jacodb-core"))
88
api(project(":jacodb-api"))
9+
api(project(":jacodb-taint-configuration"))
910

1011
implementation(Libs.kotlin_logging)
1112
implementation(Libs.slf4j_simple)
1213
implementation(Libs.kotlinx_coroutines_core)
1314
implementation(Libs.kotlinx_serialization_json)
15+
api(Libs.sarif4k)
1416

1517
testImplementation(testFixtures(project(":jacodb-core")))
1618
testImplementation(project(":jacodb-api"))
19+
testImplementation(kotlin("test"))
20+
testImplementation(Libs.mockk)
21+
22+
// Additional deps for analysis:
1723
testImplementation(files("src/test/resources/pointerbench.jar"))
1824
testImplementation(Libs.joda_time)
1925
testImplementation(Libs.juliet_support)

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt

Lines changed: 0 additions & 74 deletions
This file was deleted.
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright 2022 UnitTestBot contributors (utbot.org)
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jacodb.analysis.config
18+
19+
import org.jacodb.analysis.ifds.AccessPath
20+
import org.jacodb.analysis.ifds.ElementAccessor
21+
import org.jacodb.analysis.ifds.Maybe
22+
import org.jacodb.analysis.ifds.onSome
23+
import org.jacodb.analysis.ifds.toPath
24+
import org.jacodb.analysis.taint.Tainted
25+
import org.jacodb.api.cfg.JcBool
26+
import org.jacodb.api.cfg.JcConstant
27+
import org.jacodb.api.cfg.JcInt
28+
import org.jacodb.api.cfg.JcStringConstant
29+
import org.jacodb.api.cfg.JcValue
30+
import org.jacodb.api.ext.isAssignable
31+
import org.jacodb.taint.configuration.And
32+
import org.jacodb.taint.configuration.AnnotationType
33+
import org.jacodb.taint.configuration.ConditionVisitor
34+
import org.jacodb.taint.configuration.ConstantBooleanValue
35+
import org.jacodb.taint.configuration.ConstantEq
36+
import org.jacodb.taint.configuration.ConstantGt
37+
import org.jacodb.taint.configuration.ConstantIntValue
38+
import org.jacodb.taint.configuration.ConstantLt
39+
import org.jacodb.taint.configuration.ConstantMatches
40+
import org.jacodb.taint.configuration.ConstantStringValue
41+
import org.jacodb.taint.configuration.ConstantTrue
42+
import org.jacodb.taint.configuration.ContainsMark
43+
import org.jacodb.taint.configuration.IsConstant
44+
import org.jacodb.taint.configuration.IsType
45+
import org.jacodb.taint.configuration.Not
46+
import org.jacodb.taint.configuration.Or
47+
import org.jacodb.taint.configuration.PositionResolver
48+
import org.jacodb.taint.configuration.SourceFunctionMatches
49+
import org.jacodb.taint.configuration.TypeMatches
50+
51+
open class BasicConditionEvaluator(
52+
internal val positionResolver: PositionResolver<Maybe<JcValue>>,
53+
) : ConditionVisitor<Boolean> {
54+
55+
override fun visit(condition: ConstantTrue): Boolean {
56+
return true
57+
}
58+
59+
override fun visit(condition: Not): Boolean {
60+
return !condition.arg.accept(this)
61+
}
62+
63+
override fun visit(condition: And): Boolean {
64+
return condition.args.all { it.accept(this) }
65+
}
66+
67+
override fun visit(condition: Or): Boolean {
68+
return condition.args.any { it.accept(this) }
69+
}
70+
71+
override fun visit(condition: IsConstant): Boolean {
72+
positionResolver.resolve(condition.position).onSome { return it is JcConstant }
73+
return false
74+
}
75+
76+
override fun visit(condition: IsType): Boolean {
77+
// Note: TaintConfigurationFeature.ConditionSpecializer is responsible for
78+
// expanding IsType condition upon parsing the taint configuration.
79+
error("Unexpected condition: $condition")
80+
}
81+
82+
override fun visit(condition: AnnotationType): Boolean {
83+
// Note: TaintConfigurationFeature.ConditionSpecializer is responsible for
84+
// expanding AnnotationType condition upon parsing the taint configuration.
85+
error("Unexpected condition: $condition")
86+
}
87+
88+
override fun visit(condition: ConstantEq): Boolean {
89+
positionResolver.resolve(condition.position).onSome { value ->
90+
return when (val constant = condition.value) {
91+
is ConstantBooleanValue -> {
92+
value is JcBool && value.value == constant.value
93+
}
94+
95+
is ConstantIntValue -> {
96+
value is JcInt && value.value == constant.value
97+
}
98+
99+
is ConstantStringValue -> {
100+
// TODO: if 'value' is not string, convert it to string and compare with 'constant.value'
101+
value is JcStringConstant && value.value == constant.value
102+
}
103+
}
104+
}
105+
return false
106+
}
107+
108+
override fun visit(condition: ConstantLt): Boolean {
109+
positionResolver.resolve(condition.position).onSome { value ->
110+
return when (val constant = condition.value) {
111+
is ConstantIntValue -> {
112+
value is JcInt && value.value < constant.value
113+
}
114+
115+
else -> error("Unexpected constant: $constant")
116+
}
117+
}
118+
return false
119+
}
120+
121+
override fun visit(condition: ConstantGt): Boolean {
122+
positionResolver.resolve(condition.position).onSome { value ->
123+
return when (val constant = condition.value) {
124+
is ConstantIntValue -> {
125+
value is JcInt && value.value > constant.value
126+
}
127+
128+
else -> error("Unexpected constant: $constant")
129+
}
130+
}
131+
return false
132+
}
133+
134+
override fun visit(condition: ConstantMatches): Boolean {
135+
positionResolver.resolve(condition.position).onSome { value ->
136+
val re = condition.pattern.toRegex()
137+
return re.matches(value.toString())
138+
}
139+
return false
140+
}
141+
142+
override fun visit(condition: SourceFunctionMatches): Boolean {
143+
TODO("Not implemented yet")
144+
}
145+
146+
override fun visit(condition: ContainsMark): Boolean {
147+
error("This visitor does not support condition $condition. Use FactAwareConditionEvaluator instead")
148+
}
149+
150+
override fun visit(condition: TypeMatches): Boolean {
151+
positionResolver.resolve(condition.position).onSome { value ->
152+
return value.type.isAssignable(condition.type)
153+
}
154+
return false
155+
}
156+
}
157+
158+
class FactAwareConditionEvaluator(
159+
private val fact: Tainted,
160+
positionResolver: PositionResolver<Maybe<JcValue>>,
161+
) : BasicConditionEvaluator(positionResolver) {
162+
163+
override fun visit(condition: ContainsMark): Boolean {
164+
if (fact.mark != condition.mark) return false
165+
positionResolver.resolve(condition.position).onSome { value ->
166+
val variable = value.toPath()
167+
168+
// FIXME: Adhoc for arrays
169+
val variableWithoutStars = variable.removeTrailingElementAccessors()
170+
val factWithoutStars = fact.variable.removeTrailingElementAccessors()
171+
if (variableWithoutStars == factWithoutStars) return true
172+
173+
return variable == fact.variable
174+
}
175+
return false
176+
}
177+
178+
private fun AccessPath.removeTrailingElementAccessors(): AccessPath {
179+
val accesses = accesses.toMutableList()
180+
while (accesses.lastOrNull() is ElementAccessor) {
181+
accesses.removeLast()
182+
}
183+
return AccessPath(value, accesses)
184+
}
185+
}

0 commit comments

Comments
 (0)