From e51547cd28551efc2f228ff2caa3de90b6d9d23b Mon Sep 17 00:00:00 2001 From: mertalev <101130780+mertalev@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:45:24 -0500 Subject: [PATCH] potential race conditions --- .../app/alextran/immich/core/SSLConfig.kt | 28 ++++++++++--------- .../immich/images/RemoteImagesImpl.kt | 16 +++++++---- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/SSLConfig.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/SSLConfig.kt index 9686d49c2c..f62042cd99 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/SSLConfig.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/SSLConfig.kt @@ -36,20 +36,22 @@ object SSLConfig { serverHost: String?, clientCertHash: Int ) { - val newHash = computeHash(allowSelfSigned, serverHost, clientCertHash) - val newRequiresCustomSSL = allowSelfSigned || keyManagers != null - if (newHash == configHash && sslSocketFactory != null && requiresCustomSSL == newRequiresCustomSSL) { - return // Config unchanged, skip - } + synchronized(this) { + val newHash = computeHash(allowSelfSigned, serverHost, clientCertHash) + val newRequiresCustomSSL = allowSelfSigned || keyManagers != null + if (newHash == configHash && sslSocketFactory != null && requiresCustomSSL == newRequiresCustomSSL) { + return // Config unchanged, skip + } - val sslContext = SSLContext.getInstance("TLS") - sslContext.init(keyManagers, trustManagers, null) - sslSocketFactory = sslContext.socketFactory - trustManager = trustManagers?.filterIsInstance()?.firstOrNull() - ?: getDefaultTrustManager() - requiresCustomSSL = newRequiresCustomSSL - configHash = newHash - notifyListeners() + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(keyManagers, trustManagers, null) + sslSocketFactory = sslContext.socketFactory + trustManager = trustManagers?.filterIsInstance()?.firstOrNull() + ?: getDefaultTrustManager() + requiresCustomSSL = newRequiresCustomSSL + configHash = newHash + notifyListeners() + } } private fun computeHash(allowSelfSigned: Boolean, serverHost: String?, clientCertHash: Int): Int { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/RemoteImagesImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/RemoteImagesImpl.kt index 2f5daff37d..30ce95274e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/RemoteImagesImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/RemoteImagesImpl.kt @@ -120,13 +120,15 @@ private object ImageFetcherManager { } private fun invalidate() { - val oldFetcher = fetcher - if (oldFetcher is OkHttpImageFetcher && SSLConfig.requiresCustomSSL) { - fetcher = oldFetcher.reconfigure(SSLConfig.sslSocketFactory, SSLConfig.trustManager) - return + synchronized(this) { + val oldFetcher = fetcher + if (oldFetcher is OkHttpImageFetcher && SSLConfig.requiresCustomSSL) { + fetcher = oldFetcher.reconfigure(SSLConfig.sslSocketFactory, SSLConfig.trustManager) + return + } + fetcher = build() + oldFetcher.drain() } - fetcher = build() - oldFetcher.drain() } private fun build(): ImageFetcher { @@ -205,6 +207,7 @@ private class CronetImageFetcher(context: Context, cacheDir: File) : ImageFetche override fun drain() { val shouldShutdown = synchronized(stateLock) { + if (draining) return draining = true activeCount == 0 } @@ -406,6 +409,7 @@ private class OkHttpImageFetcher private constructor( override fun drain() { val shouldClose = synchronized(stateLock) { + if (draining) return draining = true activeCount == 0 }