CVE-2026-28653 AI-generated analysis
platform/system/nfc (Android 14/15) and platform/packages/modules/Nfc libnfc-nci (Android 16/17), NFC Type 3 Tag (T3T) reader/writer, NDEF CHECK/UPDATE command builder
Adds a range check before the loop in rw_t3t_send_next_ndef_update_cmd and rw_t3t_send_next_ndef_check_cmd (src/nfc/tags/rw_t3t.cc). The first block number plus the block count can no longer go past the 16-bit range, where the commit says the uint16 block_id loop counter could wrap and cause an unbounded heap out-of-bounds write.
Root cause
In rw_t3t_send_next_ndef_update_cmd and rw_t3t_send_next_ndef_check_cmd, the block descriptor loop runs `for (block_id = first_block_to_write; block_id < (first_block_to_write + ndef_blocks_to_write); block_id++)` (and the CHECK equivalent with first_block_to_read / cur_blocks_to_read). Each iteration appends a 2- or 3-byte block descriptor with UINT8_TO_STREAM / UINT16_TO_STREAM into the command buffer through p. The commit describes this as a uint16 loop counter wrap: when the first block plus the block count reaches past 65535, block_id wraps, and the commit says this leads to an unbounded heap out-of-bounds write. The block count comes from ndef_blocks_remaining and blocks_per_update, and blocks_per_update is capped by p_cb->ndef_attrib.nbw.
How the fix works
- src/nfc/tags/rw_t3t.cc:rw_t3t_send_next_ndef_update_cmd: after the UPDATE header is written and the timeout is computed, the fix adds `if (ndef_blocks_to_write > UINT16_MAX - first_block_to_write)`. On that path it logs an error, emits android_errorWriteLog(0x534e4554, "508389055") and returns NFC_STATUS_FAILED before the block descriptor loop runs.
- src/nfc/tags/rw_t3t.cc:rw_t3t_send_next_ndef_check_cmd: the same check, `if (cur_blocks_to_read > UINT16_MAX - first_block_to_read)`, with the same log, errorWriteLog and NFC_STATUS_FAILED return before the CHECK block descriptor loop.
- The code at android-security-14.0.0_r29 shows a different form of the guard (7/8 lines of the patched region present). The condition is `(uint32_t)first_block_to_write + ndef_blocks_to_write > 0x10000`, GKI_freebuf(p_cmd_buf) is called before returning NFC_STATUS_FAILED, and the loop condition casts the sum to uint32_t. The material does not explain why the tag code differs from the linked diff.
Completeness
- Both T3T NDEF command builders with this loop (UPDATE and CHECK) are guarded. The material shows no other loop of this shape.
- The guard in the diff returns NFC_STATUS_FAILED without freeing the command buffer p_cmd_buf. The code at the 14.0.0_r29 tag calls GKI_freebuf(p_cmd_buf) before the return.
- The check only rejects the wrapping case. It does not otherwise check that the descriptors fit in the command buffer. That relies on the existing caps: blocks_per_update limited by p_cb->ndef_attrib.nbw, and the block count written as a single uint8 field.
- The check runs after the header bytes have been streamed into p. With the raw diff the buffer then leaks on the failure path. How callers handle NFC_STATUS_FAILED is not shown.
Behavior changes
- NDEF UPDATE or CHECK on a T3T tag whose block range would go past 65535 now fails with NFC_STATUS_FAILED instead of building the command.
- The rejected path logs a new LOG(ERROR) message "<func>: Heap out-of-bounds" and an android_errorWriteLog event tagged 0x534e4554 / 508389055.
- In the code at the 14 tag, the GKI command buffer is freed on the new failure path.
Per major
- Android 14: same fix: platform/system/nfc bea12a1b3e25 in android-security-14.0.0_r29 (cherry-pick of b33444ec), src/nfc/tags/rw_t3t.cc. The code at the tag shows a different form (uint32_t compare against 0x10000, GKI_freebuf); 7/8 lines of the patched region present.
- Android 15: same fix: platform/system/nfc 089a1a4123db in android-security-15.0.0_r17, same two hunks in src/nfc/tags/rw_t3t.cc; 7/8 lines of the patched region present at the tag (the actual code there is not shown).
- Android 16: same fix: platform/packages/modules/Nfc 4648c160b825 in android-security-16.0.0_r8, at libnfc-nci/src/nfc/tags/rw_t3t.cc; identical hunks; 7/8 lines of the patched region present.
- Android 17: same fix: platform/packages/modules/Nfc e55c93242c56 in android-security-17.0.0_r1, at libnfc-nci/src/nfc/tags/rw_t3t.cc; identical hunks; 7/8 lines of the patched region present.
Unknowns
- The commit that produced the different check seen at the 14 tag (GKI_freebuf, 0x10000 comparison, uint32_t loop cast) is not in the material. Whether 15/16/17 have the same form is not shown.
- The declared types of block_id, first_block_to_write/first_block_to_read and the block count variables are not shown. Only the commit title says the loop counter is uint16.
- Where ndef_attrib (nbw, NDEF length) and the block offsets come from, and whether a tag controls them, is not shown.
- How callers handle the new NFC_STATUS_FAILED return is not shown.
- The material does not explain why the bulletin classifies this as DoS.