CVE-2026-40290
Executive Summary
CVE-2026-40290 is a critical Use-After-Free (UAF) race condition vulnerability in the OP-TEE (Open Portable Trusted Execution Environment) software, specifically within its FF-A (Firmware Framework for Arm A-profile) shared memory teardown logic. This vulnerability affects OP-TEE versions 3.16.0 through 4.10.0 when configured as an SPMC (Secure Partition Manager Core) for S-EL0 Secure Partitions (SPs), enabled via the CFG_SECURE_PARTITION=y build flag.
The core issue lies in the sp_mem_remove() function, which is responsible for freeing shared memory entries. This function fails to acquire the global sp_mem_lock mutex before performing free() operations on the smem->receivers and smem->regions lists. Concurrently, other code paths like sp_mem_get_receiver() iterate over these same lists without any locking, while sp_mem_is_shared() iterates with the lock but is not serialized against the unprotected free() in sp_mem_remove(). This creates a race condition where one thread can free a memory object while another thread is still using it, leading to a Use-After-Free condition.
Successful exploitation could allow an attacker with local access to a non-secure Linux kernel to corrupt the TEE's memory, potentially leading to privilege escalation within the TrustZone environment, information disclosure, or denial of service. The vulnerability has a CVSS score of 7.8 (High) and is currently unpatched in affected versions. The fix is available in OP-TEE version 4.11.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
OP-TEE is a critical component in the Arm TrustZone ecosystem, used in many embedded and mobile devices. As an open-source project, its supply chain is managed through GitHub and the OP-TEE build system. The vulnerability is introduced in the source code and affects all downstream users who build OP-TEE with the vulnerable configuration. Organizations using OP-TEE as part of their product's secure boot chain or trusted execution environment should treat this as a high-priority supply chain risk. The lack of a public exploit or PoC does not diminish the risk, as the race condition is well-documented and could be weaponized by sophisticated threat actors.
CVSS Breakdown & Threat Level
CVSS Score: 7.8 (High) Vector: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
- Attack Vector (AV): Local (L) - The attacker must have local access to the system.
- Attack Complexity (AC): High (H) - Exploitation requires precise timing to win the race condition.
- Privileges Required (PR): Low (L) - The attacker needs low-level privileges (e.g., user-level access to the non-secure OS).
- User Interaction (UI): None (N) - No user interaction is required beyond the attacker's own actions.
- Scope (S): Unchanged (U) - The vulnerability affects only the TEE component.
- Confidentiality (C): High (H) - Potential to read sensitive TEE memory.
- Integrity (I): High (H) - Potential to corrupt TEE memory.
- Availability (A): High (H) - Potential to crash the TEE.
Threat Level: High. The vulnerability allows for privilege escalation within the TrustZone environment, which is a critical security boundary. While the attack complexity is high, the potential impact is severe.
Deep Dive & Mechanics
The vulnerability is a classic Use-After-Free (UAF) race condition in the shared memory management subsystem of OP-TEE's FF-A implementation. The FF-A specification defines how software components in the secure and non-secure worlds communicate via shared memory. In OP-TEE, when configured as an SPMC, it manages shared memory regions for Secure Partitions (SPs) running at S-EL0.
The key data structures involved are:
struct sp_mem: Represents a shared memory descriptor.struct sp_mem_receiver: Represents a receiver of the shared memory.struct sp_mem_map_region: Represents a mapped region of the shared memory.
These structures are stored in linked lists: smem->receivers and smem->regions.
The vulnerable function sp_mem_remove() is called to tear down a shared memory descriptor. It iterates over the receivers and regions lists and frees each entry. However, it does not acquire the global sp_mem_lock mutex before doing so.
// Vulnerable code path in sp_mem_remove()
void sp_mem_remove(struct sp_mem *smem)
{
// ... (no lock acquisition)
while (receiver) {
next = SLIST_NEXT(receiver, link);
free(receiver); // UAF: freed without lock
receiver = next;
}
while (region) {
next = SLIST_NEXT(region, link);
free(region); // UAF: freed without lock
region = next;
}
// ...
}
Concurrently, other functions access these lists:
sp_mem_get_receiver()iterates thereceiverslist without any lock.sp_mem_is_shared()iterates theregionslist while holding the lock, but the lock does not protect against the unprotectedfree()insp_mem_remove().
The race condition unfolds as follows:
- Thread A calls
sp_mem_get_receiver()and obtains a pointer to astruct sp_mem_receiverfrom the list. - Thread B calls
sp_mem_remove()on the samesmemobject, frees the receiver entry that Thread A is pointing to. - Thread A resumes and dereferences the now-freed pointer, causing a Use-After-Free.
This can lead to arbitrary code execution if the freed memory is reallocated by an attacker-controlled object.
Vulnerable vs. Patched Code
Vulnerable Code (versions 3.16.0 to 4.10.0):
In core/arch/arm/kernel/sp_mem.c, the sp_mem_remove() function does not acquire sp_mem_lock:
void sp_mem_remove(struct sp_mem *smem)
{
struct sp_mem_receiver *receiver;
struct sp_mem_receiver *next_receiver;
struct sp_mem_map_region *region;
struct sp_mem_map_region *next_region;
// Missing: mutex_lock(&sp_mem_lock);
receiver = SLIST_FIRST(&smem->receivers);
while (receiver) {
next_receiver = SLIST_NEXT(receiver, link);
free(receiver);
receiver = next_receiver;
}
region = SLIST_FIRST(&smem->regions);
while (region) {
next_region = SLIST_NEXT(region, link);
free(region);
region = next_region;
}
// Missing: mutex_unlock(&sp_mem_lock);
}
Patched Code (version 4.11.0):
The fix adds proper locking around the free() operations:
void sp_mem_remove(struct sp_mem *smem)
{
struct sp_mem_receiver *receiver;
struct sp_mem_receiver *next_receiver;
struct sp_mem_map_region *region;
struct sp_mem_map_region *next_region;
mutex_lock(&sp_mem_lock); // Lock acquired
receiver = SLIST_FIRST(&smem->receivers);
while (receiver) {
next_receiver = SLIST_NEXT(receiver, link);
free(receiver);
receiver = next_receiver;
}
region = SLIST_FIRST(&smem->regions);
while (region) {
next_region = SLIST_NEXT(region, link);
free(region);
region = next_region;
}
mutex_unlock(&sp_mem_lock); // Lock released
}
The patch ensures that all modifications to the receivers and regions lists are serialized under the global sp_mem_lock, preventing the race condition.
Exploitation & Lab Setup
Red Team (Exploitation)
Prerequisites:
- Local access to a system running OP-TEE with
CFG_SECURE_PARTITION=y(versions 3.16.0 to 4.10.0). - Ability to create and run threads in the non-secure world.
- Knowledge of the TEE's shared memory allocation patterns.
Exploitation Strategy:
The goal is to trigger the race condition between sp_mem_get_receiver() and sp_mem_remove(). Since no public exploit exists, a custom exploit would need to:
- Spray the heap: Allocate many shared memory descriptors to increase the chance of a race.
- Create two threads:
- Thread A: Continuously calls a function that invokes
sp_mem_get_receiver()(e.g., by repeatedly sharing memory and querying receivers). - Thread B: Continuously calls a function that invokes
sp_mem_remove()(e.g., by repeatedly releasing shared memory).
- Thread A: Continuously calls a function that invokes
- Win the race: When Thread A obtains a pointer to a receiver entry, Thread B frees it. Thread A then dereferences the dangling pointer.
- Post-exploitation: After the UAF, the attacker can attempt to reallocate the freed memory with a controlled object (e.g., a fake
struct sp_mem_receiver) to hijack control flow or leak sensitive data.
Example pseudo-code for the race trigger:
// Thread A: Constantly queries receivers
void *thread_a(void *arg) {
while (1) {
struct sp_mem *smem = get_shared_mem();
struct sp_mem_receiver *rec = sp_mem_get_receiver(smem, target_id);
if (rec) {
// Use rec->some_field (UAF here)
volatile uint32_t val = rec->some_field;
}
}
return NULL;
}
// Thread B: Constantly removes shared memory
void *thread_b(void *arg) {
while (1) {
struct sp_mem *smem = get_shared_mem();
sp_mem_remove(smem);
}
return NULL;
}
Note: Exploitation is non-trivial due to the high attack complexity and the need for precise timing.
Blue Team (Defense & Detection)
Detection Strategy:
- Monitor for race condition indicators: Look for crashes or panics in the TEE that involve
sp_mem_remove()orsp_mem_get_receiver(). - Audit shared memory operations: Log all calls to
sp_mem_remove()andsp_mem_get_receiver()and look for temporal anomalies (e.g., a receiver being accessed after a remove call). - Use AddressSanitizer (ASan): If running in a debug environment, enable ASan to detect UAF conditions during testing.
- Static analysis: Run static analysis tools (e.g., Clang Static Analyzer) on the OP-TEE source code to identify missing locks.
Mitigation Steps:
- Immediate: Upgrade to OP-TEE version 4.11.0 or later.
- Workaround: If upgrade is not possible, disable
CFG_SECURE_PARTITION=yif not required. - Compile-time checks: Enable thread sanitizer (TSan) during development to catch race conditions.
- Runtime monitoring: Implement monitoring of TEE crashes and correlate with shared memory operations.
Indicators of Compromise (IOCs)
- System Logs:
- TEE panic messages containing references to
sp_mem_remove,sp_mem_get_receiver, orsp_mem_is_shared. - Kernel oops messages from the non-secure world indicating a crash in the TEE driver.
- TEE panic messages containing references to
- Memory Artifacts:
- Corrupted
struct sp_mem_receiverorstruct sp_mem_map_regionobjects in TEE memory dumps. - Unexpected pointers in freed memory regions.
- Corrupted
- Behavioral:
- Unusual number of shared memory allocation/deallocation operations.
- Repeated TEE crashes or resets.
- Network (if applicable):
- No direct network IOCs, as this is a local vulnerability.
Detection Engineering
YARA Rule
rule CVE_2026_40290_OP_TEE_UAF {
meta:
description = "Detects vulnerable OP-TEE binary with missing lock in sp_mem_remove"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2026-40290"
hash = "sha256:target_binary_hash"
strings:
$func_sp_mem_remove = "sp_mem_remove" ascii wide
$func_sp_mem_get_receiver = "sp_mem_get_receiver" ascii wide
$func_sp_mem_is_shared = "sp_mem_is_shared" ascii wide
$lock_func = "mutex_lock" ascii wide
$unlock_func = "mutex_unlock" ascii wide
condition:
$func_sp_mem_remove and $func_sp_mem_get_receiver and $func_sp_mem_is_shared and
not ($lock_func and $unlock_func)
}
Sigma Rule
title: CVE-2026-40290 - OP-TEE UAF Race Condition in sp_mem_remove
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects potential exploitation of CVE-2026-40290 by monitoring TEE crashes related to shared memory operations.
author: Valters IT
date: 2025-04-10
logsource:
product: linux
service: syslog
detection:
selection:
- "sp_mem_remove"
- "sp_mem_get_receiver"
- "sp_mem_is_shared"
- "UAF"
- "use-after-free"
condition: selection
falsepositives:
- Legitimate TEE crashes due to other bugs
level: high
Suricata Rule
alert ip any any -> any any (msg:"CVE-2026-40290 - OP-TEE UAF Race Condition"; content:"sp_mem_remove"; content:"sp_mem_get_receiver"; distance:0; within:100; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade OP-TEE: Update to version 4.11.0 or later, which contains the fix.
- Disable Feature: If upgrade is not possible, disable
CFG_SECURE_PARTITION=yin the build configuration to remove the vulnerable code path. - Patch Verification: After upgrading, verify that the
sp_mem_remove()function now acquiressp_mem_lockbefore freeing entries.
Long-term Measures:
- Code Review: Conduct a thorough code review of all shared memory operations in OP-TEE to ensure proper locking.
- Static Analysis: Integrate static analysis tools (e.g., Coverity, Clang Static Analyzer) into the CI/CD pipeline to detect missing locks.
- Dynamic Analysis: Use ThreadSanitizer (TSan) and AddressSanitizer (ASan) during testing to catch race conditions and UAF bugs.
- Security Training: Educate developers on concurrency pitfalls and secure coding practices for TEE environments.
Workarounds:
- If the system cannot be upgraded, consider using a different TEE implementation or isolating the vulnerable component.
- Implement runtime monitoring to detect and respond to TEE crashes.
Valters IT