Skip to content

Commit 25707e0

Browse files
Prohibit to set static fields from library classes #697 (#699)
1 parent 2e2d62a commit 25707e0

File tree

5 files changed

+103
-57
lines changed

5 files changed

+103
-57
lines changed

utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,46 @@ inline fun <T> heuristic(reason: WorkaroundReason, block: () -> T): T = block()
2525

2626
/**
2727
* Explains reason for applied workaround.
28-
*
29-
* Workarounds are:
30-
* - HACK - hacks behaviour for contest. Shall be removed sometimes
31-
* - MAKE_SYMBOLIC - Returns a new symbolic value with proper type instead of function result (i.e. for wrappers)
32-
* - IGNORE_SORT_INEQUALITY -- Ignores pairs of particular sorts in stores and selects
33-
* - RUN_CONCRETE -- Runs something concretely instead of symbolic run
34-
* - REMOVE_ANONYMOUS_CLASSES -- Remove anonymous classes from the results passed to the code generation till it doesn't support their generation
35-
* - IGNORE_MODEL_TYPES_INEQUALITY -- Ignore the fact that model before and model after have different types
36-
* - LONG_CODE_FRAGMENTS -- Comment too long blocks of code due to JVM restrictions [65536 bytes](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3)
37-
* - ARRAY_ELEMENT_TYPES_ALWAYS_NULLABLE -- Can't infer nullability for array elements from allocation statement, so make them nullable
38-
* Note:
39-
* - MAKE_SYMBOLIC can lose additional path constraints or branches from function call
4028
*/
4129
enum class WorkaroundReason {
30+
/**
31+
* Hacks behaviour for contest. Shall be removed sometimes
32+
*/
4233
HACK,
34+
/**
35+
* Returns a new symbolic value with proper type instead of function result (i.e. for wrappers)
36+
*
37+
* [MAKE_SYMBOLIC] can lose additional path constraints or branches from function call
38+
*/
4339
MAKE_SYMBOLIC,
40+
/**
41+
* Ignores pairs of particular sorts in stores and selects
42+
*/
4443
IGNORE_SORT_INEQUALITY,
44+
/**
45+
* Runs something concretely instead of symbolic run
46+
*/
4547
RUN_CONCRETE,
48+
/**
49+
* Remove anonymous classes from the results passed to the code generation till it doesn't support their generation
50+
*/
4651
REMOVE_ANONYMOUS_CLASSES,
52+
/**
53+
* Ignore the fact that model before and model after have different types
54+
*/
4755
IGNORE_MODEL_TYPES_INEQUALITY,
56+
/**
57+
* Comment too long blocks of code due to JVM restrictions [65536 bytes](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3)
58+
*/
4859
LONG_CODE_FRAGMENTS,
60+
/**
61+
* Can't infer nullability for array elements from allocation statement, so make them nullable
62+
*/
4963
ARRAY_ELEMENT_TYPES_ALWAYS_NULLABLE,
64+
/**
65+
* We won't branch on static field from trusted libraries and won't add them to staticsBefore. For now, it saves us
66+
* from setting [SecurityManager] and other suspicious stuff, but it can lead to coverage regression and thus it
67+
* requires thorough [investigation](https://github.com/UnitTestBot/UTBotJava/issues/716).
68+
*/
69+
IGNORE_STATICS_FROM_TRUSTED_LIBRARIES,
5070
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,13 @@ object UtSettings {
389389
*/
390390
var skipTestGenerationForSyntheticMethods by getBooleanProperty(true)
391391

392+
/**
393+
* Flag that indicates whether should we branch on and set static fields from trusted libraries or not.
394+
*
395+
* @see [org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES]
396+
*/
397+
var ignoreStaticsFromTrustedLibraries by getBooleanProperty(true)
398+
392399
override fun toString(): String =
393400
settingsValues
394401
.mapKeys { it.key.name }

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
124124
private val concrete: PersistentMap<UtAddrExpression, Concrete> = persistentHashMapOf(),
125125
private val mockInfos: PersistentList<MockInfoEnriched> = persistentListOf(),
126126
private val staticInstanceStorage: PersistentMap<ClassId, ObjectValue> = persistentHashMapOf(),
127-
private val initializedStaticFields: PersistentMap<FieldId, Any?> = persistentHashMapOf(),
127+
private val initializedStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
128128
private val staticFieldsStates: PersistentMap<FieldId, FieldStates> = persistentHashMapOf(),
129129
private val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
130130
private val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
@@ -290,7 +290,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
290290
concrete = concrete.putAll(update.concrete),
291291
mockInfos = mockInfos.mergeWithUpdate(update.mockInfos),
292292
staticInstanceStorage = staticInstanceStorage.putAll(update.staticInstanceStorage),
293-
initializedStaticFields = initializedStaticFields.putAll(update.initializedStaticFields),
293+
initializedStaticFields = initializedStaticFields.addAll(update.initializedStaticFields),
294294
staticFieldsStates = previousMemoryStates.toPersistentMap().putAll(updatedStaticFields),
295295
meaningfulStaticFields = meaningfulStaticFields.addAll(update.meaningfulStaticFields),
296296
addrToArrayType = addrToArrayType.putAll(update.addrToArrayType),
@@ -963,7 +963,7 @@ data class MemoryUpdate(
963963
val concrete: PersistentMap<UtAddrExpression, Concrete> = persistentHashMapOf(),
964964
val mockInfos: PersistentList<MockInfoEnriched> = persistentListOf(),
965965
val staticInstanceStorage: PersistentMap<ClassId, ObjectValue> = persistentHashMapOf(),
966-
val initializedStaticFields: PersistentMap<FieldId, Any?> = persistentHashMapOf(),
966+
val initializedStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
967967
val staticFieldsUpdates: PersistentList<StaticFieldMemoryUpdateInfo> = persistentListOf(),
968968
val meaningfulStaticFields: PersistentSet<FieldId> = persistentHashSetOf(),
969969
val addrToArrayType: PersistentMap<UtAddrExpression, ArrayType> = persistentHashMapOf(),
@@ -982,7 +982,7 @@ data class MemoryUpdate(
982982
concrete = concrete.putAll(other.concrete),
983983
mockInfos = mockInfos.mergeWithUpdate(other.mockInfos),
984984
staticInstanceStorage = staticInstanceStorage.putAll(other.staticInstanceStorage),
985-
initializedStaticFields = initializedStaticFields.putAll(other.initializedStaticFields),
985+
initializedStaticFields = initializedStaticFields.addAll(other.initializedStaticFields),
986986
staticFieldsUpdates = staticFieldsUpdates.addAll(other.staticFieldsUpdates),
987987
meaningfulStaticFields = meaningfulStaticFields.addAll(other.meaningfulStaticFields),
988988
addrToArrayType = addrToArrayType.putAll(other.addrToArrayType),

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.utbot.engine
22

33
import kotlinx.collections.immutable.persistentHashMapOf
4+
import kotlinx.collections.immutable.persistentHashSetOf
45
import kotlinx.collections.immutable.persistentListOf
56
import kotlinx.collections.immutable.persistentSetOf
67
import kotlinx.collections.immutable.toPersistentList
7-
import kotlinx.collections.immutable.toPersistentMap
88
import kotlinx.collections.immutable.toPersistentSet
99
import org.utbot.common.WorkaroundReason.HACK
10+
import org.utbot.framework.UtSettings.ignoreStaticsFromTrustedLibraries
11+
import org.utbot.common.WorkaroundReason.IGNORE_STATICS_FROM_TRUSTED_LIBRARIES
1012
import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES
1113
import org.utbot.common.unreachableBranch
1214
import org.utbot.common.withAccessibility
@@ -112,6 +114,7 @@ import soot.DoubleType
112114
import soot.FloatType
113115
import soot.IntType
114116
import soot.LongType
117+
import soot.Modifier
115118
import soot.PrimType
116119
import soot.RefLikeType
117120
import soot.RefType
@@ -560,7 +563,7 @@ class Traverser(
560563
}
561564

562565
val initializedStaticFieldsMemoryUpdate = MemoryUpdate(
563-
initializedStaticFields = staticFields.associate { it.first.fieldId to it.second.single() }.toPersistentMap(),
566+
initializedStaticFields = staticFields.map { it.first.fieldId }.toPersistentSet(),
564567
meaningfulStaticFields = meaningfulStaticFields.map { it.first.fieldId }.toPersistentSet(),
565568
symbolicEnumValues = enumConstantSymbolicValues.toPersistentList()
566569
)
@@ -596,7 +599,7 @@ class Traverser(
596599

597600
// Collects memory updates
598601
val initializedFieldUpdate =
599-
MemoryUpdate(initializedStaticFields = persistentHashMapOf(fieldId to concreteValue))
602+
MemoryUpdate(initializedStaticFields = persistentHashSetOf(fieldId))
600603

601604
val objectUpdate = objectUpdate(
602605
instance = findOrCreateStaticObject(declaringClass.type),
@@ -823,7 +826,7 @@ class Traverser(
823826
val staticFieldMemoryUpdate = StaticFieldMemoryUpdateInfo(fieldId, value)
824827
val touchedStaticFields = persistentListOf(staticFieldMemoryUpdate)
825828
queuedSymbolicStateUpdates += MemoryUpdate(staticFieldsUpdates = touchedStaticFields)
826-
if (!environment.method.isStaticInitializer && !fieldId.isSynthetic) {
829+
if (!environment.method.isStaticInitializer && isStaticFieldMeaningful(left.field)) {
827830
queuedSymbolicStateUpdates += MemoryUpdate(meaningfulStaticFields = persistentSetOf(fieldId))
828831
}
829832
}
@@ -1780,13 +1783,27 @@ class Traverser(
17801783
queuedSymbolicStateUpdates += MemoryUpdate(staticFieldsUpdates = touchedStaticFields)
17811784

17821785
// TODO filter enum constant static fields JIRA:1681
1783-
if (!environment.method.isStaticInitializer && !fieldId.isSynthetic) {
1786+
if (!environment.method.isStaticInitializer && isStaticFieldMeaningful(field)) {
17841787
queuedSymbolicStateUpdates += MemoryUpdate(meaningfulStaticFields = persistentSetOf(fieldId))
17851788
}
17861789

17871790
return createdField
17881791
}
17891792

1793+
/**
1794+
* For now the field is `meaningful` if it is safe to set, that is, it is not an internal system field nor a
1795+
* synthetic field. This filter is needed to prohibit changing internal fields, which can break up our own
1796+
* code and which are useless for the user.
1797+
*
1798+
* @return `true` if the field is meaningful, `false` otherwise.
1799+
*/
1800+
private fun isStaticFieldMeaningful(field: SootField) =
1801+
!Modifier.isSynthetic(field.modifiers) &&
1802+
// we don't want to set fields from library classes
1803+
!workaround(IGNORE_STATICS_FROM_TRUSTED_LIBRARIES) {
1804+
ignoreStaticsFromTrustedLibraries && field.declaringClass.isFromTrustedLibrary()
1805+
}
1806+
17901807
/**
17911808
* Locates object represents static fields of particular class.
17921809
*
@@ -3321,10 +3338,11 @@ class Traverser(
33213338
val updatedFields = staticFieldsUpdates.mapTo(mutableSetOf()) { it.fieldId }
33223339
val objectUpdates = mutableListOf<UtNamedStore>()
33233340

3324-
// we assign unbounded symbolic variables for every non-final field of the class
3341+
// we assign unbounded symbolic variables for every non-final meaningful field of the class
3342+
// fields from predefined library classes are excluded, because there are not meaningful
33253343
typeResolver
33263344
.findFields(declaringClass.type)
3327-
.filter { !it.isFinal && it.fieldId in updatedFields }
3345+
.filter { !it.isFinal && it.fieldId in updatedFields && isStaticFieldMeaningful(it) }
33283346
.forEach {
33293347
// remove updates from clinit, because we'll replace those values
33303348
// with new unbounded symbolic variable

0 commit comments

Comments
 (0)