Merge "init: Ignore "ro." restrictions when reading prop files"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cc85408..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,9 @@
"name": "fs_mgr_unit_test"
},
{
+ "name": "fs_mgr_vendor_overlay_test"
+ },
+ {
"name": "init_tests"
},
{
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 605d27d..91b0d1f 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -187,79 +187,3 @@
return false;
}
}
-
-#if defined(__linux__)
-bool SendFileDescriptor(int socket_fd, int fd) {
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- union {
- cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)CMSG_DATA(cmsg))[0] = fd;
-
- int ret = TEMP_FAILURE_RETRY(sendmsg(socket_fd, &msg, 0));
- if (ret < 0) {
- D("sending file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
- return false;
- }
-
- D("sent file descriptor %d to via socket %d", fd, socket_fd);
- return true;
-}
-
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error) {
- char dummy = '!';
- union {
- cmsghdr cm;
- char buffer[CMSG_SPACE(sizeof(int))];
- } cm_un;
-
- iovec iov;
- iov.iov_base = &dummy;
- iov.iov_len = 1;
-
- msghdr msg;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = cm_un.buffer;
- msg.msg_controllen = sizeof(cm_un.buffer);
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)(CMSG_DATA(cmsg)))[0] = -1;
-
- int rc = TEMP_FAILURE_RETRY(recvmsg(socket_fd, &msg, 0));
- if (rc <= 0) {
- *error = perror_str("receiving file descriptor via socket failed");
- D("receiving file descriptor via socket %d failed: %s", socket_fd, strerror(errno));
- return false;
- }
-
- fd->reset(((int*)(CMSG_DATA(cmsg)))[0]);
- D("received file descriptor %d to via socket %d", fd->get(), socket_fd);
-
- return true;
-}
-#endif
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 2ccaa32..e2df1b1 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -74,13 +74,4 @@
// Same as above, but formats the string to send.
bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
-#if !ADB_HOST
-// Sends an FD via Unix domain socket.
-bool SendFileDescriptor(int socket_fd, int fd);
-
-// Receives an FD via Unix domain socket.
-bool ReceiveFileDescriptor(int socket_fd, unique_fd* fd, std::string* error);
-#endif
-
#endif /* ADB_IO_H */
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index f69babe..d949dd1 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -22,6 +22,8 @@
#include <sys/wait.h>
+#include <android-base/cmsg.h>
+
namespace {
class AdbFdTextOutput : public android::TextOutput {
@@ -83,8 +85,8 @@
break;
}
- auto result = StartCommandInProcess(std::move(data), &execCmd);
- if (!SendFileDescriptor(fd, result)) {
+ unique_fd result = StartCommandInProcess(std::move(data), &execCmd);
+ if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
break;
}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
index 817aea1..d32bf52 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -20,6 +20,8 @@
#include "adb_utils.h"
#include "shell_service.h"
+#include <android-base/cmsg.h>
+
namespace {
struct AbbProcess;
@@ -59,8 +61,9 @@
unique_fd fd;
std::string error;
- if (!ReceiveFileDescriptor(socket_fd_, &fd, &error)) {
- LOG(ERROR) << "failed to receive FD from abb: " << error;
+ char buf;
+ if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+ PLOG(ERROR) << "failed to receive FD from abb";
socket_fd_.reset();
continue;
}
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 032ee42..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -32,6 +32,8 @@
#include <memory>
#include <vector>
+#include <android-base/cmsg.h>
+
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
@@ -237,7 +239,7 @@
CHECK(!proc->out_fds.empty());
int fd = proc->out_fds.back().get();
- if (!SendFileDescriptor(socket, fd)) {
+ if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
goto CloseProcess;
}
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
index 6803d93..16d2627 100644
--- a/adb/daemon/restart_service.cpp
+++ b/adb/daemon/restart_service.cpp
@@ -20,6 +20,7 @@
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <log/log_properties.h>
@@ -37,6 +38,7 @@
return;
}
+ LOG(INFO) << "adbd restarting as root";
android::base::SetProperty("service.adb.root", "1");
WriteFdExactly(fd.get(), "restarting adbd as root\n");
}
@@ -46,6 +48,8 @@
WriteFdExactly(fd.get(), "adbd not running as root\n");
return;
}
+
+ LOG(INFO) << "adbd restarting as nonroot";
android::base::SetProperty("service.adb.root", "0");
WriteFdExactly(fd.get(), "restarting adbd as non root\n");
}
@@ -56,11 +60,13 @@
return;
}
+ LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
}
void restart_usb_service(unique_fd fd) {
+ LOG(INFO) << "adbd restarting in USB mode";
android::base::SetProperty("service.adb.tcp.port", "0");
WriteFdExactly(fd.get(), "restarting in USB mode\n");
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f603d13..f0e2861 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -53,6 +53,9 @@
using android::base::StringPrintf;
+// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
+static std::optional<bool> gFfsAioSupported;
+
static constexpr size_t kUsbReadQueueDepth = 16;
static constexpr size_t kUsbReadSize = 16384;
@@ -169,8 +172,13 @@
read_fd_(std::move(read)),
write_fd_(std::move(write)) {
LOG(INFO) << "UsbFfsConnection constructed";
- event_fd_.reset(eventfd(0, EFD_CLOEXEC));
- if (event_fd_ == -1) {
+ worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (worker_event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
+ monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_event_fd_ == -1) {
PLOG(FATAL) << "failed to create eventfd";
}
@@ -181,6 +189,13 @@
LOG(INFO) << "UsbFfsConnection being destroyed";
Stop();
monitor_thread_.join();
+
+ // We need to explicitly close our file descriptors before we notify our destruction,
+ // because the thread listening on the future will immediately try to reopen the endpoint.
+ control_fd_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+
destruction_notifier_.set_value();
}
@@ -207,11 +222,18 @@
}
stopped_ = true;
uint64_t notify = 1;
- ssize_t rc = adb_write(event_fd_.get(), ¬ify, sizeof(notify));
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
if (rc < 0) {
- PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+ PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
}
CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+ rc = adb_write(monitor_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+ }
+
+ CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
}
private:
@@ -235,22 +257,33 @@
bool started = false;
bool running = true;
while (running) {
+ int timeout = -1;
if (!bound || !started) {
- adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
- int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
- if (rc == -1) {
- PLOG(FATAL) << "poll on USB control fd failed";
- } else if (rc == 0) {
- // Something in the kernel presumably went wrong.
- // Close our endpoints, wait for a bit, and then try again.
- aio_context_.reset();
- read_fd_.reset();
- write_fd_.reset();
- control_fd_.reset();
- std::this_thread::sleep_for(5s);
- HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
- return;
- }
+ timeout = 5000 /*ms*/;
+ }
+
+ adb_pollfd pfd[2] = {
+ { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+ { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+ };
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout));
+ if (rc == -1) {
+ PLOG(FATAL) << "poll on USB control fd failed";
+ } else if (rc == 0) {
+ // Something in the kernel presumably went wrong.
+ // Close our endpoints, wait for a bit, and then try again.
+ aio_context_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+ control_fd_.reset();
+ std::this_thread::sleep_for(5s);
+ HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
+ return;
+ }
+
+ if (pfd[1].revents) {
+ // We were told to die.
+ break;
}
struct usb_functionfs_event event;
@@ -313,12 +346,14 @@
adb_thread_setname("UsbFfs-worker");
for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
read_requests_[i] = CreateReadBlock(next_read_id_++);
- SubmitRead(&read_requests_[i]);
+ if (!SubmitRead(&read_requests_[i])) {
+ return;
+ }
}
while (!stopped_) {
uint64_t dummy;
- ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+ ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
if (rc == -1) {
PLOG(FATAL) << "failed to read from eventfd";
} else if (rc == 0) {
@@ -347,7 +382,7 @@
block.control.aio_fildes = read_fd_.get();
block.control.aio_offset = 0;
block.control.aio_flags = IOCB_FLAG_RESFD;
- block.control.aio_resfd = event_fd_.get();
+ block.control.aio_resfd = worker_event_fd_.get();
return block;
}
@@ -438,13 +473,22 @@
SubmitRead(block);
}
- void SubmitRead(IoBlock* block) {
+ bool SubmitRead(IoBlock* block) {
block->pending = true;
struct iocb* iocb = &block->control;
if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+ if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+ HandleError("failed to submit first read, AIO on FFS not supported");
+ gFfsAioSupported = false;
+ return false;
+ }
+
HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
- return;
+ return false;
}
+
+ gFfsAioSupported = true;
+ return true;
}
void HandleWrite(TransferId id) {
@@ -474,7 +518,7 @@
block->control.aio_nbytes = block->payload.size();
block->control.aio_offset = 0;
block->control.aio_flags = IOCB_FLAG_RESFD;
- block->control.aio_resfd = event_fd_.get();
+ block->control.aio_resfd = worker_event_fd_.get();
return block;
}
@@ -526,7 +570,8 @@
std::promise<void> destruction_notifier_;
std::once_flag error_flag_;
- unique_fd event_fd_;
+ unique_fd worker_event_fd_;
+ unique_fd monitor_event_fd_;
ScopedAioContext aio_context_;
unique_fd control_fd_;
@@ -551,10 +596,17 @@
size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
};
+void usb_init_legacy();
+
static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
while (true) {
+ if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+ LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+ return usb_init_legacy();
+ }
+
unique_fd control;
unique_fd bulk_out;
unique_fd bulk_in;
@@ -575,7 +627,6 @@
}
}
-void usb_init_legacy();
void usb_init() {
if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
usb_init_legacy();
diff --git a/adb/transport.cpp b/adb/transport.cpp
index ae53597..90f94ee 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -52,6 +52,8 @@
#include "fdevent.h"
#include "sysdeps/chrono.h"
+using android::base::ScopedLockAssertion;
+
static void remove_transport(atransport* transport);
static void transport_unref(atransport* transport);
@@ -72,17 +74,6 @@
namespace {
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
- public:
- ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
- ~ScopedAssumeLocked() RELEASE() {}
-};
-
#if ADB_HOST
// Tracks and handles atransport*s that are attempting reconnection.
class ReconnectHandler {
@@ -180,7 +171,7 @@
ReconnectAttempt attempt;
{
std::unique_lock<std::mutex> lock(reconnect_mutex_);
- ScopedAssumeLocked assume_lock(reconnect_mutex_);
+ ScopedLockAssertion assume_lock(reconnect_mutex_);
if (!reconnect_queue_.empty()) {
// FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -296,7 +287,7 @@
LOG(INFO) << this->transport_name_ << ": write thread spawning";
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
cv_.wait(lock, [this]() REQUIRES(mutex_) {
return this->stopped_ || !this->write_queue_.empty();
});
@@ -923,7 +914,7 @@
bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
return connection_established_ready_;
}) && connection_established_;
diff --git a/base/Android.bp b/base/Android.bp
index b0181aa..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -46,6 +46,7 @@
defaults: ["libbase_cflags_defaults"],
srcs: [
"chrono_utils.cpp",
+ "cmsg.cpp",
"file.cpp",
"logging.cpp",
"mapped_file.cpp",
@@ -85,6 +86,9 @@
"errors_windows.cpp",
"utf8.cpp",
],
+ exclude_srcs: [
+ "cmsg.cpp",
+ ],
enabled: true,
},
},
@@ -121,6 +125,7 @@
defaults: ["libbase_cflags_defaults"],
host_supported: true,
srcs: [
+ "cmsg_test.cpp",
"endian_test.cpp",
"errors_test.cpp",
"file_test.cpp",
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..42866f8
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ 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<unique_fd>* fds) {
+ fds->clear();
+
+ 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)) {
+ LOG(ERROR) << "message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ } else if ((msg.msg_flags & MSG_CTRUNC)) {
+ LOG(ERROR) << "control message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ }
+
+ std::vector<unique_fd> 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) {
+ LOG(ERROR) << "received unexpected cmsg: [" << 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) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+ } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+ }
+
+ 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) {
+ LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+ << received_fds.size();
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ *fds = std::move(received_fds);
+ return rc;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ PLOG(FATAL) << "fstat failed";
+ }
+
+ return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+ bool Seqpacket() { return GetParam(); }
+
+ void SetUp() override {
+ ASSERT_TRUE(
+ android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+ int dup1 = dup(tmp1.fd);
+ ASSERT_NE(-1, dup1);
+ int dup2 = dup(tmp2.fd);
+ ASSERT_NE(-1, dup2);
+
+ fd1.reset(dup1);
+ fd2.reset(dup2);
+
+ ino1 = GetInode(dup1);
+ ino2 = GetInode(dup2);
+ }
+
+ unique_fd send;
+ unique_fd recv;
+
+ TemporaryFile tmp1;
+ TemporaryFile tmp2;
+
+ unique_fd fd1;
+ unique_fd fd2;
+
+ ino_t ino1;
+ ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+ ASSERT_EQ('x', buf[0]);
+ ASSERT_NE(-1, received.get());
+
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+
+ ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+ if (Seqpacket()) {
+ ASSERT_EQ(-1, rc);
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received1.get());
+ ASSERT_EQ(-1, received2.get());
+ } else {
+ ASSERT_EQ(1, rc);
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+ ASSERT_EQ(1, read(recv.get(), buf, 2));
+ }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+ char buf[2];
+ ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+ ASSERT_EQ('a', buf[0]);
+
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+ if (Seqpacket()) {
+ return;
+ }
+
+ // fds are associated with the first byte of the write.
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+ char buf[2];
+ ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+ ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+ std::vector<unique_fd> received1;
+ ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_TRUE(received1.empty());
+
+ unique_fd received2;
+ rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('d', buf[0]);
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+ unique_fd sent1(dup(tmp1.fd));
+ unique_fd sent2(dup(tmp2.fd));
+
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+ char buf[2];
+ std::vector<unique_fd> received;
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..7f93ddc
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// 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<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(int sock, const void* data, size_t len, Args&&... sent_fds) {
+ // Do not allow implicit conversion to int: people might try to do something along the lines of:
+ // SendFileDescriptors(..., std::move(a_unique_fd))
+ // and be surprised when the unique_fd isn't closed afterwards.
+ AssertType<int>(std::forward<Args>(sent_fds)...);
+ std::vector<int> fds;
+ Append(fds, std::forward<Args>(sent_fds)...);
+ return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
+ std::vector<unique_fd*> fds;
+ Append(fds, std::forward<Args>(received_fds)...);
+
+ std::vector<unique_fd> result;
+ ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+ if (rc == -1 || result.size() != fds.size()) {
+ int err = rc == -1 ? errno : ENOMSG;
+ for (unique_fd* fd : fds) {
+ fd->reset();
+ }
+ errno = err;
+ return -1;
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ *fds[i] = std::move(result[i]);
+ }
+ return rc;
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+// template <typename... Args>
+// std::vector<int> CreateVector(Args&&... args) {
+// std::vector<int> result;
+// Append(result, std::forward<Args>(args)...);
+// return result;
+// }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+ collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+ collection.push_back(std::forward<T>(arg));
+ return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+ AssertType<T>(std::forward<Args>(args)...);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
#pragma once
+#include <mutex>
+
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#define CAPABILITY(x) \
@@ -104,3 +106,39 @@
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+// std::mutex mutex;
+// std::condition_variable cv;
+// std::vector<int> vec GUARDED_BY(mutex);
+//
+// int pop() {
+// std::unique_lock lock(mutex);
+// ScopedLockAssertion lock_assertion(mutex);
+// cv.wait(lock, []() {
+// ScopedLockAssertion lock_assertion(mutex);
+// return !vec.empty();
+// });
+//
+// int result = vec.back();
+// vec.pop_back();
+// return result;
+// }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+ ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedLockAssertion() RELEASE() {}
+};
+
+} // namespace base
+} // namespace android
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 610b96b..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -26,6 +26,7 @@
#include <chrono>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -41,6 +42,7 @@
using namespace std::chrono_literals;
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
@@ -146,15 +148,16 @@
PLOG(ERROR) << "failed to set pipe buffer size";
}
- if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+ pipe_write.reset();
+ if (rc != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
// Check to make sure we've successfully registered.
InterceptResponse response;
- ssize_t rc =
- TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
<< "timeout reached?";
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..64df53e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -53,6 +54,8 @@
#include "util.h"
using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
#if defined(__LP64__)
@@ -123,12 +126,14 @@
ASSERT_GE(pipe_buffer_size, 1024 * 1024);
- if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+ output_pipe_write.reset();
+ if (rc != sizeof(req)) {
FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
}
InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
if (rc == -1) {
FAIL() << "failed to read response from tombstoned: " << strerror(errno);
} else if (rc == 0) {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
#include <event2/event.h>
#include <event2/listener.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -31,6 +32,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
{
unique_fd rcv_fd;
InterceptRequest intercept_request;
- ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+ ssize_t result =
+ ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
if (result == -1) {
PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
#include <event2/listener.h>
#include <event2/thread.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
#include "intercept_manager.h"
using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -224,7 +226,10 @@
TombstonedCrashPacket response = {
.packet_type = CrashPacketType::kPerformDump
};
- ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+ ssize_t rc =
+ SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+ output_fd.reset();
+
if (rc == -1) {
PLOG(WARNING) << "failed to send response to CrashRequest";
goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
#include <utility>
+#include <android-base/cmsg.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <cutils/sockets.h>
@@ -28,6 +29,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
}
unique_fd tmp_output_fd;
- ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+ ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "protocol.h"
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
- return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- .msg_controllen = sizeof(cmsg_buf),
- .msg_flags = 0,
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
- ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
- if (result == -1) {
- return -1;
- }
-
- unique_fd fd;
- bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
- if (received_fd) {
- fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
- }
-
- if ((msg.msg_flags & MSG_TRUNC) != 0) {
- errno = EFBIG;
- return -1;
- } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
- errno = ERANGE;
- return -1;
- }
-
- if (out_fd) {
- *out_fd = std::move(fd);
- } else if (received_fd) {
- errno = ERANGE;
- return -1;
- }
-
- return result;
-}
-
std::string get_process_name(pid_t pid) {
std::string result = "<unknown>";
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-// EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-// The first len bytes of the packet are stored in data, but the
-// rest of the packet is dropped.
-// ERANGE: too many file descriptors were attached to the packet.
-// ENOMSG: not enough file descriptors were attached to the packet.
-//
-// plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
- android::base::unique_fd* _Nullable out_fd);
-
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4659add..82d9144 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -607,10 +607,14 @@
return userdata;
}
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
auto iter = std::remove_if(fstab->begin(), fstab->end(),
[&](const auto& entry) { return entry.mount_point == mount_point; });
- fstab->erase(iter, fstab->end());
+ if (iter != fstab->end()) {
+ fstab->erase(iter, fstab->end());
+ return true;
+ }
+ return false;
}
void TransformFstabForGsi(Fstab* fstab) {
@@ -628,11 +632,13 @@
userdata = BuildGsiUserdataFstabEntry();
}
- EraseFstabEntry(fstab, "/system");
- EraseFstabEntry(fstab, "/data");
+ if (EraseFstabEntry(fstab, "/system")) {
+ fstab->emplace_back(BuildGsiSystemFstabEntry());
+ }
- fstab->emplace_back(BuildGsiSystemFstabEntry());
- fstab->emplace_back(userdata);
+ if (EraseFstabEntry(fstab, "/data")) {
+ fstab->emplace_back(userdata);
+ }
}
} // namespace
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 565597a..59af924 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -53,3 +53,15 @@
},
host_supported: true,
}
+
+java_test_host {
+ name: "fs_mgr_vendor_overlay_test",
+
+ srcs: ["src/**/VendorOverlayHostTest.java"],
+
+ libs: ["tradefed"],
+
+ test_config: "vendor-overlay-test.xml",
+
+ test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+ boolean wasRoot = false;
+
+ @Before
+ public void setup() throws DeviceNotAvailableException {
+ wasRoot = getDevice().isAdbRoot();
+ if (!wasRoot) {
+ Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+ }
+
+ Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+ testConditionsMet());
+
+ getDevice().remountSystemWritable();
+ // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+ Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+ Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+ CommandResult result = getDevice().executeShellV2Command("df");
+ Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+ vendorPattern.matcher(result.getStdout()).find());
+ Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+ productPattern.matcher(result.getStdout()).find());
+ }
+
+ private boolean cmdSucceeded(CommandResult result) {
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
+ private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+ CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+ Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+ }
+
+ /**
+ * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+ */
+ @Test
+ public void testVendorOverlay() throws DeviceNotAvailableException {
+ String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+ // Create files and modify policy
+ CommandResult result = getDevice().executeShellV2Command(
+ "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+ "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+ Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+ assumeMkdirSuccess("/vendor/testdir");
+ assumeMkdirSuccess("/vendor/diffcontext");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ result = getDevice().executeShellV2Command(
+ "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+ Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+ result = getDevice().executeShellV2Command(
+ "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+ getDevice().reboot();
+
+ // Test that the file was overlaid properly
+ result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+ Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+ Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+ Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+ }
+
+ // Duplicate of fs_mgr_overlayfs_valid() logic
+ // Requires root
+ public boolean testConditionsMet() throws DeviceNotAvailableException {
+ if (cmdSucceeded(getDevice().executeShellV2Command(
+ "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+ return true;
+ }
+ if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+ return false;
+ }
+ CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+ Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+ Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+ kernelVersionMatcher.find();
+ int majorKernelVersion;
+ int minorKernelVersion;
+ try {
+ majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+ minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+ } catch (Exception e) {
+ return false;
+ }
+ if (majorKernelVersion < 4) {
+ return true;
+ }
+ if (majorKernelVersion > 4) {
+ return false;
+ }
+ if (minorKernelVersion > 6) {
+ return false;
+ }
+ return true;
+ }
+
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+ getDevice().reboot();
+ }
+ if (!wasRoot) {
+ getDevice().disableAdbRoot();
+ }
+ }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for vendor overlay test cases">
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+ </test>
+</configuration>
+
diff --git a/init/property_service.cpp b/init/property_service.cpp
index d60b654..4bc857a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -950,6 +950,13 @@
LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
&property_infos);
}
+ if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+ &property_infos);
+ }
+ if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+ }
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
@@ -958,6 +965,8 @@
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
+ LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5394d7e..09998f0 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -137,6 +137,12 @@
static constexpr const char* kApexPath = "/apex/";
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+#endif
+
static bool is_debuggable() {
char debuggable[PROP_VALUE_MAX];
property_get("ro.debuggable", debuggable, "0");
@@ -408,6 +414,14 @@
}
}
+ // Remove the public libs in the runtime namespace.
+ // These libs are listed in public.android.txt, but we don't want the rest of android
+ // in default namespace to dlopen the libs.
+ // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+ // Unfortunately, it does not have stable C symbols, and default namespace should only use
+ // stable symbols in libandroidicu.so. http://b/120786417
+ removePublicLibsIfExistsInRuntimeApex(sonames);
+
// android_init_namespaces() expects all the public libraries
// to be loaded so that they can be found by soname alone.
//
@@ -502,6 +516,27 @@
}
}
+ /**
+ * Remove the public libs in runtime namespace
+ */
+ void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+ for (const std::string& lib_name : kRuntimePublicLibraries) {
+ std::string path(kRuntimeApexLibPath);
+ path.append("/").append(lib_name);
+
+ struct stat s;
+ // Do nothing if the path in /apex does not exist.
+ // Runtime APEX must be mounted since libnativeloader is in the same APEX
+ if (stat(path.c_str(), &s) != 0) {
+ continue;
+ }
+
+ auto it = std::find(sonames.begin(), sonames.end(), lib_name);
+ if (it != sonames.end()) {
+ sonames.erase(it);
+ }
+ }
+ }
bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
const std::function<bool(const std::string& /* soname */,
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index af2ec0f..e4d3d5e 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "bionic-unit-tests"
+ "name": "CtsBionicTestCases"
}
]
}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e897d81..2d44bb6 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -54,6 +54,10 @@
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index df9abbd..cdfcfe1 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -132,6 +132,10 @@
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -481,7 +485,8 @@
namespace.system.link.runtime.shared_libs += libnativebridge.so
namespace.system.link.runtime.shared_libs += libnativehelper.so
namespace.system.link.runtime.shared_libs += libnativeloader.so
-
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
###############################################################################
# Namespace config for native tests that need access to both system and vendor
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 3c97a49..59fd568 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -72,6 +72,10 @@
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
# When libnetd_resolv.so can't be found in the default namespace, search for it
# in the resolv namespace. Don't allow any other libraries from the resolv namespace
@@ -340,6 +344,8 @@
namespace.default.link.runtime.shared_libs += libnativebridge.so
namespace.default.link.runtime.shared_libs += libnativehelper.so
namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
###############################################################################
# "runtime" APEX namespace
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index d10f7c1..d5665f2 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,5 +1,5 @@
# set up the global environment
-on init
+on early-init
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 57032bc..8bd7c4c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -538,7 +538,7 @@
mkdir /data/apex/active 0750 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/sessions 0700 root system
- mkdir /data/staging 0750 system system
+ mkdir /data/pkg_staging 0750 system system
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc