Loading libs/binder/RpcState.cpp +54 −13 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "Debug.h" #include "RpcWireFormat.h" #include "Utils.h" #include <random> Loading Loading @@ -493,8 +494,14 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti } } // 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) || __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, &bodySize), "Too much data %zu", data.dataSize()); RpcWireHeader command{ Loading @@ -507,6 +514,8 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .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; Loading @@ -521,6 +530,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti {&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), [&] { Loading Loading @@ -585,7 +595,9 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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); Loading @@ -593,11 +605,13 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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)); Loading @@ -605,11 +619,15 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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<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; } Loading Loading @@ -824,12 +842,22 @@ processTransactInternalTailCall: 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) { Loading Loading @@ -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<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, Loading @@ -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<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); } Loading libs/binder/RpcWireFormat.h +20 −2 Original line number Diff line number Diff line Loading @@ -131,7 +131,10 @@ struct RpcWireTransaction { uint64_t asyncNumber; uint32_t reserved[4]; // The size of the Parcel data directly following RpcWireTransaction. uint32_t parcelDataSize; uint32_t reserved[3]; uint8_t data[]; }; Loading @@ -139,8 +142,23 @@ static_assert(sizeof(RpcWireTransaction) == 40); struct RpcWireReply { int32_t status; // transact return // -- Fields below only transmitted starting at protocol version 1 -- // The size of the Parcel data directly following RpcWireReply. uint32_t parcelDataSize; uint32_t reserved[3]; // Byte size of RpcWireReply in the wire protocol. static size_t wireSize(uint32_t protocolVersion) { if (protocolVersion == 0) { return sizeof(int32_t); } return sizeof(RpcWireReply); } }; static_assert(sizeof(RpcWireReply) == 4); static_assert(sizeof(RpcWireReply) == 20); #pragma clang diagnostic pop Loading libs/binder/Utils.h +24 −1 Original line number Diff line number Diff line Loading @@ -14,8 +14,9 @@ * limitations under the License. */ #include <cstdint> #include <stddef.h> #include <cstdint> #include <optional> #include <android-base/result.h> #include <android-base/unique_fd.h> Loading @@ -39,4 +40,26 @@ android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); // View of contiguous sequence. Similar to std::span. template <typename T> struct Span { T* data = nullptr; size_t size = 0; size_t byteSize() { return size * sizeof(T); } iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; } // Truncates `this` to a length of `offset` and returns a span with the // remainder. // // Aborts if offset > size. Span<T> splitOff(size_t offset) { LOG_ALWAYS_FATAL_IF(offset > size); Span<T> rest = {data + offset, size - offset}; size = offset; return rest; } }; } // namespace android libs/binder/include/binder/RpcSession.h +7 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,13 @@ class FdTrigger; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL; // Starting with this version: // // * RpcWireReply is larger (4 bytes -> 20). // * RpcWireTransaction and RpcWireReplyV1 include the parcel data size. constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1; /** * This represents a session (group of connections) between a client Loading libs/binder/tests/binderRpcTest.cpp +84 −42 Original line number Diff line number Diff line Loading @@ -99,17 +99,19 @@ TEST(BinderRpcParcel, EntireParcelFormatted) { EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); } class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { public: static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { return newFactory(info.param)->toCString(); return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + std::to_string(std::get<1>(info.param)); } }; TEST_P(BinderRpcSimple, SetExternalServerTest) { TEST_P(BinderRpcServerOnly, SetExternalServerTest) { base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); auto server = RpcServer::make(newFactory(GetParam())); auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); Loading Loading @@ -480,7 +482,8 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t>> { public: struct Options { size_t numThreads = 1; Loading @@ -490,8 +493,9 @@ public: }; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [type, security] = info.param; return PrintToString(type) + "_" + newFactory(security)->toCString(); auto [type, security, clientVersion, serverVersion] = info.param; return PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); } static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { Loading Loading @@ -533,16 +537,19 @@ public: SocketType socketType = std::get<0>(GetParam()); RpcSecurity rpcSecurity = std::get<1>(GetParam()); uint32_t clientVersion = std::get<2>(GetParam()); uint32_t serverVersion = std::get<3>(GetParam()); unsigned int vsockPort = allocateVsockPort(); std::string addr = allocateSocketAddress(); auto ret = ProcessSession{ .host = Process([&](android::base::borrowed_fd writeEnd, .host = Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); server->setProtocolVersion(serverVersion); server->setMaxThreads(options.numThreads); unsigned int outPort = 0; Loading Loading @@ -618,6 +625,7 @@ public: status_t status; for (const auto& session : sessions) { CHECK(session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(options.numIncomingConnections); session->setMaxOutgoingThreads(options.numOutgoingConnections); Loading Loading @@ -1420,9 +1428,20 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } static std::vector<uint32_t> testVersions() { std::vector<uint32_t> versions; for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) { versions.push_back(i); } versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); return versions; } INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::Combine(::testing::ValuesIn(testSocketTypes()), ::testing::ValuesIn(RpcSecurityValues())), ::testing::ValuesIn(RpcSecurityValues()), ::testing::ValuesIn(testVersions()), ::testing::ValuesIn(testVersions())), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject Loading Loading @@ -1476,9 +1495,10 @@ private: bool mValue = false; }; TEST_P(BinderRpcSimple, Shutdown) { TEST_P(BinderRpcServerOnly, Shutdown) { auto addr = allocateSocketAddress(); auto server = RpcServer::make(newFactory(GetParam())); auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared<OneOffSignal>(); Loading Loading @@ -1548,12 +1568,17 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), BinderRpcSimple::PrintTestParam); INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), ::testing::ValuesIn(testVersions())), BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; // Only parameterized only server version because `RpcSession` is bypassed // in the client half of the tests. using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. Loading @@ -1565,8 +1590,9 @@ public: [[nodiscard]] AssertionResult setUp( const Param& param, std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { auto [socketType, rpcSecurity, certificateFormat] = param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); rpcServer->setProtocolVersion(serverVersion); switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; Loading Loading @@ -1696,7 +1722,8 @@ public: explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} Client(Client&&) = default; [[nodiscard]] AssertionResult setUp(const Param& param) { auto [socketType, rpcSecurity, certificateFormat] = param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; (void)serverVersion; mFdTrigger = FdTrigger::make(); mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; Loading Loading @@ -1767,31 +1794,37 @@ public: using Server = RpcTransportTestUtils::Server; using Client = RpcTransportTestUtils::Client; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [socketType, rpcSecurity, certificateFormat] = info.param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param; auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString(); if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat); ret += "_serverV" + std::to_string(serverVersion); return ret; } static std::vector<ParamType> getRpcTranportTestParams() { std::vector<ParamType> ret; for (auto serverVersion : testVersions()) { for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { for (auto rpcSecurity : RpcSecurityValues()) { switch (rpcSecurity) { case RpcSecurity::RAW: { ret.emplace_back(socketType, rpcSecurity, std::nullopt); ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion); } break; case RpcSecurity::TLS: { ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM, serverVersion); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER, serverVersion); } break; } } } } return ret; } template <typename A, typename B> status_t trust(const A& a, const B& b) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b); } }; Loading Loading @@ -1827,7 +1860,8 @@ TEST_P(RpcTransportTest, MultipleClients) { } TEST_P(RpcTransportTest, UntrustedServer) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto untrustedServer = std::make_unique<Server>(); ASSERT_TRUE(untrustedServer->setUp(GetParam())); Loading @@ -1845,7 +1879,9 @@ TEST_P(RpcTransportTest, UntrustedServer) { client.run(handshakeOk); } TEST_P(RpcTransportTest, MaliciousServer) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto validServer = std::make_unique<Server>(); ASSERT_TRUE(validServer->setUp(GetParam())); Loading @@ -1868,7 +1904,9 @@ TEST_P(RpcTransportTest, MaliciousServer) { } TEST_P(RpcTransportTest, UntrustedClient) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); Loading @@ -1887,7 +1925,9 @@ TEST_P(RpcTransportTest, UntrustedClient) { } TEST_P(RpcTransportTest, MaliciousClient) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); Loading Loading @@ -1973,23 +2013,24 @@ INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { : public testing::TestWithParam< std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { auto [socketType, certificateFormat, keyFormat] = GetParam(); auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [socketType, certificateFormat, keyFormat] = info.param; auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + "_key_" + PrintToString(keyFormat); return ret; auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto [socketType, certificateFormat, keyFormat] = GetParam(); auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { Loading @@ -2004,8 +2045,8 @@ TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat); auto desCert = deserializeCertificate(certData, certificateFormat); auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert)); auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat)); auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat), serverVersion); auto server = std::make_unique<RpcTransportTestUtils::Server>(); ASSERT_TRUE(server->setUp(utilsParam, std::move(auth))); Loading @@ -2024,7 +2065,8 @@ INSTANTIATE_TEST_CASE_P( BinderRpc, RpcTransportTlsKeyTest, testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)), testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER), testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)), testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER), testing::ValuesIn(testVersions())), RpcTransportTlsKeyTest::PrintParamInfo); } // namespace android Loading Loading
libs/binder/RpcState.cpp +54 −13 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include "Debug.h" #include "RpcWireFormat.h" #include "Utils.h" #include <random> Loading Loading @@ -493,8 +494,14 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti } } // 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) || __builtin_add_overflow(objectTableSpan.byteSize(), bodySize, &bodySize), "Too much data %zu", data.dataSize()); RpcWireHeader command{ Loading @@ -507,6 +514,8 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .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; Loading @@ -521,6 +530,7 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti {&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), [&] { Loading Loading @@ -585,7 +595,9 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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); Loading @@ -593,11 +605,13 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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)); Loading @@ -605,11 +619,15 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& 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<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; } Loading Loading @@ -824,12 +842,22 @@ processTransactInternalTailCall: 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) { Loading Loading @@ -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<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, Loading @@ -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<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); } Loading
libs/binder/RpcWireFormat.h +20 −2 Original line number Diff line number Diff line Loading @@ -131,7 +131,10 @@ struct RpcWireTransaction { uint64_t asyncNumber; uint32_t reserved[4]; // The size of the Parcel data directly following RpcWireTransaction. uint32_t parcelDataSize; uint32_t reserved[3]; uint8_t data[]; }; Loading @@ -139,8 +142,23 @@ static_assert(sizeof(RpcWireTransaction) == 40); struct RpcWireReply { int32_t status; // transact return // -- Fields below only transmitted starting at protocol version 1 -- // The size of the Parcel data directly following RpcWireReply. uint32_t parcelDataSize; uint32_t reserved[3]; // Byte size of RpcWireReply in the wire protocol. static size_t wireSize(uint32_t protocolVersion) { if (protocolVersion == 0) { return sizeof(int32_t); } return sizeof(RpcWireReply); } }; static_assert(sizeof(RpcWireReply) == 4); static_assert(sizeof(RpcWireReply) == 20); #pragma clang diagnostic pop Loading
libs/binder/Utils.h +24 −1 Original line number Diff line number Diff line Loading @@ -14,8 +14,9 @@ * limitations under the License. */ #include <cstdint> #include <stddef.h> #include <cstdint> #include <optional> #include <android-base/result.h> #include <android-base/unique_fd.h> Loading @@ -39,4 +40,26 @@ android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); // View of contiguous sequence. Similar to std::span. template <typename T> struct Span { T* data = nullptr; size_t size = 0; size_t byteSize() { return size * sizeof(T); } iovec toIovec() { return {const_cast<std::remove_const_t<T>*>(data), byteSize()}; } // Truncates `this` to a length of `offset` and returns a span with the // remainder. // // Aborts if offset > size. Span<T> splitOff(size_t offset) { LOG_ALWAYS_FATAL_IF(offset > size); Span<T> rest = {data + offset, size - offset}; size = offset; return rest; } }; } // namespace android
libs/binder/include/binder/RpcSession.h +7 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,13 @@ class FdTrigger; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_NEXT = 1; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL = 0xF0000000; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = 0; constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION = RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL; // Starting with this version: // // * RpcWireReply is larger (4 bytes -> 20). // * RpcWireTransaction and RpcWireReplyV1 include the parcel data size. constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_SIZE = 1; /** * This represents a session (group of connections) between a client Loading
libs/binder/tests/binderRpcTest.cpp +84 −42 Original line number Diff line number Diff line Loading @@ -99,17 +99,19 @@ TEST(BinderRpcParcel, EntireParcelFormatted) { EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); } class BinderRpcSimple : public ::testing::TestWithParam<RpcSecurity> { class BinderRpcServerOnly : public ::testing::TestWithParam<std::tuple<RpcSecurity, uint32_t>> { public: static std::string PrintTestParam(const ::testing::TestParamInfo<ParamType>& info) { return newFactory(info.param)->toCString(); return std::string(newFactory(std::get<0>(info.param))->toCString()) + "_serverV" + std::to_string(std::get<1>(info.param)); } }; TEST_P(BinderRpcSimple, SetExternalServerTest) { TEST_P(BinderRpcServerOnly, SetExternalServerTest) { base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); int sinkFd = sink.get(); auto server = RpcServer::make(newFactory(GetParam())); auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_FALSE(server->hasServer()); ASSERT_EQ(OK, server->setupExternalServer(std::move(sink))); ASSERT_TRUE(server->hasServer()); Loading Loading @@ -480,7 +482,8 @@ static base::unique_fd connectTo(const RpcSocketAddress& addr) { return serverFd; } class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity>> { class BinderRpc : public ::testing::TestWithParam<std::tuple<SocketType, RpcSecurity, uint32_t, uint32_t>> { public: struct Options { size_t numThreads = 1; Loading @@ -490,8 +493,9 @@ public: }; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [type, security] = info.param; return PrintToString(type) + "_" + newFactory(security)->toCString(); auto [type, security, clientVersion, serverVersion] = info.param; return PrintToString(type) + "_" + newFactory(security)->toCString() + "_clientV" + std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); } static inline void writeString(android::base::borrowed_fd fd, std::string_view str) { Loading Loading @@ -533,16 +537,19 @@ public: SocketType socketType = std::get<0>(GetParam()); RpcSecurity rpcSecurity = std::get<1>(GetParam()); uint32_t clientVersion = std::get<2>(GetParam()); uint32_t serverVersion = std::get<3>(GetParam()); unsigned int vsockPort = allocateVsockPort(); std::string addr = allocateSocketAddress(); auto ret = ProcessSession{ .host = Process([&](android::base::borrowed_fd writeEnd, .host = Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) { auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>(); sp<RpcServer> server = RpcServer::make(newFactory(rpcSecurity, certVerifier)); server->setProtocolVersion(serverVersion); server->setMaxThreads(options.numThreads); unsigned int outPort = 0; Loading Loading @@ -618,6 +625,7 @@ public: status_t status; for (const auto& session : sessions) { CHECK(session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(options.numIncomingConnections); session->setMaxOutgoingThreads(options.numOutgoingConnections); Loading Loading @@ -1420,9 +1428,20 @@ static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) { return ret; } static std::vector<uint32_t> testVersions() { std::vector<uint32_t> versions; for (size_t i = 0; i < RPC_WIRE_PROTOCOL_VERSION_NEXT; i++) { versions.push_back(i); } versions.push_back(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL); return versions; } INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::Combine(::testing::ValuesIn(testSocketTypes()), ::testing::ValuesIn(RpcSecurityValues())), ::testing::ValuesIn(RpcSecurityValues()), ::testing::ValuesIn(testVersions()), ::testing::ValuesIn(testVersions())), BinderRpc::PrintParamInfo); class BinderRpcServerRootObject Loading Loading @@ -1476,9 +1495,10 @@ private: bool mValue = false; }; TEST_P(BinderRpcSimple, Shutdown) { TEST_P(BinderRpcServerOnly, Shutdown) { auto addr = allocateSocketAddress(); auto server = RpcServer::make(newFactory(GetParam())); auto server = RpcServer::make(newFactory(std::get<0>(GetParam()))); server->setProtocolVersion(std::get<1>(GetParam())); ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str())); auto joinEnds = std::make_shared<OneOffSignal>(); Loading Loading @@ -1548,12 +1568,17 @@ TEST(BinderRpc, Java) { ASSERT_EQ(OK, rpcBinder->pingBinder()); } INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcSimple, ::testing::ValuesIn(RpcSecurityValues()), BinderRpcSimple::PrintTestParam); INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerOnly, ::testing::Combine(::testing::ValuesIn(RpcSecurityValues()), ::testing::ValuesIn(testVersions())), BinderRpcServerOnly::PrintTestParam); class RpcTransportTestUtils { public: using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>>; // Only parameterized only server version because `RpcSession` is bypassed // in the client half of the tests. using Param = std::tuple<SocketType, RpcSecurity, std::optional<RpcCertificateFormat>, uint32_t>; using ConnectToServer = std::function<base::unique_fd()>; // A server that handles client socket connections. Loading @@ -1565,8 +1590,9 @@ public: [[nodiscard]] AssertionResult setUp( const Param& param, std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) { auto [socketType, rpcSecurity, certificateFormat] = param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; auto rpcServer = RpcServer::make(newFactory(rpcSecurity)); rpcServer->setProtocolVersion(serverVersion); switch (socketType) { case SocketType::PRECONNECTED: { return AssertionFailure() << "Not supported by this test"; Loading Loading @@ -1696,7 +1722,8 @@ public: explicit Client(ConnectToServer connectToServer) : mConnectToServer(connectToServer) {} Client(Client&&) = default; [[nodiscard]] AssertionResult setUp(const Param& param) { auto [socketType, rpcSecurity, certificateFormat] = param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param; (void)serverVersion; mFdTrigger = FdTrigger::make(); mCtx = newFactory(rpcSecurity, mCertVerifier)->newClientCtx(); if (mCtx == nullptr) return AssertionFailure() << "newClientCtx"; Loading Loading @@ -1767,31 +1794,37 @@ public: using Server = RpcTransportTestUtils::Server; using Client = RpcTransportTestUtils::Client; static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [socketType, rpcSecurity, certificateFormat] = info.param; auto [socketType, rpcSecurity, certificateFormat, serverVersion] = info.param; auto ret = PrintToString(socketType) + "_" + newFactory(rpcSecurity)->toCString(); if (certificateFormat.has_value()) ret += "_" + PrintToString(*certificateFormat); ret += "_serverV" + std::to_string(serverVersion); return ret; } static std::vector<ParamType> getRpcTranportTestParams() { std::vector<ParamType> ret; for (auto serverVersion : testVersions()) { for (auto socketType : testSocketTypes(false /* hasPreconnected */)) { for (auto rpcSecurity : RpcSecurityValues()) { switch (rpcSecurity) { case RpcSecurity::RAW: { ret.emplace_back(socketType, rpcSecurity, std::nullopt); ret.emplace_back(socketType, rpcSecurity, std::nullopt, serverVersion); } break; case RpcSecurity::TLS: { ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::PEM, serverVersion); ret.emplace_back(socketType, rpcSecurity, RpcCertificateFormat::DER, serverVersion); } break; } } } } return ret; } template <typename A, typename B> status_t trust(const A& a, const B& b) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; return RpcTransportTestUtils::trust(rpcSecurity, certificateFormat, a, b); } }; Loading Loading @@ -1827,7 +1860,8 @@ TEST_P(RpcTransportTest, MultipleClients) { } TEST_P(RpcTransportTest, UntrustedServer) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto untrustedServer = std::make_unique<Server>(); ASSERT_TRUE(untrustedServer->setUp(GetParam())); Loading @@ -1845,7 +1879,9 @@ TEST_P(RpcTransportTest, UntrustedServer) { client.run(handshakeOk); } TEST_P(RpcTransportTest, MaliciousServer) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto validServer = std::make_unique<Server>(); ASSERT_TRUE(validServer->setUp(GetParam())); Loading @@ -1868,7 +1904,9 @@ TEST_P(RpcTransportTest, MaliciousServer) { } TEST_P(RpcTransportTest, UntrustedClient) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); Loading @@ -1887,7 +1925,9 @@ TEST_P(RpcTransportTest, UntrustedClient) { } TEST_P(RpcTransportTest, MaliciousClient) { auto [socketType, rpcSecurity, certificateFormat] = GetParam(); auto [socketType, rpcSecurity, certificateFormat, serverVersion] = GetParam(); (void)serverVersion; auto server = std::make_unique<Server>(); ASSERT_TRUE(server->setUp(GetParam())); Loading Loading @@ -1973,23 +2013,24 @@ INSTANTIATE_TEST_CASE_P(BinderRpc, RpcTransportTest, RpcTransportTest::PrintParamInfo); class RpcTransportTlsKeyTest : public testing::TestWithParam<std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat>> { : public testing::TestWithParam< std::tuple<SocketType, RpcCertificateFormat, RpcKeyFormat, uint32_t>> { public: template <typename A, typename B> status_t trust(const A& a, const B& b) { auto [socketType, certificateFormat, keyFormat] = GetParam(); auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); (void)serverVersion; return RpcTransportTestUtils::trust(RpcSecurity::TLS, certificateFormat, a, b); } static std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) { auto [socketType, certificateFormat, keyFormat] = info.param; auto ret = PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + "_key_" + PrintToString(keyFormat); return ret; auto [socketType, certificateFormat, keyFormat, serverVersion] = info.param; return PrintToString(socketType) + "_certificate_" + PrintToString(certificateFormat) + "_key_" + PrintToString(keyFormat) + "_serverV" + std::to_string(serverVersion); }; }; TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto [socketType, certificateFormat, keyFormat] = GetParam(); auto [socketType, certificateFormat, keyFormat, serverVersion] = GetParam(); std::vector<uint8_t> pkeyData, certData; { Loading @@ -2004,8 +2045,8 @@ TEST_P(RpcTransportTlsKeyTest, PreSignedCertificate) { auto desPkey = deserializeUnencryptedPrivatekey(pkeyData, keyFormat); auto desCert = deserializeCertificate(certData, certificateFormat); auto auth = std::make_unique<RpcAuthPreSigned>(std::move(desPkey), std::move(desCert)); auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat)); auto utilsParam = std::make_tuple(socketType, RpcSecurity::TLS, std::make_optional(certificateFormat), serverVersion); auto server = std::make_unique<RpcTransportTestUtils::Server>(); ASSERT_TRUE(server->setUp(utilsParam, std::move(auth))); Loading @@ -2024,7 +2065,8 @@ INSTANTIATE_TEST_CASE_P( BinderRpc, RpcTransportTlsKeyTest, testing::Combine(testing::ValuesIn(testSocketTypes(false /* hasPreconnected*/)), testing::Values(RpcCertificateFormat::PEM, RpcCertificateFormat::DER), testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER)), testing::Values(RpcKeyFormat::PEM, RpcKeyFormat::DER), testing::ValuesIn(testVersions())), RpcTransportTlsKeyTest::PrintParamInfo); } // namespace android Loading