diff options
Diffstat (limited to 'libs/binder/tests/parcel_fuzzer')
5 files changed, 228 insertions, 48 deletions
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 83db6c9b6d..fbab8f08a9 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -131,6 +131,13 @@ cc_library { "libcutils", "libutils", ], + static_libs: [ + "libbinder_random_parcel", + ], + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi/", + ], local_include_dirs: [ "include_random_parcel_seeds", ], @@ -140,8 +147,12 @@ cc_library { cc_binary_host { name: "binder2corpus", static_libs: [ + "libbinder_random_parcel", "libbinder_random_parcel_seeds", ], + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], srcs: [ "binder2corpus/binder2corpus.cpp", ], @@ -149,5 +160,6 @@ cc_binary_host { "libbase", "libbinder", "libutils", + "libcutils", ], } diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h index 8fc9263b98..7a1688b7b7 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h @@ -16,11 +16,25 @@ #pragma once +#include <binder/Binder.h> #include <binder/IBinder.h> #include <fuzzer/FuzzedDataProvider.h> namespace android { +class RandomBinder : public BBinder { +public: + RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes); + const String16& getInterfaceDescriptor() const override; + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; + +private: + String16 mDescriptor; + // note may not all be used + std::vector<uint8_t> mBytes; + FuzzedDataProvider mProvider; +}; + // Get a random binder object for use in fuzzing. // // May return nullptr. diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp index 8a1fecb212..f41c35bfbe 100644 --- a/libs/binder/tests/parcel_fuzzer/random_binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp @@ -21,56 +21,52 @@ #include <binder/IInterface.h> #include <binder/IServiceManager.h> +size_t kRandomInterfaceLength = 50; namespace android { -class RandomBinder : public BBinder { -public: - RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes) - : mDescriptor(descriptor), - mBytes(std::move(bytes)), - mProvider(mBytes.data(), mBytes.size()) {} - const String16& getInterfaceDescriptor() const override { return mDescriptor; } +RandomBinder::RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes) + : mDescriptor(descriptor), + mBytes(std::move(bytes)), + mProvider(mBytes.data(), mBytes.size()) {} - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { - (void)code; - (void)data; - (void)reply; - (void)flags; // note - for maximum coverage even ignore if oneway +const String16& RandomBinder::getInterfaceDescriptor() const { + return mDescriptor; +} - if (mProvider.ConsumeBool()) { - return mProvider.ConsumeIntegral<status_t>(); - } +status_t RandomBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + (void)code; + (void)data; + (void)reply; + (void)flags; // note - for maximum coverage even ignore if oneway - if (reply == nullptr) return OK; + if (mProvider.ConsumeBool()) { + return mProvider.ConsumeIntegral<status_t>(); + } - // TODO: things we could do to increase state space - // - also pull FDs and binders from 'data' - // (optionally combine these into random parcel 'options') - // - also pull FDs and binders from random parcel 'options' - RandomParcelOptions options; + if (reply == nullptr) return OK; - // random output - std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>( - mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes())); - fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options); + // TODO: things we could do to increase state space + // - also pull FDs and binders from 'data' + // (optionally combine these into random parcel 'options') + // - also pull FDs and binders from random parcel 'options' + RandomParcelOptions options; - return OK; - } + // random output + std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>( + mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes())); + fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options); -private: - String16 mDescriptor; - - // note may not all be used - std::vector<uint8_t> mBytes; - FuzzedDataProvider mProvider; -}; + return OK; +} sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) { auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({ [&]() { // descriptor is the length of a class name, e.g. // "some.package.Foo" - std::string str = provider->ConsumeRandomLengthString(100 /*max length*/); + std::string str = + provider->ConsumeRandomLengthString(kRandomInterfaceLength /*max length*/); // arbitrarily consume remaining data to create a binder that can return // random results - coverage guided fuzzer should ensure all of the remaining diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 4e58dc4899..62b84330cd 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -73,7 +73,7 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti return; } - if (options->extraFds.size() > 0 && provider.ConsumeBool()) { + if (provider.ConsumeBool() && options->extraFds.size() > 0) { const unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange<size_t>(0, options->extraFds.size() - @@ -102,7 +102,7 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti } sp<IBinder> binder; - if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { + if (provider.ConsumeBool() && options->extraBinders.size() > 0) { binder = options->extraBinders.at( provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp index 7b3c80642b..fd9777a916 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -14,16 +14,26 @@ * limitations under the License. */ +#include <linux/android/binder.h> + #include <android-base/logging.h> +#include <binder/Parcel.h> #include <binder/RecordedTransaction.h> #include <fuzzseeds/random_parcel_seeds.h> +#include <stack> +#include <string> #include "../../file.h" using android::binder::borrowed_fd; using android::binder::WriteFully; +using std::stack; + +extern size_t kRandomInterfaceLength; +// Keep this in sync with max_length in random_binder.cpp while creating a RandomBinder +std::string kRandomInterfaceName(kRandomInterfaceLength, 'i'); namespace android { namespace impl { @@ -66,6 +76,162 @@ void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T val) { } // namespace impl +struct ProviderMetadata { + size_t position; + size_t value; + + ProviderMetadata() { + value = 0; + position = 0; + } +}; + +// Assuming current seed path is inside the fillRandomParcel function, start of the loop. +void writeRandomBinder(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, + stack<ProviderMetadata>& remainingPositions) { + // Choose 2 index in array + size_t fillFuncIndex = 2; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + // navigate to getRandomBinder. provide consume bool false + bool flag = false; + impl::writeReversedBuffer(fillParcelBuffer, flag); + + // selecting RandomBinder, other binders in the list are not recorded as KernelObjects + size_t randomBinderIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + randomBinderIndex); + + // write random string of length 100 in actual buffer array. + CHECK(WriteFully(fd, kRandomInterfaceName.c_str(), kRandomInterfaceName.size())) << fd.get(); + + // These will be bytes which are used inside of RandomBinder + // simplest path for these bytes is going to be consume bool -> return random status + vector<uint8_t> randomBinderBuffer; + + bool returnRandomInt = true; + impl::writeReversedBuffer(randomBinderBuffer, returnRandomInt); + + status_t randomStatus = 0; + impl::writeReversedBuffer(randomBinderBuffer, randomStatus); + + // write integral in range to consume bytes for random binder + ProviderMetadata providerData; + providerData.position = fillParcelBuffer.size(); + providerData.value = randomBinderBuffer.size(); + remainingPositions.push(providerData); + + // Write to fd + CHECK(WriteFully(fd, randomBinderBuffer.data(), randomBinderBuffer.size())) << fd.get(); +} + +// Assuming current seed path is inside the fillRandomParcelFunction, start of the loop. +void writeRandomFd(vector<uint8_t>& fillParcelBuffer) { + // path to random fd + size_t fillFuncIndex = 1; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + bool flag = false; + impl::writeReversedBuffer(fillParcelBuffer, flag); + + // go for /dev/null index 1 + size_t fdIndex = 1; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(3), + fdIndex); +} + +void writeParcelData(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, + stack<ProviderMetadata>& remainingPositions, const uint8_t* data, size_t start, + size_t length) { + // need to write parcel data till next offset with instructions to pick random bytes till offset + size_t fillFuncIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + // provide how much bytes to read in control buffer + ProviderMetadata providerData; + providerData.position = fillParcelBuffer.size(); + providerData.value = length; + remainingPositions.push(providerData); + + // provide actual bytes + CHECK(WriteFully(fd, data + start, length)) << fd.get(); +} + +/** + * Generate sequence of copy data, write fd and write binder instructions and required data. + * Data which will be read using consumeBytes is written to fd directly. Data which is read in + * form integer is consumed from rear end FuzzedDataProvider. So insert it in fillParcelBuffer and + * then write to fd + */ +size_t regenerateParcel(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, const Parcel& p, + size_t dataSize, const vector<uint64_t>& objectOffsets) { + stack<ProviderMetadata> remainingPositions; + size_t copiedDataPosition = 0; + const uint8_t* parcelData = p.data(); + size_t numBinders = 0; + size_t numFds = 0; + + for (auto offset : objectOffsets) { + // Check what type of object is present here + const flat_binder_object* flatObject = + reinterpret_cast<const flat_binder_object*>(parcelData + offset); + // Copy till the object offset + writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition, + offset - copiedDataPosition); + copiedDataPosition = offset; + if (flatObject->hdr.type == BINDER_TYPE_BINDER || + flatObject->hdr.type == BINDER_TYPE_HANDLE) { + writeRandomBinder(fd, fillParcelBuffer, remainingPositions); + numBinders++; + // In case of binder, stability is written after the binder object. + // We want to move the copiedDataPosition further to account for this stability field + copiedDataPosition += sizeof(int32_t) + sizeof(flat_binder_object); + } else if (flatObject->hdr.type == BINDER_TYPE_FD) { + writeRandomFd(fillParcelBuffer); + numFds++; + copiedDataPosition += sizeof(flat_binder_object); + } + } + + if (copiedDataPosition < dataSize) { + // copy remaining data from recorded parcel -> last Object to end of the data + writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition, + dataSize - copiedDataPosition); + } + + // We need to write bytes for selecting integer within range of 0 to provide.remaining_bytes() + // is called. + size_t totalWrittenBytes = dataSize - (sizeof(flat_binder_object) * objectOffsets.size()) - + (sizeof(int32_t) * numBinders) + + (kRandomInterfaceName.size() /*Interface String*/ + sizeof(bool) + sizeof(status_t)) * + numBinders; + + // Code in fuzzService relies on provider.remaining_bytes() to select random bytes using + // consume integer. use the calculated remaining_bytes to generate byte buffer which can + // generate required fds and binders in fillRandomParcel function. + while (!remainingPositions.empty()) { + auto meta = remainingPositions.top(); + remainingPositions.pop(); + size_t remainingBytes = totalWrittenBytes + fillParcelBuffer.size() - meta.position; + + vector<uint8_t> remReversedBytes; + impl::writeReversedBuffer(remReversedBytes, static_cast<size_t>(0), remainingBytes, + meta.value); + // Check the order of buffer which is being written + fillParcelBuffer.insert(fillParcelBuffer.end() - meta.position, remReversedBytes.begin(), + remReversedBytes.end()); + } + + return totalWrittenBytes; +} + +/** + * Current corpus format + * |Reserved bytes(8)|parcel data|fillParcelBuffer|integralBuffer| + */ void generateSeedsFromRecording(borrowed_fd fd, const binder::debug::RecordedTransaction& transaction) { // Write Reserved bytes for future use @@ -123,17 +289,9 @@ void generateSeedsFromRecording(borrowed_fd fd, uint8_t writeHeaderInternal = 0; impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal); - // Choose to write data in parcel - size_t fillFuncIndex = 0; - impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), - fillFuncIndex); - - // Write parcel data size from recorded transaction - size_t toWrite = transaction.getDataParcel().dataBufferSize(); - impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), toWrite, toWrite); - - // Write parcel data with size towrite from recorded transaction - CHECK(WriteFully(fd, dataParcel.data(), toWrite)) << fd.get(); + auto objectMetadata = transaction.getObjectOffsets(); + size_t toWrite = regenerateParcel(fd, fillParcelBuffer, dataParcel, dataParcel.dataBufferSize(), + objectMetadata); // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data size_t subDataSize = toWrite + fillParcelBuffer.size(); |