Merge "Revert "Update init.rc for statsd setup""
diff --git a/adb/Android.bp b/adb/Android.bp
index c71138a..32581a2 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -341,6 +341,8 @@
"client/line_printer.cpp",
"client/fastdeploy.cpp",
"client/fastdeploycallbacks.cpp",
+ "client/incremental.cpp",
+ "client/incremental_server.cpp",
"shell_service_protocol.cpp",
],
@@ -360,6 +362,7 @@
"libfastdeploy_host",
"libdiagnose_usb",
"liblog",
+ "liblz4",
"libmdnssd",
"libprotobuf-cpp-lite",
"libusb",
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 80f146c..cea24fe 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -118,22 +118,22 @@
return;
}
- std::unordered_map<std::string, int> trace_flags = {
- {"1", -1},
- {"all", -1},
- {"adb", ADB},
- {"sockets", SOCKETS},
- {"packets", PACKETS},
- {"rwx", RWX},
- {"usb", USB},
- {"sync", SYNC},
- {"sysdeps", SYSDEPS},
- {"transport", TRANSPORT},
- {"jdwp", JDWP},
- {"services", SERVICES},
- {"auth", AUTH},
- {"fdevent", FDEVENT},
- {"shell", SHELL}};
+ std::unordered_map<std::string, int> trace_flags = {{"1", -1},
+ {"all", -1},
+ {"adb", ADB},
+ {"sockets", SOCKETS},
+ {"packets", PACKETS},
+ {"rwx", RWX},
+ {"usb", USB},
+ {"sync", SYNC},
+ {"sysdeps", SYSDEPS},
+ {"transport", TRANSPORT},
+ {"jdwp", JDWP},
+ {"services", SERVICES},
+ {"auth", AUTH},
+ {"fdevent", FDEVENT},
+ {"shell", SHELL},
+ {"incremental", INCREMENTAL}};
std::vector<std::string> elements = android::base::Split(trace_setting, " ");
for (const auto& elem : elements) {
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 1d2c8c7..ed4be88 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -25,19 +25,20 @@
* the adb_trace_init() function implemented in adb_trace.cpp.
*/
enum AdbTrace {
- ADB = 0, /* 0x001 */
+ ADB = 0, /* 0x001 */
SOCKETS,
PACKETS,
TRANSPORT,
- RWX, /* 0x010 */
+ RWX, /* 0x010 */
USB,
SYNC,
SYSDEPS,
- JDWP, /* 0x100 */
+ JDWP, /* 0x100 */
SERVICES,
AUTH,
FDEVENT,
- SHELL
+ SHELL,
+ INCREMENTAL,
};
#define VLOG_IS_ON(TAG) \
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 2bcd0a6..982a96b 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -36,8 +36,10 @@
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
+#include "incremental.h"
static constexpr int kFastDeployMinApi = 24;
+static constexpr int kIncrementalMinApi = 29;
namespace {
@@ -45,8 +47,8 @@
INSTALL_DEFAULT,
INSTALL_PUSH,
INSTALL_STREAM,
+ INSTALL_INCREMENTAL,
};
-
}
static bool can_use_feature(const char* feature) {
@@ -70,6 +72,10 @@
return can_use_feature(kFeatureApex);
}
+static bool is_abb_exec_supported() {
+ return can_use_feature(kFeatureAbbExec);
+}
+
static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";
@@ -193,14 +199,14 @@
posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
#endif
- const bool use_abb = can_use_feature(kFeatureAbbExec);
+ const bool use_abb_exec = is_abb_exec_supported();
std::string error;
- std::vector<std::string> cmd_args = {use_abb ? "package" : "exec:cmd package"};
+ std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"};
cmd_args.reserve(argc + 3);
// don't copy the APK name, but, copy the rest of the arguments as-is
while (argc-- > 1) {
- if (use_abb) {
+ if (use_abb_exec) {
cmd_args.push_back(*argv++);
} else {
cmd_args.push_back(escape_arg(*argv++));
@@ -217,7 +223,7 @@
}
unique_fd remote_fd;
- if (use_abb) {
+ if (use_abb_exec) {
remote_fd = send_abb_exec_command(cmd_args, &error);
} else {
remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
@@ -287,8 +293,60 @@
return result;
}
+template <class TimePoint>
+static int msBetween(TimePoint start, TimePoint end) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
+
+static int install_app_incremental(int argc, const char** argv) {
+ printf("Performing Incremental Install\n");
+ using clock = std::chrono::high_resolution_clock;
+ const auto start = clock::now();
+ int first_apk = -1;
+ int last_apk = -1;
+ std::string cert_path;
+ bool wait = false;
+ std::vector<std::string_view> args = {"package"};
+ for (int i = 0; i < argc; ++i) {
+ const auto arg = std::string_view(argv[i]);
+ if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
+ last_apk = i;
+ if (first_apk == -1) {
+ first_apk = i;
+ }
+ } else if (arg == "--wait") {
+ wait = true;
+ } else if (arg.starts_with("install-")) {
+ // incremental installation command on the device is the same for all its variations in
+ // the adb, e.g. install-multiple or install-multi-package
+ args.push_back("install");
+ } else {
+ args.push_back(arg);
+ }
+ }
+
+ if (first_apk == -1) error_exit("Need at least one APK file on command line");
+
+ const auto afterApk = clock::now();
+
+ auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
+ if (!server_process) {
+ return -1;
+ }
+
+ const auto end = clock::now();
+ printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
+ msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
+
+ if (wait) {
+ (*server_process).wait();
+ }
+
+ return 0;
+}
+
int install_app(int argc, const char** argv) {
- std::vector<int> processedArgIndicies;
+ std::vector<int> processedArgIndices;
InstallMode installMode = INSTALL_DEFAULT;
bool use_fastdeploy = false;
bool is_reinstall = false;
@@ -296,30 +354,42 @@
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--streaming")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
installMode = INSTALL_STREAM;
} else if (!strcmp(argv[i], "--no-streaming")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
installMode = INSTALL_PUSH;
} else if (!strcmp(argv[i], "-r")) {
- // Note that this argument is not added to processedArgIndicies because it
+ // Note that this argument is not added to processedArgIndices because it
// must be passed through to pm
is_reinstall = true;
} else if (!strcmp(argv[i], "--fastdeploy")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
use_fastdeploy = true;
} else if (!strcmp(argv[i], "--no-fastdeploy")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
use_fastdeploy = false;
} else if (!strcmp(argv[i], "--force-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateAlways;
} else if (!strcmp(argv[i], "--date-check-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
} else if (!strcmp(argv[i], "--version-check-agent")) {
- processedArgIndicies.push_back(i);
+ processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+ } else if (!strcmp(argv[i], "--incremental")) {
+ processedArgIndices.push_back(i);
+ installMode = INSTALL_INCREMENTAL;
+ } else if (!strcmp(argv[i], "--no-incremental")) {
+ processedArgIndices.push_back(i);
+ installMode = INSTALL_DEFAULT;
+ }
+ }
+
+ if (installMode == INSTALL_INCREMENTAL) {
+ if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
+ error_exit("Attempting to use incremental install on unsupported device");
}
}
@@ -341,8 +411,8 @@
std::vector<const char*> passthrough_argv;
for (int i = 0; i < argc; i++) {
- if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
- processedArgIndicies.end()) {
+ if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) ==
+ processedArgIndices.end()) {
passthrough_argv.push_back(argv[i]);
}
}
@@ -357,6 +427,8 @@
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
+ case INSTALL_INCREMENTAL:
+ return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
case INSTALL_DEFAULT:
default:
return 1;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index c302965..84c0e01 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -60,6 +60,7 @@
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
+#include "incremental_server.h"
#include "services.h"
#include "shell_protocol.h"
#include "sysdeps/chrono.h"
@@ -1959,6 +1960,18 @@
error_exit("usage: adb reconnect [device|offline]");
}
}
+ } else if (!strcmp(argv[0], "inc-server")) {
+ if (argc < 3) {
+ error_exit("usage: adb inc-server FD FILE1 FILE2 ...");
+ }
+ int fd = atoi(argv[1]);
+ if (fd < 3) {
+ // Disallow invalid FDs and stdin/out/err as well.
+ error_exit("Invalid fd number given: %d", fd);
+ }
+ fd = adb_register_socket(fd);
+ close_on_exec(fd);
+ return incremental::serve(fd, argc - 2, argv + 2);
}
error_exit("unknown command %s", argv[0]);
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 5fa0edb..c5fc12f 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -81,17 +81,21 @@
} // namespace
int get_device_api_level() {
- REPORT_FUNC_TIME();
- std::vector<char> sdk_version_output_buffer;
- std::vector<char> sdk_version_error_buffer;
- int api_level = -1;
+ static const int api_level = [] {
+ REPORT_FUNC_TIME();
+ std::vector<char> sdk_version_output_buffer;
+ std::vector<char> sdk_version_error_buffer;
+ int api_level = -1;
- int statusCode = capture_shell_command("getprop ro.build.version.sdk",
- &sdk_version_output_buffer, &sdk_version_error_buffer);
- if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
- api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
- }
+ int status_code =
+ capture_shell_command("getprop ro.build.version.sdk", &sdk_version_output_buffer,
+ &sdk_version_error_buffer);
+ if (status_code == 0 && sdk_version_output_buffer.size() > 0) {
+ api_level = strtol((char*)sdk_version_output_buffer.data(), nullptr, 10);
+ }
+ return api_level;
+ }();
return api_level;
}
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
new file mode 100644
index 0000000..cffd4bd
--- /dev/null
+++ b/adb/client/incremental.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 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 "incremental.h"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <openssl/base64.h>
+
+#include "adb_client.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be32toh(x) _byteswap_ulong(x)
+#endif
+
+using namespace std::literals;
+
+namespace incremental {
+
+namespace {
+
+static constexpr auto IDSIG = ".idsig"sv;
+
+using android::base::StringPrintf;
+
+using Size = int64_t;
+
+static inline int32_t read_int32(borrowed_fd fd) {
+ int32_t result;
+ ReadFully(fd, &result, sizeof(result));
+ return result;
+}
+
+static inline int32_t read_be_int32(borrowed_fd fd) {
+ return int32_t(be32toh(read_int32(fd)));
+}
+
+static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) {
+ int32_t be_size = read_int32(fd);
+ int32_t size = int32_t(be32toh(be_size));
+ auto old_size = bytes->size();
+ bytes->resize(old_size + sizeof(be_size) + size);
+ memcpy(bytes->data() + old_size, &be_size, sizeof(be_size));
+ ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size);
+}
+
+static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
+ std::vector<char> result;
+ append_bytes_with_size(fd, &result); // verityRootHash
+ append_bytes_with_size(fd, &result); // v3Digest
+ append_bytes_with_size(fd, &result); // pkcs7SignatureBlock
+ auto tree_size = read_be_int32(fd); // size of the verity tree
+ return {std::move(result), tree_size};
+}
+
+static inline Size verity_tree_size_for_file(Size fileSize) {
+ constexpr int INCFS_DATA_FILE_BLOCK_SIZE = 4096;
+ constexpr int SHA256_DIGEST_SIZE = 32;
+ constexpr int digest_size = SHA256_DIGEST_SIZE;
+ constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
+
+ Size total_tree_block_count = 0;
+
+ auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
+ auto hash_block_count = block_count;
+ for (auto i = 0; hash_block_count > 1; i++) {
+ hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
+ total_tree_block_count += hash_block_count;
+ }
+ return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
+}
+
+// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
+static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
+ std::string signature_file) {
+ signature_file += IDSIG;
+
+ struct stat st;
+ if (stat(signature_file.c_str(), &st)) {
+ fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
+ return {};
+ }
+
+ unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
+ return {};
+ }
+
+ auto [signature, tree_size] = read_id_sig_headers(fd);
+ if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
+ fprintf(stderr,
+ "Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
+ signature_file.c_str(), (long long)tree_size, (long long)expected);
+ return {};
+ }
+
+ size_t base64_len = 0;
+ if (!EVP_EncodedLength(&base64_len, signature.size())) {
+ fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
+ return {};
+ }
+ std::string encoded_signature;
+ encoded_signature.resize(base64_len);
+ encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
+ (const uint8_t*)signature.data(), signature.size()));
+
+ return {std::move(fd), std::move(encoded_signature)};
+}
+
+// Send install-incremental to the device along with properly configured file descriptors in
+// streaming format. Once connection established, send all fs-verity tree bytes.
+static unique_fd start_install(const std::vector<std::string>& files) {
+ std::vector<std::string> command_args{"package", "install-incremental"};
+
+ // fd's with positions at the beginning of fs-verity
+ std::vector<unique_fd> signature_fds;
+ signature_fds.reserve(files.size());
+ for (int i = 0, size = files.size(); i < size; ++i) {
+ const auto& file = files[i];
+
+ struct stat st;
+ if (stat(file.c_str(), &st)) {
+ fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
+ return {};
+ }
+
+ auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
+ if (!signature_fd.ok()) {
+ return {};
+ }
+
+ auto file_desc =
+ StringPrintf("%s:%lld:%s:%s", android::base::Basename(file).c_str(),
+ (long long)st.st_size, std::to_string(i).c_str(), signature.c_str());
+ command_args.push_back(std::move(file_desc));
+
+ signature_fds.push_back(std::move(signature_fd));
+ }
+
+ std::string error;
+ auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
+ if (connection_fd < 0) {
+ fprintf(stderr, "Failed to run: %s, error: %s\n",
+ android::base::Join(command_args, " ").c_str(), error.c_str());
+ return {};
+ }
+
+ // Pushing verity trees for all installation files.
+ for (auto&& local_fd : signature_fds) {
+ if (!copy_to_file(local_fd.get(), connection_fd.get())) {
+ fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
+ return {};
+ }
+ }
+
+ return connection_fd;
+}
+
+} // namespace
+
+std::optional<Process> install(std::vector<std::string> files) {
+ auto connection_fd = start_install(files);
+ if (connection_fd < 0) {
+ fprintf(stderr, "adb: failed to initiate installation on device.\n");
+ return {};
+ }
+
+ std::string adb_path = android::base::GetExecutablePath();
+
+ auto osh = adb_get_os_handle(connection_fd.get());
+#ifdef _WIN32
+ auto fd_param = std::to_string(reinterpret_cast<intptr_t>(osh));
+#else /* !_WIN32 a.k.a. Unix */
+ auto fd_param = std::to_string(osh);
+#endif
+
+ std::vector<std::string> args(std::move(files));
+ args.insert(args.begin(), {"inc-server", fd_param});
+ auto child = adb_launch_process(adb_path, std::move(args), {connection_fd.get()});
+ if (!child) {
+ fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
+ return {};
+ }
+
+ auto killOnExit = [](Process* p) { p->kill(); };
+ std::unique_ptr<Process, decltype(killOnExit)> serverKiller(&child, killOnExit);
+ // TODO: Terminate server process if installation fails.
+ serverKiller.release();
+
+ return child;
+}
+
+} // namespace incremental
diff --git a/adb/client/incremental.h b/adb/client/incremental.h
new file mode 100644
index 0000000..4b9f6bd
--- /dev/null
+++ b/adb/client/incremental.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+
+#include <optional>
+#include <string>
+
+#include "sysdeps.h"
+
+namespace incremental {
+
+std::optional<Process> install(std::vector<std::string> files);
+
+} // namespace incremental
diff --git a/adb/client/incremental_server.cpp b/adb/client/incremental_server.cpp
new file mode 100644
index 0000000..d9fd77a
--- /dev/null
+++ b/adb/client/incremental_server.cpp
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#define TRACE_TAG INCREMENTAL
+
+#include "incremental_server.h"
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+#ifndef _WIN32
+#include <endian.h>
+#else
+#define be64toh(x) _byteswap_uint64(x)
+#define be32toh(x) _byteswap_ulong(x)
+#define be16toh(x) _byteswap_ushort(x)
+#define htobe64(x) _byteswap_uint64(x)
+#define htobe32(x) _byteswap_ulong(x)
+#define htobe16(x) _byteswap_ushort(x)
+#endif
+
+#include <android-base/strings.h>
+#include <lz4.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <array>
+#include <deque>
+#include <fstream>
+#include <thread>
+#include <type_traits>
+#include <unordered_set>
+
+namespace incremental {
+
+static constexpr int kBlockSize = 4096;
+static constexpr int kCompressedSizeMax = kBlockSize * 0.95;
+static constexpr short kCompressionNone = 0;
+static constexpr short kCompressionLZ4 = 1;
+static constexpr int kCompressBound = std::max(kBlockSize, LZ4_COMPRESSBOUND(kBlockSize));
+static constexpr auto kReadBufferSize = 128 * 1024;
+
+using BlockSize = int16_t;
+using FileId = int16_t;
+using BlockIdx = int32_t;
+using NumBlocks = int32_t;
+using CompressionType = int16_t;
+using RequestType = int16_t;
+using ChunkHeader = int32_t;
+using MagicType = uint32_t;
+
+static constexpr MagicType INCR = 0x494e4352; // LE INCR
+
+static constexpr RequestType EXIT = 0;
+static constexpr RequestType BLOCK_MISSING = 1;
+static constexpr RequestType PREFETCH = 2;
+
+static constexpr inline int64_t roundDownToBlockOffset(int64_t val) {
+ return val & ~(kBlockSize - 1);
+}
+
+static constexpr inline int64_t roundUpToBlockOffset(int64_t val) {
+ return roundDownToBlockOffset(val + kBlockSize - 1);
+}
+
+static constexpr inline NumBlocks numBytesToNumBlocks(int64_t bytes) {
+ return roundUpToBlockOffset(bytes) / kBlockSize;
+}
+
+static constexpr inline off64_t blockIndexToOffset(BlockIdx blockIdx) {
+ return static_cast<off64_t>(blockIdx) * kBlockSize;
+}
+
+template <typename T>
+static inline constexpr T toBigEndian(T t) {
+ using unsigned_type = std::make_unsigned_t<T>;
+ if constexpr (std::is_same_v<T, int16_t>) {
+ return htobe16(static_cast<unsigned_type>(t));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return htobe32(static_cast<unsigned_type>(t));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return htobe64(static_cast<unsigned_type>(t));
+ } else {
+ return t;
+ }
+}
+
+template <typename T>
+static inline constexpr T readBigEndian(void* data) {
+ using unsigned_type = std::make_unsigned_t<T>;
+ if constexpr (std::is_same_v<T, int16_t>) {
+ return static_cast<T>(be16toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return static_cast<T>(be32toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return static_cast<T>(be64toh(*reinterpret_cast<unsigned_type*>(data)));
+ } else {
+ return T();
+ }
+}
+
+// Received from device
+// !Does not include magic!
+struct RequestCommand {
+ RequestType request_type; // 2 bytes
+ FileId file_id; // 2 bytes
+ union {
+ BlockIdx block_idx;
+ NumBlocks num_blocks;
+ }; // 4 bytes
+} __attribute__((packed));
+
+// Placed before actual data bytes of each block
+struct ResponseHeader {
+ FileId file_id; // 2 bytes
+ CompressionType compression_type; // 2 bytes
+ BlockIdx block_idx; // 4 bytes
+ BlockSize block_size; // 2 bytes
+} __attribute__((packed));
+
+// Holds streaming state for a file
+class File {
+ public:
+ // Plain file
+ File(const char* filepath, FileId id, int64_t size, unique_fd fd) : File(filepath, id, size) {
+ this->fd_ = std::move(fd);
+ }
+ int64_t ReadBlock(BlockIdx block_idx, void* buf, bool* is_zip_compressed,
+ std::string* error) const {
+ char* buf_ptr = static_cast<char*>(buf);
+ int64_t bytes_read = -1;
+ const off64_t offsetStart = blockIndexToOffset(block_idx);
+ bytes_read = adb_pread(fd_, &buf_ptr[sizeof(ResponseHeader)], kBlockSize, offsetStart);
+ return bytes_read;
+ }
+
+ const unique_fd& RawFd() const { return fd_; }
+
+ std::vector<bool> sentBlocks;
+ NumBlocks sentBlocksCount;
+
+ const char* const filepath;
+ const FileId id;
+ const int64_t size;
+
+ private:
+ File(const char* filepath, FileId id, int64_t size) : filepath(filepath), id(id), size(size) {
+ sentBlocks.resize(numBytesToNumBlocks(size));
+ }
+ unique_fd fd_;
+};
+
+class IncrementalServer {
+ public:
+ IncrementalServer(unique_fd fd, std::vector<File> files)
+ : adb_fd_(std::move(fd)), files_(std::move(files)) {
+ buffer_.reserve(kReadBufferSize);
+ }
+
+ bool Serve();
+
+ private:
+ struct PrefetchState {
+ const File* file;
+ BlockIdx overallIndex = 0;
+ BlockIdx overallEnd = 0;
+
+ PrefetchState(const File& f) : file(&f), overallEnd((BlockIdx)f.sentBlocks.size()) {}
+ PrefetchState(const File& f, BlockIdx start, int count)
+ : file(&f),
+ overallIndex(start),
+ overallEnd(std::min<BlockIdx>(start + count, f.sentBlocks.size())) {}
+
+ bool done() const { return overallIndex >= overallEnd; }
+ };
+
+ bool SkipToRequest(void* buffer, size_t* size, bool blocking);
+ std::optional<RequestCommand> ReadRequest(bool blocking);
+
+ void erase_buffer_head(int count) { buffer_.erase(buffer_.begin(), buffer_.begin() + count); }
+
+ enum class SendResult { Sent, Skipped, Error };
+ SendResult SendBlock(FileId fileId, BlockIdx blockIdx, bool flush = false);
+ bool SendDone();
+ void RunPrefetching();
+
+ void Send(const void* data, size_t size, bool flush);
+ void Flush();
+ using TimePoint = decltype(std::chrono::high_resolution_clock::now());
+ bool Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent);
+
+ unique_fd const adb_fd_;
+ std::vector<File> files_;
+
+ // Incoming data buffer.
+ std::vector<char> buffer_;
+
+ std::deque<PrefetchState> prefetches_;
+ int compressed_ = 0, uncompressed_ = 0;
+ long long sentSize_ = 0;
+
+ std::vector<char> pendingBlocks_;
+};
+
+bool IncrementalServer::SkipToRequest(void* buffer, size_t* size, bool blocking) {
+ while (true) {
+ // Looking for INCR magic.
+ bool magic_found = false;
+ int bcur = 0;
+ for (int bsize = buffer_.size(); bcur + 4 < bsize; ++bcur) {
+ uint32_t magic = be32toh(*(uint32_t*)(buffer_.data() + bcur));
+ if (magic == INCR) {
+ magic_found = true;
+ break;
+ }
+ }
+
+ if (bcur > 0) {
+ // Stream the rest to stderr.
+ fprintf(stderr, "%.*s", bcur, buffer_.data());
+ erase_buffer_head(bcur);
+ }
+
+ if (magic_found && buffer_.size() >= *size + sizeof(INCR)) {
+ // fine, return
+ memcpy(buffer, buffer_.data() + sizeof(INCR), *size);
+ erase_buffer_head(*size + sizeof(INCR));
+ return true;
+ }
+
+ adb_pollfd pfd = {adb_fd_.get(), POLLIN, 0};
+ auto res = adb_poll(&pfd, 1, blocking ? -1 : 0);
+ if (res != 1) {
+ if (res < 0) {
+ fprintf(stderr, "Failed to poll: %s\n", strerror(errno));
+ return false;
+ }
+ *size = 0;
+ return true;
+ }
+
+ auto bsize = buffer_.size();
+ buffer_.resize(kReadBufferSize);
+ int r = adb_read(adb_fd_, buffer_.data() + bsize, kReadBufferSize - bsize);
+ if (r > 0) {
+ buffer_.resize(bsize + r);
+ continue;
+ }
+
+ if (r == -1) {
+ fprintf(stderr, "Failed to read from fd %d: %d. Exit\n", adb_fd_.get(), errno);
+ return true;
+ }
+
+ // socket is closed
+ return false;
+ }
+}
+
+std::optional<RequestCommand> IncrementalServer::ReadRequest(bool blocking) {
+ uint8_t commandBuf[sizeof(RequestCommand)];
+ auto size = sizeof(commandBuf);
+ if (!SkipToRequest(&commandBuf, &size, blocking)) {
+ return {{EXIT}};
+ }
+ if (size < sizeof(RequestCommand)) {
+ return {};
+ }
+ RequestCommand request;
+ request.request_type = readBigEndian<RequestType>(&commandBuf[0]);
+ request.file_id = readBigEndian<FileId>(&commandBuf[2]);
+ request.block_idx = readBigEndian<BlockIdx>(&commandBuf[4]);
+ return request;
+}
+
+auto IncrementalServer::SendBlock(FileId fileId, BlockIdx blockIdx, bool flush) -> SendResult {
+ auto& file = files_[fileId];
+ if (blockIdx >= static_cast<long>(file.sentBlocks.size())) {
+ fprintf(stderr, "Failed to read file %s at block %" PRId32 " (past end).\n", file.filepath,
+ blockIdx);
+ return SendResult::Error;
+ }
+ if (file.sentBlocks[blockIdx]) {
+ return SendResult::Skipped;
+ }
+ std::string error;
+ char raw[sizeof(ResponseHeader) + kBlockSize];
+ bool isZipCompressed = false;
+ const int64_t bytesRead = file.ReadBlock(blockIdx, &raw, &isZipCompressed, &error);
+ if (bytesRead < 0) {
+ fprintf(stderr, "Failed to get data for %s at blockIdx=%d (%s).\n", file.filepath, blockIdx,
+ error.c_str());
+ return SendResult::Error;
+ }
+
+ ResponseHeader* header = nullptr;
+ char data[sizeof(ResponseHeader) + kCompressBound];
+ char* compressed = data + sizeof(*header);
+ int16_t compressedSize = 0;
+ if (!isZipCompressed) {
+ compressedSize =
+ LZ4_compress_default(raw + sizeof(*header), compressed, bytesRead, kCompressBound);
+ }
+ int16_t blockSize;
+ if (compressedSize > 0 && compressedSize < kCompressedSizeMax) {
+ ++compressed_;
+ blockSize = compressedSize;
+ header = reinterpret_cast<ResponseHeader*>(data);
+ header->compression_type = toBigEndian(kCompressionLZ4);
+ } else {
+ ++uncompressed_;
+ blockSize = bytesRead;
+ header = reinterpret_cast<ResponseHeader*>(raw);
+ header->compression_type = toBigEndian(kCompressionNone);
+ }
+
+ header->file_id = toBigEndian(fileId);
+ header->block_size = toBigEndian(blockSize);
+ header->block_idx = toBigEndian(blockIdx);
+
+ file.sentBlocks[blockIdx] = true;
+ file.sentBlocksCount += 1;
+ Send(header, sizeof(*header) + blockSize, flush);
+ return SendResult::Sent;
+}
+
+bool IncrementalServer::SendDone() {
+ ResponseHeader header;
+ header.file_id = -1;
+ header.compression_type = 0;
+ header.block_idx = 0;
+ header.block_size = 0;
+ Send(&header, sizeof(header), true);
+ return true;
+}
+
+void IncrementalServer::RunPrefetching() {
+ constexpr auto kPrefetchBlocksPerIteration = 128;
+
+ int blocksToSend = kPrefetchBlocksPerIteration;
+ while (!prefetches_.empty() && blocksToSend > 0) {
+ auto& prefetch = prefetches_.front();
+ const auto& file = *prefetch.file;
+ for (auto& i = prefetch.overallIndex; blocksToSend > 0 && i < prefetch.overallEnd; ++i) {
+ if (auto res = SendBlock(file.id, i); res == SendResult::Sent) {
+ --blocksToSend;
+ } else if (res == SendResult::Error) {
+ fprintf(stderr, "Failed to send block %" PRId32 "\n", i);
+ }
+ }
+ if (prefetch.done()) {
+ prefetches_.pop_front();
+ }
+ }
+}
+
+void IncrementalServer::Send(const void* data, size_t size, bool flush) {
+ constexpr auto kChunkFlushSize = 31 * kBlockSize;
+
+ if (pendingBlocks_.empty()) {
+ pendingBlocks_.resize(sizeof(ChunkHeader));
+ }
+ pendingBlocks_.insert(pendingBlocks_.end(), static_cast<const char*>(data),
+ static_cast<const char*>(data) + size);
+ if (flush || pendingBlocks_.size() > kChunkFlushSize) {
+ Flush();
+ }
+}
+
+void IncrementalServer::Flush() {
+ if (pendingBlocks_.empty()) {
+ return;
+ }
+
+ *(ChunkHeader*)pendingBlocks_.data() =
+ toBigEndian<int32_t>(pendingBlocks_.size() - sizeof(ChunkHeader));
+ if (!WriteFdExactly(adb_fd_, pendingBlocks_.data(), pendingBlocks_.size())) {
+ fprintf(stderr, "Failed to write %d bytes\n", int(pendingBlocks_.size()));
+ }
+ sentSize_ += pendingBlocks_.size();
+ pendingBlocks_.clear();
+}
+
+bool IncrementalServer::Exit(std::optional<TimePoint> startTime, int missesCount, int missesSent) {
+ using namespace std::chrono;
+ auto endTime = high_resolution_clock::now();
+ fprintf(stderr,
+ "Connection failed or received exit command. Exit.\n"
+ "Misses: %d, of those unique: %d; sent compressed: %d, uncompressed: "
+ "%d, mb: %.3f\n"
+ "Total time taken: %.3fms\n",
+ missesCount, missesSent, compressed_, uncompressed_, sentSize_ / 1024.0 / 1024.0,
+ duration_cast<microseconds>(endTime - (startTime ? *startTime : endTime)).count() /
+ 1000.0);
+ return true;
+}
+
+bool IncrementalServer::Serve() {
+ // Initial handshake to verify connection is still alive
+ if (!SendOkay(adb_fd_)) {
+ fprintf(stderr, "Connection is dead. Abort.\n");
+ return false;
+ }
+
+ std::unordered_set<FileId> prefetchedFiles;
+ bool doneSent = false;
+ int missesCount = 0;
+ int missesSent = 0;
+
+ using namespace std::chrono;
+ std::optional<TimePoint> startTime;
+
+ while (true) {
+ if (!doneSent && prefetches_.empty() &&
+ std::all_of(files_.begin(), files_.end(), [](const File& f) {
+ return f.sentBlocksCount == NumBlocks(f.sentBlocks.size());
+ })) {
+ fprintf(stdout, "All files should be loaded. Notifying the device.\n");
+ SendDone();
+ doneSent = true;
+ }
+
+ const bool blocking = prefetches_.empty();
+ if (blocking) {
+ // We've no idea how long the blocking call is, so let's flush whatever is still unsent.
+ Flush();
+ }
+ auto request = ReadRequest(blocking);
+
+ if (!startTime) {
+ startTime = high_resolution_clock::now();
+ }
+
+ if (request) {
+ FileId fileId = request->file_id;
+ BlockIdx blockIdx = request->block_idx;
+
+ switch (request->request_type) {
+ case EXIT: {
+ // Stop everything.
+ return Exit(startTime, missesCount, missesSent);
+ }
+ case BLOCK_MISSING: {
+ ++missesCount;
+ // Sends one single block ASAP.
+ if (fileId < 0 || fileId >= (FileId)files_.size() || blockIdx < 0 ||
+ blockIdx >= (BlockIdx)files_[fileId].sentBlocks.size()) {
+ fprintf(stderr,
+ "Received invalid data request for file_id %" PRId16
+ " block_idx %" PRId32 ".\n",
+ fileId, blockIdx);
+ break;
+ }
+ // fprintf(stderr, "\treading file %d block %04d\n", (int)fileId,
+ // (int)blockIdx);
+ if (auto res = SendBlock(fileId, blockIdx, true); res == SendResult::Error) {
+ fprintf(stderr, "Failed to send block %" PRId32 ".\n", blockIdx);
+ } else if (res == SendResult::Sent) {
+ ++missesSent;
+ // Make sure we send more pages from this place onward, in case if the OS is
+ // reading a bigger block.
+ prefetches_.emplace_front(files_[fileId], blockIdx + 1, 7);
+ }
+ break;
+ }
+ case PREFETCH: {
+ // Start prefetching for a file
+ if (fileId < 0) {
+ fprintf(stderr,
+ "Received invalid prefetch request for file_id %" PRId16 "\n",
+ fileId);
+ break;
+ }
+ if (!prefetchedFiles.insert(fileId).second) {
+ fprintf(stderr,
+ "Received duplicate prefetch request for file_id %" PRId16 "\n",
+ fileId);
+ break;
+ }
+ D("Received prefetch request for file_id %" PRId16 ".\n", fileId);
+ prefetches_.emplace_back(files_[fileId]);
+ break;
+ }
+ default:
+ fprintf(stderr, "Invalid request %" PRId16 ",%" PRId16 ",%" PRId32 ".\n",
+ request->request_type, fileId, blockIdx);
+ break;
+ }
+ }
+
+ RunPrefetching();
+ }
+}
+
+bool serve(int adb_fd, int argc, const char** argv) {
+ auto connection_fd = unique_fd(adb_fd);
+ if (argc <= 0) {
+ error_exit("inc-server: must specify at least one file.");
+ }
+
+ std::vector<File> files;
+ files.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ auto filepath = argv[i];
+
+ struct stat st;
+ if (stat(filepath, &st)) {
+ fprintf(stderr, "Failed to stat input file %s. Abort.\n", filepath);
+ return {};
+ }
+
+ unique_fd fd(adb_open(filepath, O_RDONLY));
+ if (fd < 0) {
+ error_exit("inc-server: failed to open file '%s'.", filepath);
+ }
+ files.emplace_back(filepath, i, st.st_size, std::move(fd));
+ }
+
+ IncrementalServer server(std::move(connection_fd), std::move(files));
+ printf("Serving...\n");
+ fclose(stdin);
+ fclose(stdout);
+ return server.Serve();
+}
+
+} // namespace incremental
diff --git a/adb/client/incremental_server.h b/adb/client/incremental_server.h
new file mode 100644
index 0000000..53f011e
--- /dev/null
+++ b/adb/client/incremental_server.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace incremental {
+
+// Expecting arguments like:
+// {FILE1 FILE2 ...}
+// Where FILE* are files to serve.
+bool serve(int adbFd, int argc, const char** argv);
+
+} // namespace incremental
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 0c5a6b4..2318395 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -267,6 +267,39 @@
#define getcwd adb_getcwd
+// A very simple wrapper over a launched child process
+class Process {
+ public:
+ constexpr explicit Process(HANDLE h = nullptr) : h_(h) {}
+ ~Process() { close(); }
+ constexpr explicit operator bool() const { return h_ != nullptr; }
+
+ void wait() {
+ if (*this) {
+ ::WaitForSingleObject(h_, INFINITE);
+ close();
+ }
+ }
+ void kill() {
+ if (*this) {
+ ::TerminateProcess(h_, -1);
+ }
+ }
+
+ private:
+ void close() {
+ if (*this) {
+ ::CloseHandle(h_);
+ h_ = nullptr;
+ }
+ }
+
+ HANDLE h_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit = {});
+
// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
// passed to main().
class NarrowArgs {
@@ -432,11 +465,11 @@
return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
-static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+static __inline__ int adb_pread(borrowed_fd fd, void* buf, size_t len, off64_t offset) {
#if defined(__APPLE__)
- return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+ return TEMP_FAILURE_RETRY(pread(fd.get(), buf, len, offset));
#else
- return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+ return TEMP_FAILURE_RETRY(pread64(fd.get(), buf, len, offset));
#endif
}
@@ -612,6 +645,32 @@
return fd.get();
}
+// A very simple wrapper over a launched child process
+class Process {
+ public:
+ constexpr explicit Process(pid_t pid) : pid_(pid) {}
+ constexpr explicit operator bool() const { return pid_ >= 0; }
+
+ void wait() {
+ if (*this) {
+ int status;
+ ::waitpid(pid_, &status, 0);
+ pid_ = -1;
+ }
+ }
+ void kill() {
+ if (*this) {
+ ::kill(pid_, SIGTERM);
+ }
+ }
+
+ private:
+ pid_t pid_;
+};
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit = {});
+
#endif /* !_WIN32 */
static inline void disable_tcp_nagle(borrowed_fd fd) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 3fdc917..e565706 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -56,3 +56,37 @@
return true;
}
+
+static __inline__ void disable_close_on_exec(borrowed_fd fd) {
+ const auto oldFlags = fcntl(fd.get(), F_GETFD);
+ const auto newFlags = (oldFlags & ~FD_CLOEXEC);
+ if (newFlags != oldFlags) {
+ fcntl(fd.get(), F_SETFD, newFlags);
+ }
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit) {
+ const auto pid = fork();
+ if (pid != 0) {
+ // parent, includes the case when failed to fork()
+ return Process(pid);
+ }
+ // child
+ std::vector<std::string> copies;
+ copies.reserve(args.size() + 1);
+ copies.emplace_back(executable);
+ copies.insert(copies.end(), std::make_move_iterator(args.begin()),
+ std::make_move_iterator(args.end()));
+
+ std::vector<char*> rawArgs;
+ rawArgs.reserve(copies.size() + 1);
+ for (auto&& str : copies) {
+ rawArgs.push_back(str.data());
+ }
+ rawArgs.push_back(nullptr);
+ for (auto fd : fds_to_inherit) {
+ disable_close_on_exec(fd);
+ }
+ exit(execv(copies.front().data(), rawArgs.data()));
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index d9cc36f..e33d51c 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2771,6 +2771,66 @@
return buf;
}
+void enable_inherit(borrowed_fd fd) {
+ auto osh = adb_get_os_handle(fd);
+ const auto h = reinterpret_cast<HANDLE>(osh);
+ ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+}
+
+void disable_inherit(borrowed_fd fd) {
+ auto osh = adb_get_os_handle(fd);
+ const auto h = reinterpret_cast<HANDLE>(osh);
+ ::SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0);
+}
+
+Process adb_launch_process(std::string_view executable, std::vector<std::string> args,
+ std::initializer_list<int> fds_to_inherit) {
+ std::wstring wexe;
+ if (!android::base::UTF8ToWide(executable.data(), executable.size(), &wexe)) {
+ return Process();
+ }
+
+ std::wstring wargs = L"\"" + wexe + L"\"";
+ std::wstring warg;
+ for (auto arg : args) {
+ warg.clear();
+ if (!android::base::UTF8ToWide(arg.data(), arg.size(), &warg)) {
+ return Process();
+ }
+ wargs += L" \"";
+ wargs += warg;
+ wargs += L'\"';
+ }
+
+ STARTUPINFOW sinfo = {sizeof(sinfo)};
+ PROCESS_INFORMATION pinfo = {};
+
+ // TODO: use the Vista+ API to pass the list of inherited handles explicitly;
+ // see http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+ for (auto fd : fds_to_inherit) {
+ enable_inherit(fd);
+ }
+ const auto created = CreateProcessW(wexe.c_str(), wargs.data(),
+ nullptr, // process attributes
+ nullptr, // thread attributes
+ fds_to_inherit.size() > 0, // inherit any handles?
+ 0, // flags
+ nullptr, // environment
+ nullptr, // current directory
+ &sinfo, // startup info
+ &pinfo);
+ for (auto fd : fds_to_inherit) {
+ disable_inherit(fd);
+ }
+
+ if (!created) {
+ return Process();
+ }
+
+ ::CloseHandle(pinfo.hThread);
+ return Process(pinfo.hProcess);
+}
+
// The SetThreadDescription API was brought in version 1607 of Windows 10.
typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
diff --git a/adb/tls/adb_ca_list.cpp b/adb/tls/adb_ca_list.cpp
index 8d37bbe..36afe42 100644
--- a/adb/tls/adb_ca_list.cpp
+++ b/adb/tls/adb_ca_list.cpp
@@ -32,13 +32,13 @@
// CA issuer identifier to distinguished embedded keys. Also has version
// information appended to the end of the string (e.g. "AdbKey-0").
static constexpr int kAdbKeyIdentifierNid = NID_organizationName;
-static constexpr char kAdbKeyIdentifierPrefix[] = "AdbKey-";
-static constexpr int kAdbKeyVersion = 0;
+static constexpr char kAdbKeyIdentifierV0[] = "AdbKey-0";
// Where we store the actual data
static constexpr int kAdbKeyValueNid = NID_commonName;
// TODO: Remove this once X509_NAME_add_entry_by_NID is fixed to use const unsigned char*
+// https://boringssl-review.googlesource.com/c/boringssl/+/39764
int X509_NAME_add_entry_by_NID_const(X509_NAME* name, int nid, int type, const unsigned char* bytes,
int len, int loc, int set) {
return X509_NAME_add_entry_by_NID(name, nid, type, const_cast<unsigned char*>(bytes), len, loc,
@@ -55,13 +55,13 @@
// |len| is the len of the text excluding the final null
int len = X509_NAME_get_text_by_NID(name, nid, nullptr, -1);
if (len <= 0) {
- return {};
+ return std::nullopt;
}
// Include the space for the final null byte
std::vector<char> buf(len + 1, '\0');
CHECK(X509_NAME_get_text_by_NID(name, nid, buf.data(), buf.size()));
- return buf.data();
+ return std::make_optional(std::string(buf.data()));
}
} // namespace
@@ -73,8 +73,7 @@
// "O=AdbKey-0;CN=<key>;"
CHECK(!key.empty());
- std::string identifier = kAdbKeyIdentifierPrefix;
- identifier += std::to_string(kAdbKeyVersion);
+ std::string identifier = kAdbKeyIdentifierV0;
bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
CHECK(X509_NAME_add_entry_by_NID_const(name.get(), kAdbKeyIdentifierNid, MBSTRING_ASC,
reinterpret_cast<const uint8_t*>(identifier.data()),
@@ -91,27 +90,34 @@
CHECK(issuer);
auto buf = GetX509NameTextByNid(issuer, kAdbKeyIdentifierNid);
- if (!buf || !android::base::StartsWith(*buf, kAdbKeyIdentifierPrefix)) {
- return {};
+ if (!buf) {
+ return std::nullopt;
}
- return GetX509NameTextByNid(issuer, kAdbKeyValueNid);
+ // Check for supported versions
+ if (*buf == kAdbKeyIdentifierV0) {
+ return GetX509NameTextByNid(issuer, kAdbKeyValueNid);
+ }
+ return std::nullopt;
}
std::string SHA256BitsToHexString(std::string_view sha256) {
CHECK_EQ(sha256.size(), static_cast<size_t>(SHA256_DIGEST_LENGTH));
std::stringstream ss;
+ auto* u8 = reinterpret_cast<const uint8_t*>(sha256.data());
ss << std::uppercase << std::setfill('0') << std::hex;
// Convert to hex-string representation
for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
- ss << std::setw(2) << (0x00FF & sha256[i]);
+ // Need to cast to something bigger than one byte, or
+ // stringstream will interpret it as a char value.
+ ss << std::setw(2) << static_cast<uint16_t>(u8[i]);
}
return ss.str();
}
std::optional<std::string> SHA256HexStringToBits(std::string_view sha256_str) {
if (sha256_str.size() != SHA256_DIGEST_LENGTH * 2) {
- return {};
+ return std::nullopt;
}
std::string result;
@@ -119,7 +125,7 @@
auto bytestr = std::string(sha256_str.substr(i * 2, 2));
if (!IsHexDigit(bytestr[0]) || !IsHexDigit(bytestr[1])) {
LOG(ERROR) << "SHA256 string has invalid non-hex chars";
- return {};
+ return std::nullopt;
}
result += static_cast<char>(std::stol(bytestr, nullptr, 16));
}
diff --git a/adb/tls/include/adb/tls/tls_connection.h b/adb/tls/include/adb/tls/tls_connection.h
index ae70857..bc5b98a 100644
--- a/adb/tls/include/adb/tls/tls_connection.h
+++ b/adb/tls/include/adb/tls/tls_connection.h
@@ -55,16 +55,15 @@
// Adds a trusted certificate to the list for the SSL connection.
// During the handshake phase, it will check the list of trusted certificates.
- // The connection will fail if the peer's certificate is not in the list. Use
- // |EnableCertificateVerification(false)| to disable certificate
- // verification.
+ // The connection will fail if the peer's certificate is not in the list. If
+ // you would like to accept any certificate, use #SetCertVerifyCallback and
+ // set your callback to always return 1.
//
// Returns true if |cert| was successfully added, false otherwise.
virtual bool AddTrustedCertificate(std::string_view cert) = 0;
// Sets a custom certificate verify callback. |cb| must return 1 if the
- // certificate is trusted. Otherwise, return 0 if not. Note that |cb| is
- // only used if EnableCertificateVerification(false).
+ // certificate is trusted. Otherwise, return 0 if not.
virtual void SetCertVerifyCallback(CertVerifyCb cb) = 0;
// Configures a client |ca_list| that the server sends to the client in the
diff --git a/adb/tls/tests/tls_connection_test.cpp b/adb/tls/tests/tls_connection_test.cpp
index 880904b..27bc1c9 100644
--- a/adb/tls/tests/tls_connection_test.cpp
+++ b/adb/tls/tests/tls_connection_test.cpp
@@ -199,24 +199,10 @@
static std::vector<CAIssuer> kCAIssuers = {
{
{NID_commonName, {'a', 'b', 'c', 'd', 'e'}},
- {NID_organizationName,
- {
- 'd',
- 'e',
- 'f',
- 'g',
- }},
+ {NID_organizationName, {'d', 'e', 'f', 'g'}},
},
{
- {NID_commonName,
- {
- 'h',
- 'i',
- 'j',
- 'k',
- 'l',
- 'm',
- }},
+ {NID_commonName, {'h', 'i', 'j', 'k', 'l', 'm'}},
{NID_countryName, {'n', 'o'}},
},
};
@@ -224,8 +210,6 @@
class AdbWifiTlsConnectionTest : public testing::Test {
protected:
virtual void SetUp() override {
- // TODO: move client code in each test into its own thread, as the
- // socket pair buffer is limited.
android::base::Socketpair(SOCK_STREAM, &server_fd_, &client_fd_);
server_ = TlsConnection::Create(TlsConnection::Role::Server, kTestRsa2048ServerCert,
kTestRsa2048ServerPrivKey, server_fd_);
@@ -257,14 +241,8 @@
return ret;
}
- void StartClientHandshakeAsync(bool expect_success) {
- client_thread_ = std::thread([=]() {
- if (expect_success) {
- EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
- } else {
- EXPECT_NE(client_->DoHandshake(), TlsError::Success);
- }
- });
+ void StartClientHandshakeAsync(TlsError expected) {
+ client_thread_ = std::thread([=]() { EXPECT_EQ(client_->DoHandshake(), expected); });
}
void WaitForClientConnection() {
@@ -313,45 +291,52 @@
// Allow any certificate
server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
client_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
- StartClientHandshakeAsync(true);
+ StartClientHandshakeAsync(TlsError::Success);
// Handshake should succeed
- EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
WaitForClientConnection();
- // Client write, server read
- EXPECT_TRUE(client_->WriteFully(
- std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // Test client/server read and writes
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // Try with overloaded ReadFully
+ std::vector<uint8_t> buf(msg_.size());
+ ASSERT_TRUE(client_->ReadFully(buf.data(), msg_.size()));
+ EXPECT_EQ(buf, msg_);
+ });
+
auto data = server_->ReadFully(msg_.size());
EXPECT_EQ(data, msg_);
-
- // Client read, server write
EXPECT_TRUE(server_->WriteFully(
std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
- // Try with overloaded ReadFully
- std::vector<uint8_t> buf(msg_.size());
- ASSERT_TRUE(client_->ReadFully(buf.data(), msg_.size()));
- EXPECT_EQ(buf, msg_);
+
+ WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, NoTrustedCertificates) {
- StartClientHandshakeAsync(false);
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
// Handshake should not succeed
- EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
WaitForClientConnection();
- // Client write, server read should fail
- EXPECT_FALSE(client_->WriteFully(
- std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // All writes and reads should fail
+ client_thread_ = std::thread([&]() {
+ // Client write, server read should fail
+ EXPECT_FALSE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
auto data = server_->ReadFully(msg_.size());
EXPECT_EQ(data.size(), 0);
-
- // Client read, server write should fail
EXPECT_FALSE(server_->WriteFully(
std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
- data = client_->ReadFully(msg_.size());
- EXPECT_EQ(data.size(), 0);
+
+ WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates) {
@@ -359,23 +344,26 @@
EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
- StartClientHandshakeAsync(true);
+ StartClientHandshakeAsync(TlsError::Success);
// Handshake should succeed
- EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
WaitForClientConnection();
- // Client write, server read
- EXPECT_TRUE(client_->WriteFully(
- std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // All read writes should succeed
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data, msg_);
+ });
+
auto data = server_->ReadFully(msg_.size());
EXPECT_EQ(data, msg_);
-
- // Client read, server write
EXPECT_TRUE(server_->WriteFully(
std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
- data = client_->ReadFully(msg_.size());
- EXPECT_EQ(data, msg_);
+
+ WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, AddTrustedCertificates_ClientWrongCert) {
@@ -387,23 +375,26 @@
// Without enabling EnableClientPostHandshakeCheck(), DoHandshake() will
// succeed, because in TLS 1.3, the client doesn't get notified if the
// server rejected the certificate until a read operation is called.
- StartClientHandshakeAsync(true);
+ StartClientHandshakeAsync(TlsError::Success);
// Handshake should fail for server, succeed for client
- EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
WaitForClientConnection();
- // Client write succeeds, server read should fail
- EXPECT_TRUE(client_->WriteFully(
- std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // Client writes will succeed, everything else will fail.
+ client_thread_ = std::thread([&]() {
+ EXPECT_TRUE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
auto data = server_->ReadFully(msg_.size());
EXPECT_EQ(data.size(), 0);
-
- // Client read, server write should fail
EXPECT_FALSE(server_->WriteFully(
std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
- data = client_->ReadFully(msg_.size());
- EXPECT_EQ(data.size(), 0);
+
+ WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, ExportKeyingMaterial) {
@@ -415,10 +406,10 @@
EXPECT_TRUE(client_->AddTrustedCertificate(kTestRsa2048ServerCert));
EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048ClientCert));
- StartClientHandshakeAsync(true);
+ StartClientHandshakeAsync(TlsError::Success);
// Handshake should succeed
- EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
WaitForClientConnection();
// Verify the client and server's exported key material match.
@@ -439,10 +430,10 @@
// Client handshake should succeed, because in TLS 1.3, client does not
// realize that the peer rejected the certificate until after a read
// operation.
- client_thread_ = std::thread([&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::Success); });
+ StartClientHandshakeAsync(TlsError::Success);
// Server handshake should fail
- EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
WaitForClientConnection();
}
@@ -455,11 +446,10 @@
server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 0; });
// Client handshake should fail because server rejects everything
- client_thread_ = std::thread(
- [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::PeerRejectedCertificate); });
+ StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
// Server handshake should fail
- EXPECT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
WaitForClientConnection();
}
@@ -469,11 +459,10 @@
// Server accepts all
server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
// Client handshake should fail
- client_thread_ = std::thread(
- [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); });
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
// Server handshake should fail
- EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
WaitForClientConnection();
}
@@ -488,15 +477,15 @@
server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
// Client handshake should fail
- client_thread_ = std::thread(
- [&]() { EXPECT_EQ(client_->DoHandshake(), TlsError::CertificateRejected); });
+ StartClientHandshakeAsync(TlsError::CertificateRejected);
// Server handshake should fail
- EXPECT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::PeerRejectedCertificate);
WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, EnableClientPostHandshakeCheck_ClientWrongCert) {
+ client_->AddTrustedCertificate(kTestRsa2048ServerCert);
// client's DoHandshake() will fail if the server rejected the certificate
client_->EnableClientPostHandshakeCheck(true);
@@ -504,23 +493,26 @@
EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
// Handshake should fail for client
- StartClientHandshakeAsync(false);
+ StartClientHandshakeAsync(TlsError::PeerRejectedCertificate);
// Handshake should fail for server
- EXPECT_NE(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::CertificateRejected);
WaitForClientConnection();
- // Client write fails, server read should fail
- EXPECT_FALSE(client_->WriteFully(
- std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ // All read writes should fail
+ client_thread_ = std::thread([&]() {
+ EXPECT_FALSE(client_->WriteFully(
+ std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
+ auto data = client_->ReadFully(msg_.size());
+ EXPECT_EQ(data.size(), 0);
+ });
+
auto data = server_->ReadFully(msg_.size());
EXPECT_EQ(data.size(), 0);
-
- // Client read, server write should fail
EXPECT_FALSE(server_->WriteFully(
std::string_view(reinterpret_cast<const char*>(msg_.data()), msg_.size())));
- data = client_->ReadFully(msg_.size());
- EXPECT_EQ(data.size(), 0);
+
+ WaitForClientConnection();
}
TEST_F(AdbWifiTlsConnectionTest, SetClientCAList_Empty) {
@@ -569,12 +561,12 @@
return 1;
});
// Client handshake should succeed
- EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
});
EXPECT_TRUE(server_->AddTrustedCertificate(kTestRsa2048UnknownCert));
// Server handshake should succeed
- EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
client_thread_.join();
}
@@ -604,12 +596,12 @@
return 1;
});
// Client handshake should succeed
- EXPECT_EQ(client_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(client_->DoHandshake(), TlsError::Success);
});
server_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
// Server handshake should succeed
- EXPECT_EQ(server_->DoHandshake(), TlsError::Success);
+ ASSERT_EQ(server_->DoHandshake(), TlsError::Success);
client_thread_.join();
}
} // namespace tls
diff --git a/adb/tls/tls_connection.cpp b/adb/tls/tls_connection.cpp
index 7df6ef4..853cdac 100644
--- a/adb/tls/tls_connection.cpp
+++ b/adb/tls/tls_connection.cpp
@@ -61,6 +61,7 @@
static const char* SSLErrorString();
void Invalidate();
TlsError GetFailureReason(int err);
+ const char* RoleToString() { return role_ == Role::Server ? kServerRoleStr : kClientRoleStr; }
Role role_;
bssl::UniquePtr<EVP_PKEY> priv_key_;
@@ -75,15 +76,19 @@
CertVerifyCb cert_verify_cb_;
SetCertCb set_cert_cb_;
borrowed_fd fd_;
+ static constexpr char kClientRoleStr[] = "[client]: ";
+ static constexpr char kServerRoleStr[] = "[server]: ";
}; // TlsConnectionImpl
TlsConnectionImpl::TlsConnectionImpl(Role role, std::string_view cert, std::string_view priv_key,
borrowed_fd fd)
: role_(role), fd_(fd) {
CHECK(!cert.empty() && !priv_key.empty());
- LOG(INFO) << "Initializing adbwifi TlsConnection";
+ LOG(INFO) << RoleToString() << "Initializing adbwifi TlsConnection";
cert_ = BufferFromPEM(cert);
+ CHECK(cert_);
priv_key_ = EvpPkeyFromPEM(priv_key);
+ CHECK(priv_key_);
}
TlsConnectionImpl::~TlsConnectionImpl() {
@@ -149,7 +154,7 @@
// Create X509 buffer from the certificate string
auto buf = X509FromBuffer(BufferFromPEM(cert));
if (buf == nullptr) {
- LOG(ERROR) << "Failed to create a X509 buffer for the certificate.";
+ LOG(ERROR) << RoleToString() << "Failed to create a X509 buffer for the certificate.";
return false;
}
known_certificates_.push_back(std::move(buf));
@@ -205,8 +210,7 @@
}
TlsConnection::TlsError TlsConnectionImpl::DoHandshake() {
- int err = -1;
- LOG(INFO) << "Starting adbwifi tls handshake";
+ LOG(INFO) << RoleToString() << "Starting adbwifi tls handshake";
ssl_ctx_.reset(SSL_CTX_new(TLS_method()));
// TODO: Remove set_max_proto_version() once external/boringssl is updated
// past
@@ -214,14 +218,14 @@
if (ssl_ctx_.get() == nullptr ||
!SSL_CTX_set_min_proto_version(ssl_ctx_.get(), TLS1_3_VERSION) ||
!SSL_CTX_set_max_proto_version(ssl_ctx_.get(), TLS1_3_VERSION)) {
- LOG(ERROR) << "Failed to create SSL context";
+ LOG(ERROR) << RoleToString() << "Failed to create SSL context";
return TlsError::UnknownFailure;
}
// Register user-supplied known certificates
for (auto const& cert : known_certificates_) {
if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx_.get()), cert.get()) == 0) {
- LOG(ERROR) << "Unable to add certificates into the X509_STORE";
+ LOG(ERROR) << RoleToString() << "Unable to add certificates into the X509_STORE";
return TlsError::UnknownFailure;
}
}
@@ -248,7 +252,8 @@
};
if (!SSL_CTX_set_chain_and_key(ssl_ctx_.get(), cert_chain.data(), cert_chain.size(),
priv_key_.get(), nullptr)) {
- LOG(ERROR) << "Unable to register the certificate chain file and private key ["
+ LOG(ERROR) << RoleToString()
+ << "Unable to register the certificate chain file and private key ["
<< SSLErrorString() << "]";
Invalidate();
return TlsError::UnknownFailure;
@@ -259,19 +264,21 @@
// Okay! Let's try to do the handshake!
ssl_.reset(SSL_new(ssl_ctx_.get()));
if (!SSL_set_fd(ssl_.get(), fd_.get())) {
- LOG(ERROR) << "SSL_set_fd failed. [" << SSLErrorString() << "]";
+ LOG(ERROR) << RoleToString() << "SSL_set_fd failed. [" << SSLErrorString() << "]";
return TlsError::UnknownFailure;
}
+
switch (role_) {
case Role::Server:
- err = SSL_accept(ssl_.get());
+ SSL_set_accept_state(ssl_.get());
break;
case Role::Client:
- err = SSL_connect(ssl_.get());
+ SSL_set_connect_state(ssl_.get());
break;
}
- if (err != 1) {
- LOG(ERROR) << "Handshake failed in SSL_accept/SSL_connect [" << SSLErrorString() << "]";
+ if (SSL_do_handshake(ssl_.get()) != 1) {
+ LOG(ERROR) << RoleToString() << "Handshake failed in SSL_accept/SSL_connect ["
+ << SSLErrorString() << "]";
auto sslerr = ERR_get_error();
Invalidate();
return GetFailureReason(sslerr);
@@ -281,16 +288,16 @@
uint8_t check;
// Try to peek one byte for any failures. This assumes on success that
// the server actually sends something.
- err = SSL_peek(ssl_.get(), &check, 1);
- if (err <= 0) {
- LOG(ERROR) << "Post-handshake SSL_peek failed [" << SSLErrorString() << "]";
+ if (SSL_peek(ssl_.get(), &check, 1) <= 0) {
+ LOG(ERROR) << RoleToString() << "Post-handshake SSL_peek failed [" << SSLErrorString()
+ << "]";
auto sslerr = ERR_get_error();
Invalidate();
return GetFailureReason(sslerr);
}
}
- LOG(INFO) << "Handshake succeeded.";
+ LOG(INFO) << RoleToString() << "Handshake succeeded.";
return TlsError::Success;
}
@@ -311,7 +318,7 @@
bool TlsConnectionImpl::ReadFully(void* buf, size_t size) {
CHECK_GT(size, 0U);
if (!ssl_) {
- LOG(ERROR) << "Tried to read on a null SSL connection";
+ LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
return false;
}
@@ -321,7 +328,7 @@
int bytes_read =
SSL_read(ssl_.get(), p8 + offset, std::min(static_cast<size_t>(INT_MAX), size));
if (bytes_read <= 0) {
- LOG(WARNING) << "SSL_read failed [" << SSLErrorString() << "]";
+ LOG(ERROR) << RoleToString() << "SSL_read failed [" << SSLErrorString() << "]";
return false;
}
size -= bytes_read;
@@ -333,7 +340,7 @@
bool TlsConnectionImpl::WriteFully(std::string_view data) {
CHECK(!data.empty());
if (!ssl_) {
- LOG(ERROR) << "Tried to read on a null SSL connection";
+ LOG(ERROR) << RoleToString() << "Tried to read on a null SSL connection";
return false;
}
@@ -341,7 +348,7 @@
int bytes_out = SSL_write(ssl_.get(), data.data(),
std::min(static_cast<size_t>(INT_MAX), data.size()));
if (bytes_out <= 0) {
- LOG(WARNING) << "SSL_write failed [" << SSLErrorString() << "]";
+ LOG(ERROR) << RoleToString() << "SSL_write failed [" << SSLErrorString() << "]";
return false;
}
data = data.substr(bytes_out);
diff --git a/base/Android.bp b/base/Android.bp
index a32959b..25c74f2 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -128,6 +128,10 @@
static_libs: ["fmtlib"],
whole_static_libs: ["fmtlib"],
export_static_lib_headers: ["fmtlib"],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
}
cc_library_static {
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index e3ce531..f28c778 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -171,6 +171,7 @@
srcs: [
"libdebuggerd/backtrace.cpp",
+ "libdebuggerd/gwp_asan.cpp",
"libdebuggerd/open_files_list.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
@@ -181,7 +182,10 @@
// Needed for private/bionic_fdsan.h
include_dirs: ["bionic/libc"],
- header_libs: ["bionic_libc_platform_headers"],
+ header_libs: [
+ "bionic_libc_platform_headers",
+ "gwp_asan_headers",
+ ],
static_libs: [
"libdexfile_support_static", // libunwindstack dependency
@@ -192,6 +196,8 @@
"liblog",
],
+ whole_static_libs: ["gwp_asan_crash_handler"],
+
target: {
recovery: {
exclude_static_libs: [
@@ -246,10 +252,12 @@
static_libs: [
"libdebuggerd",
+ "libgmock",
],
header_libs: [
"bionic_libc_platform_headers",
+ "gwp_asan_headers",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 2545cd6..ebb8d86 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,8 +73,8 @@
unique_fd pipe_read, pipe_write;
ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
- // 64 MiB should be enough for everyone.
- constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+ // 16 MiB should be enough for everyone.
+ constexpr int PIPE_SIZE = 16 * 1024 * 1024;
ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
// Wait for a bit to let the child spawn all of its threads.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index e8f366f..3e99880 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -255,7 +255,8 @@
static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
std::unique_ptr<unwindstack::Regs>* regs, uintptr_t* abort_msg_address,
- uintptr_t* fdsan_table_address) {
+ uintptr_t* fdsan_table_address, uintptr_t* gwp_asan_state,
+ uintptr_t* gwp_asan_metadata) {
std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
@@ -272,6 +273,10 @@
expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
break;
+ case 3:
+ expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
+ break;
+
default:
LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
break;
@@ -284,7 +289,13 @@
}
*fdsan_table_address = 0;
+ *gwp_asan_state = 0;
+ *gwp_asan_metadata = 0;
switch (crash_info->header.version) {
+ case 3:
+ *gwp_asan_state = crash_info->data.v3.gwp_asan_state;
+ *gwp_asan_metadata = crash_info->data.v3.gwp_asan_metadata;
+ FALLTHROUGH_INTENDED;
case 2:
*fdsan_table_address = crash_info->data.v2.fdsan_table_address;
FALLTHROUGH_INTENDED;
@@ -416,6 +427,8 @@
DebuggerdDumpType dump_type;
uintptr_t abort_msg_address = 0;
uintptr_t fdsan_table_address = 0;
+ uintptr_t gwp_asan_state = 0;
+ uintptr_t gwp_asan_metadata = 0;
Initialize(argv);
ParseArgs(argc, argv, &pseudothread_tid, &dump_type);
@@ -477,7 +490,7 @@
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_msg_address,
- &fdsan_table_address);
+ &fdsan_table_address, &gwp_asan_state, &gwp_asan_metadata);
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
} else {
@@ -592,7 +605,8 @@
{
ATRACE_NAME("engrave_tombstone");
engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
- abort_msg_address, &open_files, &amfd_data);
+ abort_msg_address, &open_files, &amfd_data, gwp_asan_state,
+ gwp_asan_metadata);
}
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index f8192b5..8b4b630 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -49,6 +49,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/macros.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
@@ -298,6 +299,8 @@
void* ucontext;
uintptr_t abort_msg;
uintptr_t fdsan_table;
+ uintptr_t gwp_asan_state;
+ uintptr_t gwp_asan_metadata;
};
// Logging and contacting debuggerd requires free file descriptors, which we might not have.
@@ -342,23 +345,25 @@
}
// ucontext_t is absurdly large on AArch64, so piece it together manually with writev.
- uint32_t version = 2;
- constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV2);
+ uint32_t version = 3;
+ constexpr size_t expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataV3);
errno = 0;
if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {
fatal_errno("failed to set pipe buffer size");
}
- struct iovec iovs[5] = {
+ struct iovec iovs[] = {
{.iov_base = &version, .iov_len = sizeof(version)},
{.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
{.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
{.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
{.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
+ {.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
};
- ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 5));
+ ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
if (rc == -1) {
fatal_errno("failed to write crash info");
} else if (rc != expected) {
@@ -485,6 +490,8 @@
}
void* abort_message = nullptr;
+ const gwp_asan::AllocatorState* gwp_asan_state = nullptr;
+ const gwp_asan::AllocationMetadata* gwp_asan_metadata = nullptr;
uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
@@ -499,6 +506,12 @@
if (g_callbacks.get_abort_message) {
abort_message = g_callbacks.get_abort_message();
}
+ if (g_callbacks.get_gwp_asan_state) {
+ gwp_asan_state = g_callbacks.get_gwp_asan_state();
+ }
+ if (g_callbacks.get_gwp_asan_metadata) {
+ gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
+ }
}
// If sival_int is ~0, it means that the fallback handler has been called
@@ -532,6 +545,8 @@
.ucontext = context,
.abort_msg = reinterpret_cast<uintptr_t>(abort_message),
.fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
+ .gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
+ .gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index cd6fc05..4f24360 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -24,11 +24,20 @@
__BEGIN_DECLS
+// Forward declare these classes so not everyone has to include GWP-ASan
+// headers.
+namespace gwp_asan {
+struct AllocatorState;
+struct AllocationMetadata;
+}; // namespace gwp_asan
+
// These callbacks are called in a signal handler, and thus must be async signal safe.
// If null, the callbacks will not be called.
typedef struct {
struct abort_msg_t* (*get_abort_message)();
void (*post_dump)();
+ const struct gwp_asan::AllocatorState* (*get_gwp_asan_state)();
+ const struct gwp_asan::AllocationMetadata* (*get_gwp_asan_metadata)();
} debuggerd_callbacks_t;
void debuggerd_init(debuggerd_callbacks_t* callbacks);
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
new file mode 100644
index 0000000..53df783
--- /dev/null
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 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 "libdebuggerd/gwp_asan.h"
+#include "libdebuggerd/utility.h"
+
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+// Retrieve GWP-ASan state from `state_addr` inside the process at
+// `process_memory`. Place the state into `*state`.
+static bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,
+ gwp_asan::AllocatorState* state) {
+ return process_memory->ReadFully(state_addr, state, sizeof(*state));
+}
+
+// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process
+// at `process_memory`. The number of metadata slots is retrieved from the
+// allocator state provided. This function returns a heap-allocated copy of the
+// metadata pool whose ownership should be managed by the caller. Returns
+// nullptr on failure.
+static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
+ unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
+ uintptr_t metadata_addr) {
+ if (state.MaxSimultaneousAllocations > 1024) {
+ ALOGE(
+ "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
+ "exceeds maximum allowed (1024).",
+ state.MaxSimultaneousAllocations);
+ return nullptr;
+ }
+
+ gwp_asan::AllocationMetadata* meta =
+ new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];
+ if (!process_memory->ReadFully(metadata_addr, meta,
+ sizeof(*meta) * state.MaxSimultaneousAllocations)) {
+ ALOGE(
+ "Error when retrieving GWP-ASan metadata, could not retrieve %zu "
+ "pieces of metadata.",
+ state.MaxSimultaneousAllocations);
+ delete[] meta;
+ meta = nullptr;
+ }
+ return meta;
+}
+
+GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
+ uintptr_t gwp_asan_state_ptr, uintptr_t gwp_asan_metadata_ptr,
+ const ThreadInfo& thread_info) {
+ if (!process_memory || !gwp_asan_metadata_ptr || !gwp_asan_state_ptr) return;
+ // Extract the GWP-ASan regions from the dead process.
+ if (!retrieve_gwp_asan_state(process_memory, gwp_asan_state_ptr, &state_)) return;
+ metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, gwp_asan_metadata_ptr));
+ if (!metadata_.get()) return;
+
+ // Get the external crash address from the thread info.
+ crash_address_ = 0u;
+ if (signal_has_si_addr(thread_info.siginfo)) {
+ crash_address_ = reinterpret_cast<uintptr_t>(thread_info.siginfo->si_addr);
+ }
+
+ // Ensure the error belongs to GWP-ASan.
+ if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;
+
+ is_gwp_asan_responsible_ = true;
+ thread_id_ = thread_info.tid;
+
+ // Grab the internal error address, if it exists.
+ uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+ if (internal_crash_address) {
+ crash_address_ = internal_crash_address;
+ }
+
+ // Get other information from the internal state.
+ error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);
+ error_string_ = gwp_asan::ErrorToString(error_);
+ responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);
+}
+
+bool GwpAsanCrashData::CrashIsMine() const {
+ return is_gwp_asan_responsible_;
+}
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+ if (!CrashIsMine()) {
+ ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+ return;
+ }
+
+ if (error_ == gwp_asan::Error::UNKNOWN) {
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+ crash_address_);
+ return;
+ }
+
+ if (!responsible_allocation_) {
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+ crash_address_);
+ return;
+ }
+
+ uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
+ size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+
+ if (crash_address_ == alloc_address) {
+ // Use After Free on a 41-byte allocation at 0xdeadbeef.
+ _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s on a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_string_, alloc_size, alloc_address);
+ return;
+ }
+
+ uintptr_t diff;
+ const char* location_str;
+
+ if (crash_address_ < alloc_address) {
+ // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+ location_str = "left of";
+ diff = alloc_address - crash_address_;
+ } else if (crash_address_ - alloc_address < alloc_size) {
+ // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+ location_str = "into";
+ diff = crash_address_ - alloc_address;
+ } else {
+ // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+ // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+ location_str = "right of";
+ diff = crash_address_ - alloc_address;
+ if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+ diff -= alloc_size;
+ }
+ }
+
+ // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+ const char* byte_suffix = "s";
+ if (diff == 1) {
+ byte_suffix = "";
+ }
+ _LOG(log, logtype::HEADER,
+ "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+ error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
+ size_t frame_num) {
+ unwindstack::FrameData frame;
+ frame.num = frame_num;
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (!map_info) {
+ frame.rel_pc = pc;
+ return frame;
+ }
+
+ unwindstack::Elf* elf =
+ map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+ // Create registers just to get PC adjustment. Doesn't matter what they point
+ // to.
+ unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+ uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+ relative_pc -= pc_adjustment;
+ // The debug PC may be different if the PC comes from the JIT.
+ uint64_t debug_pc = relative_pc;
+
+ // If we don't have a valid ELF file, check the JIT.
+ if (!elf->valid()) {
+ unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+ uint64_t jit_pc = pc - pc_adjustment;
+ unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ if (jit_elf != nullptr) {
+ debug_pc = jit_pc;
+ elf = jit_elf;
+ }
+ }
+
+ // Copy all the things we need into the frame for symbolization.
+ frame.rel_pc = relative_pc;
+ frame.pc = pc - pc_adjustment;
+ frame.map_name = map_info->name;
+ frame.map_elf_start_offset = map_info->elf_start_offset;
+ frame.map_exact_offset = map_info->offset;
+ frame.map_start = map_info->start;
+ frame.map_end = map_info->end;
+ frame.map_flags = map_info->flags;
+ frame.map_load_bias = elf->GetLoadBias();
+
+ if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ return frame;
+}
+
+constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
+
+bool GwpAsanCrashData::HasDeallocationTrace() const {
+ assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
+ if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
+ return false;
+ }
+ return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+ assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
+ uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
+
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+ size_t num_frames =
+ __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+ if (thread_id == gwp_asan::kInvalidThreadID) {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+ } else {
+ _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < num_frames; ++i) {
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+ assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+ return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+ assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
+ uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
+
+ std::unique_ptr<uintptr_t> frames(new uintptr_t[kMaxTraceLength]);
+ size_t num_frames =
+ __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+ if (thread_id == gwp_asan::kInvalidThreadID) {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+ } else {
+ _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < num_frames; ++i) {
+ unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
+ _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
+ }
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
new file mode 100644
index 0000000..aef4c62
--- /dev/null
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <log/log.h>
+#include <unwindstack/Memory.h>
+
+#include "gwp_asan/common.h"
+#include "types.h"
+#include "utility.h"
+
+class GwpAsanCrashData {
+ public:
+ GwpAsanCrashData() = delete;
+ ~GwpAsanCrashData() = default;
+
+ // Construct the crash data object. Takes a handle to the object that can
+ // supply the memory of the dead process, and pointers to the GWP-ASan state
+ // and metadata regions within that process. Also takes the thread information
+ // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may
+ // still be responsible, as it terminates when it detects an internal error
+ // (double free, invalid free). In these cases, we will retrieve the fault
+ // address from the GWP-ASan allocator's state.
+ GwpAsanCrashData(unwindstack::Memory* process_memory, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr, const ThreadInfo& thread_info);
+
+ // Is GWP-ASan responsible for this crash.
+ bool CrashIsMine() const;
+
+ // Returns the fault address. The fault address may be the same as provided
+ // during construction, or it may have been retrieved from GWP-ASan's internal
+ // allocator crash state.
+ uintptr_t GetFaultAddress() const;
+
+ // Dump the GWP-ASan stringified cause of this crash. May only be called if
+ // CrashIsMine() returns true.
+ void DumpCause(log_t* log) const;
+
+ // Returns whether this crash has a deallocation trace. May only be called if
+ // CrashIsMine() returns true.
+ bool HasDeallocationTrace() const;
+
+ // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+ // HasDeallocationTrace() returns true.
+ void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ // Returns whether this crash has a allocation trace. May only be called if
+ // CrashIsMine() returns true.
+ bool HasAllocationTrace() const;
+
+ // Dump the GWP-ASan allocation trace for this crash. May only be called if
+ // HasAllocationTrace() returns true.
+ void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+ protected:
+ // Is GWP-ASan responsible for this crash.
+ bool is_gwp_asan_responsible_ = false;
+
+ // Thread ID of the crash.
+ size_t thread_id_;
+
+ // The type of error that GWP-ASan caused (and the stringified version),
+ // Undefined if GWP-ASan isn't responsible for the crash.
+ gwp_asan::Error error_;
+ const char* error_string_;
+
+ // Pointer to the crash address. Holds the internal crash address if it
+ // exists, otherwise the address provided at construction.
+ uintptr_t crash_address_ = 0u;
+
+ // Pointer to the metadata for the responsible allocation, nullptr if it
+ // doesn't exist.
+ const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;
+
+ // Internal state.
+ gwp_asan::AllocatorState state_;
+ std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;
+};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7133f77..291d994 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -55,6 +55,7 @@
void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data);
+ std::string* amfd_data, uintptr_t gwp_asan_state,
+ uintptr_t gwp_asan_metadata);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index b33adf3..eed95bc 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "libdebuggerd/utility.h"
@@ -31,8 +32,13 @@
#include "host_signal_fixup.h"
#include "log_fake.h"
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
#include "tombstone.cpp"
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
class TombstoneTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -359,3 +365,131 @@
dump_timestamp(&log_, 0);
ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
}
+
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+ GwpAsanCrashDataTest(
+ gwp_asan::Error error,
+ const gwp_asan::AllocationMetadata *responsible_allocation) :
+ GwpAsanCrashData(nullptr, 0u, 0u, ThreadInfo{}) {
+ is_gwp_asan_responsible_ = true;
+ error_ = error;
+ responsible_allocation_ = responsible_allocation;
+ error_string_ = gwp_asan::ErrorToString(error_);
+ }
+
+ void SetCrashAddress(uintptr_t crash_address) {
+ crash_address_ = crash_address;
+ }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+ crash_data.SetCrashAddress(0x1000);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+ crash_data.SetCrashAddress(0x1000);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(tombstone_contents,
+ MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free on a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+ crash_data.SetCrashAddress(0x1025);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+ crash_data.SetCrashAddress(0xffe);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+ crash_data.SetCrashAddress(0x1001);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+ gwp_asan::AllocationMetadata meta;
+ meta.Addr = 0x1000;
+ meta.Size = 32;
+
+ GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+ crash_data.SetCrashAddress(0x1021);
+
+ crash_data.DumpCause(&log_);
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ std::string tombstone_contents;
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_THAT(
+ tombstone_contents,
+ MatchesRegex(
+ "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+ "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 4e7f35c..fd52e81 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -53,9 +53,13 @@
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/gwp_asan.h"
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
using android::base::GetBoolProperty;
using android::base::GetProperty;
using android::base::StringPrintf;
@@ -372,7 +376,8 @@
}
static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
- uint64_t abort_msg_address, bool primary_thread) {
+ uint64_t abort_msg_address, bool primary_thread,
+ const GwpAsanCrashData& gwp_asan_crash_data) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -381,7 +386,13 @@
if (thread_info.siginfo) {
dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
- dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+ }
+
+ if (primary_thread && gwp_asan_crash_data.CrashIsMine()) {
+ gwp_asan_crash_data.DumpCause(log);
+ } else if (thread_info.siginfo) {
+ dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(),
+ thread_info.registers.get());
}
if (primary_thread) {
@@ -402,6 +413,14 @@
}
if (primary_thread) {
+ if (gwp_asan_crash_data.HasDeallocationTrace()) {
+ gwp_asan_crash_data.DumpDeallocationTrace(log, unwinder);
+ }
+
+ if (gwp_asan_crash_data.HasAllocationTrace()) {
+ gwp_asan_crash_data.DumpAllocationTrace(log, unwinder);
+ }
+
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
@@ -583,13 +602,14 @@
}
engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
- nullptr, nullptr);
+ nullptr, nullptr, 0u, 0u);
}
void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
- std::string* amfd_data) {
+ std::string* amfd_data, uintptr_t gwp_asan_state_ptr,
+ uintptr_t gwp_asan_metadata_ptr) {
// don't copy log messages to tombstone unless this is a dev device
bool want_logs = android::base::GetBoolProperty("ro.debuggable", false);
@@ -607,7 +627,13 @@
if (it == threads.end()) {
LOG(FATAL) << "failed to find target thread";
}
- dump_thread(&log, unwinder, it->second, abort_msg_address, true);
+
+ GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(),
+ gwp_asan_state_ptr,
+ gwp_asan_metadata_ptr, it->second);
+
+ dump_thread(&log, unwinder, it->second, abort_msg_address, true,
+ gwp_asan_crash_data);
if (want_logs) {
dump_logs(&log, it->second.pid, 50);
@@ -618,7 +644,7 @@
continue;
}
- dump_thread(&log, unwinder, thread_info, 0, false);
+ dump_thread(&log, unwinder, thread_info, 0, false, gwp_asan_crash_data);
}
if (open_files) {
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index bfd0fbb..bf53864 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -95,10 +95,16 @@
uintptr_t fdsan_table_address;
};
+struct __attribute__((__packed__)) CrashInfoDataV3 : public CrashInfoDataV2 {
+ uintptr_t gwp_asan_state;
+ uintptr_t gwp_asan_metadata;
+};
+
struct __attribute__((__packed__)) CrashInfo {
CrashInfoHeader header;
union {
CrashInfoDataV1 v1;
CrashInfoDataV2 v2;
+ CrashInfoDataV3 v3;
} data;
};
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2099170..c845b6b 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -277,9 +277,9 @@
} else if (StartsWith(flag, "keydirectory=")) {
// The metadata flag is followed by an = and the directory for the keys.
entry->metadata_key_dir = arg;
- } else if (StartsWith(flag, "metadata_cipher=")) {
- // Specify the cipher to use for metadata encryption
- entry->metadata_cipher = arg;
+ } else if (StartsWith(flag, "metadata_encryption=")) {
+ // Specify the cipher and flags to use for metadata encryption
+ entry->metadata_encryption = arg;
} else if (StartsWith(flag, "sysfs_path=")) {
// The path to trigger device gc by idle-maint of vold.
entry->sysfs_path = arg;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c769f07..f0e1f83 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,7 @@
std::string fs_options;
std::string key_loc;
std::string metadata_key_dir;
- std::string metadata_cipher;
+ std::string metadata_encryption;
off64_t length = 0;
std::string label;
int partnum = -1;
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index d7b689e..6461788 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -280,6 +280,7 @@
extra_argv.emplace_back("allow_discards");
extra_argv.emplace_back("sector_size:4096");
extra_argv.emplace_back("iv_large_sectors");
+ if (is_hw_wrapped_) extra_argv.emplace_back("wrappedkey_v0");
}
if (!extra_argv.empty()) {
argv.emplace_back(std::to_string(extra_argv.size()));
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index affdd29..67af59a 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -526,13 +526,18 @@
bool is_legacy;
ASSERT_TRUE(DmTargetDefaultKey::IsLegacy(&is_legacy));
// set_dun only in the non-is_legacy case
- DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0,
- is_legacy, !is_legacy);
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ if (is_legacy) {
+ target.SetIsLegacy();
+ } else {
+ target.SetSetDun();
+ }
ASSERT_EQ(target.name(), "default-key");
ASSERT_TRUE(target.Valid());
if (is_legacy) {
ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
} else {
+ // TODO: Add case for wrapped key enabled
ASSERT_EQ(target.GetParameterString(),
"AES-256-XTS abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 "
"iv_large_sectors");
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index e3dd92b..d2e50d3 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -280,20 +280,20 @@
class DmTargetDefaultKey final : public DmTarget {
public:
DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
- const std::string& key, const std::string& blockdev, uint64_t start_sector,
- bool is_legacy, bool set_dun)
+ const std::string& key, const std::string& blockdev, uint64_t start_sector)
: DmTarget(start, length),
cipher_(cipher),
key_(key),
blockdev_(blockdev),
- start_sector_(start_sector),
- is_legacy_(is_legacy),
- set_dun_(set_dun) {}
+ start_sector_(start_sector) {}
std::string name() const override { return name_; }
bool Valid() const override;
std::string GetParameterString() const override;
static bool IsLegacy(bool* result);
+ void SetIsLegacy() { is_legacy_ = true; }
+ void SetSetDun() { set_dun_ = true; }
+ void SetWrappedKeyV0() { is_hw_wrapped_ = true; }
private:
static const std::string name_;
@@ -301,8 +301,9 @@
std::string key_;
std::string blockdev_;
uint64_t start_sector_;
- bool is_legacy_;
- bool set_dun_;
+ bool is_legacy_ = false;
+ bool set_dun_ = false;
+ bool is_hw_wrapped_ = false;
};
} // namespace dm
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index d274ba4..63bdcc5 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -238,6 +238,7 @@
"liblog",
"liblp",
"libprotobuf-cpp-lite",
+ "libstatslog",
"libutils",
// TODO(b/148818798): remove when parent bug is fixed.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index e35ad4b..34d3d69 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -24,9 +24,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/snapshot.h>
-#include "utility.h"
+#include <statslog.h>
+#include "snapshot_stats.h"
#include "utility.h"
using namespace std::string_literals;
@@ -37,17 +39,29 @@
"Actions:\n"
" dump\n"
" Print snapshot states.\n"
- " merge [--logcat] [--log-to-file]\n"
+ " merge [--logcat] [--log-to-file] [--report] [--dry-run]\n"
" Initialize merge and wait for it to be completed.\n"
" If --logcat is specified, log to logcat.\n"
" If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n"
- " If both specified, log to both. If none specified, log to stdout.\n";
+ " If both specified, log to both. If none specified, log to stdout.\n"
+ " If --report is specified, send merge statistics to statsd.\n"
+ " If --dry-run flag, no real merge operation is is triggered, and\n"
+ " sample statistics are sent to statsd for testing purpose.\n";
return EX_USAGE;
}
namespace android {
namespace snapshot {
+static SnapshotMergeReport GetDummySnapshotMergeReport() {
+ SnapshotMergeReport fake_report;
+
+ fake_report.set_state(UpdateState::MergeCompleted);
+ fake_report.set_resume_count(56);
+
+ return fake_report;
+}
+
bool DumpCmdHandler(int /*argc*/, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
return SnapshotManager::New()->Dump(std::cout);
@@ -113,24 +127,53 @@
};
bool MergeCmdHandler(int argc, char** argv) {
- auto begin = std::chrono::steady_clock::now();
+ std::chrono::milliseconds passed_ms;
+
+ bool report_to_statsd = false;
+ bool dry_run = false;
+ for (int i = 2; i < argc; ++i) {
+ if (argv[i] == "--report"s) {
+ report_to_statsd = true;
+ } else if (argv[i] == "--dry-run"s) {
+ dry_run = true;
+ }
+ }
// 'snapshotctl merge' is stripped away from arguments to
// Logger.
android::base::InitLogging(argv);
android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2));
- auto state = SnapshotManager::New()->InitiateMergeAndWait();
+ UpdateState state;
+ SnapshotMergeReport merge_report;
+ if (dry_run) {
+ merge_report = GetDummySnapshotMergeReport();
+ state = merge_report.state();
+ passed_ms = std::chrono::milliseconds(1234);
+ } else {
+ auto begin = std::chrono::steady_clock::now();
- // We could wind up in the Unverified state if the device rolled back or
- // hasn't fully rebooted. Ignore this.
- if (state == UpdateState::None || state == UpdateState::Unverified) {
- return true;
- }
- if (state == UpdateState::MergeCompleted) {
+ state = SnapshotManager::New()->InitiateMergeAndWait(&merge_report);
+
+ // We could wind up in the Unverified state if the device rolled back or
+ // hasn't fully rebooted. Ignore this.
+ if (state == UpdateState::None || state == UpdateState::Unverified) {
+ return true;
+ }
+
auto end = std::chrono::steady_clock::now();
- auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
- LOG(INFO) << "Snapshot merged in " << passed << " ms.";
+ passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
+ }
+
+ if (report_to_statsd) {
+ android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED,
+ static_cast<int32_t>(merge_report.state()),
+ static_cast<int64_t>(passed_ms.count()),
+ static_cast<int32_t>(merge_report.resume_count()));
+ }
+
+ if (state == UpdateState::MergeCompleted) {
+ LOG(INFO) << "Snapshot merged in " << passed_ms.count() << " ms.";
return true;
}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 64fb157..9caae35 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -891,11 +891,11 @@
EXPECT_EQ("/dir/key", entry->metadata_key_dir);
}
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataCipher) {
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source none0 swap defaults keydirectory=/dir/key,metadata_cipher=adiantum
+source none0 swap defaults keydirectory=/dir/key,metadata_encryption=adiantum
)fs";
ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -905,7 +905,28 @@
ASSERT_EQ(1U, fstab.size());
auto entry = fstab.begin();
- EXPECT_EQ("adiantum", entry->metadata_cipher);
+ EXPECT_EQ("adiantum", entry->metadata_encryption);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults keydirectory=/dir/key,metadata_encryption=aes-256-xts:wrappedkey_v0
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
+ auto parts = android::base::Split(entry->metadata_encryption, ":");
+ EXPECT_EQ(2U, parts.size());
+ EXPECT_EQ("aes-256-xts", parts[0]);
+ EXPECT_EQ("wrappedkey_v0", parts[1]);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b85f23f..8e9e074 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -55,6 +55,7 @@
using android::hardware::health::V1_0::BatteryHealth;
using android::hardware::health::V1_0::BatteryStatus;
using android::hardware::health::V2_1::BatteryCapacityLevel;
+using android::hardware::health::V2_1::Constants;
namespace android {
@@ -79,6 +80,8 @@
// HIDL enum values are zero initialized, so they need to be initialized
// properly.
health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
+ health_info_2_1->batteryChargeTimeToFullNowSeconds =
+ (int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
auto* props = &health_info_2_1->legacy.legacy;
props->batteryStatus = BatteryStatus::UNKNOWN;
props->batteryHealth = BatteryHealth::UNKNOWN;
@@ -134,13 +137,13 @@
{"Normal", BatteryCapacityLevel::NORMAL},
{"High", BatteryCapacityLevel::HIGH},
{"Full", BatteryCapacityLevel::FULL},
- {NULL, BatteryCapacityLevel::UNKNOWN},
+ {NULL, BatteryCapacityLevel::UNSUPPORTED},
};
auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
if (!ret) {
- KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
- *ret = BatteryCapacityLevel::UNKNOWN;
+ KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+ *ret = BatteryCapacityLevel::UNSUPPORTED;
}
return *ret;
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
new file mode 100644
index 0000000..7ef3854
--- /dev/null
+++ b/libcutils/include/private/android_projectid_config.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/*
+ * This file describes the project ID values we use for filesystem quota
+ * tracking. It is used on devices that don't have the sdcardfs kernel module,
+ * which requires us to use filesystem project IDs for efficient quota
+ * calculation.
+ *
+ * These values are typically set on files and directories using extended
+ * attributes; see vold for examples.
+ */
+
+/* Default project ID for files on external storage. */
+#define PROJECT_ID_EXT_DEFAULT 1000
+/* Project ID for audio files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_AUDIO 1001
+/* Project ID for video files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_VIDEO 1002
+/* Project ID for image files on external storage. */
+#define PROJECT_ID_EXT_MEDIA_IMAGE 1003
+
+/* Start of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_START 20000
+/* End of project IDs for apps to mark external app data. */
+#define PROJECT_ID_EXT_DATA_END 29999
+
+/* Start of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_START 30000
+/* End of project IDs for apps to mark external cached data. */
+#define PROJECT_ID_EXT_CACHE_END 39999
+
+/* Start of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_START 40000
+/* End of project IDs for apps to mark external OBB data. */
+#define PROJECT_ID_EXT_OBB_END 49999
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index c84ddf7..c98455d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -274,7 +274,7 @@
* Gets the minimum priority that will be logged for this process. If none has been set by a
* previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT.
*/
-int __android_log_get_minimum_priority();
+int __android_log_get_minimum_priority(void);
/**
* Sets the default tag if no tag is provided when writing a log message. Defaults to
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index f30058a..abd48fc 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -405,7 +405,7 @@
static struct cache2_char security = {
PTHREAD_MUTEX_INITIALIZER, 0,
"persist.logd.security", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
- "ro.device_owner", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ "ro.organization_owned", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
evaluate_security};
return do_cache2_char(&security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 75a26bf..a60d2df 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1637,7 +1637,7 @@
TEST(liblog, __security) {
#ifdef __ANDROID__
static const char persist_key[] = "persist.logd.security";
- static const char readonly_key[] = "ro.device_owner";
+ static const char readonly_key[] = "ro.organization_owned";
// A silly default value that can never be in readonly_key so
// that it can be determined the property is not set.
static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
@@ -1657,7 +1657,7 @@
if (!strcmp(readonly, nothing_val)) {
// Lets check if we can set the value (we should not be allowed to do so)
EXPECT_FALSE(__android_log_security());
- fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
+ fprintf(stderr, "WARNING: setting ro.organization_owned to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
EXPECT_NE(0, property_set(readonly_key, domain));
useconds_t total_time = 0;
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index ccda5d1..627f0d4 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -39,6 +39,10 @@
"clang-analyzer-security*",
"android-*",
],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
}
cc_test {
diff --git a/logd/README.property b/logd/README.property
index d2a2cbb..1b7e165 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -6,7 +6,7 @@
ro.logd.auditd.main bool true selinux audit messages sent to main.
ro.logd.auditd.events bool true selinux audit messages sent to events.
persist.logd.security bool false Enable security buffer.
-ro.device_owner bool false Override persist.logd.security to false
+ro.organization_owned bool false Override persist.logd.security to false
ro.logd.kernel bool+ svelte+ Enable klogd daemon
ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
ro.debuggable number if not "1", logd.statistics &
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f4a846f..a643062 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -317,7 +317,7 @@
{"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
{"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
{"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
- {"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
+ {"ro.organization_owned", "u:object_r:device_logging_prop:s0", "string", false},
{"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
@@ -669,7 +669,7 @@
{"ro.crypto.type", "u:object_r:vold_prop:s0"},
{"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
{"ro.debuggable", "u:object_r:default_prop:s0"},
- {"ro.device_owner", "u:object_r:device_logging_prop:s0"},
+ {"ro.organization_owned", "u:object_r:device_logging_prop:s0"},
{"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
{"ro.frp.pst", "u:object_r:default_prop:s0"},
{"ro.hardware", "u:object_r:default_prop:s0"},