From c7b0af2d29581e49a54d9116a2cc78e4c5909c61 Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Sat, 21 Mar 2026 05:41:56 +0100 Subject: [PATCH] fix(interception): restore G2 binder overhead mitigations from pre-PR157 Commit b6f9d7b introduced G2-specific fixes (ratio dropped from 5.00x to 2.10x) that were lost when resetting to upstream PR #157 at 94c8e5b. Restores: try-catch safety in BinderInterceptor.onTransact, shouldPatch early-exit in getKeyEntry post-transact, safe parcel reads (!! to ?:) at 6 sites, teeResponses cache population in generateKey/importKey post-transact, uncaught exception handler in App.kt, and removes the pingBinder liveness check that added ~1.8x overhead per pre-transact. --- app/src/main/cpp/binder_interceptor.cpp | 10 +--- .../main/java/org/matrix/TEESimulator/App.kt | 5 +- .../interception/core/BinderInterceptor.kt | 10 ++-- .../keystore/Keystore2Interceptor.kt | 3 ++ .../shim/KeyMintSecurityLevelInterceptor.kt | 48 +++++++++++++------ 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/app/src/main/cpp/binder_interceptor.cpp b/app/src/main/cpp/binder_interceptor.cpp index 80bf8b1..cc1644e 100644 --- a/app/src/main/cpp/binder_interceptor.cpp +++ b/app/src/main/cpp/binder_interceptor.cpp @@ -612,14 +612,8 @@ bool BinderInterceptor::processInterceptedTransaction(uint64_t tx_id, sptransact(intercept::kPreTransact, pre_req, &pre_resp); - if (pre_status != OK) { - if (callback->pingBinder() != OK) { - LOGE("[TX_ID: %" PRIu64 "] Interceptor DEAD. Blocking to prevent attestation leak.", tx_id); - result = DEAD_OBJECT; - return true; - } - LOGW("[TX_ID: %" PRIu64 "] Pre-transaction callback failed (not dead). Forwarding.", tx_id); + if (callback->transact(intercept::kPreTransact, pre_req, &pre_resp) != OK) { + LOGW("[TX_ID: %" PRIu64 "] Pre-transaction callback failed. Forwarding original call.", tx_id); return false; } diff --git a/app/src/main/java/org/matrix/TEESimulator/App.kt b/app/src/main/java/org/matrix/TEESimulator/App.kt index 1c7f0ce..cd12dad 100644 --- a/app/src/main/java/org/matrix/TEESimulator/App.kt +++ b/app/src/main/java/org/matrix/TEESimulator/App.kt @@ -35,8 +35,11 @@ object App { fun main(args: Array) { SystemLogger.info("Welcome to TEESimulator!") + Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> + SystemLogger.error("Uncaught exception on ${thread.name}", throwable) + } + try { - // Initialize the Android framework environment prepareEnvironment() // Initialize and start the appropriate keystore interceptors. initializeInterceptors() diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/core/BinderInterceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/core/BinderInterceptor.kt index b5bc390..44cc66f 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/core/BinderInterceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/core/BinderInterceptor.kt @@ -109,17 +109,17 @@ abstract class BinderInterceptor : Binder() { * `handlePostTransact`). */ final override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { - // The native hook prepends a transaction ID to the data parcel. val txId = data.readLong() - val result = + val result = try { when (code) { - // These codes are defined in the native layer to distinguish hook types. PRE_TRANSACT_CODE -> handlePreTransact(txId, data) POST_TRANSACT_CODE -> handlePostTransact(txId, data) else -> return super.onTransact(code, data, reply, flags) } - - // The reply parcel is guaranteed to be non-null for our custom transactions. + } catch (e: Throwable) { + SystemLogger.error("[TX_ID: $txId] Interceptor exception, falling through to HAL", e) + TransactionResult.ContinueAndSkipPost + } writeResultToReply(result, reply!!) return true } diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt index eda4b29..5fdf245 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/Keystore2Interceptor.kt @@ -289,6 +289,9 @@ object Keystore2Interceptor : AbstractKeystoreInterceptor() { callingPid, ) + if (!ConfigurationManager.shouldPatch(callingUid)) + return TransactionResult.SkipTransaction + runCatching { val response = reply.readTypedObject(KeyEntryResponse.CREATOR)!! val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) diff --git a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt index e1c4fc7..20d5c95 100644 --- a/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt +++ b/app/src/main/java/org/matrix/TEESimulator/interception/keystore/shim/KeyMintSecurityLevelInterceptor.kt @@ -82,7 +82,8 @@ class KeyMintSecurityLevelInterceptor( logTransaction(txId, transactionNames[code]!!, callingUid, callingPid) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) - val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! + val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) + ?: return TransactionResult.ContinueAndSkipPost SystemLogger.info( "[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]" ) @@ -140,6 +141,10 @@ class KeyMintSecurityLevelInterceptor( metadata.authorizations = InterceptorUtils.patchAuthorizations(metadata.authorizations, callingUid) patchedChains[keyId] = newChain + teeResponses[keyId] = KeyEntryResponse().apply { + this.metadata = metadata + iSecurityLevel = original + } SystemLogger.debug("Cached patched certificate chain for imported key $keyId.") return InterceptorUtils.createTypedObjectReply(metadata) } @@ -148,8 +153,10 @@ class KeyMintSecurityLevelInterceptor( logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) - val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! - val params = data.createTypedArray(KeyParameter.CREATOR)!! + val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) + ?: return TransactionResult.SkipTransaction + val params = data.createTypedArray(KeyParameter.CREATOR) + ?: return TransactionResult.SkipTransaction val parsedParams = KeyMintAttestation(params) val forced = data.readBoolean() if (forced) @@ -157,7 +164,8 @@ class KeyMintSecurityLevelInterceptor( "[TX_ID: $txId] Current operation has a very high pruning power." ) val response: CreateOperationResponse = - reply.readTypedObject(CreateOperationResponse.CREATOR)!! + reply.readTypedObject(CreateOperationResponse.CREATOR) + ?: return TransactionResult.SkipTransaction SystemLogger.verbose( "[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}" ) @@ -190,9 +198,6 @@ class KeyMintSecurityLevelInterceptor( val metadata: KeyMetadata = reply.readTypedObject(KeyMetadata.CREATOR) ?: return TransactionResult.SkipTransaction - KeyMintAttestation( - metadata.authorizations?.map { it.keyParameter }?.toTypedArray() ?: emptyArray() - ) val originalChain = CertificateHelper.getCertificateChain(metadata) ?: return TransactionResult.SkipTransaction @@ -201,16 +206,23 @@ class KeyMintSecurityLevelInterceptor( // Cache the newly patched chain to ensure consistency across subsequent API calls. data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) - val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! - val key = metadata.key!! + val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) + ?: return TransactionResult.SkipTransaction + val key = metadata.key + ?: return TransactionResult.SkipTransaction val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() metadata.authorizations = InterceptorUtils.patchAuthorizations(metadata.authorizations, callingUid) - // We must clean up cached generated keys before storing the patched chain cleanupKeyData(keyId) patchedChains[keyId] = newChain + + teeResponses[keyId] = KeyEntryResponse().apply { + this.metadata = metadata + iSecurityLevel = original + } + SystemLogger.debug( "Cached patched certificate chain for $keyId. (${key.alias} [${key.domain}, ${key.nspace}])" ) @@ -232,9 +244,9 @@ class KeyMintSecurityLevelInterceptor( data: Parcel, ): TransactionResult { data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) - val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! + val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) + ?: return TransactionResult.ContinueAndSkipPost - // Resolve key descriptor to a generated key via nspace (KEY_ID) or alias (APP). val resolvedEntry: Map.Entry? = when (keyDescriptor.domain) { Domain.KEY_ID -> { @@ -414,13 +426,15 @@ class KeyMintSecurityLevelInterceptor( private fun handleGenerateKey(txId: Long, callingUid: Int, callingPid: Int, data: Parcel): TransactionResult { return runCatching { data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) - val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! + val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR) + ?: return@runCatching TransactionResult.ContinueAndSkipPost val attestationKey = data.readTypedObject(KeyDescriptor.CREATOR) SystemLogger.debug( "Handling generateKey ${keyDescriptor.alias}, attestKey=${attestationKey?.alias}" ) - val params = data.createTypedArray(KeyParameter.CREATOR)!! + val params = data.createTypedArray(KeyParameter.CREATOR) + ?: return@runCatching TransactionResult.ContinueAndSkipPost val parsedParams = KeyMintAttestation(params) val challenge = parsedParams.attestationChallenge @@ -478,10 +492,16 @@ class KeyMintSecurityLevelInterceptor( val isAuto = ConfigurationManager.isAutoMode(callingUid) + val isStrongBox = securityLevel == SecurityLevel.STRONGBOX + when { forceGenerate -> doSoftwareGeneration( callingUid, keyDescriptor, attestationKey, parsedParams, isAttestKeyRequest ) + // StrongBox before TEE-race: broken StrongBox HALs must never reach raceTeePatch + isAuto && isStrongBox -> doSoftwareGeneration( + callingUid, keyDescriptor, attestationKey, parsedParams, isAttestKeyRequest + ) isAuto && !teeFunctional -> raceTeePatch( callingUid, keyDescriptor, attestationKey, params, parsedParams, isAttestKeyRequest )