CVE-2025-48641 AI-generated analysis
platform/hardware/st/nfc, NFC AIDL HAL (aidl/Nfc.h, struct Nfc : BnNfc, static callbacks eventCallback/dataCallback)
In the ST NFC AIDL HAL (platform/hardware/st/nfc, aidl/Nfc.h), the static eventCallback and dataCallback now copy the static shared_ptr Nfc::mCallback into a local std::shared_ptr under a new static pthread mutex sCallbackLock and call sendEvent/sendData on that local strong reference, instead of null-checking and dereferencing the shared static directly.
Root cause
Nfc::eventCallback(uint8_t, uint8_t) and Nfc::dataCallback(uint16_t, uint8_t*) are static functions. They checked the static member shared_ptr 'mCallback' for nullptr and then called mCallback->sendEvent(...) / mCallback->sendData(...) directly on the shared static. According to the commit message, an asynchronous thread running eventCallback called a method on mCallback right after Nfc::open on the main thread had destroyed the underlying object. Because of the race, the simple null check was not enough.
How the fix works
- aidl/Nfc.h (file scope): adds 'static pthread_mutex_t sCallbackLock = PTHREAD_MUTEX_INITIALIZER;' before struct Nfc.
- aidl/Nfc.h:Nfc::eventCallback: declares 'std::shared_ptr<INfcClientCallback> localCallback', locks sCallbackLock, copies mCallback into localCallback and unlocks. It then null-checks localCallback and calls localCallback->sendEvent(mEvent, mStatus). The local copy holds a strong reference, which keeps the callback object alive while sendEvent runs.
- aidl/Nfc.h:Nfc::dataCallback: same pattern. The locked copy into localCallback is taken before the std::vector<uint8_t> data is built, and the call becomes localCallback->sendData(data), guarded by 'if (localCallback != nullptr)'.
- The commit changes only aidl/Nfc.h (diffstat: 1 file changed, 14 insertions, 4 deletions).
Completeness
- Covers both static reader paths in aidl/Nfc.h that dereference mCallback: eventCallback (sendEvent) and dataCallback (sendData). The strong local reference keeps the callee object alive for the duration of the call.
- The commit message says the read and the write of mCallback are protected by the lock, but the diff changes only the readers. The writer side (where mCallback is assigned, e.g. Nfc::open/close) is not modified by this commit and is not shown in the material. Whether the write is done under sCallbackLock cannot be confirmed.
Behavior changes
- Each eventCallback/dataCallback call now briefly takes and releases a pthread mutex before using the client callback.
- The INfcClientCallback object stays referenced for the duration of sendEvent/sendData even if mCallback is changed concurrently.
- No API, error code or return value changes are visible in the diff.
Per major
- Android 14: same fix: commit 502f1802c4e1 in android-security-14.0.0_r25 (cherry-pick of fc619c3348188ff0020cdeb7d4c4728a0246fe1a, Change-Id I8e9e83be7f939bbfc183cb3879808ceacf931450). At android-security-14.0.0_r29, 7 of the 8 lines of the patched region are present.
- Android 15: same fix: commit ffe16073e613 in android-security-15.0.0_r13, identical diff (same blob ids 2ac623b..af9140d as Android 14). At android-security-15.0.0_r17, 7 of the 8 lines of the patched region are present.
- Android 16: same fix: commit c6da9eeb710c in android-security-16.0.0_r4 (blobs 707bd65..fd23e24). The hunks are the same; a dump() override declared before eventCallback shifts the offsets by one line. At android-security-16.0.0_r8, 7 of the 8 lines of the patched region are present.
- Android 17: not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 7 of the 8 lines of the patched region are present.
Unknowns
- Whether the writer side (Nfc::open / Nfc::close) assigns mCallback under sCallbackLock. The material shows only the header change and the claim in the commit message.
- If the writer does not take the same mutex, whether a residual data race remains on the shared_ptr object itself.
- Which translation units include aidl/Nfc.h and use sCallbackLock. This matters because sCallbackLock is a static defined in a header, so each including translation unit gets its own copy.
- Which thread or component in the NFC HAL stack calls eventCallback/dataCallback, and how the callbacks are registered.
- Why Android 17 is not listed, and how the fix reached android-security-17.0.0_r1.