diff options
Diffstat (limited to 'libs/binder/OS.cpp')
-rw-r--r-- | libs/binder/OS.cpp | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/libs/binder/OS.cpp b/libs/binder/OS.cpp index 24ce2bb465..77e401f83e 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 @@ using android::base::Result; 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 @@ std::unique_ptr<RpcTransportCtxFactory> makeDefaultRpcTransportCtxFactory() { 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 |