fix(interception): harden daemon against binder stress crashes

BinderInterceptor.onTransact now catches Throwable, preventing any
exception on a binder thread from killing the daemon. Adds a global
uncaught exception handler as defense in depth.

Replace Thread.sleep with TeeLatencySimulator (LockSupport.parkNanos +
statistical delay model) for keygen latency, reducing binder thread
blocking. Move GeneratedKeyPersistence.save to a background executor
to avoid disk I/O on binder threads.

Convert force-unwrap parcel reads to safe calls with early returns in
onPreTransact/onPostTransact hot paths. Add -DNDEBUG to native release
builds to compile out verbose logging from the ioctl hook.

Targets G2 (ping overhead) and G10 (stress attestation consistency).
This commit is contained in:
Enginex0
2026-03-19 17:38:29 +01:00
parent 99e6b0b5ea
commit b6f9d7b486
5 changed files with 57 additions and 34 deletions
+1
View File
@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
# LSPlt configuration # LSPlt configuration
OPTION(LSPLT_BUILD_SHARED OFF) OPTION(LSPLT_BUILD_SHARED OFF)
@@ -33,8 +33,11 @@ object App {
fun main(args: Array<String>) { fun main(args: Array<String>) {
SystemLogger.info("Welcome to TEESimulator!") SystemLogger.info("Welcome to TEESimulator!")
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
SystemLogger.error("Uncaught exception on ${thread.name}", throwable)
}
try { try {
// Initialize the Android framework environment
prepareEnvironment() prepareEnvironment()
// Initialize and start the appropriate keystore interceptors. // Initialize and start the appropriate keystore interceptors.
initializeInterceptors() initializeInterceptors()
@@ -109,17 +109,17 @@ abstract class BinderInterceptor : Binder() {
* `handlePostTransact`). * `handlePostTransact`).
*/ */
final override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { 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 txId = data.readLong()
val result = val result = try {
when (code) { when (code) {
// These codes are defined in the native layer to distinguish hook types.
PRE_TRANSACT_CODE -> handlePreTransact(txId, data) PRE_TRANSACT_CODE -> handlePreTransact(txId, data)
POST_TRANSACT_CODE -> handlePostTransact(txId, data) POST_TRANSACT_CODE -> handlePostTransact(txId, data)
else -> return super.onTransact(code, data, reply, flags) else -> return super.onTransact(code, data, reply, flags)
} }
} catch (e: Throwable) {
// The reply parcel is guaranteed to be non-null for our custom transactions. SystemLogger.error("[TX_ID: $txId] Interceptor exception, falling through to HAL", e)
TransactionResult.ContinueAndSkipPost
}
writeResultToReply(result, reply!!) writeResultToReply(result, reply!!)
return true return true
} }
@@ -22,7 +22,9 @@ import java.security.cert.CertificateFactory
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedDeque import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.LockSupport
import org.matrix.TEESimulator.attestation.AttestationBuilder import org.matrix.TEESimulator.attestation.AttestationBuilder
import org.matrix.TEESimulator.attestation.AttestationConstants import org.matrix.TEESimulator.attestation.AttestationConstants
import org.matrix.TEESimulator.attestation.AttestationPatcher import org.matrix.TEESimulator.attestation.AttestationPatcher
@@ -39,6 +41,7 @@ import org.matrix.TEESimulator.pki.KeyBoxManager
import org.matrix.TEESimulator.pki.NativeCertGen import org.matrix.TEESimulator.pki.NativeCertGen
import org.matrix.TEESimulator.util.AndroidDeviceUtils import org.matrix.TEESimulator.util.AndroidDeviceUtils
import org.matrix.TEESimulator.util.AndroidPermissionUtils import org.matrix.TEESimulator.util.AndroidPermissionUtils
import org.matrix.TEESimulator.util.TeeLatencySimulator
class KeyMintSecurityLevelInterceptor( class KeyMintSecurityLevelInterceptor(
private val original: IKeystoreSecurityLevel, private val original: IKeystoreSecurityLevel,
@@ -82,7 +85,8 @@ class KeyMintSecurityLevelInterceptor(
logTransaction(txId, transactionNames[code]!!, callingUid, callingPid) logTransaction(txId, transactionNames[code]!!, callingUid, callingPid)
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
?: return TransactionResult.ContinueAndSkipPost
SystemLogger.info( SystemLogger.info(
"[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]" "[TX_ID: $txId] Forward to post-importKey hook for ${keyDescriptor.alias}[${keyDescriptor.nspace}]"
) )
@@ -161,8 +165,10 @@ class KeyMintSecurityLevelInterceptor(
logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid) logTransaction(txId, "post-${transactionNames[code]!!}", callingUid, callingPid)
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
val params = data.createTypedArray(KeyParameter.CREATOR)!! ?: return TransactionResult.SkipTransaction
val params = data.createTypedArray(KeyParameter.CREATOR)
?: return TransactionResult.SkipTransaction
val parsedParams = KeyMintAttestation(params) val parsedParams = KeyMintAttestation(params)
val forced = data.readBoolean() val forced = data.readBoolean()
if (forced) if (forced)
@@ -170,7 +176,8 @@ class KeyMintSecurityLevelInterceptor(
"[TX_ID: $txId] Current operation has a very high pruning power." "[TX_ID: $txId] Current operation has a very high pruning power."
) )
val response: CreateOperationResponse = val response: CreateOperationResponse =
reply.readTypedObject(CreateOperationResponse.CREATOR)!! reply.readTypedObject(CreateOperationResponse.CREATOR)
?: return TransactionResult.SkipTransaction
SystemLogger.verbose( SystemLogger.verbose(
"[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}" "[TX_ID: $txId] CreateOperationResponse: ${response.iOperation} ${response.operationChallenge}"
) )
@@ -206,8 +213,10 @@ class KeyMintSecurityLevelInterceptor(
// Cache the newly patched chain to ensure consistency across subsequent API calls. // Cache the newly patched chain to ensure consistency across subsequent API calls.
data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR) data.enforceInterface(IKeystoreSecurityLevel.DESCRIPTOR)
val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)!! val keyDescriptor = data.readTypedObject(KeyDescriptor.CREATOR)
val key = metadata.key!! ?: return TransactionResult.SkipTransaction
val key = metadata.key
?: return TransactionResult.SkipTransaction
val keyId = KeyIdentifier(callingUid, keyDescriptor.alias) val keyId = KeyIdentifier(callingUid, keyDescriptor.alias)
CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow() CertificateHelper.updateCertificateChain(metadata, newChain).getOrThrow()
metadata.authorizations = metadata.authorizations =
@@ -547,10 +556,12 @@ class KeyMintSecurityLevelInterceptor(
} }
generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams) generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams)
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000 if (securityLevel == SecurityLevel.STRONGBOX) {
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
val delayMs = floor - elapsedMs if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
if (delayMs > 0) Thread.sleep(delayMs) } else {
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
}
return InterceptorUtils.createTypedObjectReply(metadata) return InterceptorUtils.createTypedObjectReply(metadata)
} }
@@ -570,24 +581,29 @@ class KeyMintSecurityLevelInterceptor(
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams) generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams)
if (isAttestKeyRequest) attestationKeys.add(keyId) if (isAttestKeyRequest) attestationKeys.add(keyId)
GeneratedKeyPersistence.save( val certChainCopy = keyData.second.toList()
keyId = keyId, persistExecutor.execute {
keyPair = keyData.first, GeneratedKeyPersistence.save(
nspace = keyDescriptor.nspace, keyId = keyId,
securityLevel = securityLevel, keyPair = keyData.first,
certChain = keyData.second.toList(), nspace = keyDescriptor.nspace,
algorithm = parsedParams.algorithm, securityLevel = securityLevel,
keySize = parsedParams.keySize, certChain = certChainCopy,
ecCurve = parsedParams.ecCurve ?: 0, algorithm = parsedParams.algorithm,
purposes = parsedParams.purpose, keySize = parsedParams.keySize,
digests = parsedParams.digest, ecCurve = parsedParams.ecCurve ?: 0,
isAttestationKey = isAttestKeyRequest, purposes = parsedParams.purpose,
) digests = parsedParams.digest,
isAttestationKey = isAttestKeyRequest,
)
}
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000 if (securityLevel == SecurityLevel.STRONGBOX) {
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
val delayMs = floor - elapsedMs if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
if (delayMs > 0) Thread.sleep(delayMs) } else {
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
}
return InterceptorUtils.createTypedObjectReply(response.metadata) return InterceptorUtils.createTypedObjectReply(response.metadata)
} }
@@ -875,6 +891,8 @@ class KeyMintSecurityLevelInterceptor(
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] } .associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
} }
private val persistExecutor = Executors.newSingleThreadExecutor()
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>() val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
val teeResponses = ConcurrentHashMap<KeyIdentifier, KeyEntryResponse>() val teeResponses = ConcurrentHashMap<KeyIdentifier, KeyEntryResponse>()
val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>() val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
@@ -9,6 +9,7 @@ import android.hardware.security.keymint.KeyPurpose
import android.hardware.security.keymint.PaddingMode import android.hardware.security.keymint.PaddingMode
import android.hardware.security.keymint.Tag import android.hardware.security.keymint.Tag
import android.os.ServiceSpecificException import android.os.ServiceSpecificException
import java.util.concurrent.locks.LockSupport
import android.system.keystore2.IKeystoreOperation import android.system.keystore2.IKeystoreOperation
import android.system.keystore2.KeyParameters import android.system.keystore2.KeyParameters
import java.security.KeyPair import java.security.KeyPair
@@ -275,7 +276,7 @@ class SoftwareOperation(
if (latencyFloorMs > 0) { if (latencyFloorMs > 0) {
val elapsedMs = (System.nanoTime() - startNs) / 1_000_000 val elapsedMs = (System.nanoTime() - startNs) / 1_000_000
val delayMs = latencyFloorMs - elapsedMs val delayMs = latencyFloorMs - elapsedMs
if (delayMs > 0) Thread.sleep(delayMs) if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
} }
finalized = true finalized = true
onFinishCallback?.invoke() onFinishCallback?.invoke()