From dc07cf88e61e0f3026f1a8d80ebfa2788f235200 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Thu, 26 May 2022 20:30:12 +0000 Subject: libbinder: Add object offsets to RPC Binder protocol The list of object offsets is always empty in this CL. That will change in follow up CLs when file descriptor support is added. The size of the parcel data is included in the RpcWireTransaction and RpcWireReply headers and then the object offsets are written after the parcel data. There was no space in RpcWireReply, so we must start a new wire protocol version. I've added some reserved space to RpcWireReply to match RpcWireTransaction so that it might be easier to make backwards compatible changes later. binderRpcTest on host went from 36 seconds to 2 minutes 24 seconds because of the added parameterization (x4 the tests => x4 the time). Bug: 185909244 Test: TH Change-Id: I0121a42f8b60362e6a6d0294a350255b5f9f5673 --- libs/binder/RpcState.cpp | 67 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 13 deletions(-) (limited to 'libs/binder/RpcState.cpp') diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index f5de5b1eaf..419df86a4c 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -27,6 +27,7 @@ #include "Debug.h" #include "RpcWireFormat.h" +#include "Utils.h" #include @@ -493,9 +494,15 @@ status_t RpcState::transactAddress(const sp& connecti } } + // objectTable always empty for now. Will be populated from `data` soon. + std::vector objectTable; + Span objectTableSpan = {objectTable.data(), objectTable.size()}; + uint32_t bodySize; LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireTransaction), data.dataSize(), - &bodySize), + &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), "Too much data %zu", data.dataSize()); RpcWireHeader command{ .command = RPC_COMMAND_TRANSACT, @@ -507,6 +514,8 @@ status_t RpcState::transactAddress(const sp& connecti .code = code, .flags = flags, .asyncNumber = asyncNumber, + // bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast(data.dataSize()), }; constexpr size_t kWaitMaxUs = 1000000; @@ -521,6 +530,7 @@ status_t RpcState::transactAddress(const sp& connecti {&command, sizeof(RpcWireHeader)}, {&transaction, sizeof(RpcWireTransaction)}, {const_cast(data.data()), data.dataSize()}, + objectTableSpan.toIovec(), }; if (status_t status = rpcSend(connection, session, "transaction", iovs, arraysize(iovs), [&] { @@ -585,7 +595,9 @@ status_t RpcState::waitForReply(const sp& connection, return status; } - if (command.bodySize < sizeof(RpcWireReply)) { + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); + + if (command.bodySize < rpcReplyWireSize) { ALOGE("Expecting %zu but got %" PRId32 " bytes for RpcWireReply. Terminating!", sizeof(RpcWireReply), command.bodySize); (void)session->shutdownAndWait(false); @@ -593,11 +605,13 @@ status_t RpcState::waitForReply(const sp& connection, } RpcWireReply rpcReply; - CommandData data(command.bodySize - sizeof(RpcWireReply)); + memset(&rpcReply, 0, sizeof(RpcWireReply)); // zero because of potential short read + + CommandData data(command.bodySize - rpcReplyWireSize); if (!data.valid()) return NO_MEMORY; iovec iovs[]{ - {&rpcReply, sizeof(RpcWireReply)}, + {&rpcReply, rpcReplyWireSize}, {data.data(), data.size()}, }; if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs)); @@ -605,11 +619,15 @@ status_t RpcState::waitForReply(const sp& connection, return status; if (rpcReply.status != OK) return rpcReply.status; - uint8_t* parcelData = data.data(); - size_t parcelDataSize = data.size(); - data.release(); - reply->rpcSetDataReference(session, parcelData, parcelDataSize, cleanup_reply_data); + Span parcelSpan = {data.data(), data.size()}; + if (session->getProtocolVersion().value() >= + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + Span objectTableBytes = parcelSpan.splitOff(rpcReply.parcelDataSize); + LOG_ALWAYS_FATAL_IF(objectTableBytes.size > 0, "Non-empty object table not supported yet."); + } + data.release(); + reply->rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, cleanup_reply_data); return OK; } @@ -824,12 +842,22 @@ processTransactInternalTailCall: reply.markForRpc(session); if (replyStatus == OK) { + Span parcelSpan = {transaction->data, + transactionData.size() - + offsetof(RpcWireTransaction, data)}; + if (session->getProtocolVersion().value() >= + RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) { + Span objectTableBytes = parcelSpan.splitOff(transaction->parcelDataSize); + LOG_ALWAYS_FATAL_IF(objectTableBytes.size > 0, + "Non-empty object table not supported yet."); + } + Parcel data; // transaction->data is owned by this function. Parcel borrows this data and // only holds onto it for the duration of this function call. Parcel will be // deleted before the 'transactionData' object. - data.rpcSetDataReference(session, transaction->data, - transactionData.size() - offsetof(RpcWireTransaction, data), + + data.rpcSetDataReference(session, parcelSpan.data, parcelSpan.size, do_nothing_to_transact_data); if (target) { @@ -941,8 +969,16 @@ processTransactInternalTailCall: replyStatus = flushExcessBinderRefs(session, addr, target); } + const size_t rpcReplyWireSize = RpcWireReply::wireSize(session->getProtocolVersion().value()); + + // objectTable always empty for now. Will be populated from `reply` soon. + std::vector objectTable; + Span objectTableSpan = {objectTable.data(), objectTable.size()}; + uint32_t bodySize; - LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(sizeof(RpcWireReply), reply.dataSize(), &bodySize), + LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(rpcReplyWireSize, reply.dataSize(), &bodySize) || + __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, + &bodySize), "Too much data for reply %zu", reply.dataSize()); RpcWireHeader cmdReply{ .command = RPC_COMMAND_REPLY, @@ -950,12 +986,17 @@ processTransactInternalTailCall: }; RpcWireReply rpcReply{ .status = replyStatus, + // NOTE: Not necessarily written to socket depending on session + // version. + // NOTE: bodySize didn't overflow => this cast is safe + .parcelDataSize = static_cast(reply.dataSize()), + .reserved = {0, 0, 0}, }; - iovec iovs[]{ {&cmdReply, sizeof(RpcWireHeader)}, - {&rpcReply, sizeof(RpcWireReply)}, + {&rpcReply, rpcReplyWireSize}, {const_cast(reply.data()), reply.dataSize()}, + objectTableSpan.toIovec(), }; return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt); } -- cgit v1.2.3-59-g8ed1b