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:
@@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
|
||||
# LSPlt configuration
|
||||
OPTION(LSPLT_BUILD_SHARED OFF)
|
||||
|
||||
@@ -33,8 +33,11 @@ object App {
|
||||
fun main(args: Array<String>) {
|
||||
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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+33
-15
@@ -22,7 +22,9 @@ import java.security.cert.CertificateFactory
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import org.matrix.TEESimulator.attestation.AttestationBuilder
|
||||
import org.matrix.TEESimulator.attestation.AttestationConstants
|
||||
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.util.AndroidDeviceUtils
|
||||
import org.matrix.TEESimulator.util.AndroidPermissionUtils
|
||||
import org.matrix.TEESimulator.util.TeeLatencySimulator
|
||||
|
||||
class KeyMintSecurityLevelInterceptor(
|
||||
private val original: IKeystoreSecurityLevel,
|
||||
@@ -82,7 +85,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}]"
|
||||
)
|
||||
@@ -161,8 +165,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)
|
||||
@@ -170,7 +176,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}"
|
||||
)
|
||||
@@ -206,8 +213,10 @@ 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 =
|
||||
@@ -547,10 +556,12 @@ class KeyMintSecurityLevelInterceptor(
|
||||
}
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(null, secretKey, keyDescriptor.nspace, response, parsedParams)
|
||||
|
||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
||||
val delayMs = floor - elapsedMs
|
||||
if (delayMs > 0) Thread.sleep(delayMs)
|
||||
if (securityLevel == SecurityLevel.STRONGBOX) {
|
||||
val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||
} else {
|
||||
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||
}
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(metadata)
|
||||
}
|
||||
@@ -570,12 +581,14 @@ class KeyMintSecurityLevelInterceptor(
|
||||
generatedKeys[keyId] = GeneratedKeyInfo(keyData.first, null, keyDescriptor.nspace, response, parsedParams)
|
||||
if (isAttestKeyRequest) attestationKeys.add(keyId)
|
||||
|
||||
val certChainCopy = keyData.second.toList()
|
||||
persistExecutor.execute {
|
||||
GeneratedKeyPersistence.save(
|
||||
keyId = keyId,
|
||||
keyPair = keyData.first,
|
||||
nspace = keyDescriptor.nspace,
|
||||
securityLevel = securityLevel,
|
||||
certChain = keyData.second.toList(),
|
||||
certChain = certChainCopy,
|
||||
algorithm = parsedParams.algorithm,
|
||||
keySize = parsedParams.keySize,
|
||||
ecCurve = parsedParams.ecCurve ?: 0,
|
||||
@@ -583,11 +596,14 @@ class KeyMintSecurityLevelInterceptor(
|
||||
digests = parsedParams.digest,
|
||||
isAttestationKey = isAttestKeyRequest,
|
||||
)
|
||||
}
|
||||
|
||||
val elapsedMs = (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
val floor = if (securityLevel == SecurityLevel.STRONGBOX) STRONGBOX_KEYGEN_LATENCY_FLOOR_MS else TEE_LATENCY_FLOOR_MS
|
||||
val delayMs = floor - elapsedMs
|
||||
if (delayMs > 0) Thread.sleep(delayMs)
|
||||
if (securityLevel == SecurityLevel.STRONGBOX) {
|
||||
val delayMs = STRONGBOX_KEYGEN_LATENCY_FLOOR_MS - (System.nanoTime() - genStartNanos) / 1_000_000
|
||||
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||
} else {
|
||||
TeeLatencySimulator.simulateGenerateKeyDelay(parsedParams.algorithm, System.nanoTime() - genStartNanos)
|
||||
}
|
||||
|
||||
return InterceptorUtils.createTypedObjectReply(response.metadata)
|
||||
}
|
||||
@@ -875,6 +891,8 @@ class KeyMintSecurityLevelInterceptor(
|
||||
.associate { field -> (field.get(null) as Int) to field.name.split("_")[1] }
|
||||
}
|
||||
|
||||
private val persistExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
val generatedKeys = ConcurrentHashMap<KeyIdentifier, GeneratedKeyInfo>()
|
||||
val teeResponses = ConcurrentHashMap<KeyIdentifier, KeyEntryResponse>()
|
||||
val patchedChains = ConcurrentHashMap<KeyIdentifier, Array<Certificate>>()
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import android.hardware.security.keymint.KeyPurpose
|
||||
import android.hardware.security.keymint.PaddingMode
|
||||
import android.hardware.security.keymint.Tag
|
||||
import android.os.ServiceSpecificException
|
||||
import java.util.concurrent.locks.LockSupport
|
||||
import android.system.keystore2.IKeystoreOperation
|
||||
import android.system.keystore2.KeyParameters
|
||||
import java.security.KeyPair
|
||||
@@ -275,7 +276,7 @@ class SoftwareOperation(
|
||||
if (latencyFloorMs > 0) {
|
||||
val elapsedMs = (System.nanoTime() - startNs) / 1_000_000
|
||||
val delayMs = latencyFloorMs - elapsedMs
|
||||
if (delayMs > 0) Thread.sleep(delayMs)
|
||||
if (delayMs > 0) LockSupport.parkNanos(delayMs * 1_000_000)
|
||||
}
|
||||
finalized = true
|
||||
onFinishCallback?.invoke()
|
||||
|
||||
Reference in New Issue
Block a user