CVE-2024-49738 AI-generated analysis
platform/frameworks/native, libbinder (libs/binder/Parcel.cpp)
In libbinder Parcel.cpp, writeInplace(), writeObject() and writeAligned() now call validateReadData() on the end offset of the write before storing into mData+mDataPos, and refuse the write if it fails.
Root cause
The in-place write paths of Parcel (writeInplace, writeObject, writeAligned) checked only that mDataPos plus the write length fits within mDataCapacity (writeObject also checks object capacity). They then wrote directly at mData+mDataPos without calling validateReadData() on the target range. The commit message says this validation is required to prevent memory corruption. What exactly validateReadData() guards against is not shown in the material.
How the fix works
- libs/binder/Parcel.cpp:Parcel::writeInplace(size_t len): in the restart_write block, after computing data = mData+mDataPos, it calls validateReadData(mDataPos + padded). On failure it returns nullptr and drops the status, as the code comment says. The padding mask and finishWrite(padded) run only after the validation passes.
- libs/binder/Parcel.cpp:Parcel::writeObject(const flat_binder_object& val, bool nullMetaData): at restart_write, it calls validateReadData(mDataPos + sizeof(val)) before storing val into mData+mDataPos with reinterpret_cast. On failure it returns the status.
- libs/binder/Parcel.cpp:Parcel::writeAligned(T val): at restart_write, it calls validateReadData(mDataPos + sizeof(val)) before memcpy(mData + mDataPos, &val, sizeof(val)). On failure it returns the status.
- In all three functions the check sits right after the restart_write label. In writeInplace the growData() success path jumps back to that label (lines 1129-1130), so the check also runs after the buffer grows. The commit accepts the performance cost ('This is slow, but it's required to prevent memory corruption').
Completeness
- The fix covers three write primitives in Parcel.cpp: writeInplace, writeObject and the writeAligned template. The material does not show whether every other write helper goes through these three.
- writeInplace drops the validateReadData status and returns nullptr. The caller shown at lines 1076-1081 then returns mError. Whether mError reflects the validation failure cannot be seen in the material.
- How much protection the fix gives depends on what validateReadData() checks. Its body is not in the material.
- Per index search, system/libhwbinder/Parcel.cpp has a writeAligned with the same structure and no validateReadData call. This fix does not touch it.
Behavior changes
- Writes for which validateReadData() fails are now refused. writeObject and writeAligned return the validation status. writeInplace returns nullptr, and the caller shown then returns mError.
- Each of these in-place writes now runs an extra validation, which the commit says is slow.
Per major
- Android 14: Same fix: f3c7aac0e327 in android-security-14.0.0_r16 (cherry-picked from c54dad65317f, Change-Id Ibc5566ade0389221690dc90324f93394cf7fc9a5). All 8 patched lines are present at android-security-14.0.0_r29.
- Android 15: Same fix: 49eb0ea0816e in android-security-15.0.0_r4, with identical hunks at shifted line numbers. All 8 lines are present at android-security-15.0.0_r17.
- Android 16: Not affected per the bulletin. The patched lines are present at android-security-16.0.0_r8 (8/8).
- Android 17: Not affected per the bulletin. The patched lines are present at android-security-17.0.0_r1 (8/8).
Unknowns
- The body and exact semantics of validateReadData() are not in the material.
- The concrete trigger condition and the affected callers or processes are not shown. The bulletin gives only EoP / High.
- Fix commits for Android 12, 12L and 13, which are listed as affected, are not provided.
- Whether other write helpers in Parcel.cpp bypass these three primitives is not visible.
- Whether mError is set when writeInplace fails validation is not shown.
- The growData() paths of writeObject and writeAligned are not shown.