diff options
author | 2021-09-10 15:45:34 -0700 | |
---|---|---|
committer | 2021-09-10 16:16:07 -0700 | |
commit | 5623d1a7fac2fca2d8cd51af65a764aadd31bc43 (patch) | |
tree | ebb808081ac497909a180a40c22181a29cba9276 /libs/binder/RpcState.cpp | |
parent | 826367f2240f2e64036b6be9a7cde93fdd79cba2 (diff) |
libbinder: RpcAddress is uint64_t
This was originally a 32 byte array filled by /dev/urandom, because the
expectation was to build off of this in order to allow for transitive
binder sends (process A sends a binder to process B which sends it to
process C and then allowing C to talk directly to process A - this would
need some large cryptographic token representing the binder and address
information in order to securely open up a new communication channel to
A).
However, this is currently not supported in RPC binder (and even if it
were - which I find unlikely b/c of the security complexity, a lot more
work would need to be done). Simply, we don't need to pay this cost
since we're not using it.
Bug: 182940634
Fixes: 182939933
Test: binderRpcBenchmark
------------------------------------------------------------------------------------
Benchmark Time CPU
------------------------------------------------------------------------------------
// before this change
BM_repeatBinder/0 61680 ns 33016 ns
BM_repeatBinder/1 162368 ns 87825 ns
// after this change
BM_repeatBinder/0 60041 ns 32787 ns
BM_repeatBinder/1 109857 ns 55605 ns
-> reduce overhead of rpc binder (when sending many binders) by the
overhead of a normal kernel binder call (still a ways to go)
Change-Id: If27bffb5611bdd17f16156ddfe50ac2449f6046a
Diffstat (limited to 'libs/binder/RpcState.cpp')
-rw-r--r-- | libs/binder/RpcState.cpp | 154 |
1 files changed, 82 insertions, 72 deletions
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index f181a7bbd0..59643ba4ee 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -52,7 +52,7 @@ RpcState::RpcState() {} RpcState::~RpcState() {} status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder, - RpcAddress* outAddress) { + uint64_t* outAddress) { bool isRemote = binder->remoteBinder(); bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder(); @@ -84,12 +84,11 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind for (auto& [addr, node] : mNodeForAddress) { if (binder == node.binder) { if (isRpc) { - const RpcAddress& actualAddr = + // check integrity of data structure + uint64_t actualAddr = binder->remoteBinder()->getPrivateAccessorForId().rpcAddress(); - // TODO(b/182939933): this is only checking integrity of data structure - // a different data structure doesn't need this - LOG_ALWAYS_FATAL_IF(addr < actualAddr, "Address mismatch"); - LOG_ALWAYS_FATAL_IF(actualAddr < addr, "Address mismatch"); + LOG_ALWAYS_FATAL_IF(addr != actualAddr, "Address mismatch %" PRIu64 " vs %" PRIu64, + addr, actualAddr); } node.timesSent++; node.sentRef = binder; // might already be set @@ -101,8 +100,29 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind bool forServer = session->server() != nullptr; - for (size_t tries = 0; tries < 5; tries++) { - auto&& [it, inserted] = mNodeForAddress.insert({RpcAddress::random(forServer), + // arbitrary limit for maximum number of nodes in a process (otherwise we + // might run out of addresses) + if (mNodeForAddress.size() > 100000) { + return NO_MEMORY; + } + + while (true) { + RpcWireAddress address{ + .options = RPC_WIRE_ADDRESS_OPTION_CREATED, + .address = mNextId, + }; + if (forServer) { + address.options |= RPC_WIRE_ADDRESS_OPTION_FOR_SERVER; + } + + // avoid ubsan abort + if (mNextId >= std::numeric_limits<uint32_t>::max()) { + mNextId = 0; + } else { + mNextId++; + } + + auto&& [it, inserted] = mNodeForAddress.insert({RpcWireAddress::toRaw(address), BinderNode{ .binder = binder, .timesSent = 1, @@ -112,18 +132,10 @@ status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBind *outAddress = it->first; return OK; } - - // well, we don't have visibility into the header here, but still - static_assert(sizeof(RpcWireAddress) == 40, "this log needs updating"); - ALOGW("2**256 is 1e77. If you see this log, you probably have some entropy issue, or maybe " - "you witness something incredible!"); } - - ALOGE("Unable to create an address in order to send out %p", binder.get()); - return WOULD_BLOCK; } -status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address, +status_t RpcState::onBinderEntering(const sp<RpcSession>& session, uint64_t address, sp<IBinder>* out) { // ensure that: if we want to use addresses for something else in the future (for // instance, allowing transitive binder sends), that we don't accidentally @@ -133,8 +145,11 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddr // if we communicate with a binder, it could always be proxying // information. However, we want to make sure that isn't done on accident // by a client. - if (!address.isRecognizedType()) { - ALOGE("Address is of an unknown type, rejecting: %s", address.toString().c_str()); + RpcWireAddress addr = RpcWireAddress::fromRaw(address); + constexpr uint32_t kKnownOptions = + RPC_WIRE_ADDRESS_OPTION_CREATED | RPC_WIRE_ADDRESS_OPTION_FOR_SERVER; + if (addr.options & ~kKnownOptions) { + ALOGE("Address is of an unknown type, rejecting: %" PRIu64, address); return BAD_VALUE; } @@ -159,9 +174,9 @@ status_t RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddr // we don't know about this binder, so the other side of the connection // should have created it. - if (address.isForServer() == !!session->server()) { - ALOGE("Server received unrecognized address which we should own the creation of %s.", - address.toString().c_str()); + if ((addr.options & RPC_WIRE_ADDRESS_OPTION_FOR_SERVER) == !!session->server()) { + ALOGE("Server received unrecognized address which we should own the creation of %" PRIu64, + address); return BAD_VALUE; } @@ -241,9 +256,8 @@ void RpcState::dumpLocked() { desc = "(null)"; } - ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a:%s type:%s", - node.binder.unsafe_get(), node.timesSent, node.timesRecd, address.toString().c_str(), - desc); + ALOGE("- BINDER NODE: %p times sent:%zu times recd: %zu a: %" PRIu64 " type: %s", + node.binder.unsafe_get(), node.timesSent, node.timesRecd, address, desc); } ALOGE("END DUMP OF RpcState"); } @@ -360,8 +374,8 @@ sp<IBinder> RpcState::getRootObject(const sp<RpcSession::RpcConnection>& connect data.markForRpc(session); Parcel reply; - status_t status = transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, - data, session, &reply, 0); + status_t status = + transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_ROOT, data, session, &reply, 0); if (status != OK) { ALOGE("Error getting root object: %s", statusToString(status).c_str()); return nullptr; @@ -376,9 +390,8 @@ status_t RpcState::getMaxThreads(const sp<RpcSession::RpcConnection>& connection data.markForRpc(session); Parcel reply; - status_t status = - transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, - data, session, &reply, 0); + status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data, + session, &reply, 0); if (status != OK) { ALOGE("Error getting max threads: %s", statusToString(status).c_str()); return status; @@ -402,9 +415,8 @@ status_t RpcState::getSessionId(const sp<RpcSession::RpcConnection>& connection, data.markForRpc(session); Parcel reply; - status_t status = - transactAddress(connection, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, - data, session, &reply, 0); + status_t status = transactAddress(connection, 0, RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data, + session, &reply, 0); if (status != OK) { ALOGE("Error getting session ID: %s", statusToString(status).c_str()); return status; @@ -426,26 +438,26 @@ status_t RpcState::transact(const sp<RpcSession::RpcConnection>& connection, return BAD_TYPE; } - RpcAddress address = RpcAddress::zero(); + uint64_t address; if (status_t status = onBinderLeaving(session, binder, &address); status != OK) return status; return transactAddress(connection, address, code, data, session, reply, flags); } status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connection, - const RpcAddress& address, uint32_t code, const Parcel& data, + uint64_t address, uint32_t code, const Parcel& data, const sp<RpcSession>& session, Parcel* reply, uint32_t flags) { LOG_ALWAYS_FATAL_IF(!data.isForRpc()); LOG_ALWAYS_FATAL_IF(data.objectsCount() != 0); uint64_t asyncNumber = 0; - if (!address.isZero()) { + if (address != 0) { 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()); + LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), + "Sending transact on unknown address %" PRIu64, address); if (flags & IBinder::FLAG_ONEWAY) { asyncNumber = it->second.asyncNumber; @@ -466,8 +478,9 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .command = RPC_COMMAND_TRANSACT, .bodySize = static_cast<uint32_t>(sizeof(RpcWireTransaction) + data.dataSize()), }; + RpcWireTransaction transaction{ - .address = address.viewRawEmbedded(), + .address = RpcWireAddress::fromRaw(address), .code = code, .flags = flags, .asyncNumber = asyncNumber, @@ -557,15 +570,14 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, } status_t RpcState::sendDecStrong(const sp<RpcSession::RpcConnection>& connection, - const sp<RpcSession>& session, const RpcAddress& addr) { + const sp<RpcSession>& session, uint64_t addr) { { std::lock_guard<std::mutex> _l(mNodeMutex); if (mTerminated) return DEAD_OBJECT; // avoid fatal only, otherwise races auto it = mNodeForAddress.find(addr); - LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), "Sending dec strong on unknown address %s", - addr.toString().c_str()); - LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %s", - addr.toString().c_str()); + LOG_ALWAYS_FATAL_IF(it == mNodeForAddress.end(), + "Sending dec strong on unknown address %" PRIu64, addr); + LOG_ALWAYS_FATAL_IF(it->second.timesRecd <= 0, "Bad dec strong %" PRIu64, addr); it->second.timesRecd--; LOG_ALWAYS_FATAL_IF(nullptr != tryEraseNode(it), @@ -579,8 +591,7 @@ status_t RpcState::sendDecStrong(const sp<RpcSession::RpcConnection>& connection if (status_t status = rpcSend(connection, session, "dec ref header", &cmd, sizeof(cmd)); status != OK) return status; - if (status_t status = rpcSend(connection, session, "dec ref body", &addr.viewRawEmbedded(), - sizeof(RpcWireAddress)); + if (status_t status = rpcSend(connection, session, "dec ref body", &addr, sizeof(addr)); status != OK) return status; return OK; @@ -685,14 +696,12 @@ processTransactInternalTailCall: } RpcWireTransaction* transaction = reinterpret_cast<RpcWireTransaction*>(transactionData.data()); - // TODO(b/182939933): heap allocation just for lookup in mNodeForAddress, - // maybe add an RpcAddress 'view' if the type remains 'heavy' - auto addr = RpcAddress::fromRawEmbedded(&transaction->address); + uint64_t addr = RpcWireAddress::toRaw(transaction->address); bool oneway = transaction->flags & IBinder::FLAG_ONEWAY; status_t replyStatus = OK; sp<IBinder> target; - if (!addr.isZero()) { + if (addr != 0) { if (!targetRef) { replyStatus = onBinderEntering(session, addr, &target); } else { @@ -708,21 +717,21 @@ processTransactInternalTailCall: // (any binder which is being transacted on should be holding a // strong ref count), so in either case, terminating the // session. - ALOGE("While transacting, binder has been deleted at address %s. Terminating!", - addr.toString().c_str()); + ALOGE("While transacting, binder has been deleted at address %" PRIu64 ". Terminating!", + addr); (void)session->shutdownAndWait(false); replyStatus = BAD_VALUE; } else if (target->localBinder() == nullptr) { - ALOGE("Unknown binder address or non-local binder, not address %s. Terminating!", - addr.toString().c_str()); + ALOGE("Unknown binder address or non-local binder, not address %" PRIu64 + ". Terminating!", + addr); (void)session->shutdownAndWait(false); replyStatus = BAD_VALUE; } else if (oneway) { std::unique_lock<std::mutex> _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it->second.binder.promote() != target) { - ALOGE("Binder became invalid during transaction. Bad client? %s", - addr.toString().c_str()); + ALOGE("Binder became invalid during transaction. Bad client? %" PRIu64, addr); replyStatus = BAD_VALUE; } else if (transaction->asyncNumber != it->second.asyncNumber) { // we need to process some other asynchronous transaction @@ -734,8 +743,8 @@ processTransactInternalTailCall: }); size_t numPending = it->second.asyncTodo.size(); - LOG_RPC_DETAIL("Enqueuing %" PRId64 " on %s (%zu pending)", - transaction->asyncNumber, addr.toString().c_str(), numPending); + LOG_RPC_DETAIL("Enqueuing %" PRIu64 " on %" PRIu64 " (%zu pending)", + transaction->asyncNumber, addr, numPending); constexpr size_t kArbitraryOnewayCallTerminateLevel = 10000; constexpr size_t kArbitraryOnewayCallWarnLevel = 1000; @@ -820,8 +829,8 @@ processTransactInternalTailCall: ALOGW("Oneway call failed with error: %d", replyStatus); } - LOG_RPC_DETAIL("Processed async transaction %" PRId64 " on %s", transaction->asyncNumber, - addr.toString().c_str()); + LOG_RPC_DETAIL("Processed async transaction %" PRIu64 " on %" PRIu64, + transaction->asyncNumber, addr); // Check to see if there is another asynchronous transaction to process. // This behavior differs from binder behavior, since in the binder @@ -847,8 +856,8 @@ processTransactInternalTailCall: if (it->second.asyncTodo.size() == 0) return OK; if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) { - LOG_RPC_DETAIL("Found next async transaction %" PRId64 " on %s", - it->second.asyncNumber, addr.toString().c_str()); + LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64, + it->second.asyncNumber, addr); // justification for const_cast (consider avoiding priority_queue): // - AsyncTodo operator< doesn't depend on 'data' or 'ref' objects @@ -904,7 +913,7 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect status != OK) return status; - if (command.bodySize < sizeof(RpcWireAddress)) { + if (command.bodySize != sizeof(RpcWireAddress)) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireAddress. Terminating!", sizeof(RpcWireAddress), command.bodySize); (void)session->shutdownAndWait(false); @@ -912,31 +921,32 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect } RpcWireAddress* address = reinterpret_cast<RpcWireAddress*>(commandData.data()); - // TODO(b/182939933): heap allocation just for lookup - auto addr = RpcAddress::fromRawEmbedded(address); + uint64_t addr = RpcWireAddress::toRaw(*address); + std::unique_lock<std::mutex> _l(mNodeMutex); auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { - ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str()); + ALOGE("Unknown binder address %" PRIu64 " for dec strong.", addr); return OK; } sp<IBinder> target = it->second.binder.promote(); if (target == nullptr) { - ALOGE("While requesting dec strong, binder has been deleted at address %s. Terminating!", - addr.toString().c_str()); + ALOGE("While requesting dec strong, binder has been deleted at address %" PRIu64 + ". Terminating!", + addr); _l.unlock(); (void)session->shutdownAndWait(false); return BAD_VALUE; } if (it->second.timesSent == 0) { - ALOGE("No record of sending binder, but requested decStrong: %s", addr.toString().c_str()); + ALOGE("No record of sending binder, but requested decStrong: %" PRIu64, addr); return OK; } - LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %s", - addr.toString().c_str()); + LOG_ALWAYS_FATAL_IF(it->second.sentRef == nullptr, "Inconsistent state, lost ref for %" PRIu64, + addr); it->second.timesSent--; sp<IBinder> tempHold = tryEraseNode(it); @@ -946,7 +956,7 @@ status_t RpcState::processDecStrong(const sp<RpcSession::RpcConnection>& connect return OK; } -sp<IBinder> RpcState::tryEraseNode(std::map<RpcAddress, BinderNode>::iterator& it) { +sp<IBinder> RpcState::tryEraseNode(std::map<uint64_t, BinderNode>::iterator& it) { sp<IBinder> ref; if (it->second.timesSent == 0) { |