{"record":{"id":"73310a4b355f7715","repo":"embassy-rs/embassy","slug":"the-aes-is-in-use","errorCode":null,"errorMessage":"the AES is in use","messagePattern":"the AES is in use","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/aes/driver.rs","lineNumber":53,"sourceCode":"    };\n);\n\n#[cfg(feature = \"embassy-crypto-saes\")]\nforeach_peripheral!(\n    (saes, $inst:ident) => {\n        type BlockingAes = crate::saes::Saes<'static, crate::peripherals::$inst, Blocking>;\n\n        static DRIVER: Mutex<CriticalSectionRawMutex, ResumablePeripheral<BlockingAes>> =\n            Mutex::new(ResumablePeripheral::new_suspended(unsafe { crate::peripherals::$inst::steal() }));\n    };\n);\n\n/// Takes the peripheral, which is clocked for as long as the guard's borrow lives.\nfn lock() -> MutexGuard<'static, CriticalSectionRawMutex, ResumablePeripheral<BlockingAes>> {\n    // The SAES fetches random numbers from the RNG whenever it is reset.\n    #[cfg(all(feature = \"embassy-crypto-saes\", feature = \"embassy-crypto-rng\"))]\n    crate::rng::driver::ensure_running();\n    DRIVER.try_lock().expect(\"the AES is in use\")\n}\n\nfn map_error(error: super::Error) -> CryptoError {\n    match error {\n        super::Error::KeyError => CryptoError::InvalidKey,\n        super::Error::ConfigError => CryptoError::InvalidInput,\n        super::Error::ReadError | super::Error::WriteError => CryptoError::HardwareError,\n    }\n}\n\nfn run_in_place<'c, C>(\n    aes: &mut BlockingAes,\n    cipher: &'c C,\n    direction: Direction,\n    buffer: &mut [u8],\n) -> Result<(), CryptoError>\nwhere\n    C: super::Cipher<'c> + super::CipherSized + super::IVSized,","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/aes/driver.rs#L35-L71","documentation":"The embassy-stm32 AES driver wraps the hardware peripheral in a global async Mutex (DRIVER). lock() uses try_lock().expect(...), so it panics instead of waiting whenever the AES peripheral is already held by another concurrent user. The guard is meant to be held only for the duration of a crypto operation; overlapping operations are a programming error.","triggerScenarios":"Calling any of encrypt_blocks/decrypt_blocks/encrypt/decrypt/apply_keystream while another task or context already holds the AES lock (e.g. two tasks doing AES-CCM and AES-GCM concurrently, or a blocking call from an interrupt while a task holds the guard).","commonSituations":"Spawning two tasks that both perform AES operations without synchronization; calling AES from an ISR while the main task is mid-encryption; shared crypto helpers invoked from multiple connection handlers.","solutions":["Serialize AES usage: perform operations from one task, or guard calls with your own shared mutex/semaphore and await properly.","Use the async (non-blocking) AES API so callers await the lock instead of try_lock-panicking, if available.","Move blocking AES calls out of interrupt handlers.","Keep crypto operations short and drop guards promptly (avoid holding the returned MutexGuard across await points of unrelated work)."],"exampleFix":"// before\nlet a = aes.encrypt(&k, p1).await;\nlet b = other_task_aes.encrypt(&k, p2).await; // panics if concurrent\n\n// after\nstatic AES_SEM: Mutex<CriticalSectionRawMutex, AesDriver> = Mutex::new(...);\nlet guard = AES_SEM.lock().await;\nlet a = guard.encrypt(&k, p1).await;\nlet b = guard.encrypt(&k, p2).await;","handlingStrategy":"try-catch","validationCode":"// Prevent overlap before calling AES APIs:\n// track ownership at the app level\nstatic AES_BUSY: AtomicBool = AtomicBool::new(false);\nfn aes_available() -> bool { !AES_BUSY.load(Ordering::Acquire) }","typeGuard":"fn can_encrypt() -> bool {\n    // only call encrypt/decrypt when no other context holds the AES guard\n    AES_BUSY.load(core::sync::atomic::Ordering::Acquire) == false\n}","tryCatchPattern":"// Rust panics cannot be caught in embedded; prevent instead.\n// Wrap all AES access in one async mutex:\nstatic AES: Mutex<CriticalSectionRawMutex, Aes<'static>> = Mutex::new(Aes::new());\nasync fn do_aes(f: impl FnOnce(&Aes<'static>) -> ...) -> ... {\n    let a = AES.lock().await;\n    f(&a)\n}","preventionTips":["Route all AES calls through a single async Mutex or a dedicated crypto task.","Never call blocking AES functions from interrupt handlers.","Hold the driver lock only for the duration of the crypto operation."],"tags":["embedded","stm32","aes","concurrency","mutex","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}