fix(auth-utils): prevent transactions after destroy with destroyed flag
CRITICAL FIX: Adds destroyed flag to transaction capability to prevent use-after-free crashes when transactions are initiated after socket.end() is called. Changes: - Add destroyed flag set to true at start of destroy() - Check destroyed flag in transaction() and throw error if set - Reorder destroy() to check locked mutexes BEFORE destroying resources - Skip resource destruction if any mutexes are locked (prevents corrupted state) This prevents 4 critical race conditions: 1. sendMessage() after end() (messages-send.ts:868) 2. sendRetryRequest() after end() (messages-recv.ts:498) 3. resyncAppState() after end() (chats.ts:476, 769) 4. LID mapping operations after end() (lid-mapping.ts:417) Timeline before fix: T0: sendMessage() called T1: end() sets closed=true T2: end() awaits uploadPreKeysPromise (5s) T3: sendMessage() calls keys.transaction() ← NO GUARD T4: keys.destroy() executes T5: Transaction crashes (resources destroyed) Timeline after fix: T0: sendMessage() called T1: end() → destroy() sets destroyed=true T2: sendMessage() → transaction() → throws error ✓ https://claude.ai/code/session_VMxqX
This commit is contained in:
+38
-20
@@ -129,6 +129,9 @@ export const addTransactionCapability = (
|
||||
// Pre-key manager for specialized operations
|
||||
const preKeyManager = new PreKeyManager(state, logger)
|
||||
|
||||
// Destroyed flag to prevent operations after cleanup
|
||||
let destroyed = false
|
||||
|
||||
/**
|
||||
* Get or create a queue for a specific key type
|
||||
*/
|
||||
@@ -300,6 +303,11 @@ export const addTransactionCapability = (
|
||||
isInTransaction,
|
||||
|
||||
transaction: async (work, key) => {
|
||||
// CRITICAL: Prevent transactions after destroy to avoid use-after-free
|
||||
if (destroyed) {
|
||||
throw new Error('Transaction capability destroyed - socket closed')
|
||||
}
|
||||
|
||||
const existing = txStorage.getStore()
|
||||
|
||||
// Nested transaction - reuse existing context
|
||||
@@ -346,7 +354,36 @@ export const addTransactionCapability = (
|
||||
* Should be called during connection cleanup
|
||||
*/
|
||||
destroy: () => {
|
||||
// CRITICAL: Set destroyed flag FIRST to prevent new transactions
|
||||
destroyed = true
|
||||
logger.debug('🗑️ Cleaning up transaction capability resources')
|
||||
|
||||
// Count locked mutexes BEFORE destroying resources
|
||||
let clearedCount = 0
|
||||
let lockedCount = 0
|
||||
const lockedKeys: string[] = []
|
||||
|
||||
txMutexes.forEach((mutex, key) => {
|
||||
if (!mutex.isLocked()) {
|
||||
txMutexes.delete(key)
|
||||
txMutexRefCounts.delete(key)
|
||||
clearedCount++
|
||||
} else {
|
||||
lockedCount++
|
||||
lockedKeys.push(key)
|
||||
}
|
||||
})
|
||||
|
||||
// If there are locked mutexes, log error and skip resource destruction
|
||||
if (lockedCount > 0) {
|
||||
logger.error(
|
||||
{ lockedCount, lockedKeys },
|
||||
'❌ Cannot destroy resources - transactions still active! Resources will be cleaned up by GC.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Safe to destroy resources (no active transactions)
|
||||
preKeyManager.destroy()
|
||||
|
||||
// Clear all key queues
|
||||
@@ -357,26 +394,7 @@ export const addTransactionCapability = (
|
||||
})
|
||||
keyQueues.clear()
|
||||
|
||||
// Clear transaction mutexes and reference counts
|
||||
// CRITICAL: Only delete unlocked mutexes to prevent corrupting active transactions
|
||||
let clearedCount = 0
|
||||
let lockedCount = 0
|
||||
txMutexes.forEach((mutex, key) => {
|
||||
if (!mutex.isLocked()) {
|
||||
txMutexes.delete(key)
|
||||
txMutexRefCounts.delete(key)
|
||||
clearedCount++
|
||||
} else {
|
||||
lockedCount++
|
||||
logger.warn(
|
||||
{ key },
|
||||
'Transaction mutex still locked during cleanup - transaction may be in progress'
|
||||
)
|
||||
}
|
||||
})
|
||||
logger.debug({ clearedCount, lockedCount }, 'Transaction mutexes cleanup completed')
|
||||
|
||||
logger.debug('Transaction capability cleanup completed')
|
||||
logger.debug({ clearedCount }, 'Transaction capability cleanup completed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user