CVE-2026-49885 AI-generated analysis
platform/system/nfc (Android 14/15) and platform/packages/modules/Nfc libnfc-nci (Android 16/17), NFC Type 4 Tag reader/writer (rw_t4t.cc)
Adds a check in rw_t4t_set_ber_tlv() that p_t4t->max_update_size is larger than the ODO/DDO data header before computing data_length = max_update_size - data_header (in both realignment branches), and makes the caller rw_t4t_update_file() free the C-APDU GKI buffer when rw_t4t_set_ber_tlv() fails.
Root cause
In src/nfc/tags/rw_t4t.cc rw_t4t_set_ber_tlv(), when the write has to be realigned to the tag's maximum update size (p_t4t->max_update_size), the code sets length = max_update_size and computes data_length = p_t4t->max_update_size - data_header, where data_header = RW_T4T_ODO_DDO_HEADER_MIN_LENGTH + length_size. Nothing checked that max_update_size is larger than data_header, so a max_update_size that is too small could make this subtraction underflow (the commit message calls this a potential underflow). The same pattern was in both the length == 0 branch and the length != 0 branch. Separately, rw_t4t_update_file() returned the result of rw_t4t_set_ber_tlv() directly, so a false return left the allocated p_c_apdu GKI buffer unfreed.
How the fix works
- src/nfc/tags/rw_t4t.cc:rw_t4t_set_ber_tlv(): in the length == 0 path, inside the else branch where length is realigned to p_t4t->max_update_size, add `if (p_t4t->max_update_size <= data_header)` with LOG(ERROR) and return false, before data_length is computed. Because the comparison is <=, it also rejects the case where data_length would be zero.
- src/nfc/tags/rw_t4t.cc:rw_t4t_set_ber_tlv(): the same check is added in the length != 0 path, in the else branch where (length + data_header) > p_t4t->max_update_size, again before `data_length = p_t4t->max_update_size - data_header`.
- src/nfc/tags/rw_t4t.cc:rw_t4t_update_file(): in the MV 3.0 / NDEF size > 0x7FFF path, replace `return rw_t4t_set_ber_tlv(p_c_apdu, p, length);` with code that calls GKI_freebuf(p_c_apdu) and returns false when rw_t4t_set_ber_tlv() returns false, and otherwise returns true. As a result, the new error returns do not leak the C-APDU buffer.
Completeness
- Both `data_length = p_t4t->max_update_size - data_header` subtractions in rw_t4t_set_ber_tlv() are now guarded. No other `max_update_size - data_header` subtraction is visible in the shown context.
- The branches that do not realign (data_length = T4T_MAX_LENGTH_LC - data_header, and length += data_header) are unchanged. They do not subtract max_update_size.
- The leak fix covers only the visible caller, rw_t4t_update_file(). The material does not show whether rw_t4t_set_ber_tlv() has other callers (per index search, none were found, but that search is not conclusive).
- The fix checks max_update_size only where it is used. The code that sets max_update_size is not shown and is not changed by this commit.
Behavior changes
- When the tag's max_update_size is less than or equal to the ODO/DDO header size and the realignment branch is taken, rw_t4t_set_ber_tlv() now logs "max_update_size too small for ODO/DDO header" and returns false instead of building the C-APDU. The NDEF update fails in that case.
- rw_t4t_update_file() now frees p_c_apdu with GKI_freebuf() when rw_t4t_set_ber_tlv() returns false, so these failures no longer leak a GKI buffer.
Per major
- Android 14: same fix: platform/system/nfc 90576eadff36 in android-security-14.0.0_r29, src/nfc/tags/rw_t4t.cc; 8/8 patched lines present at android-security-14.0.0_r30. The surrounding code uses DLOG_IF(INFO, nfc_debug_enabled) logging.
- Android 15: same fix: platform/system/nfc f01d5b0889f5 in android-security-15.0.0_r17, same file. Only the surrounding code differs (LOG(VERBOSE) instead of DLOG_IF, shifted line numbers). 8/8 lines present at android-security-15.0.0_r18.
- Android 16: same fix: platform/packages/modules/Nfc 9c0ae67d8733 in android-security-16.0.0_r8, in libnfc-nci/src/nfc/tags/rw_t4t.cc; 8/8 lines present at android-security-16.0.0_r9.
- Android 17: same fix: platform/packages/modules/Nfc 0dc8fad01628 in android-security-17.0.0_r1, in libnfc-nci/src/nfc/tags/rw_t4t.cc; 8/8 lines present at android-security-17.0.0_r2.
Unknowns
- The declared types of max_update_size, data_header and data_length, and how data_length is used after the subtraction, are not in the shown context. The exact memory-safety consequence of the underflow is therefore not shown by the material.
- Where p_t4t->max_update_size is set, and whether it is checked anywhere else, is not shown.
- Whether rw_t4t_set_ber_tlv() has callers other than rw_t4t_update_file().
- Whether rw_t4t_set_ber_tlv() had any false returns before this fix.