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
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index f181a7b..59643ba 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -52,7 +52,7 @@
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 @@
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 @@
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 @@
*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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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::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 @@
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 @@
}
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 @@
// (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 @@
});
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 @@
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 @@
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 != 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 @@
}
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 @@
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) {