CVE-2026-55290 AI-generated analysis
platform/frameworks/base, libs/androidfw (ResStringPool resource string pool parser)
ResStringPool::setTo in libandroidfw now checks stringsStart and stylesStart against the chunk size unconditionally. It also checks that the style-offset array (styleCount * 4 bytes) fits inside the data buffer and ends before stringsStart. This closes an out-of-bounds heap read in ResStringPool::styleAt.
Root cause
In libs/androidfw/ResourceTypes.cpp, ResStringPool::setTo skipped validation of stringsStart when stringCount was zero (per the commit message), so stringsStart could hold an arbitrary value such as 0xFFFFFFFF. In the styleCount > 0 path, the old code had three weak checks: 1. It only checked that the start of the style-offset array (mEntryStyles - mHeader) was not past size (an int comparison). It did not check that styleCount * sizeof(uint32_t) bytes fit. 2. The overflow guard only bounded styleCount against the uint32_t maximum / 4. 3. The overlap check `styleCount * sizeof(uint32_t) > (stringsStart - styleOffsetsStart)` did not fire when stringsStart was huge and unvalidated. A pool with stringCount == 0, a large styleCount and a huge stringsStart therefore passed setTo. Per the commit message, this led to an OOB heap read in ResStringPool::styleAt.
How the fix works
- libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo: two new unconditional checks go right after `mSize = mHeader->header.size;` and before any stringCount/styleCount branching. The pool is rejected (mError=BAD_TYPE) if mHeader->stringsStart > mSize or mHeader->stylesStart > mSize. This closes the stringCount == 0 gap.
- libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo, inside the styleCount > 0 block: the old start-only check `(mEntryStyles - mHeader) > (int)size` is replaced by these steps: 1. styleOffsetsStart is computed. 2. styleCount is checked against SIZE_MAX / sizeof(uint32_t). 3. styleOffsetsSize = styleCount * sizeof(uint32_t) is computed. 4. The pool is rejected if styleOffsetsSize > size or styleOffsetsStart > size - styleOffsetsSize. The subtraction is ordered to avoid underflow.
- libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo, same block: a new check rejects the pool if styleOffsetsStart > stringsStart or styleOffsetsSize > stringsStart - styleOffsetsStart. The style-offset array therefore cannot extend past the string data start, and stringsStart has already been bounded by mSize.
- libs/androidfw/ResourceTypes.cpp:ResStringPool::setTo: two older checks that came after `mStyles = ...` are deleted, because the new checks above replace them: - the uint32_t-max overflow check; - the original `styleCount * sizeof(uint32_t) > (stringsStart - styleOffsetsStart)` check. The existing `stylesStart >= header.size` check and the mStylePoolSize computation are unchanged.
- libs/androidfw/tests/ResourceTypes_test.cpp adds two tests: - ResStringPool_StyleCountUnboundedWhenStringCountIsZero: stringCount 0, styleCount 100, stringsStart 0xFFFFFFFF, expects BAD_TYPE. - ResStringPool_StyleCountCalculationOverflow: stringCount 0, styleCount 0x40000000, expects BAD_TYPE.
Completeness
- The fix covers the style-offset array: both its start and its full styleCount * 4 extent are now bounded by the buffer size and by stringsStart, whatever the value of stringCount.
- stringsStart and stylesStart are now validated against header.size for every pool. The new early check uses `> mSize`, so a value equal to mSize is still accepted.
- This fix does not change the style span data region (mStyles / mStylePoolSize, derived from header.size - stylesStart). That region still relies on the existing `stylesStart >= header.size` check in setTo.
- The new extent check compares against the `size` argument rather than mSize (header.size).
- Only ResStringPool::setTo is modified; this change touches no other parser in ResourceTypes.cpp.
Behavior changes
- ResStringPool::setTo now returns BAD_TYPE (and logs ALOGW) for pools whose stringsStart or stylesStart exceeds header.size, even when stringCount == 0 or styleCount == 0.
- Pools are now rejected if their style-offset array does not fit within the data buffer or extends past stringsStart.
- The overflow guard on styleCount is now against SIZE_MAX / 4 instead of the uint32_t maximum / 4. The log messages for these rejections have new text.
Per major
- Android 14: same fix: platform/frameworks/base 1eb9fc572c86 in android-security-14.0.0_r29, a cherry-pick of 968cb0736ca6 (Change-Id I3e1e4579fbda8ee89c5fea3945d523c6e5b72dc9). The hunks are identical to the other branches; only line offsets differ.
- Android 15: same fix: platform/frameworks/base ed4eedc69050 in android-security-15.0.0_r17, with identical code and test changes.
- Android 16: same fix: platform/frameworks/base 01bff648b538 in android-security-16.0.0_r8, with identical code and test changes.
- Android 17: same fix: platform/frameworks/base 7e42d365a0c2 in android-security-17.0.0_r1, with identical code and test changes.
Unknowns
- The material does not show the body of ResStringPool::styleAt.
- The material does not show which callers reach setTo with untrusted data. The index search returned only unrelated Java TypedValue.setTo matches.
- The material does not show the body of validate_chunk, so it does not confirm that header.size is bounded by the buffer size.
- The material does not show the stringCount > 0 validation block.
- The material does not show whether upstream commit 968cb0736ca6 came with any other related changes.