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
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index f5de5b1..419df86 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 <random>
@@ -493,9 +494,15 @@
}
}
+ // objectTable always empty for now. Will be populated from `data` soon.
+ std::vector<uint32_t> objectTable;
+ Span<const uint32_t> 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 @@
.code = code,
.flags = flags,
.asyncNumber = asyncNumber,
+ // bodySize didn't overflow => this cast is safe
+ .parcelDataSize = static_cast<uint32_t>(data.dataSize()),
};
constexpr size_t kWaitMaxUs = 1000000;
@@ -521,6 +530,7 @@
{&command, sizeof(RpcWireHeader)},
{&transaction, sizeof(RpcWireTransaction)},
{const_cast<uint8_t*>(data.data()), data.dataSize()},
+ objectTableSpan.toIovec(),
};
if (status_t status = rpcSend(connection, session, "transaction", iovs, arraysize(iovs),
[&] {
@@ -585,7 +595,9 @@
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 @@
}
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 @@
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<const uint8_t> parcelSpan = {data.data(), data.size()};
+ if (session->getProtocolVersion().value() >=
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
+ Span<const uint8_t> 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 @@
reply.markForRpc(session);
if (replyStatus == OK) {
+ Span<const uint8_t> parcelSpan = {transaction->data,
+ transactionData.size() -
+ offsetof(RpcWireTransaction, data)};
+ if (session->getProtocolVersion().value() >=
+ RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE) {
+ Span<const uint8_t> 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 @@
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<uint32_t> objectTable;
+ Span<const uint32_t> 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 @@
};
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<uint32_t>(reply.dataSize()),
+ .reserved = {0, 0, 0},
};
-
iovec iovs[]{
{&cmdReply, sizeof(RpcWireHeader)},
- {&rpcReply, sizeof(RpcWireReply)},
+ {&rpcReply, rpcReplyWireSize},
{const_cast<uint8_t*>(reply.data()), reply.dataSize()},
+ objectTableSpan.toIovec(),
};
return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt);
}