Protect objects in Parcel::appendFrom * only aquire objects within the range supplied to appendFrom * don't append over existing objects * unset the mObjectsSorted flag a couple more cases * keep mObjectPositions sorted Flag: EXEMPT bug fix Ignore-AOSP-First: security fix Test: binder_parcel_fuzzer Bug: 402319736 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:28e7af08b92e7b97f46d8ecd88ebd3f27a065e08) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:71efd8fd6b717ec427a264fc901e3c62ef7de8ec) (cherry picked from commit 919b3602d669d7c3be34917fca7e08bcd20b83c4) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:253679d4d39663083ec5e544ee5988fe86c925e3) Merged-In: I63715fdd81781aaf04f5fc0cb8bdb74c09d5d807 Change-Id: I63715fdd81781aaf04f5fc0cb8bdb74c09d5d807
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index bbcac54..7f168f6 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp
@@ -537,6 +537,11 @@ return BAD_VALUE; } + // Make sure we aren't appending over objects. + if (status_t status = validateReadData(mDataPos + len); status != OK) { + return status; + } + if ((mDataSize+len) > mDataCapacity) { // grow data err = growData(len); @@ -560,17 +565,13 @@ const binder_size_t* objects = otherKernelFields->mObjects; size_t size = otherKernelFields->mObjectsSize; // Count objects in range - int firstIndex = -1, lastIndex = -2; + int numObjects = 0; for (int i = 0; i < (int)size; i++) { - size_t off = objects[i]; - if ((off >= offset) && (off + sizeof(flat_binder_object) <= offset + len)) { - if (firstIndex == -1) { - firstIndex = i; - } - lastIndex = i; + size_t pos = objects[i]; + if ((pos >= offset) && (pos + sizeof(flat_binder_object) <= offset + len)) { + numObjects++; } } - int numObjects = lastIndex - firstIndex + 1; if (numObjects > 0) { const sp<ProcessState> proc(ProcessState::self()); // grow objects @@ -592,8 +593,12 @@ // append and acquire objects int idx = kernelFields->mObjectsSize; - for (int i = firstIndex; i <= lastIndex; i++) { - size_t off = objects[i] - offset + startPos; + for (int i = 0; i < (int)size; i++) { + size_t pos = objects[i]; + if (!(pos >= offset) || !(pos + sizeof(flat_binder_object) <= offset + len)) { + continue; + } + size_t off = pos - offset + startPos; kernelFields->mObjects[idx++] = off; kernelFields->mObjectsSize++; @@ -613,6 +618,9 @@ acquire_object(proc, *flat, this); } + // Always clear sorted flag. It is tricky to infer if the append + // result maintains the sort or not. + kernelFields->mObjectsSorted = false; } #else LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); @@ -643,7 +651,10 @@ const binder_size_t objPos = otherRpcFields->mObjectPositions[i]; if (offset <= objPos && objPos < offset + len) { size_t newDataPos = objPos - offset + startPos; - rpcFields->mObjectPositions.push_back(newDataPos); + rpcFields->mObjectPositions + .insert(std::upper_bound(rpcFields->mObjectPositions.begin(), + rpcFields->mObjectPositions.end(), newDataPos), + newDataPos); mDataPos = newDataPos; int32_t objectType; @@ -1610,7 +1621,10 @@ if (status_t err = writeInt32(rpcFields->mFds->size()); err != OK) { return err; } - rpcFields->mObjectPositions.push_back(dataPos); + rpcFields->mObjectPositions + .insert(std::upper_bound(rpcFields->mObjectPositions.begin(), + rpcFields->mObjectPositions.end(), dataPos), + dataPos); rpcFields->mFds->push_back(std::move(fdVariant)); return OK; } @@ -1816,6 +1830,8 @@ kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; acquire_object(ProcessState::self(), val, this); kernelFields->mObjectsSize++; + // Clear sorted flag if we aren't appending to the end. + kernelFields->mObjectsSorted &= mDataPos == mDataSize; } return finishWrite(sizeof(flat_binder_object));
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index 32a70e5..647f67c 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -26,6 +26,7 @@ using android::NO_ERROR; using android::OK; using android::Parcel; +using android::PERMISSION_DENIED; using android::sp; using android::status_t; using android::String16; @@ -313,6 +314,16 @@ ASSERT_NE(-1, p1.readFileDescriptor()); } +TEST(Parcel, AppendOverObject) { + Parcel p1; + p1.writeDupFileDescriptor(0); + Parcel p2; + p2.writeInt32(2); + + p1.setDataPosition(8); + ASSERT_EQ(PERMISSION_DENIED, p1.appendFrom(&p2, 0, p2.dataSize())); +} + // Tests a second operation results in a parcel at the same location as it // started. void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {