diff options
author | 2022-03-24 00:32:25 +0000 | |
---|---|---|
committer | 2022-03-24 15:34:42 +0000 | |
commit | 4b2f18d41ce9b7e5eec383b9c5d90706c32283b0 (patch) | |
tree | f86c14dd9bed5f6ae1b34dd7de793d7b32505bd6 | |
parent | 4fe07911ea6b870ea31e39c8efd7e26fd45f3662 (diff) |
libbinder: service driver fuzzes nested ifaces
We:
- make a binder call
- get all of the FDs and binder objects from this call
- also fuzz these additional binder objects
and use them and the FDs to construct parcels
Bug: 224646709
Fixes: 225083088
Test: android.hardware.vibrator-service.example_fuzzer
Test: binder_parcel_fuzzer
Change-Id: I3a1594c5777d7dfd4776b751d6f9054428655302
-rw-r--r-- | libs/binder/Parcel.cpp | 41 | ||||
-rw-r--r-- | libs/binder/include/binder/Parcel.h | 6 | ||||
-rw-r--r-- | libs/binder/tests/binderParcelUnitTest.cpp | 37 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/binder.cpp | 2 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h | 5 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h | 12 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp | 39 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/random_fd.cpp | 4 | ||||
-rw-r--r-- | libs/binder/tests/parcel_fuzzer/random_parcel.cpp | 32 |
9 files changed, 156 insertions, 22 deletions
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a217a157c9..be50a7500a 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -569,6 +569,47 @@ bool Parcel::hasFileDescriptors() const return mHasFds; } +std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const { + std::vector<sp<IBinder>> ret; + + size_t initPosition = dataPosition(); + for (size_t i = 0; i < mObjectsSize; i++) { + binder_size_t offset = mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_BINDER) continue; + + setDataPosition(offset); + + sp<IBinder> binder = readStrongBinder(); + if (binder != nullptr) ret.push_back(binder); + } + + setDataPosition(initPosition); + return ret; +} + +std::vector<int> Parcel::debugReadAllFileDescriptors() const { + std::vector<int> ret; + + size_t initPosition = dataPosition(); + for (size_t i = 0; i < mObjectsSize; i++) { + binder_size_t offset = mObjects[i]; + const flat_binder_object* flat = + reinterpret_cast<const flat_binder_object*>(mData + offset); + if (flat->hdr.type != BINDER_TYPE_FD) continue; + + setDataPosition(offset); + + int fd = readFileDescriptor(); + LOG_ALWAYS_FATAL_IF(fd == -1); + ret.push_back(fd); + } + + setDataPosition(initPosition); + return ret; +} + status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const { if (len > INT32_MAX || offset > INT32_MAX) { // Don't accept size_t values which may have come from an inadvertent conversion from a diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 450e3888f1..e2b2c5128d 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -95,6 +95,12 @@ public: bool hasFileDescriptors() const; status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const; + // returns all binder objects in the Parcel + std::vector<sp<IBinder>> debugReadAllStrongBinders() const; + // returns all file descriptors in the Parcel + // does not dup + std::vector<int> debugReadAllFileDescriptors() const; + // Zeros data when reallocating. Other mitigations may be added // in the future. // diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index aee15d8bd9..359c783de5 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -20,9 +20,12 @@ #include <cutils/ashmem.h> #include <gtest/gtest.h> +using android::BBinder; +using android::IBinder; using android::IPCThreadState; using android::OK; using android::Parcel; +using android::sp; using android::status_t; using android::String16; using android::String8; @@ -75,6 +78,40 @@ TEST(Parcel, EnforceNoDataAvail) { EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE); } +TEST(Parcel, DebugReadAllBinders) { + sp<IBinder> binder1 = sp<BBinder>::make(); + sp<IBinder> binder2 = sp<BBinder>::make(); + + Parcel p; + p.writeInt32(4); + p.writeStrongBinder(binder1); + p.writeStrongBinder(nullptr); + p.writeInt32(4); + p.writeStrongBinder(binder2); + p.writeInt32(4); + + auto ret = p.debugReadAllStrongBinders(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], binder1); + EXPECT_EQ(ret[1], binder2); +} + +TEST(Parcel, DebugReadAllFds) { + Parcel p; + p.writeInt32(4); + p.writeFileDescriptor(STDOUT_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + p.writeFileDescriptor(STDIN_FILENO, false /*takeOwnership*/); + p.writeInt32(4); + + auto ret = p.debugReadAllFileDescriptors(); + + ASSERT_EQ(ret.size(), 2); + EXPECT_EQ(ret[0], STDOUT_FILENO); + EXPECT_EQ(ret[1], STDIN_FILENO); +} + // 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) { diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 13f7195eb7..47ec776990 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -109,6 +109,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { }, PARCEL_READ_NO_STATUS(size_t, allowFds), PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), + PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders), + PARCEL_READ_NO_STATUS(std::vector<int>, debugReadAllFileDescriptors), [] (const ::android::Parcel& p, uint8_t len) { std::string interface(len, 'a'); FUZZ_LOG() << "about to enforceInterface: " << interface; diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 0a083d7665..843b6e3011 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h @@ -16,12 +16,13 @@ #pragma once +#include <android-base/unique_fd.h> #include <fuzzer/FuzzedDataProvider.h> namespace android { -// ownership to callee, always valid or aborts +// always valid or aborts // get a random FD for use in fuzzing, of a few different specific types -int getRandomFd(FuzzedDataProvider* provider); +base::unique_fd getRandomFd(FuzzedDataProvider* provider); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 633626ca44..459fb127c2 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -20,8 +20,16 @@ #include <fuzzer/FuzzedDataProvider.h> #include <functional> +#include <vector> namespace android { + +struct RandomParcelOptions { + std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader; + std::vector<sp<IBinder>> extraBinders; + std::vector<base::unique_fd> extraFds; +}; + /** * Fill parcel data, including some random binder objects and FDs * @@ -30,7 +38,5 @@ namespace android { * writeHeader - optional function to write a specific header once the format of the parcel is * picked (for instance, to write an interface header) */ -void fillRandomParcel( - Parcel* p, FuzzedDataProvider&& provider, - std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader = nullptr); +void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, const RandomParcelOptions& = {}); } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index be39bb9195..d5aa353af0 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -20,24 +20,45 @@ namespace android { void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) { + sp<IBinder> target; + + RandomParcelOptions options{ + .extraBinders = {binder}, + .extraFds = {}, + }; + while (provider.remaining_bytes() > 0) { uint32_t code = provider.ConsumeIntegral<uint32_t>(); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; + sp<IBinder> target = options.extraBinders.at( + provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1)); + options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) { + // most code will be behind checks that the head of the Parcel + // is exactly this, so make it easier for fuzzers to reach this + if (provider.ConsumeBool()) { + p->writeInterfaceToken(target->getInterfaceDescriptor()); + } + }; + std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>( provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); - fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), - [&binder](Parcel* p, FuzzedDataProvider& provider) { - // most code will be behind checks that the head of the Parcel - // is exactly this, so make it easier for fuzzers to reach this - if (provider.ConsumeBool()) { - p->writeInterfaceToken(binder->getInterfaceDescriptor()); - } - }); + fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), options); Parcel reply; - (void)binder->transact(code, data, &reply, flags); + (void)target->transact(code, data, &reply, flags); + + // feed back in binders and fds that are returned from the service, so that + // we can fuzz those binders, and use the fds and binders to feed back into + // the binders + auto retBinders = reply.debugReadAllStrongBinders(); + options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(), + retBinders.end()); + auto retFds = reply.debugReadAllFileDescriptors(); + for (size_t i = 0; i < retFds.size(); i++) { + options.extraFds.push_back(base::unique_fd(dup(retFds[i]))); + } } } diff --git a/libs/binder/tests/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index cef6adb82d..ab0b7e306f 100644 --- a/libs/binder/tests/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp @@ -23,13 +23,13 @@ namespace android { -int getRandomFd(FuzzedDataProvider* provider) { +base::unique_fd getRandomFd(FuzzedDataProvider* provider) { int fd = provider->PickValueInArray<std::function<int()>>({ []() { return ashmem_create_region("binder test region", 1024); }, []() { return open("/dev/null", O_RDWR); }, })(); CHECK(fd >= 0); - return fd; + return base::unique_fd(fd); } } // namespace android diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index cfabc1e6b5..0204f5ed61 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -40,19 +40,23 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { } void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, - std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader) { + const RandomParcelOptions& options) { if (provider.ConsumeBool()) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); - writeHeader(p, provider); + if (options.writeHeader) { + options.writeHeader(p, provider); + } fillRandomParcelData(p, std::move(provider)); return; } - writeHeader(p, provider); + if (options.writeHeader) { + options.writeHeader(p, provider); + } while (provider.remaining_bytes() > 0) { auto fillFunc = provider.PickValueInArray<const std::function<void()>>({ @@ -65,8 +69,16 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, }, // write FD [&]() { - int fd = getRandomFd(&provider); - CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/)); + if (options.extraFds.size() > 0 && provider.ConsumeBool()) { + const base::unique_fd& fd = options.extraFds.at( + provider.ConsumeIntegralInRange<size_t>(0, + options.extraFds.size() - + 1)); + CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/)); + } else { + base::unique_fd fd = getRandomFd(&provider); + CHECK(OK == p->writeFileDescriptor(fd.release(), true /*takeOwnership*/)); + } }, // write binder [&]() { @@ -85,7 +97,15 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, // candidate for checking usage of an actual BpBinder return IInterface::asBinder(defaultServiceManager()); }, - []() { return nullptr; }, + [&]() -> sp<IBinder> { + if (options.extraBinders.size() > 0 && provider.ConsumeBool()) { + return options.extraBinders.at( + provider.ConsumeIntegralInRange< + size_t>(0, options.extraBinders.size() - 1)); + } else { + return nullptr; + } + }, }); sp<IBinder> binder = makeFunc(); CHECK(OK == p->writeStrongBinder(binder)); |