Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72963bc3 authored by Steven Moreland's avatar Steven Moreland
Browse files

binder_parcel_fuzzer: support write functions

This generalizes the append parcel fuzzing functionality,
for additional hardening.

Fixes: 328161314
Test: manual, run binder_parcel_fuzzer for 3min
Ignore-AOSP-First: internal only for security hardening

Change-Id: I1f470e0d56241327bff7422d061912b592cc0ddc
parent 391920f5
Loading
Loading
Loading
Loading
+120 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
#include <fuzzbinder/random_binder.h>
#include <fuzzbinder/random_fd.h>
#include <utils/Flattenable.h>

#include "../../Utils.h"
@@ -404,5 +406,123 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
        FUZZ_LOG() << " toString() result: " << toString;
    },
};

std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS {
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setDataSize";
        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
        p.setDataSize(len);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setDataCapacity";
        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
        p.setDataCapacity(len);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setData";
        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
        p.setData(bytes.data(), bytes.size());
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
        FUZZ_LOG() << "about to call appendFrom";

        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 4096));
        ::android::Parcel p2;
        fillRandomParcel(&p2, FuzzedDataProvider(bytes.data(), bytes.size()), options);

        int32_t start = provider.ConsumeIntegral<int32_t>();
        int32_t len = provider.ConsumeIntegral<int32_t>();
        p.appendFrom(&p2, start, len);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setData";
        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
        p.setData(bytes.data(), bytes.size());
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call pushAllowFds";
        bool val = provider.ConsumeBool();
        p.pushAllowFds(val);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call restoreAllowFds";
        bool val = provider.ConsumeBool();
        p.restoreAllowFds(val);
    },
    // markForBinder - covered by fillRandomParcel, aborts if called multiple times
    // markForRpc - covered by fillRandomParcel, aborts if called multiple times
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call writeInterfaceToken";
        std::string interface = provider.ConsumeRandomLengthString();
        p.writeInterfaceToken(android::String16(interface.c_str()));
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setEnforceNoDataAvail";
        p.setEnforceNoDataAvail(provider.ConsumeBool());
    },
    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call setServiceFuzzing";
        p.setServiceFuzzing();
    },
    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call freeData";
        p.freeData();
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call write";
        size_t len = provider.ConsumeIntegralInRange<size_t>(0, 256);
        std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
        p.write(bytes.data(), bytes.size());
    },
    // write* - write functions all implemented by calling 'write' itself.
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
        FUZZ_LOG() << "about to call writeStrongBinder";

        // TODO: this logic is somewhat duplicated with random parcel
       android::sp<android::IBinder> binder;
       if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
            binder = options->extraBinders.at(
                    provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
        } else {
            binder = android::getRandomBinder(&provider);
            options->extraBinders.push_back(binder);
        }

        p.writeStrongBinder(binder);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call writeFileDescriptor (no ownership)";
        p.writeFileDescriptor(STDERR_FILENO, false /* takeOwnership */);
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
        FUZZ_LOG() << "about to call writeFileDescriptor (take ownership)";
        std::vector<unique_fd> fds = android::getRandomFds(&provider);
        if (fds.size() == 0) return;

        p.writeDupFileDescriptor(fds.at(0).get());
        options->extraFds.insert(options->extraFds.end(),
             std::make_move_iterator(fds.begin() + 1),
             std::make_move_iterator(fds.end()));
    },
    // TODO: writeBlob
    // TODO: writeDupImmutableBlobFileDescriptor
    // TODO: writeObject (or make the API private more likely)
    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call writeNoException";
        p.writeNoException();
    },
    [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call closeFileDescriptors";
        p.closeFileDescriptors();
    },
    [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
        FUZZ_LOG() << "about to call replaceCallingWorkSourceUid";
        uid_t uid = provider.ConsumeIntegral<uid_t>();
        p.replaceCallingWorkSourceUid(uid);
    },
};

// clang-format on
#pragma clang diagnostic pop
+1 −0
Original line number Diff line number Diff line
@@ -21,3 +21,4 @@
#include "parcel_fuzzer.h"

extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
extern std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS;
+46 −8
Original line number Diff line number Diff line
@@ -20,8 +20,11 @@
#include "aidl/parcelables/GenericDataParcelable.h"
#include "aidl/parcelables/SingleDataParcelable.h"

#include <android/binder_libbinder.h>
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
#include <fuzzbinder/random_binder.h>
#include <fuzzbinder/random_fd.h>

#include "util.h"

@@ -210,16 +213,51 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
            binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
            FUZZ_LOG() << "status: " << status;
        },
        [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
            FUZZ_LOG() << "about to unmarshal AParcel";
};
std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS{
        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
            FUZZ_LOG() << "about to call AParcel_writeStrongBinder";

            // TODO: this logic is somewhat duplicated with random parcel
            android::sp<android::IBinder> binder;
            if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
                binder = options->extraBinders.at(
                        provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
            } else {
                binder = android::getRandomBinder(&provider);
                options->extraBinders.push_back(binder);
            }

            ndk::SpAIBinder abinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(binder));
            AParcel_writeStrongBinder(p.aParcel(), abinder.get());
        },
        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
            FUZZ_LOG() << "about to call AParcel_writeParcelFileDescriptor";

            auto fds = android::getRandomFds(&provider);
            if (fds.size() == 0) return;

            AParcel_writeParcelFileDescriptor(p.aParcel(), fds.at(0).get());
            options->extraFds.insert(options->extraFds.end(),
                 std::make_move_iterator(fds.begin() + 1),
                 std::make_move_iterator(fds.end()));
        },
        // all possible data writes can be done as a series of 4-byte reads
        [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
            FUZZ_LOG() << "about to call AParcel_writeInt32";
            int32_t val = provider.ConsumeIntegral<int32_t>();
            AParcel_writeInt32(p.aParcel(), val);
        },
        [] (NdkParcelAdapter& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
            FUZZ_LOG() << "about to call AParcel_reset";
            AParcel_reset(p.aParcel());
        },
        [](NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
            FUZZ_LOG() << "about to call AParcel_unmarshal";
            size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
            std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
            const uint8_t* buffer = parcelData.data();
            const size_t bufferLen = parcelData.size();
            NdkParcelAdapter adapter;
            binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
            std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(len);
            binder_status_t status = AParcel_unmarshal(p.aParcel(), data.data(), data.size());
            FUZZ_LOG() << "status: " << status;
        },

};
// clang-format on
+1 −0
Original line number Diff line number Diff line
@@ -50,3 +50,4 @@ private:
};

extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
extern std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS;
+28 −18
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider
    (void)binder->transact(code, data, &reply, flag);
}

// start with a Parcel full of data (e.g. like you get from another process)
template <typename P>
void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
                FuzzedDataProvider&& provider) {
@@ -98,7 +99,7 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
    fillRandomParcel(&p, std::move(provider), &options);

    // since we are only using a byte to index
    CHECK(reads.size() <= 255) << reads.size();
    CHECK_LE(reads.size(), 255u) << reads.size();

    FUZZ_LOG() << "backend: " << backend;
    FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
@@ -115,26 +116,31 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
    }
}

// Append two random parcels.
template <typename P>
void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) {
    int32_t start = provider.ConsumeIntegral<int32_t>();
    int32_t len = provider.ConsumeIntegral<int32_t>();

    std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));

    // same options so that FDs and binders could be shared in both Parcels
void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
                     const std::vector<ParcelWrite<P>>& writes, FuzzedDataProvider&& provider) {
    RandomParcelOptions options;

    P p0, p1;
    fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
    fillRandomParcel(&p1, std::move(provider), &options);
    P p;
    fillRandomParcel(&p, std::move(provider), &options);

    // since we are only using a byte to index
    CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();

    FUZZ_LOG() << "backend: " << backend;
    FUZZ_LOG() << "start: " << start << " len: " << len;

    p0.appendFrom(&p1, start, len);
    while (provider.remaining_bytes() > 0) {
        uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);

        FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
                   << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();

        if (idx < reads.size()) {
            reads.at(idx)(p, provider);
        } else {
            writes.at(idx - reads.size())(p, provider, &options);
        }
    }
}

void* NothingClass_onCreate(void* args) {
@@ -187,10 +193,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
                                             std::move(provider));
            },
            [](FuzzedDataProvider&& provider) {
                doAppendFuzz<::android::Parcel>("binder", std::move(provider));
                doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
                                                   BINDER_PARCEL_WRITE_FUNCTIONS,
                                                   std::move(provider));
            },
            [](FuzzedDataProvider&& provider) {
                doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
                doReadWriteFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
                                                  BINDER_NDK_PARCEL_WRITE_FUNCTIONS,
                                                  std::move(provider));
            },
    };

Loading