On May 2024, a significant vulnerability was fixed in the Linux kernel, specifically affecting Qualcomm systems. This bug, assigned as CVE-2024-46868, is tied to a deadlock in the firmware driver (qcom: uefisecapp) that could cause system instability, and theoretically, be leveraged for a denial-of-service (DoS) by exploiting the deadlock. In this long-read post, we break down how the vulnerability worked, show you the affected code, detail the fix, and explain possible abuse scenarios.
Technical Background
The uefisecapp is a Qualcomm firmware interface in the Linux kernel under the firmware directory. It involves secure communications between the kernel and the firmware, often relating to platform security and boot. To ensure safe concurrent access, the function qcuefi_acquire() uses a lock.
The issue arose because the code failed to release this lock correctly in certain error paths—specifically, if a critical pointer (__qcuefi) wasn't set when attempting to acquire it, the lock would remain held. Any subsequent code trying to acquire the lock would hang indefinitely, resulting in a deadlock.
Vulnerable Code Snippet (Before the Fix)
Take a look at the essential part that was causing the problem (upstream commit):
static struct qcuefi *__qcuefi;
static struct qcuefi *qcuefi_acquire(void)
{
mutex_lock(&qcuefi_lock);
if (!__qcuefi)
return NULL; // Lock never released here!
__qcuefi->users++;
mutex_unlock(&qcuefi_lock);
return __qcuefi;
}
Here, if __qcuefi is not set, control returns early—*without releasing the lock!* That means any code path that later tries acquiring qcuefi_lock will block forever, leading to a deadlock.
Code finds __qcuefi is NULL, so returns NULL without unlocking.
5. Any later attempt—by this thread or a different one—to call any function using qcuefi_lock will hang forever due to the lock being held and never released.
Exploit and Impact Scenarios
While this vulnerability is a logic bug and not a direct code execution flaw, attackers with the ability to trigger the qcuefi_acquire() path at the right time can cause a denial-of-service (DoS).
Example Exploit
An unprivileged local user (if device node permissions allowed; typically root-only, but policy/configuration errors are possible) or a buggy process could:
Unload the firmware instance or race early kernel boot to leave __qcuefi == NULL.
2. Call a userspace interface (e.g., via /dev node or ioctl), which in turn invokes qcuefi_acquire().
3. Cause the kernel code to deadlock on future firmware operations, possibly freezing user interactions with security features, or even hanging the boot process.
The Fix: Correct Lock Handling
The corrected code (patch) makes sure the lock is always released, even if there's an error:
static struct qcuefi *qcuefi_acquire(void)
{
mutex_lock(&qcuefi_lock);
if (!__qcuefi) {
mutex_unlock(&qcuefi_lock); // Always release the lock
return NULL;
}
__qcuefi->users++;
mutex_unlock(&qcuefi_lock);
return __qcuefi;
}
Now, whether __qcuefi is set or not, the lock will always be *unlocked* before returning.
CVE Reference:
Patch commit:
firmware: qcom: uefisecapp: Fix deadlock in qcuefi_acquire() (git.kernel.org)
Kernel discussion:
Conclusion & Recommendations
CVE-2024-46868 highlights how simple mistakes in concurrency code—even in error handling—can have system-wide effects, especially on critical infrastructure like firmware interfaces. While not directly exploitable for privilege escalation, users and kernel maintainers are recommended to:
Apply available kernel updates with the fix.
- Review local access policies to /dev nodes that connect to firmware.
Monitor for any abnormal driver behavior that could hint at deadlocked resources.
Always keep kernel and drivers up to date to avoid these and similar low-level logic bugs.
Timeline
Published on: 09/27/2024 13:15:18 UTC
Last modified on: 10/01/2024 17:09:12 UTC