Skip to content

Commit 4005f69

Browse files
qwwdfsadpablobaxter
authored andcommitted
Verify integrity of the recovered exception's message on both code paths
Fixes Kotlin#2749
1 parent 93a9674 commit 4005f69

File tree

3 files changed

+39
-18
lines changed

3 files changed

+39
-18
lines changed

kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt

+9-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ private val stackTraceRecoveryClassName = runCatching {
2929
internal actual fun <E : Throwable> recoverStackTrace(exception: E): E {
3030
if (!RECOVER_STACK_TRACES) return exception
3131
// No unwrapping on continuation-less path: exception is not reported multiple times via slow paths
32-
val copy = tryCopyException(exception) ?: return exception
32+
val copy = tryCopyAndVerify(exception) ?: return exception
3333
return copy.sanitizeStackTrace()
3434
}
3535

@@ -66,9 +66,7 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
6666
val (cause, recoveredStacktrace) = exception.causeAndStacktrace()
6767

6868
// Try to create an exception of the same type and get stacktrace from continuation
69-
val newException = tryCopyException(cause) ?: return exception
70-
// Verify that the new exception has the same message as the original one (bail out if not, see #1631)
71-
if (newException.message != cause.message) return exception
69+
val newException = tryCopyAndVerify(cause) ?: return exception
7270
// Update stacktrace
7371
val stacktrace = createStackTrace(continuation)
7472
if (stacktrace.isEmpty()) return exception
@@ -80,6 +78,13 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
8078
return createFinalException(cause, newException, stacktrace)
8179
}
8280

81+
private fun <E : Throwable> tryCopyAndVerify(exception: E): E? {
82+
val newException = tryCopyException(exception) ?: return null
83+
// Verify that the new exception has the same message as the original one (bail out if not, see #1631)
84+
if (newException.message != exception.message) return null
85+
return newException
86+
}
87+
8388
/*
8489
* Here we partially copy original exception stackTrace to make current one much prettier.
8590
* E.g. for

kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryCustomExceptionsTest.kt

+30
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package kotlinx.coroutines.exceptions
66

77
import kotlinx.coroutines.*
8+
import kotlinx.coroutines.channels.*
89
import org.junit.Test
910
import kotlin.test.*
1011

@@ -71,4 +72,33 @@ class StackTraceRecoveryCustomExceptionsTest : TestBase() {
7172
assertEquals("custom", cause.message)
7273
}
7374
}
75+
76+
class WrongMessageException(token: String) : RuntimeException("Token $token")
77+
78+
@Test
79+
fun testWrongMessageException() = runTest {
80+
val result = runCatching {
81+
coroutineScope<Unit> {
82+
throw WrongMessageException("OK")
83+
}
84+
}
85+
val ex = result.exceptionOrNull() ?: error("Expected to fail")
86+
assertTrue(ex is WrongMessageException)
87+
assertEquals("Token OK", ex.message)
88+
}
89+
90+
@Test
91+
fun testWrongMessageExceptionInChannel() = runTest {
92+
// Separate code path
93+
val result = produce<Unit>(SupervisorJob() + Dispatchers.Unconfined) {
94+
throw WrongMessageException("OK")
95+
}
96+
val ex = runCatching {
97+
for (unit in result) {
98+
// Iterator has a special code path
99+
}
100+
}.exceptionOrNull() ?: error("Expected to fail")
101+
assertTrue(ex is WrongMessageException)
102+
assertEquals("Token OK", ex.message)
103+
}
74104
}

kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt

-14
Original file line numberDiff line numberDiff line change
@@ -261,18 +261,4 @@ class StackTraceRecoveryTest : TestBase() {
261261
}
262262
yield() // nop to make sure it is not a tail call
263263
}
264-
265-
@Test
266-
fun testWrongMessageException() = runTest {
267-
val result = runCatching {
268-
coroutineScope<Unit> {
269-
throw WrongMessageException("OK")
270-
}
271-
}
272-
val ex = result.exceptionOrNull() ?: error("Expected to fail")
273-
assertTrue(ex is WrongMessageException)
274-
assertEquals("Token OK", ex.message)
275-
}
276-
277-
public class WrongMessageException(token: String) : RuntimeException("Token $token")
278264
}

0 commit comments

Comments
 (0)