Merge changes from topic "charger-shared-libs"
* changes:
healthd: Add charger.recovery module.
healthd: charger uses shared libraries.
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 3286959..3d5d9db 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -222,7 +222,9 @@
" all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
" $ADB_VENDOR_KEYS colon-separated list of keys (files or directories)\n"
" $ANDROID_SERIAL serial number to connect to (see -s)\n"
- " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n");
+ " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n"
+ " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+ );
// clang-format on
}
@@ -1003,7 +1005,8 @@
#endif /* !defined(_WIN32) */
}
-static bool wait_for_device(const char* service) {
+static bool wait_for_device(const char* service,
+ std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
std::vector<std::string> components = android::base::Split(service, "-");
if (components.size() < 3 || components.size() > 4) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
@@ -1041,6 +1044,13 @@
}
std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+ if (timeout) {
+ std::thread([timeout]() {
+ std::this_thread::sleep_for(*timeout);
+ fprintf(stderr, "timeout expired while waiting for device\n");
+ _exit(1);
+ }).detach();
+ }
return adb_command(cmd);
}
@@ -1082,8 +1092,21 @@
}
// Wait for the device to go away.
+ TransportType previous_type;
+ const char* previous_serial;
+ TransportId previous_id;
+ adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
adb_set_transport(kTransportAny, nullptr, transport_id);
wait_for_device("wait-for-disconnect");
+
+ // Wait for the device to come back.
+ // If we were using a specific transport ID, there's nothing we can wait for.
+ if (previous_id == 0) {
+ adb_set_transport(previous_type, previous_serial, 0);
+ wait_for_device("wait-for-device", 3000ms);
+ }
+
return true;
}
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index 7999ddc..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -27,68 +27,62 @@
#include "adb_io.h"
#include "adb_unique_fd.h"
-void remount_service(unique_fd fd, const std::string& cmd) {
- static constexpr char remount_cmd[] = "/system/bin/remount";
- static constexpr char remount_failed[] = "remount failed\n";
+static constexpr char kRemountCmd[] = "/system/bin/remount";
+static bool do_remount(int fd, const std::string& cmd) {
if (getuid() != 0) {
- WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
- WriteFdExactly(fd.get(), remount_failed);
- return;
+ WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
+ return false;
}
- auto pid = vfork();
+ auto pid = fork();
if (pid < 0) {
- WriteFdFmt(fd.get(), "Failed to fork to %s: %s\n", remount_cmd, strerror(errno));
- WriteFdExactly(fd.get(), remount_failed);
- return;
+ WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
+ return false;
}
if (pid == 0) {
// child side of the fork
- fcntl(fd.get(), F_SETFD, 0);
- dup2(fd.get(), STDIN_FILENO);
- dup2(fd.get(), STDOUT_FILENO);
- dup2(fd.get(), STDERR_FILENO);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
- execl(remount_cmd, remount_cmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
- _exit(-errno ?: 42);
+ execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+ _exit(errno);
}
int wstatus = 0;
auto ret = waitpid(pid, &wstatus, 0);
if (ret == -1) {
- WriteFdFmt(fd.get(), "Failed to wait for %s: %s\n", remount_cmd, strerror(errno));
- goto err;
- }
-
- if (ret != pid) {
- WriteFdFmt(fd.get(), "pid %d and waitpid return %d do not match for %s\n",
- static_cast<int>(pid), static_cast<int>(ret), remount_cmd);
- goto err;
+ WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+ return false;
+ } else if (ret != pid) {
+ WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+ static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
+ return false;
}
if (WIFSIGNALED(wstatus)) {
- WriteFdFmt(fd.get(), "%s terminated with signal %s\n", remount_cmd,
+ WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
strsignal(WTERMSIG(wstatus)));
- goto err;
+ return false;
}
if (!WIFEXITED(wstatus)) {
- WriteFdFmt(fd.get(), "%s stopped with status 0x%x\n", remount_cmd, wstatus);
- goto err;
+ WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
+ return false;
}
if (WEXITSTATUS(wstatus)) {
- WriteFdFmt(fd.get(), "%s exited with status %d\n", remount_cmd,
- static_cast<signed char>(WEXITSTATUS(wstatus)));
- goto err;
+ WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
+ return false;
}
- WriteFdExactly(fd.get(), "remount succeeded\n");
- return;
+ return true;
+}
-err:
- WriteFdExactly(fd.get(), remount_failed);
+void remount_service(unique_fd fd, const std::string& cmd) {
+ const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+ WriteFdFmt(fd.get(), "remount %s\n", success);
}
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 658261e..889229f 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -52,14 +52,13 @@
}
static bool make_block_device_writable(const std::string& dev) {
- int fd = unix_open(dev, O_RDONLY | O_CLOEXEC);
+ unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
if (fd == -1) {
return false;
}
int OFF = 0;
bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
- unix_close(fd);
return result;
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 598f2cd..f4458a2 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -270,29 +270,15 @@
bool started = false;
bool running = true;
while (running) {
- int timeout = -1;
- if (!bound || !started) {
- 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));
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, -1));
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.
- StopWorker();
- 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;
+ LOG(FATAL) << "poll on USB control fd returned 0";
}
if (pfd[1].revents) {
@@ -654,9 +640,10 @@
}
void usb_init() {
- if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
- usb_init_legacy();
- } else {
+ bool use_nonblocking = android::base::GetBoolProperty("persist.adb.nonblocking_ffs", true);
+ if (use_nonblocking) {
std::thread(usb_ffs_open_thread).detach();
+ } else {
+ usb_init_legacy();
}
}
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 9a74fb3..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -53,12 +53,27 @@
// Android Wear has been using port 5601 in all of its documentation/tooling,
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
static std::mutex& local_transports_lock = *new std::mutex();
+static void adb_local_transport_max_port_env_override() {
+ const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+ if (env_max_s != nullptr) {
+ size_t env_max;
+ if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+ // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+ adb_local_transport_max_port = env_max;
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+ } else {
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+ env_max_s);
+ }
+ }
+}
+
// We keep a map from emulator port to transport.
// TODO: weak_ptr?
static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -110,7 +125,6 @@
D("reconnect failed: %s", response.c_str());
return ReconnectResult::Retry;
}
-
// This invokes the part of register_socket_transport() that needs to be
// invoked if the atransport* has already been setup. This eventually
// calls atransport->SetConnection() with a newly created Connection*
@@ -168,12 +182,10 @@
#if ADB_HOST
static void PollAllLocalPortsForEmulator() {
- int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- int count = ADB_LOCAL_TRANSPORT_MAX;
-
// Try to connect to any number of running emulator instances.
- for ( ; count > 0; count--, port += 2 ) {
- local_connect(port);
+ for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+ port += 2) {
+ local_connect(port); // Note, uses port and port-1, so '=max_port' is OK.
}
}
@@ -289,6 +301,7 @@
#if ADB_HOST
D("transport: local client init");
std::thread(client_socket_thread, port).detach();
+ adb_local_transport_max_port_env_override();
#elif !defined(__ANDROID__)
// Host adbd.
D("transport: local server init");
@@ -371,10 +384,6 @@
if (existing_transport != nullptr) {
D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
fail = -1;
- } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
- // Too many emulators.
- D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
- fail = -1;
} else {
local_transports[adb_port] = t;
}
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index faa845d..7c65dc3 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -41,7 +41,14 @@
HANDLE handle =
CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
(prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
- if (handle == nullptr) return nullptr;
+ if (handle == nullptr) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // Return a MappedFile that's only valid for reading the size.
+ if (length == 0) {
+ return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+ }
+ return nullptr;
+ }
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
file_offset, file_length);
if (base == nullptr) {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index ed955ea..ea49798 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -293,6 +293,8 @@
{"kernel_panic,dsps", 166},
{"kernel_panic,wcnss", 167},
{"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+ {"recovery,quiescent", 169},
+ {"reboot,quiescent", 170},
};
// Converts a string value representing the reason the system booted to an
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 40da36d..e61f588 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -769,9 +769,10 @@
// in recovery, fastbootd, or gsi mode, not allowed!
if (fs_mgr_access("/system/bin/recovery")) return true;
- if (android::gsi::IsGsiRunning()) return true;
-
- return false;
+ auto save_errno = errno;
+ auto ret = android::gsi::IsGsiRunning();
+ errno = save_errno;
+ return ret;
}
} // namespace
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
index 7463810..32fc3d2 100644
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -41,6 +41,9 @@
cc_test {
name: "fiemap_writer_test",
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
static_libs: [
"libbase",
"libdm",
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 3d41876..45b997f 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -51,6 +51,9 @@
FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+// Large file support must be enabled.
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
static inline void cleanup(const std::string& file_path, bool created) {
if (created) {
unlink(file_path.c_str());
@@ -229,6 +232,42 @@
return true;
}
+static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
+ const std::string& file_path,
+ const std::function<bool(uint64_t, uint64_t)>& on_progress) {
+ // Even though this is much faster than writing zeroes, it is still slow
+ // enough that we need to fire the progress callback periodically. To
+ // easily achieve this, we seek in chunks. We use 1000 chunks since
+ // normally we only fire the callback on 1/1000th increments.
+ uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
+
+ // Seek just to the end of each chunk and write a single byte, causing
+ // the filesystem to allocate blocks.
+ off_t cursor = 0;
+ off_t end = static_cast<off_t>(file_size);
+ while (cursor < end) {
+ cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
+ auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
+ if (rv < 0) {
+ PLOG(ERROR) << "Failed to lseek " << file_path;
+ return false;
+ }
+ if (rv != cursor - 1) {
+ LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
+ return false;
+ }
+ char buffer[] = {0};
+ if (!android::base::WriteFully(file_fd, buffer, 1)) {
+ PLOG(ERROR) << "Write failed: " << file_path;
+ return false;
+ }
+ if (on_progress && !on_progress(cursor, file_size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
uint64_t file_size, unsigned int fs_type,
std::function<bool(uint64_t, uint64_t)> on_progress) {
@@ -245,28 +284,10 @@
return false;
}
break;
- case MSDOS_SUPER_MAGIC: {
+ case MSDOS_SUPER_MAGIC:
// fallocate() is not supported, and not needed, since VFAT does not support holes.
// Instead we can perform a much faster allocation.
- auto offset = TEMP_FAILURE_RETRY(lseek(file_fd, file_size - 1, SEEK_SET));
- if (offset < 0) {
- PLOG(ERROR) << "Failed to lseek " << file_path;
- return false;
- }
- if (offset != file_size - 1) {
- LOG(ERROR) << "Seek returned wrong offset " << offset << " for file " << file_path;
- return false;
- }
- char buffer[] = {0};
- if (!android::base::WriteFully(file_fd, buffer, 1)) {
- PLOG(ERROR) << "Write failed: " << file_path;
- return false;
- }
- if (on_progress && !on_progress(file_size, file_size)) {
- return false;
- }
- return true;
- }
+ return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
default:
LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
return false;
@@ -288,16 +309,19 @@
}
int permille = -1;
- for (; offset < file_size; offset += blocksz) {
+ while (offset < file_size) {
if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
<< " in file " << file_path;
return false;
}
+
+ offset += blocksz;
+
// Don't invoke the callback every iteration - wait until a significant
// chunk (here, 1/1000th) of the data has been processed.
int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
- if (new_permille != permille) {
+ if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
if (on_progress && !on_progress(offset, file_size)) {
return false;
}
@@ -501,12 +525,13 @@
}
if (!extents->empty() && block == last_block + 1) {
- extents->back().fe_length++;
+ extents->back().fe_length += s.st_blksize;
} else {
- extents->push_back(fiemap_extent{.fe_logical = block_number,
- .fe_physical = block,
- .fe_length = 1,
- .fe_flags = 0});
+ extents->push_back(
+ fiemap_extent{.fe_logical = block_number,
+ .fe_physical = static_cast<uint64_t>(block) * s.st_blksize,
+ .fe_length = static_cast<uint64_t>(s.st_blksize),
+ .fe_flags = 0});
}
last_block = block;
}
@@ -618,7 +643,6 @@
fmap->file_path_ = abs_path;
fmap->bdev_path_ = bdev_path;
- fmap->file_fd_ = std::move(file_fd);
fmap->file_size_ = file_size;
fmap->bdev_size_ = bdevsz;
fmap->fs_type_ = fs_type;
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
index ab4efae..4a1a5ab 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
@@ -38,6 +38,9 @@
#include "utility.h"
+namespace android {
+namespace fiemap_writer {
+
using namespace std;
using namespace std::string_literals;
using namespace android::fiemap_writer;
@@ -105,28 +108,29 @@
EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
}
+TEST_F(FiemapWriterTest, CheckFileSize) {
+ // Create a large-ish file and test that the expected size matches.
+ FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
+ ASSERT_NE(fptr, nullptr);
+
+ struct stat s;
+ ASSERT_EQ(stat(testfile.c_str(), &s), 0);
+ EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
+}
+
TEST_F(FiemapWriterTest, CheckProgress) {
std::vector<uint64_t> expected;
size_t invocations = 0;
auto callback = [&](uint64_t done, uint64_t total) -> bool {
- EXPECT_LT(invocations, expected.size());
+ if (invocations >= expected.size()) {
+ return false;
+ }
EXPECT_EQ(done, expected[invocations]);
EXPECT_EQ(total, gBlockSize);
invocations++;
return true;
};
- uint32_t fs_type;
- {
- auto ptr = FiemapWriter::Open(testfile, gBlockSize, true);
- ASSERT_NE(ptr, nullptr);
- fs_type = ptr->fs_type();
- }
- ASSERT_EQ(unlink(testfile.c_str()), 0);
-
- if (fs_type != MSDOS_SUPER_MAGIC) {
- expected.push_back(0);
- }
expected.push_back(gBlockSize);
auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
@@ -160,7 +164,7 @@
struct stat sb;
ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
- EXPECT_EQ(sb.st_size, testfile_size);
+ EXPECT_GE(sb.st_size, testfile_size);
}
TEST_F(FiemapWriterTest, CheckFileExtents) {
@@ -224,7 +228,7 @@
}
TEST_F(SplitFiemapTest, DeleteOnFail) {
- auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 10, 1);
+ auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
ASSERT_EQ(ptr, nullptr);
std::string first_file = testfile + ".0001";
@@ -234,6 +238,105 @@
ASSERT_EQ(errno, ENOENT);
}
+static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
+ std::string result;
+ for (int i = 0; i < num_files; i++) {
+ std::string path = base_path + android::base::StringPrintf(".%04d", i);
+ std::string data;
+ if (!android::base::ReadFileToString(path, &data)) {
+ return {};
+ }
+ result += data;
+ }
+ return result;
+}
+
+TEST_F(SplitFiemapTest, WriteWholeFile) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+ ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks1) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+
+ // Write in chunks of 1000 (so some writes straddle the boundary of two
+ // files).
+ size_t bytes_written = 0;
+ while (bytes_written < kSize) {
+ size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
+ char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+ ASSERT_TRUE(ptr->Write(data, to_write));
+ bytes_written += to_write;
+ }
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WriteFileInChunks2) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+
+ // Write in chunks of 32KiB so every write is exactly at the end of the
+ // current file.
+ size_t bytes_written = 0;
+ while (bytes_written < kSize) {
+ size_t to_write = std::min(kSize - bytes_written, kChunkSize);
+ char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
+ ASSERT_TRUE(ptr->Write(data, to_write));
+ bytes_written += to_write;
+ }
+
+ std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
+ auto actual = ReadSplitFiles(testfile, 3);
+ ASSERT_EQ(expected.size(), actual.size());
+ EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
+}
+
+TEST_F(SplitFiemapTest, WritePastEnd) {
+ static constexpr size_t kChunkSize = 32768;
+ static constexpr size_t kSize = kChunkSize * 3;
+ auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
+ ASSERT_NE(ptr, nullptr);
+
+ auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
+ for (size_t i = 0; i < kSize / sizeof(int); i++) {
+ buffer[i] = i;
+ }
+ ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
+ ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
+}
+
class VerifyBlockWritesExt4 : public ::testing::Test {
// 2GB Filesystem and 4k block size by default
static constexpr uint64_t block_size = 4096;
@@ -333,6 +436,11 @@
return true;
}
+} // namespace fiemap_writer
+} // namespace android
+
+using namespace android::fiemap_writer;
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
if (argc <= 1) {
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
index 831bc75..9486122 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -88,9 +88,6 @@
// Block device on which we have created the file.
std::string bdev_path_;
- // File descriptors for the file and block device
- ::android::base::unique_fd file_fd_;
-
// Size in bytes of the file this class is writing
uint64_t file_size_;
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
index 765cc84..7b977e1 100644
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
@@ -23,6 +23,8 @@
#include <string>
#include <vector>
+#include <android-base/unique_fd.h>
+
#include "fiemap_writer.h"
namespace android {
@@ -54,9 +56,20 @@
// this returns true and does not report an error.
static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
+ // Return whether all components of a split file still have pinned extents.
+ bool HasPinnedExtents() const;
+
+ // Helper method for writing data that spans files. Note there is no seek
+ // method (yet); this starts at 0 and increments the position by |bytes|.
+ bool Write(const void* data, uint64_t bytes);
+
+ // Flush all writes to all split files.
+ bool Flush();
+
const std::vector<struct fiemap_extent>& extents();
uint32_t block_size() const;
uint64_t size() const { return total_size_; }
+ const std::string& bdev_path() const;
// Non-copyable & Non-movable
SplitFiemap(const SplitFiemap&) = delete;
@@ -73,6 +86,11 @@
std::vector<FiemapUniquePtr> files_;
std::vector<struct fiemap_extent> extents_;
uint64_t total_size_ = 0;
+
+ // Most recently open file and position for Write().
+ size_t cursor_index_ = 0;
+ uint64_t cursor_file_pos_ = 0;
+ android::base::unique_fd cursor_fd_;
};
} // namespace fiemap_writer
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
index 1f80370..a0ccc10 100644
--- a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -176,6 +176,15 @@
return ok;
}
+bool SplitFiemap::HasPinnedExtents() const {
+ for (const auto& file : files_) {
+ if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+ return false;
+ }
+ }
+ return true;
+}
+
const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
if (extents_.empty()) {
for (const auto& file : files_) {
@@ -186,6 +195,76 @@
return extents_;
}
+bool SplitFiemap::Write(const void* data, uint64_t bytes) {
+ // Open the current file.
+ FiemapWriter* file = files_[cursor_index_].get();
+
+ const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
+ uint64_t bytes_remaining = bytes;
+ while (bytes_remaining) {
+ // How many bytes can we write into the current file?
+ uint64_t file_bytes_left = file->size() - cursor_file_pos_;
+ if (!file_bytes_left) {
+ if (cursor_index_ == files_.size() - 1) {
+ LOG(ERROR) << "write past end of file requested";
+ return false;
+ }
+
+ // No space left in the current file, but we have more files to
+ // use, so prep the next one.
+ cursor_fd_ = {};
+ cursor_file_pos_ = 0;
+ file = files_[++cursor_index_].get();
+ file_bytes_left = file->size();
+ }
+
+ // Open the current file if it's not open.
+ if (cursor_fd_ < 0) {
+ cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
+ if (cursor_fd_ < 0) {
+ PLOG(ERROR) << "open failed: " << file->file_path();
+ return false;
+ }
+ CHECK(cursor_file_pos_ == 0);
+ }
+
+ if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
+ LOG(ERROR) << "file is no longer pinned: " << file->file_path();
+ return false;
+ }
+
+ uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
+ if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
+ PLOG(ERROR) << "write failed: " << file->file_path();
+ return false;
+ }
+ data_ptr += bytes_to_write;
+ bytes_remaining -= bytes_to_write;
+ cursor_file_pos_ += bytes_to_write;
+ }
+
+ // If we've reached the end of the current file, close it for sanity.
+ if (cursor_file_pos_ == file->size()) {
+ cursor_fd_ = {};
+ }
+ return true;
+}
+
+bool SplitFiemap::Flush() {
+ for (const auto& file : files_) {
+ unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << file->file_path();
+ return false;
+ }
+ if (fsync(fd)) {
+ PLOG(ERROR) << "fsync failed: " << file->file_path();
+ return false;
+ }
+ }
+ return true;
+}
+
SplitFiemap::~SplitFiemap() {
if (!creating_) {
return;
@@ -210,5 +289,9 @@
return files_[0]->block_size();
}
+const std::string& SplitFiemap::bdev_path() const {
+ return files_[0]->bdev_path();
+}
+
} // namespace fiemap_writer
} // namespace android
diff --git a/init/Android.mk b/init/Android.mk
index ac05542..cc514ed 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -63,8 +63,9 @@
LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
# Set up the same mount points on the ramdisk that system-as-root contains.
-LOCAL_POST_INSTALL_CMD := \
- mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+ $(TARGET_RAMDISK_OUT)/apex \
+ $(TARGET_RAMDISK_OUT)/dev \
$(TARGET_RAMDISK_OUT)/mnt \
$(TARGET_RAMDISK_OUT)/proc \
$(TARGET_RAMDISK_OUT)/sys \
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 4bca09e..94dd553 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -16,6 +16,7 @@
#include "epoll.h"
+#include <stdint.h>
#include <sys/epoll.h>
#include <chrono>
@@ -37,13 +38,16 @@
return Success();
}
-Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+ if (!events) {
+ return Error() << "Must specify events";
+ }
auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
if (!inserted) {
return Error() << "Cannot specify two epoll handlers for a given FD";
}
epoll_event ev;
- ev.events = EPOLLIN;
+ ev.events = events;
// std::map's iterators do not get invalidated until erased, so we use the
// pointer to the std::function in the map directly for epoll_ctl.
ev.data.ptr = reinterpret_cast<void*>(&it->second);
diff --git a/init/epoll.h b/init/epoll.h
index 85a791c..9789bef 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -17,6 +17,9 @@
#ifndef _INIT_EPOLL_H
#define _INIT_EPOLL_H
+#include <stdint.h>
+#include <sys/epoll.h>
+
#include <chrono>
#include <functional>
#include <map>
@@ -34,7 +37,8 @@
Epoll();
Result<Success> Open();
- Result<Success> RegisterHandler(int fd, std::function<void()> handler);
+ Result<Success> RegisterHandler(int fd, std::function<void()> handler,
+ uint32_t events = EPOLLIN);
Result<Success> UnregisterHandler(int fd);
Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
diff --git a/init/service.cpp b/init/service.cpp
index 2186a85..cba42c4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1059,7 +1059,7 @@
std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
if (!WriteStringToFile(oom_str, oom_file)) {
- PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+ PLOG(ERROR) << "couldn't write oom_score_adj";
}
}
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 0fd2a3a..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,7 +27,6 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
- double_loadable: true,
recovery_available: true,
host_supported: true,
srcs: [
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f1dcd50..a3df380 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -219,8 +219,6 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 10f5bc0..f6cae36 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -23,6 +23,9 @@
extern "C" {
#endif
+#define NATIVE_HANDLE_MAX_FDS 1024
+#define NATIVE_HANDLE_MAX_INTS 1024
+
/* Declare a char array for use with native_handle_init */
#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
alignas(native_handle_t) char (name)[ \
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 66f7a3d..b409e5b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,9 +22,6 @@
#include <string.h>
#include <unistd.h>
-static const int kMaxNativeFds = 1024;
-static const int kMaxNativeInts = 1024;
-
native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
if ((uintptr_t) storage % alignof(native_handle_t)) {
errno = EINVAL;
@@ -39,7 +36,8 @@
}
native_handle_t* native_handle_create(int numFds, int numInts) {
- if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
+ numInts > NATIVE_HANDLE_MAX_INTS) {
errno = EINVAL;
return NULL;
}
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index e816926..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -19,13 +19,14 @@
cc_binary {
name: "mini-keyctl",
- srcs: ["mini_keyctl.cpp"],
-
+ srcs: [
+ "mini_keyctl.cpp",
+ "mini_keyctl_utils.cpp"
+ ],
shared_libs: [
"libbase",
"libkeyutils",
"liblog",
],
-
- cflags: ["-Werror", "-Wall", "-Wextra"],
+ cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
}
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
index abc8f82..4fe4c3c 100644
--- a/libkeyutils/mini_keyctl.cpp
+++ b/libkeyutils/mini_keyctl.cpp
@@ -18,159 +18,57 @@
* A tool loads keys to keyring.
*/
-#include <dirent.h>
-#include <errno.h>
-#include <sys/types.h>
+#include "mini_keyctl_utils.h"
+
#include <unistd.h>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
-// added.
-int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& keyring_desc,
- int start_index) {
- std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(path.c_str()), closedir);
- if (!dir) {
- PLOG(WARNING) << "Failed to open directory " << path;
- return 0;
- }
- int keys_added = 0;
- struct dirent* dp;
- while ((dp = readdir(dir.get())) != NULL) {
- if (dp->d_type != DT_REG) {
- continue;
- }
- std::string cert_path = path + "/" + dp->d_name;
- std::string cert_buf;
- if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
- LOG(ERROR) << "Failed to read " << cert_path;
- continue;
- }
-
- if (cert_buf.size() > kMaxCertSize) {
- LOG(ERROR) << "Certficate size too large: " << cert_path;
- continue;
- }
-
- // Add key to keyring.
- int key_desc_index = keys_added + start_index;
- std::string key_desc = keyring_desc + "-key" + std::to_string(key_desc_index);
- key_serial_t key =
- add_key("asymmetric", key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
- if (key < 0) {
- PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
- continue;
- }
- keys_added++;
- }
- return keys_added;
-}
-
-std::vector<std::string> SplitBySpace(const std::string& s) {
- std::istringstream iss(s);
- return std::vector<std::string>{std::istream_iterator<std::string>{iss},
- std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
- if (!keyring_id) {
- LOG(ERROR) << "keyring_id is null";
- return false;
- }
-
- // Only keys allowed by SELinux rules will be shown here.
- std::ifstream proc_keys_file("/proc/keys");
- if (!proc_keys_file.is_open()) {
- PLOG(ERROR) << "Failed to open /proc/keys";
- return false;
- }
-
- std::string line;
- while (getline(proc_keys_file, line)) {
- std::vector<std::string> tokens = SplitBySpace(line);
- if (tokens.size() < 9) {
- continue;
- }
- std::string key_id = tokens[0];
- std::string key_type = tokens[7];
- // The key description may contain space.
- std::string key_desc_prefix = tokens[8];
- // The prefix has a ":" at the end
- std::string key_desc_pattern = keyring_desc + ":";
- if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
- continue;
- }
- *keyring_id = std::stoi(key_id, nullptr, 16);
- return true;
- }
- return false;
-}
-
static void Usage(int exit_code) {
- fprintf(stderr, "usage: mini-keyctl -c PATHS -s DESCRIPTION\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "-c, --cert_dirs the certificate locations, separated by comma\n");
- fprintf(stderr, "-k, --keyring the keyring description\n");
+ fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+ fprintf(stderr, " mini-keyctl add <type> <desc> <data> <keyring>\n");
+ fprintf(stderr, " mini-keyctl padd <type> <desc> <keyring>\n");
+ fprintf(stderr, " mini-keyctl dadd <type> <desc_prefix> <cert_dir> <keyring>\n");
+ fprintf(stderr, " mini-keyctl unlink <key> <keyring>\n");
+ fprintf(stderr, " mini-keyctl restrict_keyring <keyring>\n");
_exit(exit_code);
}
-int main(int argc, char** argv) {
- if (argc < 5) Usage(1);
+int main(int argc, const char** argv) {
+ if (argc < 2) Usage(1);
+ const std::string action = argv[1];
- std::string arg_cert_dirs;
- std::string arg_keyring_desc;
-
- for (int i = 1; i < argc; i++) {
- std::string option = argv[i];
- if (option == "-c" || option == "--cert_dirs") {
- if (i + 1 < argc) arg_cert_dirs = argv[++i];
- } else if (option == "-k" || option == "--keyring") {
- if (i + 1 < argc) arg_keyring_desc = argv[++i];
- }
- }
-
- if (arg_cert_dirs.empty() || arg_keyring_desc.empty()) {
- LOG(ERROR) << "Missing cert_dirs or keyring desc";
+ if (action == "add") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string data = argv[4];
+ std::string keyring = argv[5];
+ return Add(type, desc, data, keyring);
+ } else if (action == "dadd") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ // The key description contains desc_prefix and an index.
+ std::string desc_prefix = argv[3];
+ std::string cert_dir = argv[4];
+ std::string keyring = argv[5];
+ return AddCertsFromDir(type, desc_prefix, cert_dir, keyring);
+ } else if (action == "padd") {
+ if (argc != 5) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string keyring = argv[4];
+ return Padd(type, desc, keyring);
+ } else if (action == "restrict_keyring") {
+ if (argc != 3) Usage(1);
+ std::string keyring = argv[2];
+ return RestrictKeyring(keyring);
+ } else if (action == "unlink") {
+ if (argc != 4) Usage(1);
+ key_serial_t key = std::stoi(argv[2], nullptr, 16);
+ const std::string keyring = argv[3];
+ return Unlink(key, keyring);
+ } else {
Usage(1);
}
- // Get the keyring id
- key_serial_t key_ring_id;
- if (!GetKeyringId(arg_keyring_desc, &key_ring_id)) {
- PLOG(ERROR) << "Can't find keyring with " << arg_keyring_desc;
- return 1;
- }
-
- std::vector<std::string> cert_dirs = android::base::Split(arg_cert_dirs, ",");
- int start_index = 0;
- for (const auto& cert_dir : cert_dirs) {
- int keys_added = AddKeys(cert_dir, key_ring_id, arg_keyring_desc, start_index);
- start_index += keys_added;
- }
-
- // Prevent new keys to be added.
- if (!android::base::GetBoolProperty("ro.debuggable", false) &&
- keyctl_restrict_keyring(key_ring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Failed to restrict key ring " << arg_keyring_desc;
- return 1;
- }
-
return 0;
}
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..c4fc96c
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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 <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+std::vector<std::string> SplitBySpace(const std::string& s) {
+ std::istringstream iss(s);
+ return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+ std::istream_iterator<std::string>{}};
+}
+
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+ const std::string& cert_dir, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(cert_dir.c_str()), closedir);
+ if (!dir) {
+ PLOG(WARNING) << "Failed to open directory " << cert_dir;
+ return 1;
+ }
+ int keys_added = 0;
+ struct dirent* dp;
+ while ((dp = readdir(dir.get())) != NULL) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cert_path = cert_dir + "/" + dp->d_name;
+ std::string cert_buf;
+ if (!android::base::ReadFileToString(cert_path, &cert_buf, false /* follow_symlinks */)) {
+ LOG(ERROR) << "Failed to read " << cert_path;
+ continue;
+ }
+
+ if (cert_buf.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certficate size too large: " << cert_path;
+ continue;
+ }
+
+ // Add key to keyring.
+ int key_desc_index = keys_added;
+ std::string key_desc = desc_prefix + std::to_string(key_desc_index);
+ key_serial_t key =
+ add_key(type.c_str(), key_desc.c_str(), &cert_buf[0], cert_buf.size(), keyring_id);
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key to keyring: " << cert_path;
+ continue;
+ }
+ LOG(INFO) << "Key " << cert_path << " added to " << keyring << " with key id 0x" << std::hex
+ << key;
+ keys_added++;
+ }
+ return 0;
+}
+
+bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+ if (!keyring_id) {
+ LOG(ERROR) << "keyring_id is null";
+ return false;
+ }
+
+ // If the keyring id is already a hex number, directly convert it to keyring id
+ try {
+ key_serial_t id = std::stoi(keyring_desc, nullptr, 16);
+ *keyring_id = id;
+ return true;
+ } catch (const std::exception& e) {
+ LOG(INFO) << "search /proc/keys for keyring id";
+ }
+
+ // Only keys allowed by SELinux rules will be shown here.
+ std::ifstream proc_keys_file("/proc/keys");
+ if (!proc_keys_file.is_open()) {
+ PLOG(ERROR) << "Failed to open /proc/keys";
+ return false;
+ }
+
+ std::string line;
+ while (getline(proc_keys_file, line)) {
+ std::vector<std::string> tokens = SplitBySpace(line);
+ if (tokens.size() < 9) {
+ continue;
+ }
+ std::string key_id = tokens[0];
+ std::string key_type = tokens[7];
+ // The key description may contain space.
+ std::string key_desc_prefix = tokens[8];
+ // The prefix has a ":" at the end
+ std::string key_desc_pattern = keyring_desc + ":";
+ if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+ continue;
+ }
+ *keyring_id = std::stoi(key_id, nullptr, 16);
+ return true;
+ }
+ return false;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can't find keyring " << keyring;
+ return 1;
+ }
+
+ if (keyctl_unlink(key, keyring_id) < 0) {
+ PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+ return 1;
+ }
+ return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring) {
+ if (data.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certificate too large";
+ return 1;
+ }
+
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key";
+ return 1;
+ }
+
+ LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Can not find keyring id";
+ return 1;
+ }
+
+ // read from stdin to get the certificates
+ std::istreambuf_iterator<char> begin(std::cin), end;
+ std::string data(begin, end);
+
+ if (data.size() > kMaxCertSize) {
+ LOG(ERROR) << "Certificate too large";
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ PLOG(ERROR) << "Failed to add key";
+ return 1;
+ }
+
+ LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+ return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+ key_serial_t keyring_id;
+ if (!GetKeyringId(keyring, &keyring_id)) {
+ LOG(ERROR) << "Cannot find keyring id";
+ return 1;
+ }
+
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+ return 1;
+ }
+ return 0;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..3c69611
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,48 @@
+/*
+ * 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 "include/keyutils.h"
+
+#include <string>
+
+// Add all files in a directory as certificates to a keyring. |keyring| could be the keyring
+// description or keyring id in hex.
+int AddCertsFromDir(const std::string& type, const std::string& desc_prefix,
+ const std::string& cert_dir, const std::string& keyring);
+
+// Add all the certs from directory path to keyring with keyring_id. Returns the number of keys
+// added. Returns non-zero if any error happens.
+int AddKeys(const std::string& path, const key_serial_t keyring_id, const std::string& type,
+ const std::string& desc, int start_index);
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id);
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 320b851..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -6,7 +6,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 260f655..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -36,14 +36,9 @@
__attribute__((visibility("default")))
void InitializeNativeLoader();
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
- jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+ JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+ jstring library_path, jstring permitted_path);
__attribute__((visibility("default"))) void* OpenNativeLibrary(
JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 043f038..1c2581f 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -31,6 +31,7 @@
#include <list>
#include <memory>
#include <mutex>
+#include <regex>
#include <string>
#include <vector>
@@ -140,10 +141,24 @@
#if defined(__LP64__)
static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+static constexpr const char* kVendorLibPath = "/vendor/lib64";
+static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
#else
static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+static constexpr const char* kVendorLibPath = "/vendor/lib";
+static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
#endif
+static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+static const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
static bool is_debuggable() {
bool debuggable = false;
#ifdef __BIONIC__
@@ -179,7 +194,7 @@
LibraryNamespaces() : initialized_(false) { }
NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
- bool is_shared, bool is_for_vendor, jstring java_library_path,
+ bool is_shared, jstring dex_path, jstring java_library_path,
jstring java_permitted_path, std::string* error_msg) {
std::string library_path; // empty string by default.
@@ -188,6 +203,8 @@
library_path = library_path_utf_chars.c_str();
}
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
// (http://b/27588281) This is a workaround for apps using custom
// classloaders and calling System.load() with an absolute path which
// is outside of the classloader library search path.
@@ -234,31 +251,50 @@
std::string system_exposed_libraries = system_public_libraries_;
const char* namespace_name = kClassloaderNamespaceName;
android_namespace_t* vndk_ns = nullptr;
- if (is_for_vendor && !is_shared) {
- LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ LOG_FATAL_IF(is_native_bridge,
+ "Unbundled vendor / product apk must not use translated architecture");
- // For vendor apks, give access to the vendor lib even though
+ // For vendor / product apks, give access to the vendor / product lib even though
// they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor partition.
-#if defined(__LP64__)
- std::string vendor_lib_path = "/vendor/lib64";
-#else
- std::string vendor_lib_path = "/vendor/lib";
-#endif
- library_path = library_path + ":" + vendor_lib_path.c_str();
- permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+
+ LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+ origin_partition);
+
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
// Also give access to LLNDK libraries since they are available to vendors
system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
// Give access to VNDK-SP libraries from the 'vndk' namespace.
vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
- LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
- "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
+ LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr, "Cannot find \"%s\" namespace for %s apks",
+ kVndkNamespaceName, origin_partition);
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
} else {
// oem and product public libraries are NOT available to vendor apks, otherwise it
// would be system->vendor violation.
@@ -660,6 +696,28 @@
return nullptr;
}
+ ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+
+ return apk_origin;
+ }
+
bool initialized_;
std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
std::string system_public_libraries_;
@@ -690,31 +748,20 @@
#endif
}
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
jstring permitted_path) {
#if defined(__ANDROID__)
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
std::string error_msg;
- bool success = g_namespaces->Create(env,
- target_sdk_version,
- class_loader,
- is_shared,
- is_for_vendor,
- library_path,
- permitted_path,
- &error_msg) != nullptr;
+ bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+ library_path, permitted_path, &error_msg) != nullptr;
if (!success) {
return env->NewStringUTF(error_msg.c_str());
}
#else
- UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
- library_path, permitted_path);
+ UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
#endif
return nullptr;
}
@@ -779,8 +826,7 @@
// In this case we create an isolated not-shared namespace for it.
std::string create_error_msg;
if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
- false /* is_for_vendor */, library_path, nullptr,
- &create_error_msg)) == nullptr) {
+ nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
*error_msg = strdup(create_error_msg.c_str());
return nullptr;
}
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
index 11ecc43..2eb1203 100644
--- a/libnativeloader/native_loader_lazy.cpp
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -50,10 +50,10 @@
}
jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
- bool is_shared, bool is_for_vendor, jstring library_path,
+ bool is_shared, jstring dex_path, jstring library_path,
jstring permitted_path) {
static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
- return f(env, target_sdk_version, class_loader, is_shared, is_for_vendor, library_path,
+ return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
permitted_path);
}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 26d0754..9951621 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -117,7 +117,7 @@
std::string json_doc;
if (!android::base::ReadFileToString(file_name, &json_doc)) {
- LOG(ERROR) << "Failed to read task profiles from " << file_name;
+ PLOG(ERROR) << "Failed to read task profiles from " << file_name;
return false;
}
@@ -185,7 +185,7 @@
// mkdir <path> [mode] [owner] [group]
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
- PLOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
@@ -289,7 +289,7 @@
std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
std::string content;
if (!android::base::ReadFileToString(file_name, &content)) {
- LOG(ERROR) << "Failed to read " << file_name;
+ PLOG(ERROR) << "Failed to read " << file_name;
return false;
}
@@ -323,7 +323,7 @@
CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
if (!LoadRcFile()) {
- PLOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+ LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
}
}
@@ -360,27 +360,42 @@
return false;
}
- cg_file_size_ = sb.st_size;
- if (cg_file_size_ < sizeof(CgroupFile)) {
- PLOG(ERROR) << "Invalid file format " << cgroup_rc_path;
+ size_t file_size = sb.st_size;
+ if (file_size < sizeof(CgroupFile)) {
+ LOG(ERROR) << "Invalid file format " << cgroup_rc_path;
return false;
}
- cg_file_data_ = (CgroupFile*)mmap(nullptr, cg_file_size_, PROT_READ, MAP_SHARED, fd, 0);
- if (cg_file_data_ == MAP_FAILED) {
+ CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (file_data == MAP_FAILED) {
PLOG(ERROR) << "Failed to mmap " << cgroup_rc_path;
return false;
}
- if (cg_file_data_->version_ != CgroupFile::FILE_CURR_VERSION) {
- PLOG(ERROR) << cgroup_rc_path << " file version mismatch";
+ if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+ LOG(ERROR) << cgroup_rc_path << " file version mismatch";
+ munmap(file_data, file_size);
return false;
}
+ if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
+ LOG(ERROR) << cgroup_rc_path << " file has invalid size";
+ munmap(file_data, file_size);
+ return false;
+ }
+
+ cg_file_data_ = file_data;
+ cg_file_size_ = file_size;
+
return true;
}
-void CgroupMap::Print() {
+void CgroupMap::Print() const {
+ if (!cg_file_data_) {
+ LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return;
+ }
LOG(INFO) << "File version = " << cg_file_data_->version_;
LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
@@ -397,7 +412,7 @@
// load cgroups.json file
if (!ReadDescriptors(&descriptors)) {
- PLOG(ERROR) << "Failed to load cgroup description file";
+ LOG(ERROR) << "Failed to load cgroup description file";
return false;
}
@@ -412,7 +427,7 @@
// mkdir <CGROUPS_RC_DIR> 0711 system system
if (!Mkdir(CGROUPS_RC_DIR, 0711, "system", "system")) {
- PLOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
+ LOG(ERROR) << "Failed to create directory for <CGROUPS_RC_FILE> file";
return false;
}
@@ -428,7 +443,7 @@
std::string cgroup_rc_path = StringPrintf("%s/%s", CGROUPS_RC_DIR, CGROUPS_RC_FILE);
// chmod 0644 <cgroup_rc_path>
if (fchmodat(AT_FDCWD, cgroup_rc_path.c_str(), 0644, AT_SYMLINK_NOFOLLOW) < 0) {
- LOG(ERROR) << "fchmodat() failed";
+ PLOG(ERROR) << "fchmodat() failed";
return false;
}
@@ -437,6 +452,8 @@
const CgroupController* CgroupMap::FindController(const std::string& name) const {
if (!cg_file_data_) {
+ LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
return nullptr;
}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index ba2caf7..1c355cd 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -92,5 +92,5 @@
~CgroupMap();
bool LoadRcFile();
- void Print();
+ void Print() const;
};
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 026c011..1eefada 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -167,7 +167,7 @@
if (!controller) return -1;
if (!controller->GetTaskGroup(tid, &subgroup)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return -1;
}
return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index fded417..dcf53f9 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -119,7 +119,7 @@
std::string path;
if (!attribute_->GetPathForTask(tid, &path)) {
- PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return false;
}
@@ -174,7 +174,7 @@
if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
// If the thread is in the process of exiting, don't flag an error
if (errno != ESRCH) {
- PLOG(ERROR) << "JoinGroup failed to write '" << value << "'; fd=" << fd;
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
return false;
}
}
@@ -187,7 +187,7 @@
if (fd_ >= 0) {
// fd is cached, reuse it
if (!AddTidToCgroup(pid, fd_)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
return true;
@@ -206,7 +206,7 @@
return false;
}
if (!AddTidToCgroup(pid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -219,7 +219,7 @@
return true;
}
if (!AddTidToCgroup(pid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
@@ -232,7 +232,7 @@
if (fd_ >= 0) {
// fd is cached, reuse it
if (!AddTidToCgroup(tid, fd_)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
return true;
@@ -244,7 +244,7 @@
}
// application-dependent path can't be used with tid
- PLOG(ERROR) << "Application profile can't be applied to a thread";
+ LOG(ERROR) << "Application profile can't be applied to a thread";
return false;
#else
std::string tasks_path = controller_->GetTasksFilePath(path_);
@@ -254,7 +254,7 @@
return true;
}
if (!AddTidToCgroup(tid, tmp_fd)) {
- PLOG(ERROR) << "Failed to add task into cgroup";
+ LOG(ERROR) << "Failed to add task into cgroup";
return false;
}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 27cda93..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -31,7 +31,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
host_supported: true,
srcs: [
"process.cpp",
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index a49fd9e..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -248,6 +248,7 @@
"tests/files/offline/offset_arm/*",
"tests/files/offline/shared_lib_in_apk_arm64/*",
"tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 2f5eed9..4b93abb 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -93,9 +93,12 @@
valid_ = false;
}
-bool Elf::GetSoname(std::string* name) {
+std::string Elf::GetSoname() {
std::lock_guard<std::mutex> guard(lock_);
- return valid_ && interface_->GetSoname(name);
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetSoname();
}
uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index c1b98d9..12efb94 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -374,13 +374,12 @@
}
template <typename DynType>
-bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+std::string ElfInterface::GetSonameWithTemplate() {
if (soname_type_ == SONAME_INVALID) {
- return false;
+ return "";
}
if (soname_type_ == SONAME_VALID) {
- *soname = soname_;
- return true;
+ return soname_;
}
soname_type_ = SONAME_INVALID;
@@ -397,7 +396,7 @@
if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
last_error_.code = ERROR_MEMORY_INVALID;
last_error_.address = offset;
- return false;
+ return "";
}
if (dyn.d_tag == DT_STRTAB) {
@@ -416,17 +415,16 @@
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
if (soname_offset >= entry.second + strtab_size) {
- return false;
+ return "";
}
if (!memory_->ReadString(soname_offset, &soname_)) {
- return false;
+ return "";
}
soname_type_ = SONAME_VALID;
- *soname = soname_;
- return true;
+ return soname_;
}
}
- return false;
+ return "";
}
template <typename SymType>
@@ -653,8 +651,8 @@
template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
-template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
-template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
uint64_t*);
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 89a6a79..28373b2 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -88,6 +88,7 @@
// Check if the start of this map is an embedded elf.
uint64_t max_size = 0;
if (Elf::GetInfo(memory.get(), &max_size)) {
+ elf_start_offset = offset;
if (max_size > map_size) {
if (memory->Init(name, offset, max_size)) {
return memory.release();
@@ -96,6 +97,7 @@
if (memory->Init(name, offset, map_size)) {
return memory.release();
}
+ elf_start_offset = 0;
return nullptr;
}
return memory.release();
@@ -186,44 +188,57 @@
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
- // Make sure no other thread is trying to add the elf to this map.
- std::lock_guard<std::mutex> guard(mutex_);
+ {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(mutex_);
- if (elf.get() != nullptr) {
- return elf.get();
- }
-
- bool locked = false;
- if (Elf::CachingEnabled() && !name.empty()) {
- Elf::CacheLock();
- locked = true;
- if (Elf::CacheGet(this)) {
- Elf::CacheUnlock();
+ if (elf.get() != nullptr) {
return elf.get();
}
+
+ bool locked = false;
+ if (Elf::CachingEnabled() && !name.empty()) {
+ Elf::CacheLock();
+ locked = true;
+ if (Elf::CacheGet(this)) {
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+
+ Memory* memory = CreateMemory(process_memory);
+ if (locked) {
+ if (Elf::CacheAfterCreateMemory(this)) {
+ delete memory;
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+ elf.reset(new Elf(memory));
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
+
+ if (locked) {
+ Elf::CacheAdd(this);
+ Elf::CacheUnlock();
+ }
}
- Memory* memory = CreateMemory(process_memory);
- if (locked) {
- if (Elf::CacheAfterCreateMemory(this)) {
- delete memory;
- Elf::CacheUnlock();
- return elf.get();
+ // If there is a read-only map then a read-execute map that represents the
+ // same elf object, make sure the previous map is using the same elf
+ // object if it hasn't already been set.
+ if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
+ prev_map->name == name) {
+ std::lock_guard<std::mutex> guard(prev_map->mutex_);
+ if (prev_map->elf.get() == nullptr) {
+ prev_map->elf = elf;
}
}
- elf.reset(new Elf(memory));
- // If the init fails, keep the elf around as an invalid object so we
- // don't try to reinit the object.
- elf->Init();
- if (elf->valid() && expected_arch != elf->arch()) {
- // Make the elf invalid, mismatch between arch and expected arch.
- elf->Invalidate();
- }
-
- if (locked) {
- Elf::CacheAdd(this);
- Elf::CacheUnlock();
- }
return elf.get();
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2734cf8..a1c58dd 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -105,6 +105,12 @@
if (resolve_names_) {
frame->map_name = map_info->name;
+ if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+ std::string soname = elf->GetSoname();
+ if (!soname.empty()) {
+ frame->map_name += '!' + soname;
+ }
+ }
}
frame->map_elf_start_offset = map_info->elf_start_offset;
frame->map_exact_offset = map_info->offset;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 00a249f..ac94f10 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,7 +59,7 @@
void Invalidate();
- bool GetSoname(std::string* name);
+ std::string GetSoname();
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index d41bb13..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -56,7 +56,7 @@
virtual void InitHeaders(uint64_t load_bias) = 0;
- virtual bool GetSoname(std::string* name) = 0;
+ virtual std::string GetSoname() = 0;
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
@@ -117,7 +117,7 @@
void ReadSectionHeaders(const EhdrType& ehdr);
template <typename DynType>
- bool GetSonameWithTemplate(std::string* soname);
+ std::string GetSonameWithTemplate();
template <typename SymType>
bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -183,9 +183,7 @@
ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
@@ -215,9 +213,7 @@
ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index dba41d1..b3beb6e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -101,7 +101,7 @@
size_t Size() { return size_; }
- void Clear();
+ void Clear() override;
protected:
size_t size_ = 0;
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_REGS_H
#include <stdint.h>
+#include <unistd.h>
#include <functional>
#include <string>
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index ddda7fd..a0554e2 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -100,6 +100,11 @@
// set to an empty string and the function offset being set to zero.
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+ // Enable/disable soname printing the soname for a map name if the elf is
+ // embedded in a file. This is enabled by default.
+ // NOTE: This does nothing unless resolving names is enabled.
+ void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
@@ -124,6 +129,7 @@
DexFiles* dex_files_ = nullptr;
#endif
bool resolve_names_ = true;
+ bool embedded_soname_ = true;
ErrorData last_error_;
};
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 946bc3c..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,7 +68,7 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return fake_soname_; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
@@ -83,6 +83,8 @@
void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+ void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -98,6 +100,7 @@
private:
std::unordered_map<std::string, uint64_t> globals_;
std::string fake_build_id_;
+ std::string fake_soname_;
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 7239749..d895863 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -555,9 +555,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_TRUE(elf->GetSoname(&name));
- ASSERT_STREQ("fake_soname.so", name.c_str());
+ ASSERT_EQ("fake_soname.so", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname) {
@@ -578,8 +576,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@@ -600,8 +597,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_size) {
@@ -624,8 +620,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 1ff2306..23c9cf8 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -126,9 +126,9 @@
ASSERT_FALSE(elf.valid());
ASSERT_TRUE(elf.interface() == nullptr);
- std::string name;
- ASSERT_FALSE(elf.GetSoname(&name));
+ ASSERT_EQ("", elf.GetSoname());
+ std::string name;
uint64_t func_offset;
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
@@ -309,7 +309,7 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return ""; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
std::string GetBuildID() override { return ""; }
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index a66685a..2ddadef 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -178,7 +178,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@@ -202,7 +202,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -219,7 +219,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
- EXPECT_EQ(0U, info.elf_start_offset);
+ EXPECT_EQ(0x2000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index d7b8485..d60b8b1 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -371,4 +371,35 @@
}
}
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+ MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+ Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+ MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index b5feb38..655579e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -300,7 +300,7 @@
EXPECT_EQ(
" #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
- " #02 pc 000021a8 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+ " #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
@@ -601,7 +601,7 @@
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
- " #01 pc 0000212d 137-cfi.odex (offset 0x2000) (boolean Main.unwindInProcess(boolean, int, "
+ " #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
@@ -1312,7 +1312,8 @@
" #02 pc 00000000000008bc vdso.so\n"
" #03 pc 00000000000846f4 libc.so (abort+172)\n"
" #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x4000) (ANGLEGetUtilityAPI+56)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+ "(ANGLEGetUtilityAPI+56)\n"
" #06 pc 000000000007fe68 libc.so (__libc_init)\n",
frame_info);
@@ -1372,4 +1373,56 @@
// Ignore top frame since the test code was modified to end in __libc_init.
}
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000814bc libc.so (syscall+28)\n"
+ " #01 pc 00000000008cdf5c test.apk (offset 0x5000)\n"
+ " #02 pc 00000000008cde9c test.apk (offset 0x5000)\n"
+ " #03 pc 00000000008cdd70 test.apk (offset 0x5000)\n"
+ " #04 pc 00000000008ce408 test.apk (offset 0x5000)\n"
+ " #05 pc 00000000008ce8d8 test.apk (offset 0x5000)\n"
+ " #06 pc 00000000008ce814 test.apk (offset 0x5000)\n"
+ " #07 pc 00000000008bcf60 test.apk (offset 0x5000)\n"
+ " #08 pc 0000000000133024 test.apk (offset 0x5000)\n"
+ " #09 pc 0000000000134ad0 test.apk (offset 0x5000)\n"
+ " #10 pc 0000000000134b64 test.apk (offset 0x5000)\n"
+ " #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)\n"
+ " #12 pc 0000000000085e18 libc.so (__start_thread+64)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2dc5118..504b57a 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -79,8 +79,13 @@
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
elf = new ElfFake(new MemoryFake);
- elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetSoname("lib_fake.so");
+ elf->FakeSetInterface(interface);
AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+ MapInfo* map_info = maps_->Find(0x43000);
+ ASSERT_TRUE(map_info != nullptr);
+ map_info->elf_start_offset = 0x1d000;
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
@@ -324,8 +329,38 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.SetEmbeddedSoname(false);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x43000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
@@ -813,8 +848,8 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index 3cd9d40..4043122 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -1,5 +1,6 @@
ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0 137-cfi.odex
e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
e648e000-e690f000 r-xp 0 00:00 0 libart.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index a8d215c..f255a44 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -1,5 +1,6 @@
56573000-56577000 r-xp 0 00:00 0 dalvikvm32
eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0 137-cfi.odex
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 0000000..cac1dd9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 0000000..2c5ca62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0 test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0 libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 0000000..090aeda
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 0000000..27d5bf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 0000000..70a9c71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 19982d8..92e5c0a 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -118,8 +118,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n", soname.c_str());
}
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 4b6f49a..b77a86b 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -185,8 +185,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 9128430..b0a4dd0 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -71,8 +71,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 39bf3a5..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -20,7 +20,6 @@
vndk: {
enabled: true,
},
- double_loadable: true,
host_supported: true,
srcs: ["usbhost.c"],
cflags: ["-Werror"],
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 2e0cf6e..c9b4894 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -45,6 +45,44 @@
bool* mDeleted;
};
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+ static void* operator new(size_t size) {
+ if (mAllocCount != 0) {
+ abort();
+ }
+ mAllocCount = 1;
+ if (theMemory == nullptr) {
+ theMemory = malloc(size);
+ }
+ return theMemory;
+ }
+
+ static void operator delete(void *p) {
+ if (mAllocCount != 1 || p != theMemory) {
+ abort();
+ }
+ mAllocCount = 0;
+ }
+
+ FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~FooFixedAlloc() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+ static int mAllocCount;
+ static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
TEST(RefBase, StrongMoves) {
bool isDeleted;
Foo* foo = new Foo(&isDeleted);
@@ -90,6 +128,118 @@
ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
}
+TEST(RefBase, Comparisons) {
+ bool isDeleted, isDeleted2, isDeleted3;
+ Foo* foo = new Foo(&isDeleted);
+ Foo* foo2 = new Foo(&isDeleted2);
+ sp<Foo> sp1(foo);
+ sp<Foo> sp2(foo2);
+ wp<Foo> wp1(sp1);
+ wp<Foo> wp2(sp1);
+ wp<Foo> wp3(sp2);
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_TRUE(wp1 == sp1);
+ ASSERT_TRUE(wp3 == sp2);
+ ASSERT_TRUE(wp1 != sp2);
+ ASSERT_TRUE(wp1 <= wp2);
+ ASSERT_TRUE(wp1 >= wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ ASSERT_FALSE(wp1 > wp2);
+ ASSERT_FALSE(wp1 < wp2);
+ ASSERT_FALSE(sp1 == sp2);
+ ASSERT_TRUE(sp1 != sp2);
+ bool sp1_smaller = sp1 < sp2;
+ wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+ wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ sp2 = nullptr;
+ ASSERT_TRUE(isDeleted2);
+ ASSERT_FALSE(isDeleted);
+ ASSERT_FALSE(wp3 == sp2);
+ // Comparison results on weak pointers should not be affected.
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ wp2 = nullptr;
+ ASSERT_FALSE(wp1 == wp2);
+ ASSERT_TRUE(wp1 != wp2);
+ wp1.clear();
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ wp3.clear();
+ ASSERT_TRUE(wp1 == wp3);
+ ASSERT_FALSE(wp1 != wp3);
+ ASSERT_FALSE(isDeleted);
+ sp1.clear();
+ ASSERT_TRUE(isDeleted);
+ ASSERT_TRUE(sp1 == sp2);
+ // Try to check that null pointers are properly initialized.
+ {
+ // Try once with non-null, to maximize chances of getting junk on the
+ // stack.
+ sp<Foo> sp3(new Foo(&isDeleted3));
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_FALSE(wp4 == wp5);
+ ASSERT_TRUE(wp4 != wp5);
+ ASSERT_FALSE(sp3 == wp5);
+ ASSERT_FALSE(wp5 == sp3);
+ ASSERT_TRUE(sp3 != wp5);
+ ASSERT_TRUE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+ {
+ sp<Foo> sp3;
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_TRUE(wp4 == wp5);
+ ASSERT_FALSE(wp4 != wp5);
+ ASSERT_TRUE(sp3 == wp5);
+ ASSERT_TRUE(wp5 == sp3);
+ ASSERT_FALSE(sp3 != wp5);
+ ASSERT_FALSE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+ bool isDeleted, isDeleted2;
+ FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+ sp<FooFixedAlloc> sp1(foo);
+ wp<FooFixedAlloc> wp1(sp1);
+ ASSERT_TRUE(wp1 == sp1);
+ sp1.clear(); // Deallocates the object.
+ ASSERT_TRUE(isDeleted);
+ FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+ ASSERT_FALSE(isDeleted2);
+ ASSERT_EQ(foo, foo2); // Not technically a legal comparison, but ...
+ sp<FooFixedAlloc> sp2(foo2);
+ wp<FooFixedAlloc> wp2(sp2);
+ ASSERT_TRUE(sp2 == wp2);
+ ASSERT_FALSE(sp2 != wp2);
+ ASSERT_TRUE(sp2 != wp1);
+ ASSERT_FALSE(sp2 == wp1);
+ ASSERT_FALSE(sp2 == sp1); // sp1 is null.
+ ASSERT_FALSE(wp1 == wp2); // wp1 refers to old object.
+ ASSERT_TRUE(wp1 != wp2);
+ ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+ ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+ ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+ ASSERT_FALSE(wp1 == nullptr);
+ wp1 = sp2;
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+}
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 1780cf2..a105474 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
#define ANDROID_REF_BASE_H
#include <atomic>
+#include <functional>
+#include <type_traits> // for common_type.
#include <stdint.h>
#include <sys/types.h>
@@ -192,19 +194,26 @@
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
template<typename U> \
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
+} \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _wp_compare_<_compare_>(m_ptr, o); \
}
// ---------------------------------------------------------------------------
@@ -354,7 +363,7 @@
public:
typedef typename RefBase::weakref_type weakref_type;
- inline wp() : m_ptr(nullptr) { }
+ inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
wp(T* other); // NOLINT(implicit)
wp(const wp<T>& other);
@@ -395,39 +404,51 @@
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
- COMPARE_WEAK(>)
- COMPARE_WEAK(<)
- COMPARE_WEAK(<=)
- COMPARE_WEAK(>=)
+ COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+ COMPARE_WEAK_FUNCTIONAL(<, std::less)
+ COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
- inline bool operator == (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
- }
template<typename U>
inline bool operator == (const wp<U>& o) const {
- return m_ptr == o.m_ptr;
+ return m_refs == o.m_refs; // Implies m_ptr == o.mptr; see invariants below.
}
- inline bool operator > (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ template<typename U>
+ inline bool operator == (const sp<U>& o) const {
+ // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+ // object at the same address.
+ if (o == nullptr) {
+ return m_ptr == nullptr;
+ } else {
+ return m_refs == o->getWeakRefs(); // Implies m_ptr == o.mptr.
+ }
}
+
+ template<typename U>
+ inline bool operator != (const sp<U>& o) const {
+ return !(*this == o);
+ }
+
template<typename U>
inline bool operator > (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::greater>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator < (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
- }
template<typename U>
inline bool operator < (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::less>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
- inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
- inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
@@ -446,11 +467,27 @@
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+// to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
template<typename T>
@@ -464,16 +501,14 @@
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
wp<T>::wp(U* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? other->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
@@ -483,6 +518,8 @@
if (m_ptr) {
m_refs = other.m_refs;
m_refs->incWeak(this);
+ } else {
+ m_refs = nullptr;
}
}
@@ -490,9 +527,7 @@
wp<T>::wp(const sp<U>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T>
@@ -595,6 +630,7 @@
{
if (m_ptr) {
m_refs->decWeak(this);
+ m_refs = 0;
m_ptr = 0;
}
}
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 1571129..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_STRONG_POINTER_H
#define ANDROID_STRONG_POINTER_H
+#include <functional>
+#include <type_traits> // for common_type.
+
// ---------------------------------------------------------------------------
namespace android {
@@ -24,13 +27,12 @@
// ---------------------------------------------------------------------------
-#define COMPARE(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_) \
template<typename U> \
inline bool operator _op_ (const sp<U>& o) const { \
return m_ptr _op_ o.m_ptr; \
@@ -39,14 +41,27 @@
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
} \
-inline bool operator _op_ (const wp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const wp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
}
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o.m_ptr); \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o); \
+}
// ---------------------------------------------------------------------------
template<typename T>
@@ -89,12 +104,23 @@
// Operators
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
+ COMPARE_STRONG(==)
+ COMPARE_STRONG(!=)
+ COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+ COMPARE_STRONG_FUNCTIONAL(<, std::less)
+ COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+ // Punt these to the wp<> implementation.
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return o == *this;
+ }
+
+ template<typename U>
+ inline bool operator != (const wp<U>& o) const {
+ return o != *this;
+ }
private:
template<typename Y> friend class sp;
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index 821940a..84c2132 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -20,6 +20,8 @@
#define LOG_TAG "vndksupport"
#include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
@@ -46,6 +48,11 @@
}
int android_is_in_vendor_process() {
+ // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+ // loaded (sysprop service isn't up for init to know <ver>).
+ if (getpid() == 1) {
+ return 0;
+ }
if (android_get_exported_namespace == NULL) {
ALOGD("android_get_exported_namespace() not available. Assuming system process.");
return 0;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 18421e8..2d3fbfc 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -110,7 +110,7 @@
*/
#define PSI_WINDOW_SIZE_MS 1000
/* Polling period after initial PSI signal */
-#define PSI_POLL_PERIOD_MS 200
+#define PSI_POLL_PERIOD_MS 40
/* Poll for the duration of one window after initial PSI signal */
#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
index 6b5d5d0..789bf5e 100755
--- a/mkbootimg/unpack_bootimg.py
+++ b/mkbootimg/unpack_bootimg.py
@@ -48,12 +48,12 @@
print('boot_magic: %s' % boot_magic)
kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
print('kernel_size: %s' % kernel_ramdisk_second_info[0])
- print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+ print('kernel load address: %#x' % kernel_ramdisk_second_info[1])
print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
- print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+ print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
- print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
- print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+ print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+ print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6])
print('page size: %s' % kernel_ramdisk_second_info[7])
print('boot image header version: %s' % kernel_ramdisk_second_info[8])
print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
@@ -77,7 +77,7 @@
recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
print('recovery dtbo size: %s' % recovery_dtbo_size)
recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo offset: %s' % recovery_dtbo_offset)
+ print('recovery dtbo offset: %#x' % recovery_dtbo_offset)
boot_header_size = unpack('I', args.boot_img.read(4))[0]
print('boot header size: %s' % boot_header_size)
else:
@@ -86,7 +86,7 @@
dtb_size = unpack('I', args.boot_img.read(4))[0]
print('dtb size: %s' % dtb_size)
dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
- print('dtb address: %s' % dtb_load_address)
+ print('dtb address: %#x' % dtb_load_address)
else:
dtb_size = 0
@@ -103,10 +103,11 @@
) # header + kernel
image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
- second_offset = page_size * (
- num_header_pages + num_kernel_pages + num_ramdisk_pages
- ) # header + kernel + ramdisk
- image_info_list.append((second_offset, second_size, 'second'))
+ if second_size > 0:
+ second_offset = page_size * (
+ num_header_pages + num_kernel_pages + num_ramdisk_pages
+ ) # header + kernel + ramdisk
+ image_info_list.append((second_offset, second_size, 'second'))
if recovery_dtbo_size > 0:
image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d2125d8..e0b291d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -24,44 +24,6 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-# Start of runtime APEX compatibility.
-#
-# Meta-comment:
-# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
-# entries need to be associated with something that goes into /system.
-# init-debug.rc qualifies but it could be anything else in /system until soong
-# supports creation of symlinks. http://b/123333111
-#
-# Keeping the appearance of files/dirs having old locations for apps that have
-# come to rely on them.
-
-# http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
-# A symlink can't overwrite a directory and the /system/usr/icu directory once
-# existed so the required structure must be created whatever we find.
-LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
-
-# TODO(b/124106384): Clean up compat symlinks for ART binaries.
-ART_BINARIES= \
- dalvikvm \
- dalvikvm32 \
- dalvikvm64 \
- dex2oat \
- dexdiag \
- dexdump \
- dexlist \
- dexoptanalyzer \
- oatdump \
- profman \
-
-$(foreach b,$(ART_BINARIES), \
- $(eval LOCAL_POST_INSTALL_CMD += \
- ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
-)
-
-# End of runtime APEX compatibilty.
-
include $(BUILD_PREBUILT)
#######################################
@@ -242,6 +204,45 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+# Start of runtime APEX compatibility.
+#
+# Meta-comment:
+# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
+# entries need to be associated with something that goes into /system.
+# ld.config.txt qualifies but it could be anything else in /system until soong
+# supports creation of symlinks. http://b/123333111
+#
+# Keeping the appearance of files/dirs having old locations for apps that have
+# come to rely on them.
+
+# http://b/121248172 - create a link from /system/usr/icu to
+# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# A symlink can't overwrite a directory and the /system/usr/icu directory once
+# existed so the required structure must be created whatever we find.
+LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += ; ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES= \
+ dalvikvm \
+ dalvikvm32 \
+ dalvikvm64 \
+ dex2oat \
+ dexdiag \
+ dexdump \
+ dexlist \
+ dexoptanalyzer \
+ oatdump \
+ profman \
+
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_OUT)/bin
+$(foreach b,$(ART_BINARIES), \
+ $(eval LOCAL_POST_INSTALL_CMD += \
+ ; ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
+# End of runtime APEX compatibilty.
+
ifeq ($(_enforce_vndk_at_runtime),true)
# for VNDK enforced devices
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 0cde3f2..d4d5c28 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -40,7 +40,7 @@
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
@@ -94,11 +94,7 @@
namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
namespace.media.links = default
-namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
-namespace.media.link.default.shared_libs += libandroid.so
-namespace.media.link.default.shared_libs += libbinder_ndk.so
-namespace.media.link.default.shared_libs += libmediametrics.so
-namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.media.link.default.allow_all_shared_libs = true
###############################################################################
# "conscrypt" APEX namespace
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 4b2dd07..552d685 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -125,7 +125,7 @@
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 54f4c98..beb6125 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -66,7 +66,7 @@
# ld.config.txt.
# If a shared library or an executable requests a shared library that
# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace.And then, if the
+# to load the shared library from the runtime namespace. And then, if the
# shared library cannot be loaded from the runtime namespace either, the
# dynamic linker tries to load the shared library from the resolv namespace.
# Finally, if all attempts fail, the dynamic linker returns an error.
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 5d6cd2d..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -6,6 +6,7 @@
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+ export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
export BOOTCLASSPATH %BOOTCLASSPATH%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b44cc3e..3854c73 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -387,7 +387,10 @@
restorecon_recursive /metadata
mkdir /metadata/vold
chmod 0700 /metadata/vold
+ mkdir /metadata/password_slots 0771 root system
+ mkdir /metadata/apex 0700 root system
+ mkdir /metadata/apex/sessions 0700 root system
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -423,7 +426,7 @@
mkdir /data/apex/active 0750 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/sessions 0700 root system
- mkdir /data/pkg_staging 0750 system system
+ mkdir /data/app-staging 0750 system system
start apexd
# Avoid predictable entropy pool. Carry over entropy from previous boot.
@@ -585,7 +588,10 @@
restorecon --recursive --skip-ce /data
# load fsverity keys
- exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity
+ exec -- /system/bin/mini-keyctl dadd asymmetric product_cert /product/etc/security/cacerts_fsverity .fs-verity
+ exec -- /system/bin/mini-keyctl dadd asymmetric vendor_cert /vendor/etc/security/cacerts_fsverity .fs-verity
+ # Prevent future key links to fsverity keyring
+ exec -- /system/bin/mini-keyctl restrict_keyring .fs-verity
# Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index e8c5d8e..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 9c7e807..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -22,6 +21,5 @@
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket blastula_pool_secondary stream 660 root system
- updatable
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 9908c99..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 0b5edff..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket blastula_pool stream 660 root system
- updatable
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -22,6 +21,5 @@
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket blastula_pool_secondary stream 660 root system
- updatable
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index 8752eef..432c434 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -70,32 +70,40 @@
return true; // Keep searching.
}
-static bool check_directory(const char* path, uid_t uid) {
+static void check_directory(const char* path, uid_t uid) {
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+ if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {
+ error(1, errno, "couldn't stat %s", path);
+ }
// /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return true;
+ if (strcmp("/data/user/0", path) == 0) return;
// Must be a real directory, not a symlink.
- if (!S_ISDIR(st.st_mode)) return false;
+ if (!S_ISDIR(st.st_mode)) {
+ error(1, 0, "%s not a directory: %o", path, st.st_mode);
+ }
// Must be owned by specific uid/gid.
- if (st.st_uid != uid || st.st_gid != uid) return false;
+ if (st.st_uid != uid || st.st_gid != uid) {
+ error(1, 0, "%s has wrong owner: %d/%d, not %d", path, st.st_uid, st.st_gid, uid);
+ }
// Must not be readable or writable by others.
- if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
-
- return true;
+ if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {
+ error(1, 0, "%s readable or writable by others: %o", path, st.st_mode);
+ }
}
// This function is used to check the data directory path for safety.
// We check that every sub-directory is owned by the 'system' user
// and exists and is not a symlink. We also check that the full directory
// path is properly owned by the user ID.
-static bool check_data_path(const char* data_path, uid_t uid) {
+static void check_data_path(const char* package_name, const char* data_path, uid_t uid) {
// The path should be absolute.
- if (data_path[0] != '/') return false;
+ if (data_path[0] != '/') {
+ error(1, 0, "%s data path not absolute: %s", package_name, data_path);
+ }
// Look for all sub-paths, we do that by finding
// directory separators in the input path and
@@ -110,26 +118,28 @@
if (data_path[nn+1] == '\0') break;
/* found a separator, check that data_path is not too long. */
- if (nn >= (int)(sizeof subpath)) return false;
+ if (nn >= (int)(sizeof subpath)) {
+ error(1, 0, "%s data path too long: %s", package_name, data_path);
+ }
/* reject any '..' subpath */
if (nn >= 3 &&
data_path[nn-3] == '/' &&
data_path[nn-2] == '.' &&
data_path[nn-1] == '.') {
- return false;
+ error(1, 0, "%s contains '..': %s", package_name, data_path);
}
/* copy to 'subpath', then check ownership */
memcpy(subpath, data_path, nn);
subpath[nn] = '\0';
- if (!check_directory(subpath, AID_SYSTEM)) return false;
+ check_directory(subpath, AID_SYSTEM);
}
// All sub-paths were checked, now verify that the full data
// directory is owned by the application uid.
- return check_directory(data_path, uid);
+ check_directory(data_path, uid);
}
std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
@@ -222,9 +232,7 @@
}
// Check that the data directory path is valid.
- if (!check_data_path(info.data_dir, userAppId)) {
- error(1, 0, "package has corrupt installation: %s", pkgname);
- }
+ check_data_path(pkgname, info.data_dir, userAppId);
// Ensure that we change all real/effective/saved IDs at the
// same time to avoid nasty surprises.
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index e1de130..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -99,14 +100,21 @@
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
- mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+ mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+ bool use_esdfs) {
+ // Add new options at the end of the vector.
+ std::vector<std::string> new_opts_list;
+ if (multi_user) new_opts_list.push_back("multiuser,");
+ if (derive_gid) new_opts_list.push_back("derive_gid,");
+ if (default_normal) new_opts_list.push_back("default_normal,");
+ if (unshared_obb) new_opts_list.push_back("unshared_obb,");
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i <= new_opts_list.size(); ++i) {
std::string new_opts;
- if (multi_user && i < 3) new_opts += "multiuser,";
- if (derive_gid && i < 2) new_opts += "derive_gid,";
- if (default_normal && i < 1) new_opts += "default_normal,";
+ for (int j = 0; j < new_opts_list.size() - i; ++j) {
+ new_opts += new_opts_list[j];
+ }
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
return true;
}
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
- const std::string& dest_path, uid_t fsuid, gid_t fsgid,
- bool multi_user, userid_t userid, gid_t gid, mode_t mask,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+ const std::string& source_path, const std::string& dest_path,
+ uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+ gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+ bool unshared_obb, bool use_esdfs) {
if (use_esdfs) {
return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
- derive_gid, default_normal, use_esdfs);
+ derive_gid, default_normal, unshared_obb, use_esdfs);
} else {
return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
}
@@ -156,7 +165,7 @@
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+ bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -167,16 +176,17 @@
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
- default_normal, use_esdfs) ||
+ default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
@@ -184,16 +194,17 @@
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
- default_normal, use_esdfs)) {
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
}
@@ -216,7 +227,8 @@
<< " -U: specify user ID that owns device"
<< " -m: source_path is multi-user"
<< " -w: runtime write mount has full write access"
- << " -P preserve owners on the lower file system";
+ << " -P: preserve owners on the lower file system"
+ << " -o: obb dir doesn't need to be shared between users";
return 1;
}
@@ -230,6 +242,7 @@
bool full_write = false;
bool derive_gid = false;
bool default_normal = false;
+ bool unshared_obb = false;
int i;
struct rlimit rlim;
int fs_version;
@@ -238,7 +251,7 @@
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
int opt;
- while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -261,8 +274,12 @@
case 'i':
default_normal = true;
break;
+ case 'o':
+ unshared_obb = true;
+ break;
case '?':
default:
+ LOG(ERROR) << "Unknown option: '" << opt << "'";
return usage();
}
}
@@ -304,6 +321,6 @@
}
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
- default_normal, !should_use_sdcardfs());
+ default_normal, unshared_obb, !should_use_sdcardfs());
return 1;
}
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 9a71ae3..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -39,15 +39,29 @@
static const char* rpmb_devname;
static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
-static const char* _sopts = "hp:d:r:";
+static enum dev_type dev_type = MMC_RPMB;
+
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+ if (!strcmp(dev_type_name, "mmc")) {
+ return MMC_RPMB;
+ } else if (!strcmp(dev_type_name, "virt")) {
+ return VIRT_RPMB;
+ } else {
+ return UNKNOWN_RPMB;
+ }
+}
+
+static const char* _sopts = "hp:d:r:t:";
static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
{"trusty_dev", required_argument, NULL, 'd'},
{"data_path", required_argument, NULL, 'p'},
{"rpmb_dev", required_argument, NULL, 'r'},
+ {"dev_type", required_argument, NULL, 't'},
{0, 0, 0, 0}};
static void show_usage_and_exit(int code) {
- ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+ ALOGE("Available dev types: mmc, virt\n");
exit(code);
}
@@ -195,6 +209,14 @@
rpmb_devname = strdup(optarg);
break;
+ case 't':
+ dev_type = parse_dev_type(optarg);
+ if (dev_type == UNKNOWN_RPMB) {
+ ALOGE("Unrecognized dev type: %s\n", optarg);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ break;
+
default:
ALOGE("unrecognized option (%c):\n", opt);
show_usage_and_exit(EXIT_FAILURE);
@@ -226,7 +248,7 @@
if (rc < 0) return EXIT_FAILURE;
/* open rpmb device */
- rc = rpmb_open(rpmb_devname);
+ rc = rpmb_open(rpmb_devname, dev_type);
if (rc < 0) return EXIT_FAILURE;
/* connect to Trusty secure storage server */
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index e706d0a..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,6 +51,7 @@
static int rpmb_fd = -1;
static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
#ifdef RPMB_DEBUG
@@ -68,36 +69,16 @@
#endif
-int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
- int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
struct mmc_ioc_cmd cmd_buf[3];
} mmc = {};
struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
- const struct storage_rpmb_send_req* req = r;
-
- if (req_len < sizeof(*req)) {
- ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
- if (req_len != expected_len) {
- ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
+ int rc;
const uint8_t* write_buf = req->payload;
if (req->reliable_write_size) {
- if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_RELW;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -114,12 +95,6 @@
}
if (req->write_size) {
- if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid write size %u\n", req->write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_W;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -136,12 +111,6 @@
}
if (req->read_size) {
- if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
- ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_R;
cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
@@ -154,9 +123,92 @@
cmd++;
}
- rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
if (rc < 0) {
ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
+ return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+ size_t payload_size) {
+ int rc;
+ uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+ uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+ rc = write(rpmb_fd, &res_count, sizeof(res_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, payload, payload_size);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = read(rpmb_fd, read_buf, read_size);
+ return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+ int rc;
+ const struct storage_rpmb_send_req* req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (dev_type == MMC_RPMB) {
+ rc = send_mmc_rpmb_req(rpmb_fd, req);
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else if (dev_type == VIRT_RPMB) {
+ size_t payload_size = req->reliable_write_size + req->write_size;
+ rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+ if (rc < 0) {
+ ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ if (rc != req->read_size) {
+ ALOGE("send_virt_rpmb_req got incomplete response: "
+ "(size %d, expected %d)\n",
+ rc, req->read_size);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else {
+ ALOGE("Unsupported dev_type\n");
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
@@ -178,8 +230,9 @@
return ipc_respond(msg, NULL, 0);
}
-int rpmb_open(const char* rpmb_devname) {
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
int rc;
+ dev_type = open_dev_type;
rc = open(rpmb_devname, O_RDWR, 0);
if (rc < 0) {
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 5107361..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <trusty/interface/storage.h>
-int rpmb_open(const char* rpmb_devname);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
void rpmb_close(void);