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

Commit e8393349 authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder: RPC avoid abort on too big allocations

We may want some more heuristic checks here, since allocations that are
just barely too big may interrupt the operations of other threads that
can't handle failing allocations, but this is a start so that too-big
allocations won't cause an abort.

Bug: 182938024
Test: binderRpcTest + running WIP fuzzer
Change-Id: Id9f1b63962cd22b7c799b14e595c47ea8628354f
parent 2ff0d47c
Loading
Loading
Loading
Loading
+26 −9
Original line number Diff line number Diff line
@@ -326,7 +326,11 @@ status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address
            .asyncNumber = asyncNumber,
    };

    std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize());
    ByteVec transactionData(sizeof(RpcWireTransaction) + data.dataSize());
    if (!transactionData.valid()) {
        return NO_MEMORY;
    }

    memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction));
    memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize());

@@ -379,9 +383,12 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>&
        if (status != OK) return status;
    }

    uint8_t* data = new uint8_t[command.bodySize];
    ByteVec data(command.bodySize);
    if (!data.valid()) {
        return NO_MEMORY;
    }

    if (!rpcRec(fd, "reply body", data, command.bodySize)) {
    if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) {
        return DEAD_OBJECT;
    }

@@ -391,9 +398,10 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>&
        terminate();
        return BAD_VALUE;
    }
    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data);
    RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data());
    if (rpcReply->status != OK) return rpcReply->status;

    data.release();
    reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data),
                               nullptr, 0, cleanup_reply_data);

@@ -461,7 +469,10 @@ status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSessio
                                   const RpcWireHeader& command) {
    LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command);

    std::vector<uint8_t> transactionData(command.bodySize);
    ByteVec transactionData(command.bodySize);
    if (!transactionData.valid()) {
        return NO_MEMORY;
    }
    if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) {
        return DEAD_OBJECT;
    }
@@ -479,7 +490,7 @@ static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t d
}

status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session,
                                           std::vector<uint8_t>&& transactionData) {
                                           ByteVec transactionData) {
    if (transactionData.size() < sizeof(RpcWireTransaction)) {
        ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!",
              sizeof(RpcWireTransaction), transactionData.size());
@@ -630,7 +641,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<R
                // justification for const_cast (consider avoiding priority_queue):
                // - AsyncTodo operator< doesn't depend on 'data' object
                // - gotta go fast
                std::vector<uint8_t> data = std::move(
                ByteVec data = std::move(
                        const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data);
                it->second.asyncTodo.pop();
                _l.unlock();
@@ -644,7 +655,10 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<R
            .status = replyStatus,
    };

    std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize());
    ByteVec replyData(sizeof(RpcWireReply) + reply.dataSize());
    if (!replyData.valid()) {
        return NO_MEMORY;
    }
    memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply));
    memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize());

@@ -671,7 +685,10 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<R
status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) {
    LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command);

    std::vector<uint8_t> commandData(command.bodySize);
    ByteVec commandData(command.bodySize);
    if (!commandData.valid()) {
        return NO_MEMORY;
    }
    if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) {
        return DEAD_OBJECT;
    }
+17 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <binder/RpcSession.h>

#include <map>
#include <optional>
#include <queue>

namespace android {
@@ -100,6 +101,20 @@ private:
     */
    void terminate();

    // alternative to std::vector<uint8_t> that doesn't abort on too big of allocations
    struct ByteVec {
        explicit ByteVec(size_t size)
              : mData(size > 0 ? new (std::nothrow) uint8_t[size] : nullptr), mSize(size) {}
        bool valid() { return mSize == 0 || mData != nullptr; }
        size_t size() { return mSize; }
        uint8_t* data() { return mData.get(); }
        uint8_t* release() { return mData.release(); }

    private:
        std::unique_ptr<uint8_t[]> mData;
        size_t mSize;
    };

    [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data,
                               size_t size);
    [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size);
@@ -113,7 +128,7 @@ private:
                                           const RpcWireHeader& command);
    [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd,
                                                   const sp<RpcSession>& session,
                                                   std::vector<uint8_t>&& transactionData);
                                                   ByteVec transactionData);
    [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd,
                                            const RpcWireHeader& command);

@@ -148,7 +163,7 @@ private:

        // async transaction queue, _only_ for local binder
        struct AsyncTodo {
            std::vector<uint8_t> data; // most convenient format, to move it here
            ByteVec data;
            uint64_t asyncNumber = 0;

            bool operator<(const AsyncTodo& o) const {