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

Commit 06e3c3fa authored by Steven Moreland's avatar Steven Moreland Committed by Android (Google) Code Review
Browse files

Merge changes Ib19a0cbd,I01b2312c into main

* changes:
  binder_parcel_fuzzer: support owned Parcels
  libbinder: remove overeager fdsan crash
parents f0e54183 b09a1d23
Loading
Loading
Loading
Loading
+75 −14
Original line number Diff line number Diff line
@@ -156,7 +156,7 @@ enum {

#ifdef BINDER_WITH_KERNEL_IPC
static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
                           const void* who) {
                           const void* who, bool tagFds) {
    switch (obj.hdr.type) {
        case BINDER_TYPE_BINDER:
            if (obj.binder) {
@@ -173,7 +173,7 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec
            return;
        }
        case BINDER_TYPE_FD: {
            if (obj.cookie != 0) { // owned
            if (tagFds && obj.cookie != 0) { // owned
                FdTag(obj.handle, nullptr, who);
            }
            return;
@@ -611,7 +611,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) {
                    }
                }

                acquire_object(proc, *flat, this);
                acquire_object(proc, *flat, this, true /*tagFds*/);
            }
        }
#else
@@ -1797,7 +1797,7 @@ restart_write:
        // Need to write meta-data?
        if (nullMetaData || val.binder != 0) {
            kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos;
            acquire_object(ProcessState::self(), val, this);
            acquire_object(ProcessState::self(), val, this, true /*tagFds*/);
            kernelFields->mObjectsSize++;
        }

@@ -2728,6 +2728,65 @@ size_t Parcel::ipcObjectsCount() const
    return 0;
}

static void do_nothing_release_func(const uint8_t* data, size_t dataSize,
                                    const binder_size_t* objects, size_t objectsCount) {
    (void)data;
    (void)dataSize;
    (void)objects;
    (void)objectsCount;
}
static void delete_data_release_func(const uint8_t* data, size_t dataSize,
                                     const binder_size_t* objects, size_t objectsCount) {
    delete[] data;
    (void)dataSize;
    (void)objects;
    (void)objectsCount;
}

void Parcel::makeDangerousViewOf(Parcel* p) {
    if (p->isForRpc()) {
        // warning: this must match the logic in rpcSetDataReference
        auto* rf = p->maybeRpcFields();
        LOG_ALWAYS_FATAL_IF(rf == nullptr);
        std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds;
        if (rf->mFds) {
            fds.reserve(rf->mFds->size());
            for (const auto& fd : *rf->mFds) {
                fds.push_back(binder::borrowed_fd(toRawFd(fd)));
            }
        }
        status_t result =
                rpcSetDataReference(rf->mSession, p->mData, p->mDataSize,
                                    rf->mObjectPositions.data(), rf->mObjectPositions.size(),
                                    std::move(fds), do_nothing_release_func);
        LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str());
    } else {
#ifdef BINDER_WITH_KERNEL_IPC
        // warning: this must match the logic in ipcSetDataReference
        auto* kf = p->maybeKernelFields();
        LOG_ALWAYS_FATAL_IF(kf == nullptr);

        // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored
        // to move this ownership out of Parcel and into release_func. However, today, Parcel
        // always assums it can own and close FDs today. So, for purposes of testing consistency,
        // , create new FDs it can own.

        uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func
        memcpy(newData, p->mData, p->mDataSize);
        for (size_t i = 0; i < kf->mObjectsSize; i++) {
            flat_binder_object* flat =
                    reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]);
            if (flat->hdr.type == BINDER_TYPE_FD) {
                flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0);
            }
        }

        ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize,
                            delete_data_release_func);
#endif // BINDER_WITH_KERNEL_IPC
    }
}

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
                                 size_t objectsCount, release_func relFunc) {
    // this code uses 'mOwner == nullptr' to understand whether it owns memory
@@ -2738,6 +2797,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin
    auto* kernelFields = maybeKernelFields();
    LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData.

    // must match makeDangerousViewOf
    mData = const_cast<uint8_t*>(data);
    mDataSize = mDataCapacity = dataSize;
    kernelFields->mObjects = const_cast<binder_size_t*>(objects);
@@ -2816,6 +2876,7 @@ status_t Parcel::rpcSetDataReference(
    auto* rpcFields = maybeRpcFields();
    LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc.

    // must match makeDangerousViewOf
    mData = const_cast<uint8_t*>(data);
    mDataSize = mDataCapacity = dataSize;
    mOwner = relFunc;
@@ -2883,15 +2944,17 @@ void Parcel::releaseObjects()
#endif // BINDER_WITH_KERNEL_IPC
}

void Parcel::acquireObjects()
{
void Parcel::reacquireObjects(size_t objectsSize) {
    auto* kernelFields = maybeKernelFields();
    if (kernelFields == nullptr) {
        return;
    }

#ifdef BINDER_WITH_KERNEL_IPC
    size_t i = kernelFields->mObjectsSize;
    LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize,
                        "Object size %zu out of range of %zu", objectsSize,
                        kernelFields->mObjectsSize);
    size_t i = objectsSize;
    if (i == 0) {
        return;
    }
@@ -2901,8 +2964,10 @@ void Parcel::acquireObjects()
    while (i > 0) {
        i--;
        const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
        acquire_object(proc, *flat, this);
        acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged
    }
#else
    (void) objectsSize;
#endif // BINDER_WITH_KERNEL_IPC
}

@@ -3119,12 +3184,8 @@ status_t Parcel::continueWrite(size_t desired)
                return NO_MEMORY;
            }

            // Little hack to only acquire references on objects
            // we will be keeping.
            size_t oldObjectsSize = kernelFields->mObjectsSize;
            kernelFields->mObjectsSize = objectsSize;
            acquireObjects();
            kernelFields->mObjectsSize = oldObjectsSize;
            // only acquire references on objects we are keeping
            reacquireObjects(objectsSize);
        }
        if (rpcFields) {
            if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
+7 −2
Original line number Diff line number Diff line
@@ -649,6 +649,11 @@ public:

    LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;

    // This API is to quickly become a view of another Parcel, so that we can also
    // test 'owner' paths quickly. It's extremely dangerous to use this API in
    // practice, and you should never ever do it.
    LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p);

private:
    // Close all file descriptors in the parcel at object positions >= newObjectsSize.
    void closeFileDescriptors(size_t newObjectsSize);
@@ -664,7 +669,7 @@ private:
    void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
                             size_t objectsCount, release_func relFunc);
    // Takes ownership even when an error is returned.
    status_t rpcSetDataReference(
    [[nodiscard]] status_t rpcSetDataReference(
            const sp<RpcSession>& session, const uint8_t* data, size_t dataSize,
            const uint32_t* objectTable, size_t objectTableSize,
            std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds,
@@ -672,7 +677,7 @@ private:

    status_t            finishWrite(size_t len);
    void                releaseObjects();
    void                acquireObjects();
    void reacquireObjects(size_t objectSize);
    status_t            growData(size_t len);
    // Clear the Parcel and set the capacity to `desired`.
    // Doesn't reset the RPC session association.
+3 −0
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ struct RandomParcelOptions {
    std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader;
    std::vector<sp<IBinder>> extraBinders;
    std::vector<binder::unique_fd> extraFds;

    // internal state owned by fillRandomParcel, for Parcel views
    std::vector<std::unique_ptr<Parcel>> extraParcels;
};

/**
+8 −3
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider
    uint32_t code = provider.ConsumeIntegral<uint32_t>();
    uint32_t flag = provider.ConsumeIntegral<uint32_t>();

    FUZZ_LOG() << "backend: " << backend;
    FUZZ_LOG() << "doTransactFuzz backend: " << backend;

    RandomParcelOptions options;

@@ -101,7 +101,7 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
    // since we are only using a byte to index
    CHECK_LE(reads.size(), 255u) << reads.size();

    FUZZ_LOG() << "backend: " << backend;
    FUZZ_LOG() << "doReadFuzz backend: " << backend;
    FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
    FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size());

@@ -122,10 +122,15 @@ void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& read
    RandomParcelOptions options;
    P p;

    // small amount of initial Parcel data, since fillRandomParcel uses makeDangerousViewOf
    std::vector<uint8_t> parcelData =
            provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 20));
    fillRandomParcel(&p, FuzzedDataProvider(parcelData.data(), parcelData.size()), &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() << "doReadWriteFuzz backend: " << backend;

    while (provider.remaining_bytes() > 0) {
        uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
+22 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <fuzzbinder/random_parcel.h>

#include <android-base/logging.h>
#include <binder/Functional.h>
#include <binder/RpcSession.h>
#include <binder/RpcTransportRaw.h>
#include <fuzzbinder/random_binder.h>
@@ -32,10 +33,29 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) {
    CHECK(OK == p->write(data.data(), data.size()));
}

void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) {
void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider,
                      RandomParcelOptions* options) {
    CHECK_NE(options, nullptr);

    if (provider.ConsumeBool()) {
    const uint8_t fuzzerParcelOptions = provider.ConsumeIntegral<uint8_t>();
    const bool resultShouldBeView = fuzzerParcelOptions & 1;
    const bool resultShouldBeRpc = fuzzerParcelOptions & 2;

    Parcel* p;
    if (resultShouldBeView) {
        options->extraParcels.push_back(std::make_unique<Parcel>());
        // held for duration of test, so that view will be valid
        p = options->extraParcels[options->extraParcels.size() - 1].get();
    } else {
        p = outputParcel; // directly fill out the output Parcel
    }
    auto viewify_guard = binder::impl::make_scope_guard([&]() {
        if (resultShouldBeView) {
            outputParcel->makeDangerousViewOf(p);
        }
    });

    if (resultShouldBeRpc) {
        auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make());
        CHECK_EQ(OK, session->addNullDebuggingClient());
        // Set the protocol version so that we don't crash if the session
Loading