Add file-level locking to prevent race conditions in key persistence

Per-key ReentrantLock prevents concurrent writes to same key file
This commit is contained in:
Enginex0
2026-02-07 00:47:36 +01:00
parent c99a2ab302
commit ff88543c98
@@ -7,9 +7,12 @@ import java.io.DataOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.security.KeyPair
import java.security.MessageDigest
import java.security.cert.Certificate
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantLock
import org.matrix.TEESimulator.config.ConfigurationManager.CONFIG_PATH
import org.matrix.TEESimulator.interception.keystore.KeyIdentifier
import org.matrix.TEESimulator.logging.SystemLogger
@@ -35,6 +38,13 @@ object GeneratedKeyPersistence {
private const val FORMAT_VERSION = 1
private val PERSISTENCE_DIR = File(CONFIG_PATH, "persistent_keys")
// Per-filename locks to prevent concurrent writes to the same key file
private val fileLocks = ConcurrentHashMap<String, ReentrantLock>()
private fun getLockForKey(filename: String): ReentrantLock {
return fileLocks.computeIfAbsent(filename) { ReentrantLock() }
}
fun save(
keyId: KeyIdentifier,
keyPair: KeyPair,
@@ -48,9 +58,14 @@ object GeneratedKeyPersistence {
digests: List<Int>,
isAttestationKey: Boolean,
) {
val filename = keyFileName(keyId.uid, keyId.alias)
val lock = getLockForKey(filename)
SystemLogger.debug("[Persistence] Acquiring lock for $filename")
lock.lock()
try {
SystemLogger.debug("[Persistence] Lock acquired for $filename")
runCatching {
PERSISTENCE_DIR.mkdirs()
val filename = keyFileName(keyId.uid, keyId.alias)
val finalFile = File(PERSISTENCE_DIR, filename)
val tmpFile = File(PERSISTENCE_DIR, "$filename.tmp")
@@ -94,10 +109,19 @@ object GeneratedKeyPersistence {
throw IllegalStateException("Failed to atomically rename $tmpFile -> $finalFile")
}
// Verify write succeeded - catches disk-full or filesystem errors
if (!finalFile.exists() || finalFile.length() < 20) {
throw IOException("File write verification failed - possible disk full")
}
SystemLogger.debug("Persisted key: $keyId")
}.onFailure { e ->
SystemLogger.error("Failed to persist key $keyId", e)
}
} finally {
lock.unlock()
SystemLogger.debug("[Persistence] Lock released for $filename")
}
}
fun delete(keyId: KeyIdentifier) {