CVE-2025-32325 AI-generated analysis
platform/frameworks/native, libs/binder (Parcel)
Hardens Parcel::appendFrom in libbinder in three ways: it rejects appends that would overwrite existing objects in the destination, it only copies and acquires source objects that actually fall inside the requested [offset, offset+len) range (instead of a contiguous index range), and it keeps the object-ordering bookkeeping (mObjectsSorted, RPC mObjectPositions) accurate.
Root cause
In libs/binder/Parcel.cpp, the kernel binder path of Parcel::appendFrom chose which source objects to copy by recording the first and last index whose position fell in [offset, offset+len), then copying every entry in [firstIndex, lastIndex]. This implicitly assumed the source mObjects array was sorted by position. When it was not, entries outside the requested byte range were also copied. Their new offset (objects[i] - offset + startPos) could underflow or point past the appended bytes, and acquire_object was then called on a flat_binder_object read at that offset. Separately, appendFrom never checked whether the destination write range starting at mDataPos overlapped existing objects in the destination parcel, so an append at a rewound mDataPos could overwrite object data while the matching object entries stayed in place. In addition, mObjectsSorted was not cleared in some cases where ordering could be broken (appendFrom, and object writes not at the end of the data), and the RPC path added to mObjectPositions with push_back without keeping it sorted.
How the fix works
- libs/binder/Parcel.cpp:Parcel::appendFrom: before growing data, call validateReadData(mDataPos + len) and return its error if it is not OK, so an append over existing objects in the destination is refused (the new test expects PERMISSION_DENIED).
- libs/binder/Parcel.cpp:Parcel::appendFrom (kernel path): replace the firstIndex/lastIndex range selection with a per-entry count (numObjects++ for each source object with pos >= offset and pos + sizeof(flat_binder_object) <= offset + len). The copy/acquire loop now goes through all source objects and skips any that fail the same range test, so only in-range objects are appended and acquired.
- libs/binder/Parcel.cpp:Parcel::appendFrom (kernel path): always set kernelFields->mObjectsSorted = false after appending objects.
- libs/binder/Parcel.cpp:Parcel::appendFrom (RPC path): insert newDataPos into rpcFields->mObjectPositions at std::upper_bound instead of using push_back, keeping the vector sorted.
- libs/binder/Parcel.cpp:Parcel::writeFileDescriptor (RPC path): likewise insert dataPos into rpcFields->mObjectPositions at std::upper_bound instead of using push_back.
- libs/binder/Parcel.cpp, object write path at the restart_write label (kernel path): after recording a new object, clear mObjectsSorted unless the write is at the end of the data (mObjectsSorted &= mDataPos == mDataSize).
- libs/binder/tests/binderParcelUnitTest.cpp: add TEST(Parcel, AppendOverObject). A parcel holds a dup'd FD, its position is rewound to 8, and appendFrom of a plain-int parcel must fail with PERMISSION_DENIED.
Completeness
- Covers both the kernel-binder and RPC object paths of Parcel::appendFrom, plus two other places that change object bookkeeping without preserving order: the object write at restart_write when not at the end, and RPC writeFileDescriptor.
- The overlap check (validateReadData(mDataPos + len)) is placed before growData, ahead of both paths. It relies on how validateReadData handles the object list, which the material does not show.
- The new unit test only covers the append-over-existing-object case. The unsorted-source selection change and the sorted-flag changes have no dedicated test in the diff (the commit cites binder_parcel_fuzzer).
- The Java Parcel and NDK AParcel_appendFrom wrappers are not changed. They inherit the new error return from the native function.
Behavior changes
- Parcel::appendFrom now fails when the destination range starting at mDataPos would overlap existing objects. The error comes from validateReadData (PERMISSION_DENIED in the test). Previously such appends succeeded. The error propagates through AParcel_appendFrom (per index search) and any other wrapper.
- Source objects that lie outside [offset, offset+len) are no longer copied or acquired, even if their index lies between in-range objects.
- mObjectsSorted is cleared after every appendFrom that carries objects, and after object writes not at the end of the data.
- RPC mObjectPositions is kept sorted on insertion in appendFrom and writeFileDescriptor.
Per major
- Android 14: Same fix: commit 7be10040534a in android-security-14.0.0_r22 (cherry-pick of the internal change, Change-Id I63715fdd81781aaf04f5fc0cb8bdb74c09d5d807). The test is placed after DebugReadAllFds. At android-security-14.0.0_r29, 6/8 lines of the patched region are present.
- Android 15: Same fix: commit 96f0dd71bc07 in android-security-15.0.0_r10. Code changes are identical; only context differs (test placed after AppendWithFdPartial). At android-security-15.0.0_r17, 6/8 lines are present.
- Android 16: Same fix: commit 46e72b490edd in android-security-16.0.0_r1. Code changes are identical; context differs (the growData check uses mDataPos + len, and acquire_object takes a tagFds argument). At android-security-16.0.0_r8, 7/8 lines are present.
- Android 17: Not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 6/8 lines of the patched region are present.
Unknowns
- The body of validateReadData, and whether it always returns PERMISSION_DENIED for the overlap case, are not shown (the test expects PERMISSION_DENIED).
- The material does not show why the latest security tags contain only 6/8 or 7/8 lines of the patched region (later refactoring vs. partial presence), or whether the fix is in the Android 17 tree.
- Whether the Android 14 test file at the fix point contains AppendWithFd* tests is not shown.
- The specific consequence reported for this bug is not described beyond the bulletin's EoP/High rating.