CVE-2026-49884 AI-generated analysis
platform/system/nfc (Android 14/15) and platform/packages/modules/Nfc libnfc-nci (Android 16/17), NFC reader/writer Mifare Classic tag handling (rw_mfc.cc)
In rw_mfc_handle_read_op (Mifare Classic NDEF read), the fix snapshots p_mfc->ndef_length and p_mfc->ndef_start_pos before rw_nfc_decodeTlv, treats an inflated ndef_length as a failure, and rolls both fields back to their pre-decode values. This keeps the inflated length out of the control block.
Root cause
In rw_mfc.cc rw_mfc_handle_read_op, when work_offset == 0 the code calls rw_nfc_decodeTlv(data), which re-parses the NDEF TLV from the tag response and updates p_mfc->ndef_length and p_mfc->ndef_start_pos. The old code compared a local copy (saved_length) with the new p_mfc->ndef_length and set failed = true when the length grew. It did not restore the control block fields, so the inflated, tag-controlled values stayed in p_mfc. The copy loop writes p[offset] into p_mfc->p_ndef_buffer[p_mfc->work_offset] and is bounded only by p_mfc->ndef_length. Later use of that persisted length could therefore lead to writes past the end of p_ndef_buffer. The material does not show the exact sequence or the buffer size, so this part is inferred. The commit message calls it a heap buffer overflow in com.android.nfc during Mifare Classic NDEF reads.
How the fix works
- src/nfc/tags/rw_mfc.cc:rw_mfc_handle_read_op: removes the function-scope saved_length. Inside the work_offset == 0 branch it now snapshots saved_len = p_mfc->ndef_length and saved_pos = p_mfc->ndef_start_pos immediately before rw_nfc_decodeTlv(data).
- Same function: the failure condition becomes `!rw_nfc_decodeTlv(data) || p_mfc->ndef_length > saved_len`. On failure it logs android_errorWriteLog(0x534e4554, "178725766") when inflation is detected, restores p_mfc->ndef_length = saved_len and p_mfc->ndef_start_pos = saved_pos, and sets failed = true. Because offset = p_mfc->ndef_start_pos is assigned after this block, offset also gets the restored start position.
- Same function: the copy loop guard becomes `if (!failed)`. The old `saved_length >= p_mfc->ndef_length` comparison and its else branch (log + failed = true) are deleted. The loop body that writes into p_mfc->p_ndef_buffer[p_mfc->work_offset] is unchanged.
Completeness
- Covers the re-derivation of ndef_length shown in the diff (the work_offset == 0 TLV decode). After a failed decode, the control block keeps the values it had before that decode.
- A decoded ndef_length smaller than or equal to the original is still accepted. Only growth is rejected. A change of ndef_start_pos without length growth is also accepted. Rollback happens only on failure.
- The copy loop still bounds writes only by p_mfc->ndef_length against work_offset. This function has no independent check against the p_ndef_buffer allocation size.
- The commit message says control flow was updated to prevent further block reads on failure. The diff contains only the hunk shown. The code after `if (p_mfc->work_offset >= p_mfc->ndef_length)` is not in the material, so how `failed` ends the operation cannot be verified here.
Behavior changes
- When a Mifare Classic tag's first-block TLV decodes to a larger NDEF length than before, the read now fails (failed = true) and the original ndef_length/ndef_start_pos are restored. Previously the inflated values stayed in the control block.
- The android_errorWriteLog(0x534e4554, "178725766") event is now emitted only for the inflation case, not for plain rw_nfc_decodeTlv failures.
- The debug/verbose log line " FAILED finding TLV" now also fires for the inflation case.
Per major
- Android 14: same fix: platform/system/nfc 7eaf8e16d9ad in android-security-14.0.0_r29, src/nfc/tags/rw_mfc.cc, log macro DLOG_IF(INFO, nfc_debug_enabled).
- Android 15: same fix: platform/system/nfc 5dd24f8b4a6d in android-security-15.0.0_r17. The hunk is identical except for LOG(VERBOSE) logging.
- Android 16: same fix: platform/packages/modules/Nfc b79e5fd8cf14 in android-security-16.0.0_r8, file at libnfc-nci/src/nfc/tags/rw_mfc.cc. Identical hunk with LOG(VERBOSE).
- Android 17: same fix: platform/packages/modules/Nfc 19fc329133fe in android-security-17.0.0_r1, libnfc-nci/src/nfc/tags/rw_mfc.cc. Identical hunk with LOG(VERBOSE).
Unknowns
- The code after the hunk is not in the material: how `failed` and `done` end the read, and what reads the next block. So the exact path from the persisted inflated ndef_length to the overflow is inferred.
- Where p_ndef_buffer is allocated and with what size is not shown.
- The internals of rw_nfc_decodeTlv and what it validates are not shown.
- Why the resolver reports only 7/8 lines of the patched region present at every tag, including the tags that contain the fix, is not explained by the material.