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

Commit 583a14a8 authored by Steven Moreland's avatar Steven Moreland
Browse files

libbinder: RPC explicit check when out of async IDs

There are far more IDs in a uint64_t than is reasonable to be called on
a binder (commented in the CL, 1000 oneway transactions per second for
585 million years to a single binder). If necessary, we could remove
this limit by rotating the IDs (no more than 2**64 simultaneous
transactions to a single binder). However, there are more critical
problems to handle, so leaving this as-is for now.

Bug: 183140903
Test: binderRpcTest
Change-Id: I5fe33278c965745e31fc0b14a21626a4c680fa70
parent a6e11cf5
Loading
Loading
Loading
Loading
+38 −22
Original line number Diff line number Diff line
@@ -134,6 +134,15 @@ size_t RpcState::countBinders() {

void RpcState::dump() {
    std::lock_guard<std::mutex> _l(mNodeMutex);
    dumpLocked();
}

void RpcState::terminate() {
    std::unique_lock<std::mutex> _l(mNodeMutex);
    terminate(_l);
}

void RpcState::dumpLocked() {
    ALOGE("DUMP OF RpcState %p", this);
    ALOGE("DUMP OF RpcState (%zu nodes)", mNodeForAddress.size());
    for (const auto& [address, node] : mNodeForAddress) {
@@ -161,10 +170,10 @@ void RpcState::dump() {
    ALOGE("END DUMP OF RpcState");
}

void RpcState::terminate() {
void RpcState::terminate(std::unique_lock<std::mutex>& lock) {
    if (SHOULD_LOG_RPC_DETAIL) {
        ALOGE("RpcState::terminate()");
        dump();
        dumpLocked();
    }

    // if the destructor of a binder object makes another RPC call, then calling
@@ -172,8 +181,6 @@ void RpcState::terminate() {
    // mNodeMutex is no longer taken.
    std::vector<sp<IBinder>> tempHoldBinder;

    {
        std::lock_guard<std::mutex> _l(mNodeMutex);
    mTerminated = true;
    for (auto& [address, node] : mNodeForAddress) {
        sp<IBinder> binder = node.binder.promote();
@@ -185,7 +192,9 @@ void RpcState::terminate() {
    }

    mNodeForAddress.clear();
    }

    lock.unlock();
    tempHoldBinder.clear(); // explicit
}

RpcState::CommandData::CommandData(size_t size) : mSize(size) {
@@ -341,14 +350,15 @@ status_t RpcState::transactAddress(const base::unique_fd& fd, const RpcAddress&
    uint64_t asyncNumber = 0;

    if (!address.isZero()) {
        std::lock_guard<std::mutex> _l(mNodeMutex);
        std::unique_lock<std::mutex> _l(mNodeMutex);
        if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races
        auto it = mNodeForAddress.find(address);
        LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending transact on unknown address %s",
                            address.toString().c_str());

        if (flags & IBinder::FLAG_ONEWAY) {
            asyncNumber = it->second.asyncNumber++;
            asyncNumber = it->second.asyncNumber;
            if (!nodeProgressAsyncNumber(&it->second, _l)) return DEAD_OBJECT;
        }
    }

@@ -697,13 +707,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<R
            // last refcount dropped after this transaction happened
            if (it == mNodeForAddress.end()) return OK;

            // note - only updated now, instead of later, so that other threads
            // will queue any later transactions

            // TODO(b/183140903): support > 2**64 async transactions
            //     (we can do this by allowing asyncNumber to wrap, since we
            //     don't expect more than 2**64 simultaneous transactions)
            it->second.asyncNumber++;
            if (!nodeProgressAsyncNumber(&it->second, _l)) return DEAD_OBJECT;

            if (it->second.asyncTodo.size() == 0) return OK;
            if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) {
@@ -822,4 +826,16 @@ sp<IBinder> RpcState::tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& i
    return ref;
}

bool RpcState::nodeProgressAsyncNumber(BinderNode* node, std::unique_lock<std::mutex>& lock) {
    // 2**64 =~ 10**19 =~ 1000 transactions per second for 585 million years to
    // a single binder
    if (node->asyncNumber >= std::numeric_limits<decltype(node->asyncNumber)>::max()) {
        ALOGE("Out of async transaction IDs. Terminating");
        terminate(lock);
        return false;
    }
    node->asyncNumber++;
    return true;
}

} // namespace android
+7 −0
Original line number Diff line number Diff line
@@ -113,6 +113,9 @@ public:
    void terminate();

private:
    void dumpLocked();
    void terminate(std::unique_lock<std::mutex>& lock);

    // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps
    // large allocations to avoid being requested from allocating too much data.
    struct CommandData {
@@ -200,6 +203,10 @@ private:
    // binderNode, this returns that strong reference, so that it can be
    // dropped after any locks are removed.
    sp<IBinder> tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it);
    // true - success
    // false - state terminated, lock gone, halt
    [[nodiscard]] bool nodeProgressAsyncNumber(BinderNode* node,
                                               std::unique_lock<std::mutex>& lock);

    std::mutex mNodeMutex;
    bool mTerminated = false;