Created
November 28, 2020 17:54
-
-
Save RikkaW/17a5e793499e6f0fb7ba85fe688c4cf0 to your computer and use it in GitHub Desktop.
Socket send fd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void api::OpenFiles(RiruFile *files, size_t count) { | |
struct sockaddr_un addr{}; | |
int fd; | |
socklen_t socklen; | |
uint8_t *data; | |
uint32_t data_size; | |
int32_t reply[count]; | |
uint32_t reply_size; | |
std::vector<int> fds; | |
flatbuffers::FlatBufferBuilder builder; | |
std::vector<flatbuffers::Offset<rirud::OpenFileRequest>> requests_vector; | |
for (auto i = 0; i < count; ++i) { | |
auto file = files[i]; | |
requests_vector.emplace_back(rirud::CreateOpenFileRequestDirect(builder, file.path, file.flags, file.modes)); | |
} | |
auto requests = rirud::CreateOpenFilesRequestDirect(builder, count, &requests_vector); | |
rirud::FinishOpenFilesRequestBuffer(builder, requests); | |
data = builder.GetBufferPointer(); | |
data_size = builder.GetSize(); | |
reply_size = count * sizeof(int32_t); | |
std::fill_n(reply, count, -1); | |
if ((fd = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)) < 0) { | |
PLOGE("socket"); | |
goto clean; | |
} | |
socklen = setup_sockaddr(&addr, SOCKET_ADDRESS); | |
if (connect(fd, (struct sockaddr *) &addr, socklen) == -1) { | |
PLOGE("connect %s", SOCKET_ADDRESS); | |
goto clean; | |
} | |
if (write_full(fd, &Status::ACTION_OPEN_FILE, sizeof(Status::ACTION_OPEN_FILE)) != 0 | |
|| write_full(fd, &data_size, sizeof(uint32_t)) != 0 | |
|| write_full(fd, data, data_size) != 0) { | |
PLOGE("write %s", SOCKET_ADDRESS); | |
goto clean; | |
} | |
if (ReceiveFileDescriptorVector(fd, reply, reply_size, count, &fds) <= 0) { | |
LOGE("ReceiveFileDescriptorVector"); | |
} else { | |
size_t fd_index = 0; | |
for (size_t i = 0; i < count; ++i) { | |
auto file = files[i]; | |
auto code = reply[i]; | |
if (code == 0) { | |
if (fd_index >= fds.size()) { | |
LOGE("received too little file descriptors"); | |
file.fd = -1; | |
continue; | |
} | |
file.fd = fds[fd_index]; | |
fd_index += 1; | |
LOGD("%s=%d", file.path, (int) file.fd); | |
} else { | |
file.fd = -1; | |
if (code != -1) { | |
LOGE("open %s from daemon failed with %d: %s", file.path, code, strerror(code)); | |
} else { | |
LOGE("open %s from daemon failed", file.path); | |
} | |
} | |
} | |
} | |
clean: | |
if (fd != -1) close(fd); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2019 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/* | |
* system/libbase/cmsg.cpp | |
*/ | |
#include <cerrno> | |
#include <fcntl.h> | |
#include <cstdlib> | |
#include <sys/socket.h> | |
#include <sys/user.h> | |
#include <bits/sysconf.h> | |
#include <vector> | |
#include <unistd.h> | |
#include <logging.h> | |
#include <cinttypes> | |
ssize_t SendFileDescriptorVector(int sockfd, const void *data, size_t len, const std::vector<int> &fds) { | |
static const size_t page_size = sysconf(_SC_PAGE_SIZE); | |
size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size()); | |
size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size()); | |
if (cmsg_space >= page_size) { | |
errno = ENOMEM; | |
return -1; | |
} | |
alignas(struct cmsghdr) char cmsg_buf[cmsg_space]; | |
iovec iov = {.iov_base = const_cast<void *>(data), .iov_len = len}; | |
msghdr msg = { | |
.msg_name = nullptr, | |
.msg_namelen = 0, | |
.msg_iov = &iov, | |
.msg_iovlen = 1, | |
.msg_control = cmsg_buf, | |
// We can't cast to the actual type of the field, because it's different across platforms. | |
.msg_controllen = static_cast<unsigned int>(cmsg_space), | |
.msg_flags = 0, | |
}; | |
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); | |
cmsg->cmsg_level = SOL_SOCKET; | |
cmsg->cmsg_type = SCM_RIGHTS; | |
cmsg->cmsg_len = cmsg_len; | |
int *cmsg_fds = reinterpret_cast<int *>(CMSG_DATA(cmsg)); | |
for (size_t i = 0; i < fds.size(); ++i) { | |
cmsg_fds[i] = fds[i]; | |
} | |
#if defined(__linux__) | |
int flags = MSG_NOSIGNAL; | |
#else | |
int flags = 0; | |
#endif | |
return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags)); | |
} | |
ssize_t ReceiveFileDescriptorVector(int sockfd, void *data, size_t len, size_t max_fds, std::vector<int> *fds) { | |
fds->clear(); | |
static const size_t page_size = sysconf(_SC_PAGE_SIZE); | |
size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds); | |
if (cmsg_space >= page_size) { | |
errno = ENOMEM; | |
return -1; | |
} | |
alignas(struct cmsghdr) char cmsg_buf[cmsg_space]; | |
iovec iov = {.iov_base = const_cast<void *>(data), .iov_len = len}; | |
msghdr msg = { | |
.msg_name = nullptr, | |
.msg_namelen = 0, | |
.msg_iov = &iov, | |
.msg_iovlen = 1, | |
.msg_control = cmsg_buf, | |
// We can't cast to the actual type of the field, because it's different across platforms. | |
.msg_controllen = static_cast<unsigned int>(cmsg_space), | |
.msg_flags = 0, | |
}; | |
int flags = MSG_TRUNC | MSG_CTRUNC; | |
#if defined(__linux__) | |
flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL; | |
#endif | |
ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags)); | |
if (rc == -1) { | |
return -1; | |
} | |
int error = 0; | |
if ((msg.msg_flags & MSG_TRUNC)) { | |
LOGE("message was truncated when receiving file descriptors"); | |
error = EMSGSIZE; | |
} else if ((msg.msg_flags & MSG_CTRUNC)) { | |
LOGE("control message was truncated when receiving file descriptors"); | |
error = EMSGSIZE; | |
} | |
std::vector<int> received_fds; | |
struct cmsghdr *cmsg; | |
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | |
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { | |
LOGE("received unexpected cmsg: [%d, %d]", cmsg->cmsg_level, cmsg->cmsg_type); | |
error = EBADMSG; | |
continue; | |
} | |
// There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with | |
// some asserts to ensure that CMSG_LEN behaves as we expect. | |
#if defined(__linux__) | |
#define CMSG_ASSERT static_assert | |
#else | |
// CMSG_LEN is somehow not constexpr on darwin. | |
#define CMSG_ASSERT CHECK | |
#endif | |
CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)), ""); | |
CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)), ""); | |
CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)), ""); | |
CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)), ""); | |
if (cmsg->cmsg_len % sizeof(int) != 0) { | |
LOGE("cmsg_len(%" PRIdPTR") not aligned to sizeof(int)", cmsg->cmsg_len); | |
} else if (cmsg->cmsg_len <= CMSG_LEN(0)) { | |
LOGE("cmsg_len(%" PRIdPTR") not long enough to hold any data", cmsg->cmsg_len); | |
} | |
int *cmsg_fds = reinterpret_cast<int *>(CMSG_DATA(cmsg)); | |
size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); | |
for (size_t i = 0; i < cmsg_fdcount; ++i) { | |
#if !defined(__linux__) | |
// Linux uses MSG_CMSG_CLOEXEC instead of doing this manually. | |
fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC); | |
#endif | |
received_fds.emplace_back(cmsg_fds[i]); | |
} | |
} | |
if (error != 0) { | |
errno = error; | |
return -1; | |
} | |
if (received_fds.size() > max_fds) { | |
LOGE("received too many file descriptors, expected %" PRIdPTR", received %" PRIdPTR, fds->size(), received_fds.size()); | |
errno = EMSGSIZE; | |
return -1; | |
} | |
*fds = std::move(received_fds); | |
return rc; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2019 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#pragma once | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <vector> | |
// Helpers for sending and receiving file descriptors across Unix domain sockets. | |
// | |
// The cmsg(3) API is very hard to get right, with multiple landmines that can | |
// lead to death. Almost all of the uses of cmsg in Android make at least one of | |
// the following mistakes: | |
// | |
// - not aligning the cmsg buffer | |
// - leaking fds if more fds are received than expected | |
// - blindly dereferencing CMSG_DATA without checking the header | |
// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len | |
// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen | |
// - using a length specified in number of fds instead of bytes | |
// | |
// These functions wrap the hard-to-use cmsg API with an easier to use abstraction. | |
// Send file descriptors across a Unix domain socket. | |
// | |
// Note that the write can return short if the socket type is SOCK_STREAM. When | |
// this happens, file descriptors are still sent to the other end, but with | |
// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended. | |
ssize_t SendFileDescriptorVector(int sock, const void* data, size_t len, const std::vector<int>& fds); | |
// Receive file descriptors from a Unix domain socket. | |
// | |
// If more FDs (or bytes, for datagram sockets) are received than expected, | |
// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away. | |
ssize_t ReceiveFileDescriptorVector(int sock, void* data, size_t len, size_t max_fds, std::vector<int>* fds); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static bool handle_open_file(int sockfd) { | |
uint8_t *data; | |
uint32_t data_size; | |
std::vector<int> fds; | |
int32_t *reply; | |
size_t reply_size; | |
if (read_full(sockfd, &data_size, sizeof(uint32_t)) == -1) { | |
PLOGE("read"); | |
return false; | |
} | |
data = (uint8_t *) malloc(data_size); | |
if (read_full(sockfd, data, data_size) == -1) { | |
PLOGE("read"); | |
free(data); | |
return false; | |
} | |
flatbuffers::Verifier verifier = flatbuffers::Verifier(data, (size_t) data_size); | |
if (!rirud::VerifyOpenFilesRequestBuffer(verifier)) { | |
LOGW("invalid data"); | |
free(data); | |
return false; | |
} | |
auto requests = rirud::GetOpenFilesRequest(data); | |
auto count = requests->count(); | |
reply_size = count * sizeof(int32_t); | |
reply = (int32_t *) malloc(reply_size); | |
for (auto i = 0; i < count; ++i) { | |
auto request = requests->requests()->Get(i); | |
auto path = request->path()->c_str(); | |
auto flags = request->flags(); | |
auto modes = request->modes(); | |
LOGV("socket request: open file %s", path); | |
errno = 0; | |
int fd = open(path, flags, modes); | |
reply[i] = errno; | |
if (fd == -1) { | |
PLOGE("open %s", path); | |
} else { | |
fds.emplace_back(fd); | |
} | |
} | |
SendFileDescriptorVector(sockfd, reply, reply_size, fds); | |
for (auto fd : fds) { | |
close(fd); | |
} | |
free(data); | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment