RpcBinder: Add AF_UNIX socketpair transport
Add support for running RpcBinder over unnamed Unix domain sockets
created by socketpair(). This is useful e.g. between parent/child
processes.
The implementation uses the initial socket pair only to create more
socket pairs for individual connections. This creates a natural mapping
to syscalls used on sockets bound to an address:
socket() socketpair()
bind() n/a (preconnected)
connect() sendmsg()
listen() recvmsg()
Bug: 250685929
Test: atest binderRpcTest
Change-Id: Id4ff3946ddcfefe3592eb1149c61582f7369aa29
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 21b0354..8841021 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -233,6 +233,25 @@
return serverFd;
}
+static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
+ base::unique_fd sockClient, sockServer;
+ if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+ }
+
+ int zero = 0;
+ iovec iov{&zero, sizeof(zero)};
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ fds.emplace_back(std::move(sockServer));
+
+ if (sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+ }
+ return std::move(sockClient);
+}
+
using RunServiceFn = void (*)(android::base::borrowed_fd writeEnd,
android::base::borrowed_fd readEnd);
@@ -253,7 +272,14 @@
// Whether the test params support sending FDs in parcels.
bool supportsFdTransport() const {
return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
- (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX);
+ (socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
+ socketType() == SocketType::UNIX_BOOTSTRAP);
+ }
+
+ void SetUp() override {
+ if (socketType() == SocketType::UNIX_BOOTSTRAP && rpcSecurity() == RpcSecurity::TLS) {
+ GTEST_SKIP() << "Unix bootstrap not supported over a TLS transport";
+ }
}
static inline std::string PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
@@ -287,6 +313,14 @@
singleThreaded ? "_single_threaded" : "",
noKernel ? "_no_kernel" : "");
+ base::unique_fd bootstrapClientFd, bootstrapServerFd;
+ // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
+ // This is because we cannot pass ParcelFileDescriptor over a pipe.
+ if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &bootstrapServerFd)) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+ }
+
auto ret = ProcessSession{
.host = Process([=](android::base::borrowed_fd writeEnd,
android::base::borrowed_fd readEnd) {
@@ -304,6 +338,7 @@
serverConfig.serverVersion = serverVersion;
serverConfig.vsockPort = allocateVsockPort();
serverConfig.addr = allocateSocketAddress();
+ serverConfig.unixBootstrapFd = bootstrapServerFd.get();
for (auto mode : options.serverSupportedFileDescriptorTransportModes) {
serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
static_cast<int32_t>(mode));
@@ -353,6 +388,10 @@
case SocketType::UNIX:
status = session->setupUnixDomainClient(serverConfig.addr.c_str());
break;
+ case SocketType::UNIX_BOOTSTRAP:
+ status = session->setupUnixDomainSocketBootstrapClient(
+ base::unique_fd(dup(bootstrapClientFd.get())));
+ break;
case SocketType::VSOCK:
status = session->setupVsockClient(VMADDR_CID_LOCAL, serverConfig.vsockPort);
break;
@@ -419,7 +458,8 @@
}
SocketType type = std::get<0>(GetParam());
- if (type == SocketType::PRECONNECTED || type == SocketType::UNIX) {
+ if (type == SocketType::PRECONNECTED || type == SocketType::UNIX ||
+ type == SocketType::UNIX_BOOTSTRAP) {
// we can't get port numbers for unix sockets
return;
}
@@ -1516,7 +1556,7 @@
}
static std::vector<SocketType> testSocketTypes(bool hasPreconnected = true) {
- std::vector<SocketType> ret = {SocketType::UNIX, SocketType::INET};
+ std::vector<SocketType> ret = {SocketType::UNIX, SocketType::UNIX_BOOTSTRAP, SocketType::INET};
if (hasPreconnected) ret.push_back(SocketType::PRECONNECTED);
@@ -1717,6 +1757,8 @@
// A server that handles client socket connections.
class Server {
public:
+ using AcceptConnection = std::function<base::unique_fd(Server*)>;
+
explicit Server() {}
Server(Server&&) = default;
~Server() { shutdownAndWait(); }
@@ -1741,6 +1783,21 @@
return connectTo(UnixSocketAddress(addr.c_str()));
};
} break;
+ case SocketType::UNIX_BOOTSTRAP: {
+ base::unique_fd bootstrapFdClient, bootstrapFdServer;
+ if (!base::Socketpair(SOCK_STREAM, &bootstrapFdClient, &bootstrapFdServer)) {
+ return AssertionFailure() << "Socketpair() failed";
+ }
+ auto status = rpcServer->setupUnixDomainSocketBootstrapServer(
+ std::move(bootstrapFdServer));
+ if (status != OK) {
+ return AssertionFailure() << "setupUnixDomainSocketBootstrapServer: "
+ << statusToString(status);
+ }
+ mBootstrapSocket = RpcTransportFd(std::move(bootstrapFdClient));
+ mAcceptConnection = &Server::recvmsgServerConnection;
+ mConnectToServer = [this] { return connectToUnixBootstrap(mBootstrapSocket); };
+ } break;
case SocketType::VSOCK: {
auto port = allocateVsockPort();
auto status = rpcServer->setupVsockServer(port);
@@ -1788,14 +1845,33 @@
LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
mThread = std::make_unique<std::thread>(&Server::run, this);
}
+
+ base::unique_fd acceptServerConnection() {
+ return base::unique_fd(TEMP_FAILURE_RETRY(
+ accept4(mFd.fd.get(), nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ }
+
+ base::unique_fd recvmsgServerConnection() {
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>> fds;
+ int buf;
+ iovec iov{&buf, sizeof(buf)};
+
+ if (receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
+ int savedErrno = errno;
+ LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+ }
+ if (fds.size() != 1) {
+ LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
+ }
+ return std::move(std::get<base::unique_fd>(fds[0]));
+ }
+
void run() {
LOG_ALWAYS_FATAL_IF(!mSetup, "Call Server::setup first!");
std::vector<std::thread> threads;
while (OK == mFdTrigger->triggerablePoll(mFd, POLLIN)) {
- base::unique_fd acceptedFd(
- TEMP_FAILURE_RETRY(accept4(mFd.fd.get(), nullptr, nullptr /*length*/,
- SOCK_CLOEXEC | SOCK_NONBLOCK)));
+ base::unique_fd acceptedFd = mAcceptConnection(this);
threads.emplace_back(&Server::handleOne, this, std::move(acceptedFd));
}
@@ -1822,8 +1898,9 @@
private:
std::unique_ptr<std::thread> mThread;
ConnectToServer mConnectToServer;
+ AcceptConnection mAcceptConnection = &Server::acceptServerConnection;
std::unique_ptr<FdTrigger> mFdTrigger = FdTrigger::make();
- RpcTransportFd mFd;
+ RpcTransportFd mFd, mBootstrapSocket;
std::unique_ptr<RpcTransportCtx> mCtx;
std::shared_ptr<RpcCertificateVerifierSimple> mCertVerifier =
std::make_shared<RpcCertificateVerifierSimple>();