CVE-2026-0100 AI-generated analysis
platform/frameworks/base, libs/androidfw (libandroidfw resource table parser, LoadedArsc)
Adds a range check in LoadedPackage::Load (libs/androidfw/LoadedArsc.cpp) so that a RES_TABLE_PACKAGE_TYPE chunk whose 32-bit package id exceeds 255 is rejected and the ARSC fails to load instead of being accepted with an out-of-range package id.
Root cause
In LoadedPackage::Load, the package id was taken directly from the ResTable_package header (loaded_package->package_id_ = dtohl(header->id)) without validation. The field in the chunk is 32-bit, but per the commit message resource IDs allocate only 8 bits for the package id. A malformed ARSC with id > 255 was accepted, and the commit message states this could lead to unexpected behavior or overflow during parsing. The neighbouring typeIdOffset field already had an equivalent uint8_t range check in the same function; the id field did not.
How the fix works
- libs/androidfw/LoadedArsc.cpp : LoadedPackage::Load: reads the id into a local const uint32_t package_id = dtohl(header->id); if package_id > std::numeric_limits<uint8_t>::max() it logs 'RES_TABLE_PACKAGE_TYPE package id is out of valid range' and returns {} (null). Only then is package_id_ assigned via static_cast<int>(package_id). The check is placed before the existing package_id_ == 0 / kAppPackageId dynamic-flag logic and before the typeIdOffset check, mirroring the latter's style.
- libs/androidfw/tests/LoadedArsc_test.cpp: new TEST(LoadedArscTest, LoadArscWithInvalidPackageId) builds a minimal in-memory ResTable_header + ResTable_package with id = 256 and asserts LoadedArsc::Load returns null.
Completeness
- Covers the single assignment of package_id_ from the chunk header in LoadedPackage::Load; the new test shows that a package chunk with id 256 makes LoadedArsc::Load return null.
- The check is id > 255 only; id 0 and id 255 remain accepted (0 continues to be treated as dynamic, as before).
- The fix is confined to the LoadedArsc parser. The material does not show whether other consumers of ResTable_package::id (for example the legacy ResTable parser in ResourceTypes.cpp) perform an equivalent check.
- Error path returns {} with a LOG(ERROR), consistent with the other validation failures in the function; no new error type is introduced.
Behavior changes
- ARSC files containing a RES_TABLE_PACKAGE_TYPE chunk with id > 255 now fail to load (LoadedPackage::Load returns null; the test shows LoadedArsc::Load returns null) with the log line 'RES_TABLE_PACKAGE_TYPE package id is out of valid range'; previously they were accepted.
- Well-formed resource tables (id in 0..255) are unaffected.
- New unit test LoadArscWithInvalidPackageId in libandroidfw_tests.
Per major
- Android 14: same fix: platform/frameworks/base 6a9eb9dd74d0 in android-security-14.0.0_r28 (cherry-pick, Change-Id I438cd0e7f64e2b0ef4cc2bcb4f1f81778216e5e8); identical hunk in LoadedArsc.cpp (@@ -476) and identical test; code context at android-security-14.0.0_r29 shows the check present.
- Android 15: same fix: 86dd9d421620 in android-security-15.0.0_r16; identical hunk (@@ -481) and test; at android-security-15.0.0_r17, 7/8 lines of the patched region present.
- Android 16: same fix: ca855338abcf in android-security-16.0.0_r7; identical hunk (@@ -485) and test; at android-security-16.0.0_r8, 7/8 lines of the patched region present.
- Android 17: same fix: 50d18b36c708 in android-17.0.0_r1 (base release); identical hunk (@@ -549) and test (@@ -421); at android-security-17.0.0_r1, 7/8 lines of the patched region present.
Unknowns
- The exact downstream consequence of an out-of-range package id (which computation overflows or which structure is mis-indexed) is not shown; the commit message only says 'unexpected behavior or overflow'.
- Which process/entry point (package installation, overlay loading, app resource loading) reaches LoadedPackage::Load for this CVE is not in the material.
- Whether the legacy ResTable parser (ResourceTypes.cpp) or other readers of ResTable_package::id have equivalent validation is not shown.
- What the resolver's '7/8 lines present' figure means for branches other than 14 is not explained in the material.