Skip to content

Commit 953dc8f

Browse files
authored
Kryo race fix (#1968)
[utbot-interprocess] 1. fixing race in inter-process communication: if multiple request accesses kryo when it writes message - kryo might increment some internal object graph fields in unpredictable order, causing serialized value become severely corrupted. This bug might be the reason for many other hard traceble bugs. 2. minor refactoring and build fixes
1 parent 7065f60 commit 953dc8f

File tree

7 files changed

+18
-27
lines changed

7 files changed

+18
-27
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ apacheCommonsExecVersion=1.2
5555
apacheCommonsTextVersion=1.9
5656
rgxgenVersion=1.3
5757
antlrVersion=4.9.2
58-
kryoVersion=5.3.0
58+
kryoVersion=5.4.0
5959
kryoSerializersVersion=0.45
6060
asmVersion=9.2
6161
testNgVersion=7.6.0

utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
170170
}
171171
watchdog.measureTimeForActiveCall(findMethodParamNames, "Find method parameters names") { params ->
172172
val classId = kryoHelper.readObject<ClassId>(params.classId)
173-
val byMethodDescription = kryoHelper.readObject<Map<MethodDescription, List<String>>>(params.bySignature)
173+
val bySignatureRaw = kryoHelper.readObject<List<Pair<MethodDescription, List<String>>>>(params.bySignature)
174+
val byMethodDescription = bySignatureRaw.associate { it.first to it.second }
174175
FindMethodParamNamesResult(kryoHelper.writeObject(classId.jClass.allNestedClasses.flatMap { clazz -> clazz.id.allMethods.mapNotNull { it.method.kotlinFunction } }
175176
.mapNotNull { method -> byMethodDescription[method.methodDescription()]?.let { params -> method.executableId to params } }
176177
.toMap()))

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/util/KryoHelper.kt

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import com.esotericsoftware.kryo.kryo5.util.DefaultInstantiatorStrategy
1212
import com.jetbrains.rd.util.lifetime.Lifetime
1313
import com.jetbrains.rd.util.lifetime.throwIfNotAlive
1414
import java.io.ByteArrayOutputStream
15+
import java.util.concurrent.locks.ReentrantLock
16+
import kotlin.concurrent.withLock
1517

1618
/**
1719
* Helpful class for working with the kryo.
@@ -24,36 +26,17 @@ class KryoHelper constructor(
2426
private val kryoInput= Input()
2527
private val sendKryo: Kryo = TunedKryo()
2628
private val receiveKryo: Kryo = TunedKryo()
29+
private val myLockObject = ReentrantLock()
2730

2831
init {
32+
sendKryo.setAutoReset(true)
33+
receiveKryo.setAutoReset(true)
2934
lifetime.onTermination {
3035
kryoInput.close()
3136
kryoOutput.close()
3237
}
3338
}
3439

35-
fun <T> register(clazz: Class<T>, serializer: Serializer<T>) {
36-
sendKryo.register(clazz, serializer)
37-
receiveKryo.register(clazz, serializer)
38-
}
39-
40-
private fun <T> addInstantiatorOnKryo(kryo: Kryo, clazz: Class<T>, factory: () -> T) {
41-
val instantiator = kryo.instantiatorStrategy
42-
kryo.instantiatorStrategy = object : InstantiatorStrategy {
43-
override fun <R : Any?> newInstantiatorOf(type: Class<R>): ObjectInstantiator<R> {
44-
return if (type === clazz) {
45-
ObjectInstantiator<R> { factory() as R }
46-
}
47-
else
48-
instantiator.newInstantiatorOf(type)
49-
}
50-
}
51-
}
52-
fun <T> addInstantiator(clazz: Class<T>, factory: () -> T) {
53-
addInstantiatorOnKryo(sendKryo, clazz, factory)
54-
addInstantiatorOnKryo(receiveKryo, clazz, factory)
55-
}
56-
5740
fun setKryoClassLoader(classLoader: ClassLoader) {
5841
sendKryo.classLoader = classLoader
5942
receiveKryo.classLoader = classLoader
@@ -64,7 +47,7 @@ class KryoHelper constructor(
6447
*
6548
* @throws WritingToKryoException wraps all exceptions
6649
*/
67-
fun <T> writeObject(obj: T): ByteArray {
50+
fun <T> writeObject(obj: T): ByteArray = myLockObject.withLock {
6851
lifetime.throwIfNotAlive()
6952
try {
7053
sendKryo.writeClassAndObject(kryoOutput, obj)
@@ -84,14 +67,17 @@ class KryoHelper constructor(
8467
*
8568
* @throws ReadingFromKryoException wraps all exceptions
8669
*/
87-
fun <T> readObject(byteArray: ByteArray): T {
70+
fun <T> readObject(byteArray: ByteArray): T = myLockObject.withLock {
8871
lifetime.throwIfNotAlive()
8972
return try {
9073
kryoInput.buffer = byteArray
9174
receiveKryo.readClassAndObject(kryoInput) as T
9275
} catch (e: Exception) {
9376
throw ReadingFromKryoException(e)
9477
}
78+
finally {
79+
receiveKryo.reset()
80+
}
9581
}
9682
}
9783

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ object CodeGenerationController {
242242
)
243243
}
244244
}
245+
245246
private fun createUtilityClassIfNeeded(
246247
utilClassListener: UtilClassListener,
247248
model: GenerateTestsModel,

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class EngineProcess private constructor(val project: Project, private val classN
223223

224224
val bySignature = executeWithTimeoutSuspended {
225225
DumbService.getInstance(project).runReadActionInSmartMode(Computable {
226-
methods.associate { it.methodDescription() to it.paramNames() }
226+
methods.map { it.methodDescription() to it.paramNames() }
227227
})
228228
}
229229
val arguments = FindMethodParamNamesArguments(

utbot-maven/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ task generatePluginDescriptor(type: JavaExec, dependsOn: [compileKotlin, generat
119119
}
120120
}
121121

122+
classes.dependsOn(generatePluginDescriptor)
123+
122124
publishing {
123125
publications {
124126
pluginMaven(MavenPublication) {

utbot-sample/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ dependencies {
66
implementation group: 'org.jetbrains', name: 'annotations', version: '16.0.2'
77
implementation group: 'com.github.stephenc.findbugs', name: 'findbugs-annotations', version: '1.3.9-1'
88
implementation 'org.projectlombok:lombok:1.18.20'
9+
testImplementation 'org.mockito:mockito-core:4.2.0'
910
annotationProcessor 'org.projectlombok:lombok:1.18.20'
1011
implementation(project(":utbot-api"))
1112
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'

0 commit comments

Comments
 (0)