tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 04cd091917a368dbb9a722d9d1bbc5568f536c19
parent 6eb2a1b52bbbbdb43cc7ee8b64854f3e8a18b485
Author: Matthew Tighe <matthewdtighe@gmail.com>
Date:   Mon,  8 Dec 2025 17:05:49 +0000

Bug 2003704 - ensure throwables are serializable when starting crash services r=android-reviewers,tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D275021

Diffstat:
Mmobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/CrashReporter.kt | 28++++++++++++++++++++++++++--
Mmobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/db/CrashEntity.kt | 13+++++++++----
2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/CrashReporter.kt b/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/CrashReporter.kt @@ -7,6 +7,7 @@ package mozilla.components.lib.crash import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.os.BadParcelableException import android.os.Build import androidx.annotation.StyleRes import androidx.annotation.VisibleForTesting @@ -19,6 +20,7 @@ import kotlinx.coroutines.withContext import mozilla.components.concept.base.crash.Breadcrumb import mozilla.components.concept.base.crash.CrashReporting import mozilla.components.lib.crash.db.CrashDatabase +import mozilla.components.lib.crash.db.forceSerializable import mozilla.components.lib.crash.db.insertCrashSafely import mozilla.components.lib.crash.db.insertReportSafely import mozilla.components.lib.crash.db.toCrash @@ -382,13 +384,35 @@ class CrashReporter internal constructor( } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun sendCrashReport(context: Context, crash: Crash) { + internal fun sendCrashReport(context: Context, crash: Crash) = try { ContextCompat.startForegroundService(context, SendCrashReportService.createReportIntent(context, crash)) + } catch (e: BadParcelableException) { + (crash as? Crash.UncaughtExceptionCrash)?.let { + // We may end up with a throwable that isn't completely serializable, which will cause + // a crash when the service tries to unbundle it. + val updatedCrash = it.copy(throwable = it.throwable.forceSerializable()) + ContextCompat.startForegroundService( + context, + SendCrashReportService.createReportIntent(context, updatedCrash), + ) + logger.warn("replaced throwable for crash that could not be serialized") + } } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun sendCrashTelemetry(context: Context, crash: Crash) { + internal fun sendCrashTelemetry(context: Context, crash: Crash) = try { ContextCompat.startForegroundService(context, SendCrashTelemetryService.createReportIntent(context, crash)) + } catch (e: BadParcelableException) { + (crash as? Crash.UncaughtExceptionCrash)?.let { + // We may end up with a throwable that isn't completely serializable, which will cause + // a crash when the service tries to unbundle it. + val updatedCrash = it.copy(throwable = it.throwable.forceSerializable()) + ContextCompat.startForegroundService( + context, + SendCrashTelemetryService.createReportIntent(context, updatedCrash), + ) + logger.warn("replaced throwable for crash that could not be serialized") + } } @VisibleForTesting diff --git a/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/db/CrashEntity.kt b/mobile/android/android-components/components/lib/crash/src/main/java/mozilla/components/lib/crash/db/CrashEntity.kt @@ -173,13 +173,18 @@ private fun Throwable.serialize(): ByteArray { // If throwable isn't serializable, then use a placeholder Throwable with // the same stack and include basic name / message data. This gives us // at least some data to understand these crashes in the wild. - val innerMessage = "${javaClass.name}: $message" - val altThrowable = CrashReporterUnableToRestoreException(innerMessage) - altThrowable.stackTrace = stackTrace.clone() - altThrowable.serialize() + + this.forceSerializable().serialize() } } +internal fun Throwable.forceSerializable(): Throwable { + val innerMessage = "${javaClass.name}: $message" + val altThrowable = CrashReporterUnableToRestoreException(innerMessage) + altThrowable.stackTrace = stackTrace.clone() + return altThrowable +} + private fun ByteArray.deserializeThrowable(): Throwable { val byteArrayInputStream = ByteArrayInputStream(this) val throwable = ObjectInputStream(byteArrayInputStream).use { ois ->