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/OS.cpp b/libs/binder/OS.cpp
index 24ce2bb..77e401f 100644
--- a/libs/binder/OS.cpp
+++ b/libs/binder/OS.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <binder/RpcTransportRaw.h>
+#include <log/log.h>
#include <string.h>
using android::base::ErrnoError;
@@ -25,6 +26,9 @@
namespace android {
+// Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets.
+constexpr size_t kMaxFdsPerMsg = 253;
+
Result<void> setNonBlocking(android::base::borrowed_fd fd) {
int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL));
if (flags == -1) {
@@ -63,4 +67,99 @@
return RpcTransportCtxFactoryRaw::make();
}
+int sendMessageOnSocket(
+ const RpcTransportFd& socket, iovec* iovs, int niovs,
+ const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ if (ancillaryFds != nullptr && !ancillaryFds->empty()) {
+ if (ancillaryFds->size() > kMaxFdsPerMsg) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // CMSG_DATA is not necessarily aligned, so we copy the FDs into a buffer and then
+ // use memcpy.
+ int fds[kMaxFdsPerMsg];
+ for (size_t i = 0; i < ancillaryFds->size(); i++) {
+ fds[i] = std::visit([](const auto& fd) { return fd.get(); }, ancillaryFds->at(i));
+ }
+ const size_t fdsByteSize = sizeof(int) * ancillaryFds->size();
+
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(int) * kMaxFdsPerMsg)];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(fdsByteSize);
+ memcpy(CMSG_DATA(cmsg), fds, fdsByteSize);
+
+ msg.msg_controllen = CMSG_SPACE(fdsByteSize);
+ return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
+ }
+
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+ return TEMP_FAILURE_RETRY(sendmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
+int receiveMessageFromSocket(
+ const RpcTransportFd& socket, iovec* iovs, int niovs,
+ std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) {
+ if (ancillaryFds != nullptr) {
+ int fdBuffer[kMaxFdsPerMsg];
+ alignas(struct cmsghdr) char msgControlBuf[CMSG_SPACE(sizeof(fdBuffer))];
+
+ msghdr msg{
+ .msg_iov = iovs,
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ .msg_control = msgControlBuf,
+ .msg_controllen = sizeof(msgControlBuf),
+ };
+ ssize_t processSize = TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+ if (processSize < 0) {
+ return -1;
+ }
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ // NOTE: It is tempting to reinterpret_cast, but cmsg(3) explicitly asks
+ // application devs to memcpy the data to ensure memory alignment.
+ size_t dataLen = cmsg->cmsg_len - CMSG_LEN(0);
+ LOG_ALWAYS_FATAL_IF(dataLen > sizeof(fdBuffer)); // validity check
+ memcpy(fdBuffer, CMSG_DATA(cmsg), dataLen);
+ size_t fdCount = dataLen / sizeof(int);
+ ancillaryFds->reserve(ancillaryFds->size() + fdCount);
+ for (size_t i = 0; i < fdCount; i++) {
+ ancillaryFds->emplace_back(base::unique_fd(fdBuffer[i]));
+ }
+ break;
+ }
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ errno = EPIPE;
+ return -1;
+ }
+ return processSize;
+ }
+ msghdr msg{
+ .msg_iov = iovs,
+ // posix uses int, glibc uses size_t. niovs is a
+ // non-negative int and can be cast to either.
+ .msg_iovlen = static_cast<decltype(msg.msg_iovlen)>(niovs),
+ };
+
+ return TEMP_FAILURE_RETRY(recvmsg(socket.fd.get(), &msg, MSG_NOSIGNAL));
+}
+
} // namespace android