Merge "sh_binary_host for 'adb-remount-test.sh'."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index ad0231d..5c7d847 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -293,6 +293,13 @@
"libdebuggerd/test/utility_test.cpp",
],
+ product_variables: {
+ malloc_not_svelte: {
+ srcs: ["libdebuggerd/test/scudo_test.cpp"],
+ header_libs: ["scudo_headers"],
+ },
+ },
+
target: {
android: {
srcs: [
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e113308..ed90ab4 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1486,6 +1486,37 @@
ASSERT_BACKTRACE_FRAME(result, "abort");
}
+TEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {
+ int intercept_result;
+ unique_fd output_fd;
+
+ static const auto dump_type = kDebuggerdTombstone;
+ StartProcess(
+ []() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
+ std::thread abort_thread([] { abort(); });
+ abort_thread.join();
+ },
+ &seccomp_fork);
+
+ StartIntercept(&output_fd, dump_type);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_BACKTRACE_FRAME(result, "abort");
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
+ ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
TEST_F(CrasherTest, seccomp_backtrace) {
int intercept_result;
unique_fd output_fd;
@@ -1516,6 +1547,40 @@
ASSERT_BACKTRACE_FRAME(result, "bar");
}
+TEST_F(CrasherTest, seccomp_backtrace_from_thread) {
+ int intercept_result;
+ unique_fd output_fd;
+
+ static const auto dump_type = kDebuggerdNativeBacktrace;
+ StartProcess(
+ []() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
+ std::thread raise_thread([] {
+ raise_debugger_signal(dump_type);
+ _exit(0);
+ });
+ raise_thread.join();
+ },
+ &seccomp_fork);
+
+ StartIntercept(&output_fd, dump_type);
+ FinishCrasher();
+ AssertDeath(0);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
+ ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
TEST_F(CrasherTest, seccomp_crash_logcat) {
StartProcess([]() { abort(); }, &seccomp_fork);
FinishCrasher();
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 70e3022..4721391 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -210,7 +210,10 @@
// Send a signal to all of our siblings, asking them to dump their stack.
pid_t current_tid = gettid();
- if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
+ if (!iterate_tids(current_tid, [&output_fd, ¤t_tid](pid_t tid) {
+ if (current_tid == tid) {
+ return;
+ }
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index a506859..68bfd5b 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,9 +34,10 @@
class ScudoCrashData {
public:
- ScudoCrashData() = delete;
+ ScudoCrashData() = default;
~ScudoCrashData() = default;
- ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
+
+ bool SetErrorInfo(unwindstack::Memory* process_memory, const ProcessInfo& process_info);
bool CrashIsMine() const;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 27fae25..9483e59 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#include <stdint.h>
+#include <unistd.h>
+
+#include <vector>
+
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/tombstone.h"
@@ -25,54 +30,92 @@
#include "tombstone.pb.h"
-std::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,
- size_t size) {
- auto buf = std::make_unique<char[]>(size);
- if (!process_memory->ReadFully(addr, buf.get(), size)) {
- return std::unique_ptr<char[]>();
- }
- return buf;
-}
-
-ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
- const ProcessInfo& process_info) {
+bool ScudoCrashData::SetErrorInfo(unwindstack::Memory* process_memory,
+ const ProcessInfo& process_info) {
if (!process_info.has_fault_address) {
- return;
+ return false;
}
- auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
- __scudo_get_stack_depot_size());
- auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
- __scudo_get_region_info_size());
- auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
- __scudo_get_ring_buffer_size());
+ std::vector<char> stack_depot(__scudo_get_stack_depot_size());
+ if (!process_memory->ReadFully(process_info.scudo_stack_depot, stack_depot.data(),
+ stack_depot.size())) {
+ return false;
+ }
+ std::vector<char> region_info(__scudo_get_region_info_size());
+ if (!process_memory->ReadFully(process_info.scudo_region_info, region_info.data(),
+ region_info.size())) {
+ return false;
+ }
+ std::vector<char> ring_buffer(__scudo_get_ring_buffer_size());
+ if (!process_memory->ReadFully(process_info.scudo_ring_buffer, ring_buffer.data(),
+ ring_buffer.size())) {
+ return false;
+ }
+
+ uintptr_t page_size = getpagesize();
untagged_fault_addr_ = process_info.untagged_fault_address;
- uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+ uintptr_t fault_page = untagged_fault_addr_ & ~(page_size - 1);
- uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
- if (memory_begin > fault_page) {
- return;
+ // Attempt to get 16 pages before the fault page and 16 pages after.
+ constexpr size_t kExtraPages = 16;
+ std::vector<char> memory(page_size * (kExtraPages * 2 + 1));
+
+ // Read faulting page first.
+ size_t memory_index = kExtraPages;
+ if (!process_memory->ReadFully(fault_page, &memory[memory_index * page_size], page_size)) {
+ return false;
}
- uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
- if (memory_end < fault_page) {
- return;
+ // Attempt to read the pages after the fault page, stop as soon as we
+ // fail to read.
+ uintptr_t read_addr = fault_page;
+ if (!__builtin_add_overflow(fault_page, page_size, &read_addr)) {
+ memory_index++;
+ for (size_t i = 0; i < kExtraPages; i++, memory_index++) {
+ if (!process_memory->ReadFully(read_addr, &memory[memory_index * page_size], page_size)) {
+ break;
+ }
+ if (__builtin_add_overflow(read_addr, page_size, &read_addr)) {
+ break;
+ }
+ }
+ }
+ uintptr_t memory_end = read_addr;
+
+ // Attempt to read the pages before the fault page, stop as soon as we
+ // fail to read.
+ memory_index = kExtraPages;
+ if (fault_page > 0) {
+ read_addr = fault_page - page_size;
+ for (size_t i = 0; i < kExtraPages; i++, memory_index--) {
+ if (!process_memory->ReadFully(read_addr, &memory[(memory_index - 1) * page_size],
+ page_size)) {
+ break;
+ }
+ if (read_addr == 0) {
+ memory_index--;
+ break;
+ }
+ read_addr -= page_size;
+ }
+ }
+ size_t start_memory_index = memory_index;
+ uintptr_t memory_begin = fault_page - (kExtraPages - memory_index) * page_size;
+
+ std::vector<long> memory_tags((memory_end - memory_begin) / kTagGranuleSize);
+ read_addr = memory_begin;
+ for (size_t i = 0; i < memory_tags.size(); i++) {
+ memory_tags[i] = process_memory->ReadTag(read_addr);
+ read_addr += kTagGranuleSize;
}
- auto memory = std::make_unique<char[]>(memory_end - memory_begin);
- for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
- process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
- }
+ __scudo_get_error_info(
+ &error_info_, process_info.maybe_tagged_fault_address, stack_depot.data(), region_info.data(),
+ ring_buffer.data(), &memory[start_memory_index * page_size],
+ reinterpret_cast<const char*>(memory_tags.data()), memory_begin, memory_end - memory_begin);
- auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
- for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {
- memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);
- }
-
- __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
- region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
- memory_begin, memory_end - memory_begin);
+ return true;
}
bool ScudoCrashData::CrashIsMine() const {
diff --git a/debuggerd/libdebuggerd/test/scudo_test.cpp b/debuggerd/libdebuggerd/test/scudo_test.cpp
new file mode 100644
index 0000000..d8fc6a7
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/scudo_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2022 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 <stdlib.h>
+#include <unistd.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "libdebuggerd/scudo.h"
+#include "libdebuggerd/types.h"
+#include "unwindstack/Memory.h"
+
+#include "log_fake.h"
+
+#include <inttypes.h>
+
+// This needs to match the kExtraPages from ScudoCrashData::SetErrorInfo.
+constexpr uint64_t kMaxPages = 16;
+
+class MemoryAlwaysZero : public unwindstack::Memory {
+ public:
+ MemoryAlwaysZero() = default;
+ virtual ~MemoryAlwaysZero() = default;
+
+ size_t Read(uint64_t addr, void* buffer, size_t size) override {
+ if (test_unreadable_addrs_.count(addr) != 0) {
+ return 0;
+ }
+ test_read_addrs_.insert(addr);
+ memset(buffer, 0, size);
+ return size;
+ }
+
+ void TestAddUnreadableAddress(uint64_t addr) { test_unreadable_addrs_.insert(addr); }
+
+ void TestClearAddresses() {
+ test_read_addrs_.clear();
+ test_unreadable_addrs_.clear();
+ }
+
+ std::set<uint64_t>& test_read_addrs() { return test_read_addrs_; }
+
+ private:
+ std::set<uint64_t> test_unreadable_addrs_;
+
+ std::set<uint64_t> test_read_addrs_;
+};
+
+TEST(ScudoTest, no_fault_address) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = false;
+ info.untagged_fault_address = 0x5000;
+ info.scudo_stack_depot = 0x1000;
+ info.scudo_region_info = 0x2000;
+ info.scudo_ring_buffer = 0x3000;
+
+ ScudoCrashData crash;
+ ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
+
+ info.has_fault_address = true;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+}
+
+TEST(ScudoTest, scudo_data_read_check) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = true;
+ info.untagged_fault_address = 0x5000;
+ info.scudo_stack_depot = 0x1000;
+ info.scudo_region_info = 0x2000;
+ info.scudo_ring_buffer = 0x3000;
+
+ ScudoCrashData crash;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+
+ // Stack Depot unreadable
+ process_memory.TestClearAddresses();
+ process_memory.TestAddUnreadableAddress(0x1000);
+ ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
+
+ // The Region Info doesn't exist for 32 bit.
+#if defined(__LP64__)
+ // Region Info unreadable
+ process_memory.TestClearAddresses();
+ process_memory.TestAddUnreadableAddress(0x2000);
+ ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
+#endif
+
+ // Ring Buffer unreadable
+ process_memory.TestClearAddresses();
+ process_memory.TestAddUnreadableAddress(0x3000);
+ ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
+
+ // Verify that with all scudo data readable, the error info works.
+ process_memory.TestClearAddresses();
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+}
+
+TEST(ScudoTest, fault_page_unreadable) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = true;
+ info.untagged_fault_address = 0x5124;
+ info.scudo_stack_depot = 0x1000;
+ info.scudo_region_info = 0x2000;
+ info.scudo_ring_buffer = 0x3000;
+
+ ScudoCrashData crash;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+
+ uint64_t fault_page = info.untagged_fault_address & ~(getpagesize() - 1);
+ process_memory.TestAddUnreadableAddress(fault_page);
+ ASSERT_FALSE(crash.SetErrorInfo(&process_memory, info));
+}
+
+TEST(ScudoTest, pages_before_fault_unreadable) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = true;
+ info.untagged_fault_address = 0x15124;
+ info.scudo_stack_depot = 0x1000;
+ info.scudo_region_info = 0x2000;
+ info.scudo_ring_buffer = 0x3000;
+
+ ScudoCrashData crash;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+
+ uint64_t page_size = getpagesize();
+ uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
+
+ std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
+ for (size_t i = 0; i <= kMaxPages; i++) {
+ expected_reads.emplace_back(fault_page + i * page_size);
+ }
+
+ // Loop through and make pages before the fault page unreadable.
+ for (size_t i = 1; i <= kMaxPages + 1; i++) {
+ process_memory.TestClearAddresses();
+ uint64_t unreadable_addr = fault_page - i * page_size;
+ SCOPED_TRACE(testing::Message()
+ << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
+ process_memory.TestAddUnreadableAddress(unreadable_addr);
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+ ASSERT_THAT(process_memory.test_read_addrs(),
+ testing::UnorderedElementsAreArray(expected_reads));
+ // Need to add the previous unreadable_addr to the list of expected addresses.
+ expected_reads.emplace_back(unreadable_addr);
+ }
+}
+
+TEST(ScudoTest, pages_after_fault_unreadable) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = true;
+ info.untagged_fault_address = 0x15124;
+ info.scudo_stack_depot = 0x1000;
+ info.scudo_region_info = 0x2000;
+ info.scudo_ring_buffer = 0x3000;
+
+ ScudoCrashData crash;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+
+ uint64_t page_size = getpagesize();
+ uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
+
+ std::vector<uint64_t> expected_reads = {0x1000, 0x2000, 0x3000};
+ for (size_t i = 0; i <= kMaxPages; i++) {
+ expected_reads.emplace_back(fault_page - i * page_size);
+ }
+
+ // Loop through and make pages after the fault page unreadable.
+ for (size_t i = 1; i <= kMaxPages + 1; i++) {
+ process_memory.TestClearAddresses();
+ uint64_t unreadable_addr = fault_page + i * page_size;
+ SCOPED_TRACE(testing::Message()
+ << "Failed at unreadable address 0x" << std::hex << unreadable_addr);
+ process_memory.TestAddUnreadableAddress(unreadable_addr);
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+ ASSERT_THAT(process_memory.test_read_addrs(),
+ testing::UnorderedElementsAreArray(expected_reads));
+ // Need to add the previous unreadable_addr to the list of expected addresses.
+ expected_reads.emplace_back(unreadable_addr);
+ }
+}
+
+// Make sure that if the fault address is low, you won't underflow.
+TEST(ScudoTest, fault_address_low) {
+ MemoryAlwaysZero process_memory;
+ ProcessInfo info;
+ info.has_fault_address = true;
+ info.scudo_stack_depot = 0x21000;
+ info.scudo_region_info = 0x22000;
+ info.scudo_ring_buffer = 0x23000;
+
+ ScudoCrashData crash;
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+
+ uint64_t page_size = getpagesize();
+ for (size_t i = 0; i < kMaxPages + 1; i++) {
+ process_memory.TestClearAddresses();
+ info.untagged_fault_address = 0x124 + i * getpagesize();
+ SCOPED_TRACE(testing::Message()
+ << "Failed with fault address 0x" << std::hex << info.untagged_fault_address);
+ ASSERT_TRUE(crash.SetErrorInfo(&process_memory, info));
+ std::vector<uint64_t> expected_reads = {0x21000, 0x22000, 0x23000};
+ uint64_t fault_page = info.untagged_fault_address & ~(page_size - 1);
+ expected_reads.emplace_back(fault_page);
+ for (size_t j = 1; j <= kMaxPages; j++) {
+ expected_reads.emplace_back(fault_page + j * page_size);
+ }
+ while (fault_page != 0) {
+ fault_page -= page_size;
+ expected_reads.emplace_back(fault_page);
+ }
+ ASSERT_THAT(process_memory.test_read_addrs(),
+ testing::UnorderedElementsAreArray(expected_reads));
+ }
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 5ca2c00..e5b4d74 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -55,15 +55,15 @@
siginfo_t* siginfo, ucontext_t* ucontext) {
pid_t uid = getuid();
pid_t pid = getpid();
- pid_t tid = gettid();
+ pid_t target_tid = gettid();
log_t log;
- log.current_tid = tid;
- log.crashed_tid = tid;
+ log.current_tid = target_tid;
+ log.crashed_tid = target_tid;
log.tfd = tombstone_fd;
log.amfd_data = nullptr;
- std::string thread_name = get_thread_name(tid);
+ std::string thread_name = get_thread_name(target_tid);
std::vector<std::string> command_line = get_command_line(pid);
std::unique_ptr<unwindstack::Regs> regs(
@@ -73,32 +73,34 @@
android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
std::map<pid_t, ThreadInfo> threads;
- threads[tid] = ThreadInfo{
- .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
- .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
- .siginfo = siginfo,
+ threads[target_tid] = ThreadInfo {
+ .registers = std::move(regs), .uid = uid, .tid = target_tid,
+ .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
+ .selinux_label = std::move(selinux_label), .siginfo = siginfo,
#if defined(__aarch64__)
// Only supported on aarch64 for now.
.tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
.pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
#endif
};
- if (pid == tid) {
- const ThreadInfo& thread = threads[pid];
- if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
- threads[tid] = ThreadInfo{
- .uid = thread.uid,
- .tid = tid,
- .pid = thread.pid,
- .command_line = thread.command_line,
- .thread_name = get_thread_name(tid),
- .tagged_addr_ctrl = thread.tagged_addr_ctrl,
- .pac_enabled_keys = thread.pac_enabled_keys,
- };
- })) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
- strerror(errno));
- }
+ const ThreadInfo& thread = threads[pid];
+ if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {
+ if (target_tid == tid) {
+ return;
+ }
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Adding thread %d", tid);
+ threads[tid] = ThreadInfo{
+ .uid = thread.uid,
+ .tid = tid,
+ .pid = thread.pid,
+ .command_line = thread.command_line,
+ .thread_name = get_thread_name(tid),
+ .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+ .pac_enabled_keys = thread.pac_enabled_keys,
+ };
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+ strerror(errno));
}
// Do not use the thread cache here because it will call pthread_key_create
@@ -116,8 +118,8 @@
ProcessInfo process_info;
process_info.abort_msg_address = abort_msg_address;
- engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
- process_info, nullptr, nullptr);
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,
+ target_tid, process_info, nullptr, nullptr);
}
void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 159ebc8..6e1ce8f 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -193,8 +193,9 @@
static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& main_thread) {
#if defined(USE_SCUDO)
- ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
- if (scudo_crash_data.CrashIsMine()) {
+ ScudoCrashData scudo_crash_data;
+ if (scudo_crash_data.SetErrorInfo(unwinder->GetProcessMemory().get(), process_info) &&
+ scudo_crash_data.CrashIsMine()) {
scudo_crash_data.AddCauseProtos(tombstone, unwinder);
return;
}
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 5c6abc9..df033df 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -90,9 +90,7 @@
if (tid == 0) {
continue;
}
- if (pid != tid) {
- callback(tid);
- }
+ callback(tid);
}
return true;
}
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 06ffe0f..05186a2 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -121,7 +121,12 @@
int WriteCallback(void* priv, const void* data, size_t len) {
PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
if (!data) {
- return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
+ if (lseek64(handle->fd(), len, SEEK_CUR) < 0) {
+ int rv = -errno;
+ PLOG(ERROR) << "lseek failed";
+ return rv;
+ }
+ return 0;
}
return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
}
@@ -131,6 +136,7 @@
downloaded_data.size(), true, false);
if (!file) {
// Invalid sparse format
+ LOG(ERROR) << "Unable to open sparse data for flashing";
return -EINVAL;
}
return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
@@ -175,10 +181,13 @@
std::vector<char> data = std::move(device->download_data());
if (data.size() == 0) {
+ LOG(ERROR) << "Cannot flash empty data vector";
return -EINVAL;
}
uint64_t block_device_size = get_block_device_size(handle.fd());
if (data.size() > block_device_size) {
+ LOG(ERROR) << "Cannot flash " << data.size() << " bytes to block device of size "
+ << block_device_size;
return -EOVERFLOW;
} else if (data.size() < block_device_size &&
(partition_name == "boot" || partition_name == "boot_a" ||
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
index df9c900..08c9358 100644
--- a/fastboot/device/main.cpp
+++ b/fastboot/device/main.cpp
@@ -14,13 +14,30 @@
* limitations under the License.
*/
+#include <stdarg.h>
+
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <sparse/sparse.h>
#include "fastboot_device.h"
+static void LogSparseVerboseMessage(const char* fmt, ...) {
+ std::string message;
+
+ va_list ap;
+ va_start(ap, fmt);
+ android::base::StringAppendV(&message, fmt, ap);
+ va_end(ap);
+
+ LOG(ERROR) << "libsparse message: " << message;
+}
+
int main(int /*argc*/, char* argv[]) {
android::base::InitLogging(argv, &android::base::KernelLogger);
+ sparse_print_verbose = LogSparseVerboseMessage;
+
while (true) {
FastbootDevice device;
device.ExecuteCommands();
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 7c7979b..c12a672 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1485,7 +1485,7 @@
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
attempted_entry.mount_point, wiped ? "true" : "false",
- attempted_entry.fs_type},
+ attempted_entry.fs_type, attempted_entry.zoned_device},
nullptr)) {
LERROR << "Encryption failed";
set_type_property(encryptable);
@@ -1525,7 +1525,7 @@
if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
current_entry.mount_point, "true" /* shouldFormat */,
- current_entry.fs_type},
+ current_entry.fs_type, current_entry.zoned_device},
nullptr)) {
LERROR << "Encryption failed";
} else {
@@ -1550,7 +1550,7 @@
if (mount_errno != EBUSY && mount_errno != EACCES &&
should_use_metadata_encryption(attempted_entry)) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
- attempted_entry.mount_point},
+ attempted_entry.mount_point, attempted_entry.zoned_device},
nullptr)) {
++error_count;
} else if (current_entry.mount_point == "/data") {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 6f59ed3..7385f79 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -117,7 +117,7 @@
}
static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,
- bool needs_casefold, bool fs_compress) {
+ bool needs_casefold, bool fs_compress, const std::string& zoned_device) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -146,8 +146,15 @@
args.push_back("-O");
args.push_back("extra_attr");
}
- args.push_back(fs_blkdev.c_str());
- args.push_back(size_str.c_str());
+ if (!zoned_device.empty()) {
+ args.push_back("-c");
+ args.push_back(zoned_device.c_str());
+ args.push_back("-m");
+ args.push_back(fs_blkdev.c_str());
+ } else {
+ args.push_back(fs_blkdev.c_str());
+ args.push_back(size_str.c_str());
+ }
return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
}
@@ -164,7 +171,7 @@
if (entry.fs_type == "f2fs") {
return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,
- entry.fs_mgr_flags.fs_compress);
+ entry.fs_mgr_flags.fs_compress, entry.zoned_device);
} else if (entry.fs_type == "ext4") {
return format_ext4(entry.blk_device, entry.mount_point, needs_projid,
entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 8c719c8..06368b8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -304,6 +304,14 @@
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
}
+ } else if (StartsWith(flag, "zoned_device=")) {
+ std::string zoned;
+ if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) &&
+ android::base::StartsWith(zoned, "host-managed")) {
+ entry->zoned_device = "/dev/block/" + arg;
+ } else {
+ LWARNING << "Warning: cannot find the zoned device: " << arg;
+ }
} else {
LWARNING << "Warning: unknown flag: " << flag;
}
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f26fb24..8f200a8 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -31,6 +31,7 @@
struct FstabEntry {
std::string blk_device;
+ std::string zoned_device;
std::string logical_partition_name;
std::string mount_point;
std::string fs_type;
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index fbc8c30..57c599c 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -47,6 +47,7 @@
"libbase",
"liblog",
],
+ export_include_dirs: ["include"],
ramdisk_available: true,
}
@@ -68,6 +69,7 @@
"user-space-merge/snapuserd_readahead.cpp",
"user-space-merge/snapuserd_transitions.cpp",
"user-space-merge/snapuserd_server.cpp",
+ "user-space-merge/snapuserd_verify.cpp",
],
cflags: [
@@ -88,6 +90,11 @@
"libext4_utils",
"liburing",
],
+
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+
include_dirs: ["bionic/libc/kernel"],
system_shared_libs: [],
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index cebda1c..9a69d58 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -89,6 +89,10 @@
// Return the status of the snapshot
std::string QuerySnapshotStatus(const std::string& misc_name);
+
+ // Check the update verification status - invoked by update_verifier during
+ // boot
+ bool QueryUpdateVerification();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 7b1c7a3..e08cf9b 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -269,5 +269,15 @@
return Receivemsg();
}
+bool SnapuserdClient::QueryUpdateVerification() {
+ std::string msg = "update-verify";
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
+ }
+ std::string response = Receivemsg();
+ return response == "success";
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 692cb74..afc653f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -18,6 +18,7 @@
#include <sys/utsname.h>
+#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
@@ -70,6 +71,9 @@
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
GetSharedPtr());
+
+ update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+
return true;
}
@@ -306,206 +310,6 @@
return ReadMetadata();
}
-void SnapshotHandler::FinalizeIouring() {
- io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
- ring_ = std::make_unique<struct io_uring>();
-
- int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
- if (ret) {
- LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
- return false;
- }
-
- LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
- return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
- const std::string& partition_name, size_t size) {
- // 64k block size with io_depth of 64 is optimal
- // for a single thread. We just need a single thread
- // to read all the blocks from all dynamic partitions.
- size_t io_depth = 64;
- size_t bs = (64 * 1024);
-
- if (!InitializeIouring(io_depth)) {
- return false;
- }
-
- LOG(INFO) << "ReadBlockAsync start "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
-
- auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
- std::vector<std::unique_ptr<struct iovec>> vecs;
- using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
- std::vector<AlignedBuf> alignedBufVector;
-
- /*
- * TODO: We need aligned memory for DIRECT-IO. However, if we do
- * a DIRECT-IO and verify the blocks then we need to inform
- * update-verifier that block verification has been done and
- * there is no need to repeat the same. We are not there yet
- * as we need to see if there are any boot time improvements doing
- * a DIRECT-IO.
- *
- * Also, we could you the same function post merge for block verification;
- * again, we can do a DIRECT-IO instead of thrashing page-cache and
- * hurting other applications.
- *
- * For now, we will just create aligned buffers but rely on buffered
- * I/O until we have perf numbers to justify DIRECT-IO.
- */
- for (int i = 0; i < io_depth; i++) {
- auto iovec = std::make_unique<struct iovec>();
- vecs.push_back(std::move(iovec));
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
- LOG(ERROR) << "posix_memalign failed";
- return false;
- }
-
- iovec_ptr->iov_len = bs;
- alignedBufVector.push_back(
- std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
- << " partition-name: " << partition_name;
- return false;
- }
-
- loff_t offset = 0;
- size_t remain = size;
- size_t read_sz = io_depth * bs;
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
- size_t queue_size = to_read / bs;
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
- if (!sqe) {
- SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
- return false;
- }
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
- sqe->flags |= IOSQE_ASYNC;
- offset += bs;
- }
-
- int ret = io_uring_submit(ring_.get());
- if (ret != queue_size) {
- SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
- return false;
- }
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_cqe* cqe;
-
- int ret = io_uring_wait_cqe(ring_.get(), &cqe);
- if (ret) {
- SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
- return false;
- }
-
- if (cqe->res < 0) {
- SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
- return false;
- }
- io_uring_cqe_seen(ring_.get(), cqe);
- }
-
- remain -= to_read;
- }
-
- LOG(INFO) << "ReadBlockAsync complete: "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
- return true;
-}
-
-void SnapshotHandler::ReadBlocksToCache(const std::string& dm_block_device,
- const std::string& partition_name, off_t offset,
- size_t size) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
- << " partition-name: " << partition_name;
- return;
- }
-
- size_t remain = size;
- off_t file_offset = offset;
- // We pick 4M I/O size based on the fact that the current
- // update_verifier has a similar I/O size.
- size_t read_sz = 1024 * BLOCK_SZ;
- std::vector<uint8_t> buf(read_sz);
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
-
- if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
- SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
- << " at offset: " << file_offset
- << " partition-name: " << partition_name << " total-size: " << size
- << " remain_size: " << remain;
- return;
- }
-
- file_offset += to_read;
- remain -= to_read;
- }
-
- SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
- << " partition: " << partition_name << " size: " << size
- << " offset: " << offset;
-}
-
-void SnapshotHandler::ReadBlocks(const std::string partition_name,
- const std::string& dm_block_device) {
- SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
- << " Block-Device: " << dm_block_device;
-
- uint64_t dev_sz = 0;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- SNAP_LOG(ERROR) << "Cannot open block device";
- return;
- }
-
- dev_sz = get_block_device_size(fd.get());
- if (!dev_sz) {
- SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
- return;
- }
-
- int num_threads = 2;
- size_t num_blocks = dev_sz >> BLOCK_SHIFT;
- size_t num_blocks_per_thread = num_blocks / num_threads;
- size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
- off_t offset = 0;
-
- for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
- partition_name, offset, read_sz_per_thread);
-
- offset += read_sz_per_thread;
- }
-}
-
/*
* Entry point to launch threads
*/
@@ -526,42 +330,22 @@
std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
}
- bool second_stage_init = true;
+ bool partition_verification = true;
- // We don't want to read the blocks during first stage init.
+ // We don't want to read the blocks during first stage init or
+ // during post-install phase.
if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
- second_stage_init = false;
- }
-
- if (second_stage_init) {
- SNAP_LOG(INFO) << "Reading blocks to cache....";
- auto& dm = DeviceMapper::Instance();
- auto dm_block_devices = dm.FindDmPartitions();
- if (dm_block_devices.empty()) {
- SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
- } else {
- auto parts = android::base::Split(misc_name_, "-");
- std::string partition_name = parts[0];
-
- const char* suffix_b = "_b";
- const char* suffix_a = "_a";
-
- partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
- partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
-
- if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
- SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
- } else {
- ReadBlocks(partition_name, dm_block_devices.at(partition_name));
- }
- }
- } else {
- SNAP_LOG(INFO) << "Not reading block device into cache";
+ partition_verification = false;
}
std::future<bool> merge_thread =
std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+ // Now that the worker threads are up, scan the partitions.
+ if (partition_verification) {
+ update_verify_->VerifyUpdatePartition();
+ }
+
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 83d40f6..90fba75 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -42,12 +42,14 @@
#include <liburing.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
namespace android {
namespace snapshot {
using android::base::unique_fd;
using namespace std::chrono_literals;
+using namespace android::storage_literals;
static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
@@ -165,6 +167,36 @@
std::unique_ptr<struct io_uring> ring_;
};
+class UpdateVerify {
+ public:
+ UpdateVerify(const std::string& misc_name);
+ void VerifyUpdatePartition();
+ bool CheckPartitionVerification();
+
+ private:
+ enum class UpdateVerifyState {
+ VERIFY_UNKNOWN,
+ VERIFY_FAILED,
+ VERIFY_SUCCESS,
+ };
+
+ std::string misc_name_;
+ UpdateVerifyState state_;
+ std::mutex m_lock_;
+ std::condition_variable m_cv_;
+
+ int kMinThreadsToVerify = 1;
+ int kMaxThreadsToVerify = 4;
+ uint64_t kThresholdSize = 512_MiB;
+ uint64_t kBlockSizeVerify = 1_MiB;
+
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ void UpdatePartitionVerificationState(UpdateVerifyState state);
+ bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+ bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+ off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
class Worker {
public:
Worker(const std::string& cow_device, const std::string& backing_device,
@@ -344,24 +376,16 @@
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
bool IsIouringSupported();
+ bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
private:
bool ReadMetadata();
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
- bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
struct BufferState* GetBufferState();
void UpdateMergeCompletionPercentage();
- void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
- void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
- off_t offset, size_t size);
-
- bool InitializeIouring(int io_depth);
- void FinalizeIouring();
- bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
- size_t size);
-
// COW device
std::string cow_device_;
// Source device
@@ -413,6 +437,7 @@
bool scratch_space_ = false;
std::unique_ptr<struct io_uring> ring_;
+ std::unique_ptr<UpdateVerify> update_verify_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 9827662..b7f7f54 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -55,6 +55,7 @@
if (input == "initiate_merge") return DaemonOps::INITIATE;
if (input == "merge_percent") return DaemonOps::PERCENTAGE;
if (input == "getstatus") return DaemonOps::GETSTATUS;
+ if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
return DaemonOps::INVALID;
}
@@ -282,6 +283,14 @@
return Sendmsg(fd, merge_status);
}
}
+ case DaemonOps::UPDATE_VERIFY: {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (!UpdateVerification(&lock)) {
+ return Sendmsg(fd, "fail");
+ }
+
+ return Sendmsg(fd, "success");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -687,5 +696,22 @@
return true;
}
+bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
+ CHECK(proof_of_lock);
+
+ bool status = true;
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ auto& th = (*iter)->thread();
+ if (th.joinable() && status) {
+ status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+ } else {
+ // return immediately if there is a failure
+ return false;
+ }
+ }
+
+ return status;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 34e7941..00734a9 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -46,6 +46,7 @@
INITIATE,
PERCENTAGE,
GETSTATUS,
+ UPDATE_VERIFY,
INVALID,
};
@@ -118,6 +119,8 @@
double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
+ bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
+
public:
UserSnapshotServer() { terminating_ = false; }
~UserSnapshotServer();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
new file mode 100644
index 0000000..18c1dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 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 "snapuserd_core.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+UpdateVerify::UpdateVerify(const std::string& misc_name)
+ : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+
+bool UpdateVerify::CheckPartitionVerification() {
+ auto now = std::chrono::system_clock::now();
+ auto deadline = now + 10s;
+ {
+ std::unique_lock<std::mutex> cv_lock(m_lock_);
+ while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {
+ auto status = m_cv_.wait_until(cv_lock, deadline);
+ if (status == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ }
+
+ return (state_ == UpdateVerifyState::VERIFY_SUCCESS);
+}
+
+void UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {
+ {
+ std::lock_guard<std::mutex> lock(m_lock_);
+ state_ = state;
+ }
+ m_cv_.notify_all();
+}
+
+void UpdateVerify::VerifyUpdatePartition() {
+ bool succeeded = false;
+
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+ return;
+ }
+
+ const auto parts = android::base::Split(misc_name_, "-");
+ std::string partition_name = parts[0];
+
+ constexpr auto&& suffix_b = "_b";
+ constexpr auto&& suffix_a = "_a";
+
+ partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+ partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+ if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+ SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+ return;
+ }
+
+ if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {
+ SNAP_LOG(ERROR) << "Partition: " << partition_name
+ << " Block-device: " << dm_block_devices.at(partition_name)
+ << " verification failed";
+ }
+ succeeded = true;
+}
+
+bool UpdateVerify::VerifyBlocks(const std::string& partition_name,
+ const std::string& dm_block_device, off_t offset, int skip_blocks,
+ uint64_t dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ loff_t file_offset = offset;
+ const uint64_t read_sz = kBlockSizeVerify;
+
+ void* addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&addr, page_size, read_sz) < 0) {
+ SNAP_PLOG(ERROR) << "posix_memalign failed "
+ << " page_size: " << page_size << " read_sz: " << read_sz;
+ return false;
+ }
+
+ std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+ uint64_t bytes_read = 0;
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+ << " partition-name: " << partition_name
+ << " at offset: " << file_offset << " read-size: " << to_read
+ << " block-size: " << dev_sz;
+ return false;
+ }
+
+ bytes_read += to_read;
+ file_offset += (skip_blocks * kBlockSizeVerify);
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
+ << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+
+ return true;
+}
+
+bool UpdateVerify::VerifyPartition(const std::string& partition_name,
+ const std::string& dm_block_device) {
+ android::base::Timer timer;
+
+ SNAP_LOG(INFO) << "VerifyPartition: " << partition_name << " Block-device: " << dm_block_device;
+
+ bool succeeded = false;
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ uint64_t dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+ return false;
+ }
+
+ if (!IsBlockAligned(dev_sz)) {
+ SNAP_LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+ return false;
+ }
+
+ /*
+ * Not all partitions are of same size. Some partitions are as small as
+ * 100Mb. We can just finish them in a single thread. For bigger partitions
+ * such as product, 4 threads are sufficient enough.
+ *
+ * TODO: With io_uring SQ_POLL support, we can completely cut this
+ * down to just single thread for all partitions and potentially verify all
+ * the partitions with zero syscalls. Additionally, since block layer
+ * supports polling, IO_POLL could be used which will further cut down
+ * latency.
+ */
+ int num_threads = kMinThreadsToVerify;
+ if (dev_sz > kThresholdSize) {
+ num_threads = kMaxThreadsToVerify;
+ }
+
+ std::vector<std::future<bool>> threads;
+ off_t start_offset = 0;
+ const int skip_blocks = num_threads;
+
+ while (num_threads) {
+ threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
+ partition_name, dm_block_device, start_offset, skip_blocks,
+ dev_sz));
+ start_offset += kBlockSizeVerify;
+ num_threads -= 1;
+ if (start_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+
+ if (ret) {
+ succeeded = true;
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
+ SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
+ << " Size: " << dev_sz
+ << " verification success. Duration : " << timer.duration().count() << " ms";
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index f91265e..b4bc170 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -202,6 +202,12 @@
visibility: [":__subpackages__"],
}
+cc_library_headers {
+ name: "libinit_headers",
+ export_include_dirs: ["."],
+ visibility: [":__subpackages__"],
+}
+
cc_library_static {
name: "libinit",
recovery_available: true,
diff --git a/init/README.md b/init/README.md
index 13c6ebd..fed81db 100644
--- a/init/README.md
+++ b/init/README.md
@@ -606,8 +606,12 @@
group. If not provided, the directory is created with permissions 755 and
owned by the root user and root group. If provided, the mode, owner and group
will be updated if the directory exists already.
+ If the directory does not exist, it will receive the security context from
+ the current SELinux policy or its parent if not specified in the policy. If
+ the directory exists, its security context will not be changed (even if
+ different from the policy).
- > _action_ can be one of:
+ > _action_ can be one of:
* `None`: take no encryption action; directory will be encrypted if parent is.
* `Require`: encrypt directory, abort boot process if encryption fails
* `Attempt`: try to set an encryption policy, but continue if it fails
diff --git a/init/property_service.cpp b/init/property_service.cpp
index a1f6e04..7e92538 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1351,6 +1351,11 @@
InitPropertySet(persistent_property_record.name(),
persistent_property_record.value());
}
+ // Apply debug ramdisk special settings after persistent properties are loaded.
+ if (android::base::GetBoolProperty("ro.force.debuggable", false)) {
+ // Always enable usb adb if device is booted with debug ramdisk.
+ update_sys_usb_config();
+ }
InitPropertySet("ro.persistent_properties.ready", "true");
persistent_properties_loaded = true;
break;
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 5deaf31..5c821b0 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -29,6 +29,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -40,6 +41,7 @@
#include <snapuserd/snapuserd_client.h>
#include "block_dev_initializer.h"
+#include "lmkd_service.h"
#include "service_utils.h"
#include "util.h"
@@ -320,6 +322,14 @@
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+ // Since daemon is not started as a service, we have
+ // to explicitly set the OOM score to default which is unkillable
+ std::string oom_str = std::to_string(DEFAULT_OOM_SCORE_ADJUST);
+ std::string oom_file = android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
+ if (!android::base::WriteStringToFile(oom_str, oom_file)) {
+ PLOG(ERROR) << "couldn't write oom_score_adj to snapuserd daemon with pid: " << pid;
+ }
+
if (!TestSnapuserdIsReady()) {
PLOG(FATAL) << "snapuserd daemon failed to launch";
} else {
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 267e62c..51c810e 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -187,10 +187,6 @@
auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
auto uid_path = ConvertUidToPath(cgroup, uid);
- if (retries == 0) {
- retries = 1;
- }
-
while (retries--) {
ret = rmdir(uid_pid_path.c_str());
if (!ret || errno != EBUSY) break;
@@ -463,12 +459,13 @@
<< " in " << static_cast<int>(ms) << "ms";
}
- int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
+ // 400 retries correspond to 2 secs max timeout
+ int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
std::string memcg_apps_path;
if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
- RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+ RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
return -1;
}
}
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 028b6be..44f7557 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -670,7 +670,7 @@
int64_t len;
int ret;
- s = sparse_file_import(fd, verbose, crc);
+ s = sparse_file_import(fd, false, crc);
if (s) {
return s;
}
@@ -686,6 +686,9 @@
if (!s) {
return nullptr;
}
+ if (verbose) {
+ sparse_file_verbose(s);
+ }
ret = sparse_file_read_normal(s, fd);
if (ret < 0) {
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index 2f0cb10..f0df350 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -22,6 +22,17 @@
],
cflags: ["-Werror"],
+ cppflags: [
+ "-DALLOW_DISABLE_VERITY=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_DISABLE_VERITY",
+ "-DALLOW_DISABLE_VERITY=1",
+ ],
+ },
+ },
symlinks: [
"enable-verity",
"disable-verity",
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index cdfbd90..e77940a 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -279,4 +279,10 @@
return response;
}
+GetRootOfTrustResponse TrustyKeymaster::GetRootOfTrust(const GetRootOfTrustRequest& request) {
+ GetRootOfTrustResponse response(message_version());
+ ForwardCommand(KM_GET_ROOT_OF_TRUST, request, &response);
+ return response;
+}
+
} // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index f80e02f..9f4f39b 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -66,6 +66,7 @@
DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
const ConfigureVendorPatchlevelRequest& request);
+ GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);
uint32_t message_version() const { return message_version_; }
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index fa475ae..bf0cb70 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -59,6 +59,7 @@
KM_GENERATE_RKP_KEY = (31 << KEYMASTER_REQ_SHIFT),
KM_GENERATE_CSR = (32 << KEYMASTER_REQ_SHIFT),
KM_CONFIGURE_VENDOR_PATCHLEVEL = (33 << KEYMASTER_REQ_SHIFT),
+ KM_GET_ROOT_OF_TRUST = (34 << KEYMASTER_REQ_SHIFT),
// Bootloader/provisioning calls.
KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 44780e8..7d58162 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -325,9 +325,20 @@
return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
}
-ScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& /* challenge */,
- vector<uint8_t>* /* rootOfTrust */) {
- return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) {
+ if (!rootOfTrust) {
+ return kmError2ScopedAStatus(KM_ERROR_UNEXPECTED_NULL_POINTER);
+ }
+ keymaster::GetRootOfTrustRequest request(impl_->message_version(),
+ {challenge.begin(), challenge.end()});
+ keymaster::GetRootOfTrustResponse response = impl_->GetRootOfTrust(request);
+ if (response.error != KM_ERROR_OK) {
+ return kmError2ScopedAStatus(response.error);
+ }
+
+ *rootOfTrust = std::move(response.rootOfTrust);
+ return ScopedAStatus::ok();
}
ScopedAStatus TrustyKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index bc1dcf6..4fc162b 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -19,6 +19,7 @@
rust_library {
name: "libtrusty-rs",
crate_name: "trusty",
+ vendor_available: true,
srcs: [
"src/lib.rs"
],
diff --git a/trusty/metrics/metrics_test.cpp b/trusty/metrics/metrics_test.cpp
index 407ddf2..9897950 100644
--- a/trusty/metrics/metrics_test.cpp
+++ b/trusty/metrics/metrics_test.cpp
@@ -31,7 +31,7 @@
using android::base::unique_fd;
static void TriggerCrash() {
- size_t num_retries = 3;
+ size_t num_retries = 6;
int fd = -1;
for (size_t i = 0; i < num_retries; i++) {
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
index 8b48ae3..300e05a 100644
--- a/trusty/utils/acvp/acvp_ipc.h
+++ b/trusty/utils/acvp/acvp_ipc.h
@@ -27,7 +27,7 @@
/*
* Maximum number of arguments
*/
-#define ACVP_MAX_NUM_ARGUMENTS 8
+#define ACVP_MAX_NUM_ARGUMENTS 9
/*
* Maximum length of an algorithm name