Merge "img2simg: Add support for converting holes to "don't care" chunks"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
[Builtin Hooks]
clang_format = true
+rustfmt = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+rustfmt = --config-path=rustfmt.toml
[Hook Scripts]
aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index edcea44..ad0231d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -24,6 +24,10 @@
export_include_dirs: ["common/include"],
recovery_available: true,
vendor_ramdisk_available: true,
+ apex_available: [
+ "com.android.virt",
+ "//apex_available:platform",
+ ],
}
cc_library_shared {
@@ -44,6 +48,10 @@
"libbase",
"libcutils",
],
+ apex_available: [
+ "com.android.virt",
+ "//apex_available:platform",
+ ],
export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["tombstoned/include"],
@@ -282,7 +290,6 @@
"libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp",
"libdebuggerd/test/open_files_list_test.cpp",
- "libdebuggerd/test/tombstone_test.cpp",
"libdebuggerd/test/utility_test.cpp",
],
@@ -391,6 +398,9 @@
apex_available: [
"com.android.runtime",
],
+
+ // Required for tests.
+ required: ["crash_dump.policy"],
}
cc_binary {
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index d5327db..394447b 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -2,6 +2,14 @@
"presubmit": [
{
"name": "debuggerd_test"
+ },
+ {
+ "name": "libtombstoned_client_rust_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "debuggerd_test"
}
]
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 5bb1d75..967b942 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -502,15 +502,24 @@
continue;
}
- struct iovec iov = {
+ struct iovec tagged_addr_iov = {
&info.tagged_addr_ctrl,
sizeof(info.tagged_addr_ctrl),
};
if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TAGGED_ADDR_CTRL,
- reinterpret_cast<void*>(&iov)) == -1) {
+ reinterpret_cast<void*>(&tagged_addr_iov)) == -1) {
info.tagged_addr_ctrl = -1;
}
+ struct iovec pac_enabled_keys_iov = {
+ &info.pac_enabled_keys,
+ sizeof(info.pac_enabled_keys),
+ };
+ if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_PAC_ENABLED_KEYS,
+ reinterpret_cast<void*>(&pac_enabled_keys_iov)) == -1) {
+ info.pac_enabled_keys = -1;
+ }
+
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, &process_info);
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 23b106e..799163e 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -45,6 +45,8 @@
shared_libs: [
"libbase",
"liblog",
+ ],
+ static_libs: [
"libseccomp_policy",
],
multilib: {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index db30b8f0..55490b5 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -39,6 +39,8 @@
#include "debuggerd/handler.h"
#endif
+extern "C" void android_set_abort_message(const char* msg);
+
#if defined(__arm__)
// See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
#define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
@@ -182,6 +184,8 @@
fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
+ fprintf(stderr, " abort_with_msg call abort() setting an abort message\n");
+ fprintf(stderr, " abort_with_null_msg call abort() setting a null abort message\n");
fprintf(stderr, " assert call assert() without a function\n");
fprintf(stderr, " assert2 call assert() with a function\n");
fprintf(stderr, " exit call exit(1)\n");
@@ -259,6 +263,12 @@
return crash(42);
} else if (!strcasecmp(arg, "abort")) {
maybe_abort();
+ } else if (!strcasecmp(arg, "abort_with_msg")) {
+ android_set_abort_message("Aborting due to crasher");
+ maybe_abort();
+ } else if (!strcasecmp(arg, "abort_with_null")) {
+ android_set_abort_message(nullptr);
+ maybe_abort();
} else if (!strcasecmp(arg, "assert")) {
__assert("some_file.c", 123, "false");
} else if (!strcasecmp(arg, "assert2")) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index b107767..f4ba347 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -88,7 +88,7 @@
struct sigaction old_sigaction; \
struct sigaction new_sigaction = {}; \
new_sigaction.sa_handler = [](int) {}; \
- if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
+ if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
err(1, "sigaction failed"); \
} \
alarm(seconds); \
@@ -113,8 +113,14 @@
// Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
// process sampling, so we need to ensure we force GWP-ASan on.
__attribute__((constructor)) static void enable_gwp_asan() {
- bool force = true;
- android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force));
+ android_mallopt_gwp_asan_options_t opts;
+ // No, we're not an app, but let's turn ourselves on without sampling.
+ // Technically, if someone's using the *.default_app sysprops, they'll adjust
+ // our settings, but I don't think this will be common on a device that's
+ // running debuggerd_tests.
+ opts.desire = android_mallopt_gwp_asan_options_t::Action::TURN_ON_FOR_APP;
+ opts.program_name = "";
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &opts, sizeof(android_mallopt_gwp_asan_options_t));
}
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
@@ -173,6 +179,14 @@
*status = response.status;
}
+static bool pac_supported() {
+#if defined(__aarch64__)
+ return getauxval(AT_HWCAP) & HWCAP_PACA;
+#else
+ return false;
+#endif
+}
+
class CrasherTest : public ::testing::Test {
public:
pid_t crasher_pid = -1;
@@ -357,12 +371,20 @@
ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)"
R"( \(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\))");
}
+
+ if (pac_supported()) {
+ // Test that the default PAC_ENABLED_KEYS value is set.
+ ASSERT_MATCH(result, R"(pac_enabled_keys: 000000000000000f)"
+ R"( \(PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY\))");
+ }
}
TEST_F(CrasherTest, tagged_fault_addr) {
#if !defined(__aarch64__)
GTEST_SKIP() << "Requires aarch64";
#endif
+ // HWASan crashes with SIGABRT on tag mismatch.
+ SKIP_WITH_HWASAN;
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
@@ -394,6 +416,10 @@
#if defined(__i386__)
GTEST_SKIP() << "architecture does not pass arguments in registers";
#endif
+ // The memory dump in HWASan crashes sadly shows the memory near the registers
+ // in the HWASan dump function, rather the faulting context. This is a known
+ // issue.
+ SKIP_WITH_HWASAN;
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
@@ -472,6 +498,8 @@
// instead of GWP-ASan.
GTEST_SKIP() << "Skipped on MTE.";
}
+ // Skip this test on HWASan, which will reliably catch test errors as well.
+ SKIP_WITH_HWASAN;
GwpAsanTestParameters params = GetParam();
LogcatCollector logcat_collector;
@@ -1395,6 +1423,16 @@
return true;
}
+extern "C" void foo() {
+ LOG(INFO) << "foo";
+ std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+ LOG(INFO) << "bar";
+ std::this_thread::sleep_for(1s);
+}
+
TEST_F(CrasherTest, seccomp_tombstone) {
int intercept_result;
unique_fd output_fd;
@@ -1402,6 +1440,11 @@
static const auto dump_type = kDebuggerdTombstone;
StartProcess(
[]() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
raise_debugger_signal(dump_type);
_exit(0);
},
@@ -1416,16 +1459,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
-}
-
-extern "C" void foo() {
- LOG(INFO) << "foo";
- std::this_thread::sleep_for(1s);
-}
-
-extern "C" void bar() {
- LOG(INFO) << "bar";
- std::this_thread::sleep_for(1s);
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_backtrace) {
@@ -1804,9 +1839,9 @@
TEST_F(CrasherTest, unreadable_elf) {
int intercept_result;
unique_fd output_fd;
- StartProcess([]() {
+ std::string tmp_so_name;
+ StartProcess([&tmp_so_name]() {
TemporaryDir td;
- std::string tmp_so_name;
if (!CopySharedLibrary(td.path, &tmp_so_name)) {
_exit(1);
}
@@ -1836,6 +1871,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(NOTE: Function names and BuildId information is missing )");
+ std::string match_str = "NOTE: " + tmp_so_name;
+ ASSERT_MATCH(result, match_str);
}
TEST(tombstoned, proto) {
@@ -1998,6 +2035,9 @@
// Verify that a fault address after the last map is properly handled.
TEST_F(CrasherTest, fault_address_after_last_map) {
+ // This makes assumptions about the memory layout that are not true in HWASan
+ // processes.
+ SKIP_WITH_HWASAN;
uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);
StartProcess([crash_uptr]() {
ASSERT_EQ(0, crash_call(crash_uptr));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index baf994f..c8b25ae 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -73,14 +73,13 @@
thread.registers.reset(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
- // TODO: Create this once and store it in a global?
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
// Do not use the thread cache here because it will call pthread_key_create
// which doesn't work in linker code. See b/189803009.
// Use a normal cached object because the process is stopped, and there
// is no chance of data changing between reads.
auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwinder.SetProcessMemory(process_memory);
+ // TODO: Create this once and store it in a global?
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid(), process_memory);
dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
@@ -98,32 +97,6 @@
__linker_disable_fallback_allocator();
}
-static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
- pid_t current_tid = gettid();
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
- DIR* dir = opendir(buf);
-
- if (!dir) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
- return;
- }
-
- struct dirent* ent;
- while ((ent = readdir(dir))) {
- char* end;
- long tid = strtol(ent->d_name, &end, 10);
- if (end == ent->d_name || *end != '\0') {
- continue;
- }
-
- if (tid != current_tid) {
- callback(tid, output_fd);
- }
- }
- closedir(dir);
-}
-
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
// Make sure the thread actually got the signal.
struct pollfd pfd = {
@@ -216,21 +189,21 @@
}
// Only allow one thread to perform a trace at a time.
- static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_trylock(&trace_mutex);
- if (ret != 0) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
- strerror(ret));
+ static std::mutex trace_mutex;
+ if (!trace_mutex.try_lock()) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
return;
}
+ std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
+
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
kDebuggerdNativeBacktrace)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"missing crash_dump_fallback() in selinux policy?");
- goto exit;
+ return;
}
dump_backtrace_header(output_fd.get());
@@ -239,15 +212,15 @@
debuggerd_fallback_trace(output_fd.get(), ucontext);
// Send a signal to all of our siblings, asking them to dump their stack.
- iterate_siblings(
- [](pid_t tid, int output_fd) {
+ pid_t current_tid = gettid();
+ if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
strerror(errno));
- return false;
+ return;
}
uint64_t expected = pack_thread_fd(-1, -1);
@@ -257,7 +230,7 @@
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
close(sent_fd);
- return false;
+ return;
}
siginfo_t siginfo = {};
@@ -269,10 +242,10 @@
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno));
- return false;
+ return;
}
- bool success = forward_output(pipe_read.get(), output_fd, tid);
+ bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
if (!success) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
@@ -288,15 +261,14 @@
}
}
- return true;
- },
- output_fd.get());
+ return;
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
+ current_tid, strerror(errno));
+ }
dump_backtrace_footer(output_fd.get());
tombstoned_notify_completion(tombstone_socket.get());
-
-exit:
- pthread_mutex_unlock(&trace_mutex);
}
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 3ee309f..3d96627 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -43,10 +43,13 @@
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) {
+ // 1 million GWP-ASan slots would take 4.1GiB of space. Thankfully, copying
+ // the metadata for that amount of slots is only 532MiB, and this really will
+ // only be used with some ridiculous torture-tests.
+ if (state.MaxSimultaneousAllocations > 1000000) {
ALOGE(
"Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
- "exceeds maximum allowed (1024).",
+ "exceeds maximum allowed (1,000,000).",
state.MaxSimultaneousAllocations);
return nullptr;
}
@@ -147,7 +150,7 @@
for (size_t i = 0; i != num_frames; ++i) {
unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
BacktraceFrame* f = heap_object->add_allocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+ fill_in_backtrace_frame(f, frame_data);
}
heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_));
@@ -156,117 +159,8 @@
for (size_t i = 0; i != num_frames; ++i) {
unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
BacktraceFrame* f = heap_object->add_deallocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+ fill_in_backtrace_frame(f, frame_data);
}
set_human_readable_cause(cause, crash_address_);
}
-
-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_);
-
- 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);
-}
-
-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 = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = 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 = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = 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
index f9c2481..a979370 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -52,26 +52,6 @@
// 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;
-
void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
protected:
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index c3b95d6..172ffe9 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,16 +34,12 @@
bool CrashIsMine() const;
- void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
private:
scudo_error_info error_info_ = {};
uintptr_t untagged_fault_addr_;
- void DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const;
-
void FillInCause(Cause* cause, const scudo_error_report* report,
unwindstack::Unwinder* unwinder) const;
};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 2331f1e..7bf1688 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,7 +37,6 @@
namespace unwindstack {
struct FrameData;
-class Maps;
class Unwinder;
}
@@ -68,8 +67,7 @@
const Tombstone& tombstone,
std::function<void(const std::string& line, bool should_log)> callback);
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
- unwindstack::Maps* maps);
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 086dc97..a51e276 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -25,6 +25,7 @@
struct ThreadInfo {
std::unique_ptr<unwindstack::Regs> registers;
long tagged_addr_ctrl = -1;
+ long pac_enabled_keys = -1;
pid_t uid;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 002321f..63e142f 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -1,22 +1,20 @@
-/* system/debuggerd/utility.h
-**
-** Copyright 2008, 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.
-*/
+/*
+ * Copyright 2008, 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.
+ */
-#ifndef _DEBUGGERD_UTILITY_H
-#define _DEBUGGERD_UTILITY_H
+#pragma once
#include <inttypes.h>
#include <signal.h>
@@ -93,6 +91,7 @@
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
std::string describe_tagged_addr_ctrl(long ctrl);
+std::string describe_pac_enabled_keys(long keys);
// Number of bytes per MTE granule.
constexpr size_t kTagGranuleSize = 16;
@@ -100,5 +99,3 @@
// Number of rows and columns to display in an MTE tag dump.
constexpr size_t kNumTagColumns = 16;
constexpr size_t kNumTagRows = 16;
-
-#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a89f385..a4836d7 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -108,7 +108,7 @@
for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
BacktraceFrame* f = heap_object->add_allocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+ fill_in_backtrace_frame(f, frame_data);
}
heap_object->set_deallocation_tid(report->deallocation_tid);
@@ -117,7 +117,7 @@
unwindstack::FrameData frame_data =
unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
BacktraceFrame* f = heap_object->add_deallocation_backtrace();
- fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
+ fill_in_backtrace_frame(f, frame_data);
}
set_human_readable_cause(cause, untagged_fault_addr_);
@@ -130,87 +130,3 @@
FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);
}
}
-
-void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
- if (error_info_.reports[1].error_type != UNKNOWN) {
- _LOG(log, logtype::HEADER,
- "\nNote: multiple potential causes for this crash were detected, listing them in "
- "decreasing order of likelihood.\n");
- }
-
- size_t report_num = 0;
- while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
- error_info_.reports[report_num].error_type != UNKNOWN) {
- DumpReport(&error_info_.reports[report_num++], log, unwinder);
- }
-}
-
-void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const {
- const char *error_type_str;
- switch (report->error_type) {
- case USE_AFTER_FREE:
- error_type_str = "Use After Free";
- break;
- case BUFFER_OVERFLOW:
- error_type_str = "Buffer Overflow";
- break;
- case BUFFER_UNDERFLOW:
- error_type_str = "Buffer Underflow";
- break;
- default:
- error_type_str = "Unknown";
- break;
- }
-
- uintptr_t diff;
- const char* location_str;
-
- if (untagged_fault_addr_ < report->allocation_address) {
- // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
- location_str = "left of";
- diff = report->allocation_address - untagged_fault_addr_;
- } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
- // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
- location_str = "into";
- diff = untagged_fault_addr_ - report->allocation_address;
- } else {
- // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
- location_str = "right of";
- diff = untagged_fault_addr_ - report->allocation_address - report->allocation_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,
- "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
- error_type_str, diff, byte_suffix, location_str, report->allocation_size,
- report->allocation_address);
-
- if (report->allocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-
- if (report->deallocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
deleted file mode 100644
index 1cbfb56..0000000
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2015 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 <sys/mman.h>
-#include <time.h>
-
-#include <memory>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "libdebuggerd/utility.h"
-
-#include "UnwinderMock.h"
-#include "host_signal_fixup.h"
-#include "log_fake.h"
-
-#include "gwp_asan.cpp"
-
-using ::testing::MatchesRegex;
-
-class TombstoneTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- unwinder_mock_.reset(new UnwinderMock());
-
- char tmp_file[256];
- const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, data_template, sizeof(data_template));
- int tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, tmp_template, sizeof(tmp_template));
- tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- abort();
- }
- }
- if (unlink(tmp_file) == -1) {
- abort();
- }
-
- log_.tfd = tombstone_fd;
- amfd_data_.clear();
- log_.amfd_data = &amfd_data_;
- log_.crashed_tid = 12;
- log_.current_tid = 12;
- log_.should_retrieve_logcat = false;
-
- resetLogs();
- }
-
- virtual void TearDown() {
- if (log_.tfd >= 0) {
- close(log_.tfd);
- }
- }
-
- std::unique_ptr<UnwinderMock> unwinder_mock_;
-
- log_t log_;
- std::string amfd_data_;
-};
-
-class GwpAsanCrashDataTest : public GwpAsanCrashData {
-public:
- GwpAsanCrashDataTest(
- gwp_asan::Error error,
- const gwp_asan::AllocationMetadata *responsible_allocation) :
- GwpAsanCrashData(nullptr, ProcessInfo{}, 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.RequestedSize = 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, 0 bytes "
- "into 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.RequestedSize = 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, 0 bytes into 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.RequestedSize = 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.RequestedSize = 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.RequestedSize = 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.RequestedSize = 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/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp
index 97328b7..dad3380 100644
--- a/debuggerd/libdebuggerd/test/utility_test.cpp
+++ b/debuggerd/libdebuggerd/test/utility_test.cpp
@@ -31,3 +31,12 @@
describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)));
}
+
+TEST(UtilityTest, describe_pac_enabled_keys) {
+ EXPECT_EQ("", describe_pac_enabled_keys(0));
+ EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY));
+ EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)",
+ describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY));
+ EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)",
+ describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000));
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index f21a203..eda7182 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -73,22 +74,40 @@
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,
+ .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,
+#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));
+ }
+ }
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
auto process_memory =
unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwinder.SetProcessMemory(process_memory);
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch(), nullptr,
+ process_memory);
if (!unwinder.Init()) {
- async_safe_fatal("failed to init unwinder object");
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ return;
}
ProcessInfo process_info;
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index b1c4ef3..bee4a67 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -35,6 +35,7 @@
#include <memory>
#include <optional>
+#include <set>
#include <string>
#include <async_safe/log.h>
@@ -48,6 +49,7 @@
#include <android/log.h>
#include <bionic/macros.h>
+#include <bionic/reserved_signals.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -312,8 +314,7 @@
}
}
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
- unwindstack::Maps* maps) {
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame) {
f->set_rel_pc(frame.rel_pc);
f->set_pc(frame.pc);
f->set_sp(frame.sp);
@@ -331,20 +332,117 @@
f->set_function_offset(frame.function_offset);
- if (frame.map_start == frame.map_end) {
+ if (frame.map_info == nullptr) {
// No valid map associated with this frame.
f->set_file_name("<unknown>");
- } else if (!frame.map_name.empty()) {
- f->set_file_name(frame.map_name);
- } else {
- f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start));
+ return;
}
- f->set_file_map_offset(frame.map_elf_start_offset);
+ if (!frame.map_info->name().empty()) {
+ f->set_file_name(frame.map_info->GetFullName());
+ } else {
+ f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_info->start()));
+ }
+ f->set_file_map_offset(frame.map_info->elf_start_offset());
- auto map_info = maps->Find(frame.map_start);
- if (map_info.get() != nullptr) {
- f->set_build_id(map_info->GetPrintableBuildID());
+ f->set_build_id(frame.map_info->GetPrintableBuildID());
+}
+
+static void dump_registers(unwindstack::Unwinder* unwinder,
+ const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
+ bool memory_dump) {
+ if (regs == nullptr) {
+ return;
+ }
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+ regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
+ Register r;
+ r.set_name(name);
+ r.set_u64(value);
+ *thread.add_registers() = r;
+
+ if (memory_dump) {
+ MemoryDump dump;
+
+ dump.set_register_name(name);
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
+ if (map_info) {
+ dump.set_mapping_name(map_info->name());
+ }
+
+ constexpr size_t kNumBytesAroundRegister = 256;
+ constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
+ char buf[kNumBytesAroundRegister];
+ uint8_t tags[kNumTagsAroundRegister];
+ ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+ if (bytes == -1) {
+ return;
+ }
+ dump.set_begin_address(value);
+ dump.set_memory(buf, bytes);
+
+ bool has_tags = false;
+#if defined(__aarch64__)
+ for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
+ if (tags[i] != 0) {
+ has_tags = true;
+ }
+ }
+#endif // defined(__aarch64__)
+
+ if (has_tags) {
+ dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
+ }
+
+ *thread.add_memory_dump() = std::move(dump);
+ }
+ });
+}
+
+static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
+ if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
+ return;
+ }
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
+ unwinder->LastErrorCodeString());
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
+ unwinder->LastErrorAddress());
+}
+
+static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
+ if (unwinder->NumFrames() == 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
+ log_unwinder_error(unwinder);
+ return;
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ std::set<std::string> unreadable_elf_files;
+ for (const auto& frame : unwinder->frames()) {
+ BacktraceFrame* f = thread.add_current_backtrace();
+ fill_in_backtrace_frame(f, frame);
+ if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+ unreadable_elf_files.emplace(frame.map_info->name());
+ }
+ }
+
+ if (!unreadable_elf_files.empty()) {
+ auto unreadable_elf_files_proto = thread.mutable_unreadable_elf_files();
+ auto backtrace_note = thread.mutable_backtrace_note();
+ *backtrace_note->Add() =
+ "Function names and BuildId information is missing for some frames due";
+ *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
+ *backtrace_note->Add() = "found under the lib/ directory are readable.";
+ *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+ *backtrace_note->Add() = "Unreadable libraries:";
+ for (auto& name : unreadable_elf_files) {
+ *backtrace_note->Add() = " " + name;
+ *unreadable_elf_files_proto->Add() = name;
+ }
}
}
@@ -355,98 +453,34 @@
thread.set_id(thread_info.tid);
thread.set_name(thread_info.thread_name);
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
+ thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- thread_info.registers->IterateRegisters(
- [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
- Register r;
- r.set_name(name);
- r.set_u64(value);
- *thread.add_registers() = r;
-
- if (memory_dump) {
- MemoryDump dump;
-
- dump.set_register_name(name);
- std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
- if (map_info) {
- dump.set_mapping_name(map_info->name());
- }
-
- constexpr size_t kNumBytesAroundRegister = 256;
- constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
- char buf[kNumBytesAroundRegister];
- uint8_t tags[kNumTagsAroundRegister];
- size_t start_offset = 0;
- ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
- if (bytes == -1) {
- return;
- }
- dump.set_begin_address(value);
-
- if (start_offset + bytes > sizeof(buf)) {
- async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
- start_offset, bytes);
- }
-
- dump.set_memory(buf, bytes);
-
- bool has_tags = false;
-#if defined(__aarch64__)
- for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
- if (tags[i] != 0) {
- has_tags = true;
- }
- }
-#endif // defined(__aarch64__)
-
- if (has_tags) {
- dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
- }
-
- *thread.add_memory_dump() = std::move(dump);
- }
- });
-
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
+ if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
+ // Fallback path for non-main thread, doing unwind from running process.
+ unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
+ if (!thread_unwinder.Init()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unable to initialize ThreadUnwinder object.");
+ log_unwinder_error(&thread_unwinder);
+ return;
}
+
+ std::unique_ptr<unwindstack::Regs> initial_regs;
+ thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
+ dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
+ dump_thread_backtrace(&thread_unwinder, thread);
} else {
- if (unwinder->elf_from_memory_not_file()) {
- auto backtrace_note = thread.mutable_backtrace_note();
- *backtrace_note->Add() =
- "Function names and BuildId information is missing for some frames due";
- *backtrace_note->Add() =
- "to unreadable libraries. For unwinds of apps, only shared libraries";
- *backtrace_note->Add() = "found under the lib/ directory are readable.";
- *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame, maps);
- }
+ dump_registers(unwinder, thread_info.registers, thread, memory_dump);
+ std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+ unwinder->SetRegs(regs_copy.get());
+ unwinder->Unwind();
+ dump_thread_backtrace(unwinder, thread);
}
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info) {
- dump_thread(tombstone, unwinder, thread_info, true);
-}
-
static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
unwindstack::Maps* maps = unwinder->GetMaps();
std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
@@ -664,7 +698,8 @@
dump_abort_message(&result, unwinder, process_info);
- dump_main_thread(&result, unwinder, main_thread);
+ // Dump the main thread, but save the memory around the registers.
+ dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
for (const auto& [tid, thread_info] : threads) {
if (tid != target_thread) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index de86b0a..0265641 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -85,6 +85,10 @@
CB(should_log, "tagged_addr_ctrl: %016" PRIx64 "%s", thread.tagged_addr_ctrl(),
describe_tagged_addr_ctrl(thread.tagged_addr_ctrl()).c_str());
}
+ if (thread.pac_enabled_keys() != -1) {
+ CB(should_log, "pac_enabled_keys: %016" PRIx64 "%s", thread.pac_enabled_keys(),
+ describe_pac_enabled_keys(thread.pac_enabled_keys()).c_str());
+ }
}
static void print_register_row(CallbackType callback, int word_size,
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 71f0c09..ecd98a4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -28,6 +28,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <set>
#include <string>
#include <android-base/properties.h>
@@ -446,35 +447,53 @@
return "?";
}
-std::string describe_tagged_addr_ctrl(long ctrl) {
+#define DESCRIBE_FLAG(flag) \
+ if (value & flag) { \
+ desc += ", "; \
+ desc += #flag; \
+ value &= ~flag; \
+ }
+
+static std::string describe_end(long value, std::string& desc) {
+ if (value) {
+ desc += StringPrintf(", unknown 0x%lx", value);
+ }
+ return desc.empty() ? "" : " (" + desc.substr(2) + ")";
+}
+
+std::string describe_tagged_addr_ctrl(long value) {
std::string desc;
- if (ctrl & PR_TAGGED_ADDR_ENABLE) {
- desc += ", PR_TAGGED_ADDR_ENABLE";
- ctrl &= ~PR_TAGGED_ADDR_ENABLE;
+ DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
+ DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
+ DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
+ if (value & PR_MTE_TAG_MASK) {
+ desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
+ value &= ~PR_MTE_TAG_MASK;
}
- if (ctrl & PR_MTE_TCF_SYNC) {
- desc += ", PR_MTE_TCF_SYNC";
- ctrl &= ~PR_MTE_TCF_SYNC;
- }
- if (ctrl & PR_MTE_TCF_ASYNC) {
- desc += ", PR_MTE_TCF_ASYNC";
- ctrl &= ~PR_MTE_TCF_ASYNC;
- }
- if (ctrl & PR_MTE_TAG_MASK) {
- desc += StringPrintf(", mask 0x%04lx", (ctrl & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
- ctrl &= ~PR_MTE_TAG_MASK;
- }
- if (ctrl) {
- desc += StringPrintf(", unknown 0x%lx", ctrl);
- }
- if (desc.empty()) {
- return "";
- }
- return " (" + desc.substr(2) + ")";
+ return describe_end(value, desc);
+}
+
+std::string describe_pac_enabled_keys(long value) {
+ std::string desc;
+ DESCRIBE_FLAG(PR_PAC_APIAKEY);
+ DESCRIBE_FLAG(PR_PAC_APIBKEY);
+ DESCRIBE_FLAG(PR_PAC_APDAKEY);
+ DESCRIBE_FLAG(PR_PAC_APDBKEY);
+ DESCRIBE_FLAG(PR_PAC_APGAKEY);
+ return describe_end(value, desc);
}
void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
- if (unwinder->elf_from_memory_not_file()) {
+ std::set<std::string> unreadable_elf_files;
+ unwinder->SetDisplayBuildID(true);
+ for (const auto& frame : unwinder->frames()) {
+ if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+ unreadable_elf_files.emplace(frame.map_info->name());
+ }
+ }
+
+ // Put the preamble ahead of the backtrace.
+ if (!unreadable_elf_files.empty()) {
_LOG(log, logtype::BACKTRACE,
"%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
_LOG(log, logtype::BACKTRACE,
@@ -484,10 +503,13 @@
_LOG(log, logtype::BACKTRACE,
"%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
#endif
+ _LOG(log, logtype::BACKTRACE, "%sNOTE: Unreadable libraries:\n", prefix);
+ for (auto& name : unreadable_elf_files) {
+ _LOG(log, logtype::BACKTRACE, "%sNOTE: %s\n", prefix, name.c_str());
+ }
}
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < unwinder->NumFrames(); i++) {
- _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
+ for (const auto& frame : unwinder->frames()) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
}
}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index a701212..a0f2f82 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -123,11 +123,13 @@
string name = 2;
repeated Register registers = 3;
repeated string backtrace_note = 7;
+ repeated string unreadable_elf_files = 9;
repeated BacktraceFrame current_backtrace = 4;
repeated MemoryDump memory_dump = 5;
int64 tagged_addr_ctrl = 6;
+ int64 pac_enabled_keys = 8;
- reserved 8 to 999;
+ reserved 10 to 999;
}
message BacktraceFrame {
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
new file mode 100644
index 0000000..2007f39
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/Android.bp
@@ -0,0 +1,59 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "libtombstoned_client_wrapper",
+ srcs: [
+ "wrapper.cpp",
+ ],
+ generated_sources: [
+ "libtombstoned_client_rust_bridge_code"
+ ],
+ header_libs: [
+ "libbase_headers",
+ "libdebuggerd_common_headers",
+ ],
+ shared_libs: [
+ "libtombstoned_client",
+ ],
+ apex_available: ["com.android.virt"],
+}
+
+rust_defaults {
+ name: "libtombstoned_client_rust_defaults",
+ crate_name: "tombstoned_client",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libcxx",
+ "libthiserror",
+ ],
+ static_libs: [
+ "libtombstoned_client_wrapper",
+ ],
+ shared_libs: [
+ "libtombstoned_client",
+ ],
+}
+
+rust_library {
+ name: "libtombstoned_client_rust",
+ defaults: ["libtombstoned_client_rust_defaults"],
+ apex_available: ["com.android.virt"],
+}
+
+rust_test {
+ name: "libtombstoned_client_rust_test",
+ defaults: ["libtombstoned_client_rust_defaults"],
+ require_root: true,
+ test_suites: ["device-tests"],
+}
+
+genrule {
+ name: "libtombstoned_client_rust_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["libtombstoned_client_cxx_generated.cc"],
+}
diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs
new file mode 100644
index 0000000..5c8abef
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/src/lib.rs
@@ -0,0 +1,153 @@
+// Copyright 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.
+
+//! Rust wrapper for tombstoned client.
+
+pub use ffi::DebuggerdDumpType;
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use thiserror::Error;
+
+/// Error communicating with tombstoned.
+#[derive(Clone, Debug, Error, Eq, PartialEq)]
+#[error("Error communicating with tombstoned")]
+pub struct Error;
+
+/// File descriptors for communicating with tombstoned.
+pub struct TombstonedConnection {
+ /// The socket connection to tombstoned.
+ ///
+ /// This is actually a Unix SOCK_SEQPACKET socket not a file, but the Rust standard library
+ /// doesn't have an appropriate type and it's not really worth bringing in a dependency on `uds`
+ /// or something when all we do is pass it back to C++ or close it.
+ tombstoned_socket: File,
+ /// The file descriptor for text output.
+ pub text_output: Option<File>,
+ /// The file descriptor for proto output.
+ pub proto_output: Option<File>,
+}
+
+impl TombstonedConnection {
+ unsafe fn from_raw_fds(
+ tombstoned_socket: RawFd,
+ text_output_fd: RawFd,
+ proto_output_fd: RawFd,
+ ) -> Self {
+ Self {
+ tombstoned_socket: File::from_raw_fd(tombstoned_socket),
+ text_output: if text_output_fd >= 0 {
+ Some(File::from_raw_fd(text_output_fd))
+ } else {
+ None
+ },
+ proto_output: if proto_output_fd >= 0 {
+ Some(File::from_raw_fd(proto_output_fd))
+ } else {
+ None
+ },
+ }
+ }
+
+ /// Connects to tombstoned.
+ pub fn connect(pid: i32, dump_type: DebuggerdDumpType) -> Result<Self, Error> {
+ let mut tombstoned_socket = -1;
+ let mut text_output_fd = -1;
+ let mut proto_output_fd = -1;
+ if ffi::tombstoned_connect_files(
+ pid,
+ &mut tombstoned_socket,
+ &mut text_output_fd,
+ &mut proto_output_fd,
+ dump_type,
+ ) {
+ Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
+ } else {
+ Err(Error)
+ }
+ }
+
+ /// Notifies tombstoned that the dump is complete.
+ pub fn notify_completion(&self) -> Result<(), Error> {
+ if ffi::tombstoned_notify_completion(self.tombstoned_socket.as_raw_fd()) {
+ Ok(())
+ } else {
+ Err(Error)
+ }
+ }
+}
+
+#[cxx::bridge]
+mod ffi {
+ /// The type of dump.
+ enum DebuggerdDumpType {
+ /// A native backtrace.
+ #[cxx_name = "kDebuggerdNativeBacktrace"]
+ NativeBacktrace,
+ /// A tombstone.
+ #[cxx_name = "kDebuggerdTombstone"]
+ Tombstone,
+ /// A Java backtrace.
+ #[cxx_name = "kDebuggerdJavaBacktrace"]
+ JavaBacktrace,
+ /// Any intercept.
+ #[cxx_name = "kDebuggerdAnyIntercept"]
+ AnyIntercept,
+ /// A tombstone proto.
+ #[cxx_name = "kDebuggerdTombstoneProto"]
+ TombstoneProto,
+ }
+
+ unsafe extern "C++" {
+ include!("wrapper.hpp");
+
+ type DebuggerdDumpType;
+
+ fn tombstoned_connect_files(
+ pid: i32,
+ tombstoned_socket: &mut i32,
+ text_output_fd: &mut i32,
+ proto_output_fd: &mut i32,
+ dump_type: DebuggerdDumpType,
+ ) -> bool;
+
+ fn tombstoned_notify_completion(tombstoned_socket: i32) -> bool;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::{io::Write, process};
+
+ // Verify that we can connect to tombstoned, write something to the file descriptor it returns,
+ // and notify completion, without any errors.
+ #[test]
+ fn test() {
+ let connection =
+ TombstonedConnection::connect(process::id() as i32, DebuggerdDumpType::Tombstone)
+ .expect("Failed to connect to tombstoned.");
+
+ assert!(connection.proto_output.is_none());
+ connection
+ .text_output
+ .as_ref()
+ .expect("No text output FD returned.")
+ .write_all(b"test data")
+ .expect("Failed to write to text output FD.");
+
+ connection
+ .notify_completion()
+ .expect("Failed to notify completion.");
+ }
+}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.cpp b/debuggerd/rust/tombstoned_client/wrapper.cpp
new file mode 100644
index 0000000..7492329
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/wrapper.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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 "wrapper.hpp"
+
+#include <android-base/unique_fd.h>
+
+#include "tombstoned/tombstoned.h"
+
+using android::base::unique_fd;
+
+bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
+ int& proto_output_fd, DebuggerdDumpType dump_type) {
+ unique_fd tombstoned_socket_unique, text_output_unique, proto_output_unique;
+
+ bool result = tombstoned_connect(pid, &tombstoned_socket_unique, &text_output_unique,
+ &proto_output_unique, dump_type);
+ if (result) {
+ tombstoned_socket = tombstoned_socket_unique.release();
+ text_output_fd = text_output_unique.release();
+ proto_output_fd = proto_output_unique.release();
+ }
+
+ return result;
+}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.hpp b/debuggerd/rust/tombstoned_client/wrapper.hpp
new file mode 100644
index 0000000..95d3865
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/wrapper.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include "tombstoned/tombstoned.h"
+
+bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
+ int& proto_output_fd, DebuggerdDumpType dump_type);
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 858a338..8241f0e 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,7 +25,7 @@
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 152697c..0cb8e08 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -34,10 +34,18 @@
rt_sigaction: 1
rt_tgsigqueueinfo: 1
+// this is referenced from mainline modules running on Q devices, where not all
+// of the constants used here are defined in headers, so minijail rejects them.
+// we define them here to avoid those errors.
+ // constants introduced in R
#define PR_SET_VMA 0x53564d41
+#define PR_GET_TAGGED_ADDR_CTRL 56
+ // constants introduced in S
+#define PR_PAC_GET_ENABLED_KEYS 61
+
#if defined(__aarch64__)
// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
#else
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
#endif
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index ce0fd30..5c6abc9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,6 +18,7 @@
#include <time.h>
+#include <functional>
#include <string>
#include <utility>
@@ -74,3 +75,24 @@
n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
return buf;
}
+
+bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
+ if (dir == nullptr) {
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ pid_t tid = atoi(entry->d_name);
+ if (tid == 0) {
+ continue;
+ }
+ if (pid != tid) {
+ callback(tid);
+ }
+ }
+ return true;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index ec2862a..4375870 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <string>
#include <vector>
@@ -27,3 +28,4 @@
std::string get_thread_name(pid_t tid);
std::string get_timestamp();
+bool iterate_tids(pid_t, std::function<void(pid_t)>);
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 708a677..5c07eb0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -15,7 +15,10 @@
// This is required because no Android.bp can include a library defined in an
// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
package {
- default_applicable_licenses: ["system_core_fastboot_license"],
+ default_applicable_licenses: [
+ "system_core_fastboot_license",
+ "Android-Apache-2.0",
+ ],
}
// Added automatically by a large-scale-change that took the approach of
@@ -36,10 +39,9 @@
name: "system_core_fastboot_license",
visibility: [":__subpackages__"],
license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
"SPDX-license-identifier-BSD",
],
- // large-scale-change unable to identify any license_text files
+ license_text: ["LICENSE"],
}
cc_library_host_static {
@@ -128,12 +130,10 @@
"-Werror",
"-Wvla",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-Wthread-safety",
],
rtti: true,
- clang_cflags: [
- "-Wthread-safety",
- ],
}
cc_binary {
@@ -213,7 +213,7 @@
"-Werror",
"-Wunreachable-code",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-D_FILE_OFFSET_BITS=64"
+ "-D_FILE_OFFSET_BITS=64",
],
target: {
@@ -409,7 +409,7 @@
":fastboot_test_vendor_ramdisk_replace",
":fastboot_test_vendor_boot_v3",
":fastboot_test_vendor_boot_v4_without_frag",
- ":fastboot_test_vendor_boot_v4_with_frag"
+ ":fastboot_test_vendor_boot_v4_with_frag",
],
}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 322fe5c..10bed6d 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -23,5 +23,5 @@
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
-$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
+$(call dist-for-goals,dist_files sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/LICENSE b/fastboot/LICENSE
new file mode 100644
index 0000000..f0a0e52
--- /dev/null
+++ b/fastboot/LICENSE
@@ -0,0 +1,23 @@
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 58b2a81..17b3466 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,3 @@
dvander@google.com
-hridya@google.com
+elsk@google.com
enh@google.com
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4042531..b9f6c97 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -268,10 +268,18 @@
}
// arg[0] is the command name, arg[1] contains size of data to be downloaded
+ // which should always be 8 bytes
+ if (args[1].length() != 8) {
+ return device->WriteStatus(FastbootResult::FAIL,
+ "Invalid size (length of size != 8)");
+ }
unsigned int size;
if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
}
+ if (size == 0) {
+ return device->WriteStatus(FastbootResult::FAIL, "Invalid size (0)");
+ }
device->download_data().resize(size);
if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
return false;
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index e6a834e..ae225de 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -186,6 +186,11 @@
PLOG(ERROR) << "Couldn't read command";
return;
}
+ if (std::count_if(command, command + bytes_read, iscntrl) != 0) {
+ WriteStatus(FastbootResult::FAIL,
+ "Command contains control character");
+ continue;
+ }
command[bytes_read] = '\0';
LOG(INFO) << "Fastboot command: " << command;
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 3f9bcdc1..06ffe0f 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -76,7 +76,7 @@
} // namespace
-int FlashRawDataChunk(int fd, const char* data, size_t len) {
+int FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {
size_t ret = 0;
const size_t max_write_size = 1048576;
void* aligned_buffer;
@@ -91,7 +91,15 @@
while (ret < len) {
int this_len = std::min(max_write_size, len - ret);
memcpy(aligned_buffer_unique_ptr.get(), data, this_len);
- int this_ret = write(fd, aligned_buffer_unique_ptr.get(), this_len);
+ // In case of non 4KB aligned writes, reopen without O_DIRECT flag
+ if (this_len & 0xFFF) {
+ if (handle->Reset(O_WRONLY) != true) {
+ PLOG(ERROR) << "Failed to reset file descriptor";
+ return -1;
+ }
+ }
+
+ int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);
if (this_ret < 0) {
PLOG(ERROR) << "Failed to flash data of len " << len;
return -1;
@@ -102,8 +110,8 @@
return 0;
}
-int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
- int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+int FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {
+ int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());
if (ret < 0) {
return -errno;
}
@@ -111,28 +119,30 @@
}
int WriteCallback(void* priv, const void* data, size_t len) {
- int fd = reinterpret_cast<long long>(priv);
+ PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
if (!data) {
- return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+ return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
}
- return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+ return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
}
-int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
- struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
+int FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {
+ struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),
+ downloaded_data.size(), true, false);
if (!file) {
- return -ENOENT;
+ // Invalid sparse format
+ return -EINVAL;
}
- return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+ return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
}
-int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
- lseek64(fd, 0, SEEK_SET);
+int FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {
+ lseek64(handle->fd(), 0, SEEK_SET);
if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
*reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
- return FlashSparseData(fd, downloaded_data);
+ return FlashSparseData(handle, downloaded_data);
} else {
- return FlashRawData(fd, downloaded_data);
+ return FlashRawData(handle, downloaded_data);
}
}
@@ -172,17 +182,23 @@
return -EOVERFLOW;
} else if (data.size() < block_device_size &&
(partition_name == "boot" || partition_name == "boot_a" ||
- partition_name == "boot_b")) {
+ partition_name == "boot_b" || partition_name == "init_boot" ||
+ partition_name == "init_boot_a" || partition_name == "init_boot_b")) {
CopyAVBFooter(&data, block_device_size);
}
if (android::base::GetProperty("ro.system.build.type", "") != "user") {
WipeOverlayfsForPartition(device, partition_name);
}
- int result = FlashBlockDevice(handle.fd(), data);
+ int result = FlashBlockDevice(&handle, data);
sync();
return result;
}
+static void RemoveScratchPartition() {
+ AutoMountMetadata mount_metadata;
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
+}
+
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
@@ -215,7 +231,7 @@
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -259,7 +275,7 @@
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 97b5ad4..3302c43 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -90,14 +90,7 @@
return false;
}
- flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
- unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open block device: " << handle->path();
- return false;
- }
- handle->set_fd(std::move(fd));
- return true;
+ return handle->Open(flags);
}
std::optional<std::string> FindPhysicalPartition(const std::string& name) {
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 1d81b7a..6e1453f 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -18,6 +18,8 @@
#include <optional>
#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <fstab/fstab.h>
@@ -44,11 +46,51 @@
}
const std::string& path() const { return path_; }
int fd() const { return fd_.get(); }
- void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+ bool Open(int flags) {
+ flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
+ // Attempts to open a second device can fail with EBUSY if the device is already open.
+ // Explicitly close any previously opened devices as unique_fd won't close them until
+ // after the attempt to open.
+ fd_.reset();
+
+ fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open block device: " << path_;
+ return false;
+ }
+ flags_ = flags;
+
+ return true;
+ }
+ bool Reset(int flags) {
+ if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {
+ return true;
+ }
+
+ off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;
+ if (offset < 0) {
+ PLOG(ERROR) << "Failed lseek on block device: " << path_;
+ return false;
+ }
+
+ sync();
+
+ if (Open(flags) == false) {
+ return false;
+ }
+
+ if (lseek(fd_.get(), offset, SEEK_SET) != offset) {
+ PLOG(ERROR) << "Failed lseek on block device: " << path_;
+ return false;
+ }
+
+ return true;
+ }
private:
std::string path_;
android::base::unique_fd fd_;
+ int flags_;
std::function<void()> closer_;
};
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 76e9889..0cf4699 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -332,8 +332,8 @@
auto fastboot_hal = device->fastboot_hal();
if (!fastboot_hal) {
- *message = "Fastboot HAL not found";
- return false;
+ *message = "raw";
+ return true;
}
FileSystemType type;
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index f5a3384..911071a 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $i -eq $COMP_CWORD ]]; then
- partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm"
+ partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot"
COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
else
_fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 532b524..79c3ac7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -141,6 +141,10 @@
static Image images[] = {
// clang-format off
{ "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
+ { "init_boot",
+ "init_boot.img", "init_boot.sig",
+ "init_boot",
+ true, ImageType::BootCritical },
{ nullptr, "boot_other.img", "boot.sig", "boot", true, ImageType::Normal },
{ "cache", "cache.img", "cache.sig", "cache", true, ImageType::Extra },
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
@@ -152,6 +156,10 @@
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
{ "system", "system.img", "system.sig", "system", false, ImageType::Normal },
+ { "system_dlkm",
+ "system_dlkm.img", "system_dlkm.sig",
+ "system_dlkm",
+ true, ImageType::Normal },
{ "system_ext",
"system_ext.img", "system_ext.sig",
"system_ext",
@@ -178,6 +186,11 @@
"vendor_dlkm.img", "vendor_dlkm.sig",
"vendor_dlkm",
true, ImageType::Normal },
+ { "vendor_kernel_boot",
+ "vendor_kernel_boot.img",
+ "vendor_kernel_boot.sig",
+ "vendor_kernel_boot",
+ true, ImageType::BootCritical },
{ nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
@@ -1017,7 +1030,7 @@
return partition_size;
}
-static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
if (buf->sz < AVB_FOOTER_SIZE) {
return;
}
@@ -1032,9 +1045,9 @@
// In this case, partition_size will be zero.
if (partition_size < buf->sz) {
fprintf(stderr,
- "Warning: skip copying boot image avb footer"
- " (boot partition size: %" PRId64 ", boot image size: %" PRId64 ").\n",
- partition_size, buf->sz);
+ "Warning: skip copying %s image avb footer"
+ " (%s partition size: %" PRId64 ", %s image size: %" PRId64 ").\n",
+ partition.c_str(), partition.c_str(), partition_size, partition.c_str(), buf->sz);
return;
}
@@ -1042,7 +1055,7 @@
// Because buf->fd will still be used afterwards.
std::string data;
if (!android::base::ReadFdToString(buf->fd, &data)) {
- die("Failed reading from boot");
+ die("Failed reading from %s", partition.c_str());
}
uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
@@ -1051,13 +1064,14 @@
return;
}
- unique_fd fd(make_temporary_fd("boot rewriting"));
+ const std::string tmp_fd_template = partition + " rewriting";
+ unique_fd fd(make_temporary_fd(tmp_fd_template.c_str()));
if (!android::base::WriteStringToFd(data, fd)) {
- die("Failed writing to modified boot");
+ die("Failed writing to modified %s", partition.c_str());
}
lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
- die("Failed copying AVB footer in boot");
+ die("Failed copying AVB footer in %s", partition.c_str());
}
buf->fd = std::move(fd);
buf->sz = partition_size;
@@ -1068,13 +1082,18 @@
{
sparse_file** s;
- if (partition == "boot" || partition == "boot_a" || partition == "boot_b") {
- copy_boot_avb_footer(partition, buf);
+ if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
+ partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") {
+ copy_avb_footer(partition, buf);
}
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
- if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
+ // The vbmeta partition might have additional prefix if running in virtual machine
+ // e.g., guest_vbmeta_a.
+ if (android::base::EndsWith(partition, "vbmeta") ||
+ android::base::EndsWith(partition, "vbmeta_a") ||
+ android::base::EndsWith(partition, "vbmeta_b")) {
rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
} else if (!has_vbmeta_partition() &&
(partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index fcd3bd6..1b59e4a 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -15,6 +15,11 @@
*
*/
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "fastboot_fuzzer",
host_supported: true,
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 159c314..2031170 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -9,6 +9,7 @@
cc_test_host {
name: "fuzzy_fastboot",
+ isolated: false,
compile_multilib: "first",
srcs: [
diff --git a/fastboot/fuzzy_fastboot/README.md b/fastboot/fuzzy_fastboot/README.md
index 72967c5..a5b64c7 100644
--- a/fastboot/fuzzy_fastboot/README.md
+++ b/fastboot/fuzzy_fastboot/README.md
@@ -293,7 +293,7 @@
Begin with just the generic tests (i.e. no XML file). In particular, make sure all
the conformance tests are passing before you move on. All other tests require that
the basic generic conformance tests all pass for them to be valid. The conformance
-tests can be run with `./fuzzy_fastboot --gtests_filter=Conformance.*`.
+tests can be run with `./fuzzy_fastboot --gtest_filter=Conformance.*`.
#### Understanding and Fixing Failed Tests
Whenever a test fails, it will print out to the console the reason for failure
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index b6beaf9..074306b 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -874,6 +874,12 @@
<< "Device did not respond with FAIL for malformed download command '" << cmd << "'";
}
+TEST_F(Fuzz, DownloadInvalid9) {
+ std::string cmd("download:2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPP");
+ EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+ << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
TEST_F(Fuzz, GetVarAllSpam) {
auto start = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed;
@@ -890,28 +896,51 @@
TEST_F(Fuzz, BadCommandTooLarge) {
std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
- EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+ RetCode ret = fb->RawCommand(s);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
<< "Device did not respond with failure after sending length " << s.size()
<< " string of random ASCII chars";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
std::string s1 = RandomString(1000, rand_legal);
- EXPECT_EQ(fb->RawCommand(s1), DEVICE_FAIL)
+ ret = fb->RawCommand(s1);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
<< "Device did not respond with failure after sending length " << s1.size()
<< " string of random ASCII chars";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
std::string s2 = RandomString(1000, rand_illegal);
- EXPECT_EQ(fb->RawCommand(s2), DEVICE_FAIL)
- << "Device did not respond with failure after sending length " << s1.size()
+ ret = fb->RawCommand(s2);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+ << "Device did not respond with failure after sending length " << s2.size()
<< " string of random non-ASCII chars";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
std::string s3 = RandomString(1000, rand_char);
- EXPECT_EQ(fb->RawCommand(s3), DEVICE_FAIL)
- << "Device did not respond with failure after sending length " << s1.size()
+ ret = fb->RawCommand(s3);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+ << "Device did not respond with failure after sending length " << s3.size()
<< " string of random chars";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+ std::string s4 = RandomString(10 * 1024 * 1024, rand_legal);
+ ret = fb->RawCommand(s);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+ << "Device did not respond with failure after sending length " << s4.size()
+ << " string of random ASCII chars ";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+ ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+ std::string resp;
+ EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+ << "Device is unresponsive to getvar command";
}
TEST_F(Fuzz, CommandTooLarge) {
for (const std::string& s : CMDS) {
std::string rs = RandomString(1000, rand_char);
- EXPECT_EQ(fb->RawCommand(s + rs), DEVICE_FAIL)
- << "Device did not respond with failure after '" << s + rs << "'";
+ RetCode ret;
+ ret = fb->RawCommand(s + rs);
+ EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+ << "Device did not respond with failure " << ret << "after '" << s + rs << "'";
+ if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
std::string resp;
EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
@@ -954,6 +983,80 @@
}
}
+TEST_F(Fuzz, SparseZeroBlkSize) {
+ // handcrafted malform sparse file with zero as block size
+ const std::vector<char> buf = {
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
+ };
+
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+ // It can either reject this download or reject it during flash
+ if (HandleResponse() != DEVICE_FAIL) {
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing a zero block size in sparse file should fail";
+ }
+}
+
+TEST_F(Fuzz, SparseVeryLargeBlkSize) {
+ // handcrafted sparse file with block size of ~4GB and divisible 4
+ const std::vector<char> buf = {
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
+ '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
+ '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
+ '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
+ '\x11', '\x22', '\x33', '\x44'
+ };
+
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+ ASSERT_EQ(HandleResponse(), SUCCESS) << "Not receive okay";
+ ASSERT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed";
+}
+
+TEST_F(Fuzz, SparseTrimmed) {
+ // handcrafted malform sparse file which is trimmed
+ const std::vector<char> buf = {
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
+ '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
+ };
+
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+ // It can either reject this download or reject it during flash
+ if (HandleResponse() != DEVICE_FAIL) {
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing a trimmed sparse file should fail";
+ }
+}
+
+TEST_F(Fuzz, SparseInvalidChurk) {
+ // handcrafted malform sparse file with invalid churk
+ const std::vector<char> buf = {
+ '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
+ '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
+ '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
+ };
+
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+ // It can either reject this download or reject it during flash
+ if (HandleResponse() != DEVICE_FAIL) {
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing a sparse file with invalid churk should fail";
+ }
+}
+
TEST_F(Fuzz, SparseTooManyChunks) {
SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks
ASSERT_TRUE(*sparse) << "Sparse image creation failed";
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 5872dda..49761ac 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -69,7 +69,6 @@
"file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
- "fs_mgr_verity.cpp",
"fs_mgr_dm_linear.cpp",
"fs_mgr_overlayfs.cpp",
"fs_mgr_roots.cpp",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 137b066..7639ce5 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -124,8 +124,8 @@
FS_STAT_RO_MOUNT_FAILED = 0x0040,
FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
- FS_STAT_E2FSCK_FAILED = 0x0200,
- FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+ FS_STAT_FSCK_FAILED = 0x0200,
+ FS_STAT_FSCK_FS_FIXED = 0x0400,
FS_STAT_INVALID_MAGIC = 0x0800,
FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
@@ -136,7 +136,6 @@
};
static void log_fs_stat(const std::string& blk_device, int fs_stat) {
- if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
std::string msg =
android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
@@ -166,10 +165,26 @@
return fs_stat &
(FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
- FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+ FS_STAT_FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
}
+static bool umount_retry(const std::string& mount_point) {
+ int retry_count = 5;
+ bool umounted = false;
+
+ while (retry_count-- > 0) {
+ umounted = umount(mount_point.c_str()) == 0;
+ if (umounted) {
+ LINFO << __FUNCTION__ << "(): unmount(" << mount_point << ") succeeded";
+ break;
+ }
+ PERROR << __FUNCTION__ << "(): umount(" << mount_point << ") failed";
+ if (retry_count) sleep(1);
+ }
+ return umounted;
+}
+
static void check_fs(const std::string& blk_device, const std::string& fs_type,
const std::string& target, int* fs_stat) {
int status;
@@ -209,25 +224,12 @@
tmpmnt_opts.c_str());
PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
<< ")=" << ret;
- if (!ret) {
- bool umounted = false;
- int retry_count = 5;
- while (retry_count-- > 0) {
- umounted = umount(target.c_str()) == 0;
- if (umounted) {
- LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
- break;
- }
- PERROR << __FUNCTION__ << "(): umount(" << target << ") failed";
- if (retry_count) sleep(1);
- }
- if (!umounted) {
- // boot may fail but continue and leave it to later stage for now.
- PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
- *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
- }
- } else {
+ if (ret) {
*fs_stat |= FS_STAT_RO_MOUNT_FAILED;
+ } else if (!umount_retry(target)) {
+ // boot may fail but continue and leave it to later stage for now.
+ PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
+ *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
}
}
@@ -252,10 +254,10 @@
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
LERROR << "Failed trying to run " << E2FSCK_BIN;
- *fs_stat |= FS_STAT_E2FSCK_FAILED;
+ *fs_stat |= FS_STAT_FSCK_FAILED;
} else if (status != 0) {
LINFO << "e2fsck returned status 0x" << std::hex << status;
- *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
+ *fs_stat |= FS_STAT_FSCK_FS_FIXED;
}
}
} else if (is_f2fs(fs_type)) {
@@ -278,6 +280,10 @@
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+ *fs_stat |= FS_STAT_FSCK_FAILED;
+ } else if (status != 0) {
+ LINFO << F2FS_FSCK_BIN << " returned status 0x" << std::hex << status;
+ *fs_stat |= FS_STAT_FSCK_FS_FIXED;
}
}
android::base::SetProperty("ro.boottime.init.fsck." + Basename(target),
@@ -361,8 +367,8 @@
const struct ext4_super_block* sb, int* fs_stat) {
bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
bool want_quota = entry.fs_mgr_flags.quota;
- bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
-
+ // Enable projid support by default
+ bool want_projid = true;
if (has_quota == want_quota) {
return;
}
@@ -1009,12 +1015,11 @@
// Check to see if a mountable volume has encryption requirements
static int handle_encryptable(const FstabEntry& entry) {
if (should_use_metadata_encryption(entry)) {
- if (umount(entry.mount_point.c_str()) == 0) {
+ if (umount_retry(entry.mount_point)) {
return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
- } else {
- PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
- return FS_MGR_MNTALL_FAIL;
}
+ PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
+ return FS_MGR_MNTALL_FAIL;
} else if (entry.fs_mgr_flags.file_encryption) {
LINFO << entry.mount_point << " is file encrypted";
return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
@@ -1443,14 +1448,6 @@
// Skips mounting the device.
continue;
}
- } else if ((current_entry.fs_mgr_flags.verify)) {
- int rc = fs_mgr_setup_verity(¤t_entry, true);
- if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
- LINFO << "Verity disabled";
- } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
- LERROR << "Could not set up verified partition, skipping!";
- continue;
- }
}
int last_idx_inspected;
@@ -1615,13 +1612,6 @@
ret |= FsMgrUmountStatus::ERROR_VERITY;
continue;
}
- } else if ((current_entry.fs_mgr_flags.verify)) {
- if (!fs_mgr_teardown_verity(¤t_entry)) {
- LERROR << "Failed to tear down verified partition on mount point: "
- << current_entry.mount_point;
- ret |= FsMgrUmountStatus::ERROR_VERITY;
- continue;
- }
}
}
return ret;
@@ -1822,9 +1812,13 @@
auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
// Run fsck if needed
- prepare_fs_for_mount(entry.blk_device, entry, mount_point);
+ int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
+ // Wiped case doesn't require to try __mount below.
+ if (ret & FS_STAT_INVALID_MAGIC) {
+ return FS_MGR_DOMNT_FAILED;
+ }
- int ret = __mount(entry.blk_device, mount_point, entry);
+ ret = __mount(entry.blk_device, mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
@@ -1914,20 +1908,13 @@
// Skips mounting the device.
continue;
}
- } else if (fstab_entry.fs_mgr_flags.verify) {
- int rc = fs_mgr_setup_verity(&fstab_entry, true);
- if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
- LINFO << "Verity disabled";
- } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
- LERROR << "Could not set up verified partition, skipping!";
- continue;
- }
}
int retry_count = 2;
while (retry_count-- > 0) {
if (!__mount(n_blk_device, mount_point, fstab_entry)) {
fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ log_fs_stat(fstab_entry.blk_device, fs_stat);
return FS_MGR_DOMNT_SUCCESS;
} else {
if (retry_count <= 0) break; // run check_fs only once
@@ -2140,7 +2127,7 @@
}
bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
- if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ if (!entry.fs_mgr_flags.avb) {
return false;
}
@@ -2151,17 +2138,12 @@
return false;
}
- const char* status;
std::vector<DeviceMapper::TargetInfo> table;
if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
- if (!entry.fs_mgr_flags.verify_at_boot) {
- return false;
- }
- status = "V";
- } else {
- status = table[0].data.c_str();
+ return false;
}
+ auto status = table[0].data.c_str();
if (*status == 'C' || *status == 'V') {
return true;
}
@@ -2170,7 +2152,7 @@
}
std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {
- if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ if (!entry.fs_mgr_flags.avb) {
return {};
}
DeviceMapper& dm = DeviceMapper::Instance();
@@ -2206,7 +2188,7 @@
}
bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
- if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ if (!entry.fs_mgr_flags.avb) {
return false;
}
@@ -2230,8 +2212,10 @@
// Devices upgrading to dynamic partitions are allowed to specify a super
// partition name. This includes cuttlefish, which is a non-A/B device.
std::string super_partition;
- if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
- fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ if (fs_mgr_get_boot_config("force_super_partition", &super_partition)) {
+ return super_partition;
+ }
+ if (fs_mgr_get_boot_config("super_partition", &super_partition)) {
if (fs_mgr_get_slot_suffix().empty()) {
return super_partition;
}
@@ -2278,7 +2262,8 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0
// Allowlist the mount point if user build.
static const std::vector<const std::string> kAllowedPaths = {
- "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+ "/odm", "/odm_dlkm", "/oem", "/product",
+ "/system_dlkm", "/system_ext", "/vendor", "/vendor_dlkm",
};
static const std::vector<const std::string> kAllowedPrefixes = {
"/mnt/product/",
@@ -2342,3 +2327,22 @@
LINFO << report << ret;
return true;
}
+
+bool fs_mgr_load_verity_state(int* mode) {
+ // unless otherwise specified, use EIO mode.
+ *mode = VERITY_MODE_EIO;
+
+ // The bootloader communicates verity mode via the kernel commandline
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ return false;
+ }
+
+ if (verity_mode == "enforcing") {
+ *mode = VERITY_MODE_DEFAULT;
+ } else if (verity_mode == "logging") {
+ *mode = VERITY_MODE_LOGGING;
+ }
+
+ return true;
+}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index bb49873..6f59ed3 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -156,11 +156,10 @@
LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
bool needs_casefold = false;
- bool needs_projid = false;
+ bool needs_projid = true;
if (entry.mount_point == "/data") {
needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
}
if (entry.fs_type == "f2fs") {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 07b533b..8c719c8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -167,12 +167,10 @@
CheckFlag("recoveryonly", recovery_only);
CheckFlag("noemulatedsd", no_emulated_sd);
CheckFlag("notrim", no_trim);
- CheckFlag("verify", verify);
CheckFlag("formattable", formattable);
CheckFlag("slotselect", slot_select);
CheckFlag("latemount", late_mount);
CheckFlag("nofail", no_fail);
- CheckFlag("verifyatboot", verify_at_boot);
CheckFlag("quota", quota);
CheckFlag("avb", avb);
CheckFlag("logical", logical);
@@ -289,11 +287,16 @@
entry->fs_mgr_flags.avb = true;
entry->vbmeta_partition = arg;
} else if (StartsWith(flag, "keydirectory=")) {
- // The metadata flag is followed by an = and the directory for the keys.
+ // The keydirectory flag enables metadata encryption. It is
+ // followed by an = and the directory containing the metadata
+ // encryption key.
entry->metadata_key_dir = arg;
} else if (StartsWith(flag, "metadata_encryption=")) {
- // Specify the cipher and flags to use for metadata encryption
- entry->metadata_encryption = arg;
+ // The metadata_encryption flag specifies the cipher and flags to
+ // use for metadata encryption, if the defaults aren't sufficient.
+ // It doesn't actually enable metadata encryption; that is done by
+ // "keydirectory".
+ entry->metadata_encryption_options = arg;
} else if (StartsWith(flag, "sysfs_path=")) {
// The path to trigger device gc by idle-maint of vold.
entry->sysfs_path = arg;
@@ -650,6 +653,7 @@
entry->blk_device = partition;
// AVB keys for DSU should always be under kDsuKeysDir.
entry->avb_keys = kDsuKeysDir;
+ entry->fs_mgr_flags.logical = true;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
@@ -659,7 +663,12 @@
if (partition_ext4 == fstab->end()) {
auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
new_entry.fs_type = "ext4";
- fstab->emplace_back(new_entry);
+ auto it = std::find_if(fstab->rbegin(), fstab->rend(),
+ [&mount_point](const auto& entry) {
+ return entry.mount_point == mount_point;
+ });
+ auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
+ fstab->insert(end_of_mount_point_group, new_entry);
}
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2b31119..82b5275 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -125,8 +126,12 @@
bool fs_mgr_in_recovery() {
// Check the existence of recovery binary instead of using the compile time
- // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
- // defined, albeit not in recovery. More details: system/core/init/README.md
+ // __ANDROID_RECOVERY__ macro.
+ // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+ // mode would use the same init binary, which would mean during normal boot
+ // the '/init' binary is actually a symlink pointing to
+ // init_second_stage.recovery, which would be compiled with
+ // __ANDROID_RECOVERY__ defined.
return fs_mgr_access("/system/bin/recovery");
}
@@ -880,9 +885,14 @@
errno = save_errno;
}
entry.flags &= ~MS_RDONLY;
+ entry.flags |= MS_SYNCHRONOUS;
+ entry.fs_options = "nodiscard";
fs_mgr_set_blk_ro(device_path, false);
}
- entry.fs_mgr_flags.check = true;
+ // check_fs requires apex runtime library
+ if (fs_mgr_overlayfs_already_mounted("/data", false)) {
+ entry.fs_mgr_flags.check = true;
+ }
auto save_errno = errno;
if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
@@ -1116,6 +1126,23 @@
return true;
}
+static inline uint64_t GetIdealDataScratchSize() {
+ BlockDeviceInfo super_info;
+ PartitionOpener opener;
+ if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+ LERROR << "could not get block device info for super";
+ return 0;
+ }
+
+ struct statvfs s;
+ if (statvfs("/data", &s) < 0) {
+ PERROR << "could not statfs /data";
+ return 0;
+ }
+
+ return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+}
+
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
if (change) *change = false;
@@ -1131,13 +1158,6 @@
return true;
}
- BlockDeviceInfo info;
- PartitionOpener opener;
- if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
- LERROR << "could not get block device info for super";
- return false;
- }
-
if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
@@ -1147,10 +1167,11 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 64_MiB;
- static constexpr uint64_t kMaximumSize = 2_GiB;
+ uint64_t size = GetIdealDataScratchSize();
+ if (!size) {
+ size = 2_GiB;
+ }
- uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
if (!images->CreateBackingImage(partition_name, size, flags)) {
@@ -1391,18 +1412,35 @@
return ret;
}
+struct MapInfo {
+ // If set, partition is owned by ImageManager.
+ std::unique_ptr<IImageManager> images;
+ // If set, and images is null, this is a DAP partition.
+ std::string name;
+ // If set, and images and name are empty, this is a non-dynamic partition.
+ std::string device;
+
+ MapInfo() = default;
+ MapInfo(MapInfo&&) = default;
+ ~MapInfo() {
+ if (images) {
+ images->UnmapImageDevice(name);
+ } else if (!name.empty()) {
+ DestroyLogicalPartition(name);
+ }
+ }
+};
+
// Note: This function never returns the DSU scratch device in recovery or fastbootd,
// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static bool EnsureScratchMapped(std::string* device, bool* mapped) {
- *mapped = false;
- *device = GetBootScratchDevice();
- if (!device->empty()) {
- return true;
+static std::optional<MapInfo> EnsureScratchMapped() {
+ MapInfo info;
+ info.device = GetBootScratchDevice();
+ if (!info.device.empty()) {
+ return {std::move(info)};
}
-
if (!fs_mgr_in_recovery()) {
- errno = EINVAL;
- return false;
+ return {};
}
auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1412,11 +1450,15 @@
// would otherwise always be mapped.
auto images = IImageManager::Open("remount", 10s);
if (images && images->BackingImageExists(partition_name)) {
- if (!images->MapImageDevice(partition_name, 10s, device)) {
- return false;
+ if (images->IsImageDisabled(partition_name)) {
+ return {};
}
- *mapped = true;
- return true;
+ if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ info.images = std::move(images);
+ return {std::move(info)};
}
// Avoid uart spam by first checking for a scratch partition.
@@ -1424,12 +1466,12 @@
auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
auto metadata = ReadCurrentMetadata(super_device);
if (!metadata) {
- return false;
+ return {};
}
auto partition = FindPartition(*metadata.get(), partition_name);
if (!partition) {
- return false;
+ return {};
}
CreateLogicalPartitionParams params = {
@@ -1439,11 +1481,11 @@
.force_writable = true,
.timeout_ms = 10s,
};
- if (!CreateLogicalPartition(params, device)) {
- return false;
+ if (!CreateLogicalPartition(params, &info.device)) {
+ return {};
}
- *mapped = true;
- return true;
+ info.name = partition_name;
+ return {std::move(info)};
}
// This should only be reachable in recovery, where DSU scratch is not
@@ -1597,26 +1639,35 @@
fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
}
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- bool mapped = false;
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &mapped)) {
+ if (mount_point.empty()) {
+ // Throw away the entire partition.
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->DisableImage(partition_name)) {
+ LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+ } else {
+ LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+ }
+ }
+ }
+
+ if (auto info = EnsureScratchMapped(); info.has_value()) {
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
bool should_destroy_scratch = false;
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
&should_destroy_scratch);
+ fs_mgr_overlayfs_umount_scratch();
if (should_destroy_scratch) {
fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
}
- fs_mgr_overlayfs_umount_scratch();
- }
- if (mapped) {
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
}
}
// Teardown DSU overlay if present.
+ std::string scratch_device;
if (MapDsuScratchDevice(&scratch_device)) {
fs_mgr_overlayfs_umount_scratch();
if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
deleted file mode 100644
index efa2180..0000000
--- a/fs_mgr/fs_mgr_verity.cpp
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright (C) 2013 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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <crypto_utils/android_pubkey.h>
-#include <cutils/properties.h>
-#include <fs_mgr/file_wait.h>
-#include <libdm/dm.h>
-#include <logwrap/logwrap.h>
-#include <openssl/obj_mac.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-#include "fec/io.h"
-
-#include "fs_mgr.h"
-#include "fs_mgr_dm_linear.h"
-#include "fs_mgr_priv.h"
-
-// Realistically, this file should be part of the android::fs_mgr namespace;
-using namespace android::fs_mgr;
-
-#define VERITY_TABLE_RSA_KEY "/verity_key"
-#define VERITY_TABLE_HASH_IDX 8
-#define VERITY_TABLE_SALT_IDX 9
-
-#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
-#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
-#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
-
-#define VERITY_TABLE_OPT_FEC_FORMAT \
- "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
- " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
-#define VERITY_TABLE_OPT_FEC_ARGS 9
-
-#define METADATA_MAGIC 0x01564c54
-#define METADATA_TAG_MAX_LENGTH 63
-#define METADATA_EOD "eod"
-
-#define VERITY_LASTSIG_TAG "verity_lastsig"
-
-#define VERITY_STATE_TAG "verity_state"
-#define VERITY_STATE_HEADER 0x83c0ae9d
-#define VERITY_STATE_VERSION 1
-
-#define VERITY_KMSG_RESTART "dm-verity device corrupted"
-#define VERITY_KMSG_BUFSIZE 1024
-
-#define READ_BUF_SIZE 4096
-
-#define __STRINGIFY(x) #x
-#define STRINGIFY(x) __STRINGIFY(x)
-
-struct verity_state {
- uint32_t header;
- uint32_t version;
- int32_t mode;
-};
-
-extern struct fs_info info;
-
-static RSA *load_key(const char *path)
-{
- uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
-
- auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
- if (!f) {
- LERROR << "Can't open " << path;
- return nullptr;
- }
-
- if (!fread(key_data, sizeof(key_data), 1, f.get())) {
- LERROR << "Could not read key!";
- return nullptr;
- }
-
- RSA* key = nullptr;
- if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
- LERROR << "Could not parse key!";
- return nullptr;
- }
-
- return key;
-}
-
-static int verify_table(const uint8_t *signature, size_t signature_size,
- const char *table, uint32_t table_length)
-{
- RSA *key;
- uint8_t hash_buf[SHA256_DIGEST_LENGTH];
- int retval = -1;
-
- // Hash the table
- SHA256((uint8_t*)table, table_length, hash_buf);
-
- // Now get the public key from the keyfile
- key = load_key(VERITY_TABLE_RSA_KEY);
- if (!key) {
- LERROR << "Couldn't load verity keys";
- goto out;
- }
-
- // verify the result
- if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature,
- signature_size, key)) {
- LERROR << "Couldn't verify table";
- goto out;
- }
-
- retval = 0;
-
-out:
- RSA_free(key);
- return retval;
-}
-
-static int verify_verity_signature(const struct fec_verity_metadata& verity)
-{
- if (verify_table(verity.signature, sizeof(verity.signature),
- verity.table, verity.table_length) == 0 ||
- verify_table(verity.ecc_signature, sizeof(verity.ecc_signature),
- verity.table, verity.table_length) == 0) {
- return 0;
- }
-
- return -1;
-}
-
-static int invalidate_table(char *table, size_t table_length)
-{
- size_t n = 0;
- size_t idx = 0;
- size_t cleared = 0;
-
- while (n < table_length) {
- if (table[n++] == ' ') {
- ++idx;
- }
-
- if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) {
- continue;
- }
-
- while (n < table_length && table[n] != ' ') {
- table[n++] = '0';
- }
-
- if (++cleared == 2) {
- return 0;
- }
- }
-
- return -1;
-}
-
-struct verity_table_params {
- char *table;
- int mode;
- struct fec_ecc_metadata ecc;
- const char *ecc_dev;
-};
-
-typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
- const struct verity_table_params *params);
-
-static bool format_verity_table(char *buf, const size_t bufsize,
- const struct verity_table_params *params)
-{
- const char *mode_flag = NULL;
- int res = -1;
-
- if (params->mode == VERITY_MODE_RESTART) {
- mode_flag = VERITY_TABLE_OPT_RESTART;
- } else if (params->mode == VERITY_MODE_LOGGING) {
- mode_flag = VERITY_TABLE_OPT_LOGGING;
- }
-
- if (params->ecc.valid) {
- if (mode_flag) {
- res = snprintf(buf, bufsize,
- "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
- params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
- params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
- } else {
- res = snprintf(buf, bufsize,
- "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
- params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
- params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
- }
- } else if (mode_flag) {
- res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
- mode_flag);
- } else {
- res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table);
- }
-
- if (res < 0 || (size_t)res >= bufsize) {
- LERROR << "Error building verity table; insufficient buffer size?";
- return false;
- }
-
- return true;
-}
-
-static bool format_legacy_verity_table(char *buf, const size_t bufsize,
- const struct verity_table_params *params)
-{
- int res;
-
- if (params->mode == VERITY_MODE_EIO) {
- res = strlcpy(buf, params->table, bufsize);
- } else {
- res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
- }
-
- if (res < 0 || (size_t)res >= bufsize) {
- LERROR << "Error building verity table; insufficient buffer size?";
- return false;
- }
-
- return true;
-}
-
-static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
- uint64_t device_size, const struct verity_table_params* params,
- format_verity_table_func format) {
- android::dm::DmTable table;
- table.set_readonly(true);
-
- char buffer[DM_BUF_SIZE];
- if (!format(buffer, sizeof(buffer), params)) {
- LERROR << "Failed to format verity parameters";
- return -1;
- }
-
- android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
- if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
- LERROR << "Failed to add verity target";
- return -1;
- }
- if (!dm.CreateDevice(name, table)) {
- LERROR << "Failed to create verity device \"" << name << "\"";
- return -1;
- }
- return 0;
-}
-
-static int read_partition(const char *path, uint64_t size)
-{
- char buf[READ_BUF_SIZE];
- ssize_t size_read;
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
-
- if (fd == -1) {
- PERROR << "Failed to open " << path;
- return -errno;
- }
-
- while (size) {
- size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
- if (size_read == -1) {
- PERROR << "Error in reading partition " << path;
- return -errno;
- }
- size -= size_read;
- }
-
- return 0;
-}
-
-bool fs_mgr_load_verity_state(int* mode) {
- // unless otherwise specified, use EIO mode.
- *mode = VERITY_MODE_EIO;
-
- // The bootloader communicates verity mode via the kernel commandline
- std::string verity_mode;
- if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
- return false;
- }
-
- if (verity_mode == "enforcing") {
- *mode = VERITY_MODE_DEFAULT;
- } else if (verity_mode == "logging") {
- *mode = VERITY_MODE_LOGGING;
- }
-
- return true;
-}
-
-// Update the verity table using the actual block device path.
-// Two cases:
-// Case-1: verity table is shared for devices with different by-name prefix.
-// Example:
-// verity table token: /dev/block/bootdevice/by-name/vendor
-// blk_device-1 (non-A/B): /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
-// blk_device-2 (A/B): /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
-//
-// Case-2: append A/B suffix in the verity table.
-// Example:
-// verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
-// blk_device: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
-static void update_verity_table_blk_device(const std::string& blk_device, char** table,
- bool slot_select) {
- bool updated = false;
- std::string result, ab_suffix;
- auto tokens = android::base::Split(*table, " ");
-
- // If slot_select is set, it means blk_device is already updated with ab_suffix.
- if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
-
- for (const auto& token : tokens) {
- std::string new_token;
- if (android::base::StartsWith(token, "/dev/block/")) {
- if (token == blk_device) return; // no need to update if they're already the same.
- std::size_t found1 = blk_device.find("by-name");
- std::size_t found2 = token.find("by-name");
- if (found1 != std::string::npos && found2 != std::string::npos &&
- blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
- new_token = blk_device;
- }
- }
-
- if (!new_token.empty()) {
- updated = true;
- LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
- << "'";
- } else {
- new_token = token;
- }
-
- if (result.empty()) {
- result = new_token;
- } else {
- result += " " + new_token;
- }
- }
-
- if (!updated) {
- return;
- }
-
- free(*table);
- *table = strdup(result.c_str());
-}
-
-// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
-// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
-// verity device to get created before return
-int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
- int retval = FS_MGR_SETUP_VERITY_FAIL;
- int fd = -1;
- std::string verity_blk_name;
- struct fec_handle *f = NULL;
- struct fec_verity_metadata verity;
- struct verity_table_params params = { .table = NULL };
-
- const std::string mount_point(basename(entry->mount_point.c_str()));
- bool verified_at_boot = false;
-
- android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
-
- if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
- 0) {
- PERROR << "Failed to open '" << entry->blk_device << "'";
- return retval;
- }
-
- // read verity metadata
- if (fec_verity_get_metadata(f, &verity) < 0) {
- PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
- // Allow verity disabled when the device is unlocked without metadata
- if (fs_mgr_is_device_unlocked()) {
- retval = FS_MGR_SETUP_VERITY_SKIPPED;
- LWARNING << "Allow invalid metadata when the device is unlocked";
- }
- goto out;
- }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
- if (verity.disabled) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
- LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
- goto out;
- }
-#endif
-
- // read ecc metadata
- if (fec_ecc_get_metadata(f, ¶ms.ecc) < 0) {
- params.ecc.valid = false;
- }
-
- params.ecc_dev = entry->blk_device.c_str();
-
- if (!fs_mgr_load_verity_state(¶ms.mode)) {
- /* if accessing or updating the state failed, switch to the default
- * safe mode. This makes sure the device won't end up in an endless
- * restart loop, and no corrupted data will be exposed to userspace
- * without a warning. */
- params.mode = VERITY_MODE_EIO;
- }
-
- if (!verity.table) {
- goto out;
- }
-
- params.table = strdup(verity.table);
- if (!params.table) {
- goto out;
- }
-
- // verify the signature on the table
- if (verify_verity_signature(verity) < 0) {
- // Allow signature verification error when the device is unlocked
- if (fs_mgr_is_device_unlocked()) {
- retval = FS_MGR_SETUP_VERITY_SKIPPED;
- LWARNING << "Allow signature verification error when the device is unlocked";
- goto out;
- }
- if (params.mode == VERITY_MODE_LOGGING) {
- // the user has been warned, allow mounting without dm-verity
- retval = FS_MGR_SETUP_VERITY_SKIPPED;
- goto out;
- }
-
- // invalidate root hash and salt to trigger device-specific recovery
- if (invalidate_table(params.table, verity.table_length) < 0) {
- goto out;
- }
- }
-
- LINFO << "Enabling dm-verity for " << mount_point.c_str()
- << " (mode " << params.mode << ")";
-
- // Update the verity params using the actual block device path
- update_verity_table_blk_device(entry->blk_device, ¶ms.table,
- entry->fs_mgr_flags.slot_select);
-
- // load the verity mapping table
- if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) {
- goto loaded;
- }
-
- if (params.ecc.valid) {
- // kernel may not support error correction, try without
- LINFO << "Disabling error correction for " << mount_point.c_str();
- params.ecc.valid = false;
-
- if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) {
- goto loaded;
- }
- }
-
- // try the legacy format for backwards compatibility
- if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_legacy_verity_table) ==
- 0) {
- goto loaded;
- }
-
- if (params.mode != VERITY_MODE_EIO) {
- // as a last resort, EIO mode should always be supported
- LINFO << "Falling back to EIO mode for " << mount_point.c_str();
- params.mode = VERITY_MODE_EIO;
-
- if (load_verity_table(dm, mount_point, verity.data_size, ¶ms,
- format_legacy_verity_table) == 0) {
- goto loaded;
- }
- }
-
- LERROR << "Failed to load verity table for " << mount_point.c_str();
- goto out;
-
-loaded:
- if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
- LERROR << "Couldn't get verity device number!";
- goto out;
- }
-
- // mark the underlying block device as read-only
- fs_mgr_set_blk_ro(entry->blk_device);
-
- // Verify the entire partition in one go
- // If there is an error, allow it to mount as a normal verity partition.
- if (entry->fs_mgr_flags.verify_at_boot) {
- LINFO << "Verifying partition " << entry->blk_device << " at boot";
- int err = read_partition(verity_blk_name.c_str(), verity.data_size);
- if (!err) {
- LINFO << "Verified verity partition " << entry->blk_device << " at boot";
- verified_at_boot = true;
- }
- }
-
- // assign the new verity block device as the block device
- if (!verified_at_boot) {
- entry->blk_device = verity_blk_name;
- } else if (!dm.DeleteDevice(mount_point)) {
- LERROR << "Failed to remove verity device " << mount_point.c_str();
- goto out;
- }
-
- // make sure we've set everything up properly
- if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
- goto out;
- }
-
- retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- fec_close(f);
- free(params.table);
-
- return retval;
-}
-
-bool fs_mgr_teardown_verity(FstabEntry* entry) {
- const std::string mount_point(basename(entry->mount_point.c_str()));
- if (!android::fs_mgr::UnmapDevice(mount_point)) {
- return false;
- }
- LINFO << "Unmapped verity device " << mount_point;
- return true;
-}
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
index f0afd28..b2b9be8 100644
--- a/fs_mgr/fuzz/Android.bp
+++ b/fs_mgr/fuzz/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "libfstab_fuzzer",
srcs: [
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 054300e..f26fb24 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 fs_checkpoint_opts;
std::string metadata_key_dir;
- std::string metadata_encryption;
+ std::string metadata_encryption_options;
off64_t length = 0;
std::string label;
int partnum = -1;
@@ -63,7 +63,6 @@
bool nonremovable : 1;
bool vold_managed : 1;
bool recovery_only : 1;
- bool verify : 1;
bool no_emulated_sd : 1; // No emulated sdcard daemon; sd card is the only external
// storage.
bool no_trim : 1;
@@ -72,7 +71,6 @@
bool slot_select : 1;
bool late_mount : 1;
bool no_fail : 1;
- bool verify_at_boot : 1;
bool quota : 1;
bool avb : 1;
bool logical : 1;
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a8..003e6ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,6 +66,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages() override;
@@ -219,6 +220,17 @@
return !device->empty();
}
+bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
+ bool retval;
+ auto status = manager_->isImageDisabled(name, &retval);
+ if (!status.isOk()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << " binder returned: " << status.exceptionMessage().string();
+ return false;
+ }
+ return retval;
+}
+
bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
return false;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 2c14c8a..c416f4d 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -79,7 +79,7 @@
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
// Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RECOVERY__
+#ifdef __ANDROID_RAMDISK__
device_info_.is_recovery = {true};
#else
if (!device_info_.is_recovery.has_value()) {
@@ -523,7 +523,7 @@
auto image_header = GetImageHeaderPath(name);
-#if !defined __ANDROID_RECOVERY__
+#ifndef __ANDROID_RAMDISK__
// If there is a device-mapper node wrapping the block device, then we're
// able to create another node around it; the dm layer does not carry the
// exclusion lock down the stack when a mount occurs.
@@ -744,6 +744,134 @@
return CreateLogicalPartitions(*metadata.get(), data_partition_name);
}
+std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
+ if (auto e = extent->AsLinearExtent()) {
+ return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
+ << ", device:" << e->device_index() << ">";
+ }
+ return os << "<unknown>";
+}
+
+static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
+ if (auto linear_a = a->AsLinearExtent()) {
+ auto linear_b = b->AsLinearExtent();
+ if (!linear_b) {
+ return false;
+ }
+ return linear_a->physical_sector() == linear_b->physical_sector() &&
+ linear_a->num_sectors() == linear_b->num_sectors() &&
+ linear_a->device_index() == linear_b->device_index();
+ }
+ return false;
+}
+
+static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
+ const auto& old_extents = oldp->extents();
+ const auto& new_extents = newp->extents();
+
+ auto old_iter = old_extents.begin();
+ auto new_iter = new_extents.begin();
+ while (true) {
+ if (old_iter == old_extents.end()) {
+ if (new_iter == new_extents.end()) {
+ break;
+ }
+ LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
+ return false;
+ }
+ if (new_iter == new_extents.end()) {
+ LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
+ return false;
+ }
+
+ if (!CompareExtent(old_iter->get(), new_iter->get())) {
+ LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
+ return false;
+ }
+
+ old_iter++;
+ new_iter++;
+ }
+ return true;
+}
+
+bool ImageManager::ValidateImageMaps() {
+ if (!MetadataExists(metadata_dir_)) {
+ LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
+ return true;
+ }
+
+ for (const auto& partition : metadata->partitions) {
+ auto name = GetPartitionName(partition);
+ auto image_path = GetImageHeaderPath(name);
+ auto fiemap = SplitFiemap::Open(image_path);
+ if (fiemap == nullptr) {
+ LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+ return false;
+ }
+ if (!fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+ return false;
+ }
+
+ android::fs_mgr::PartitionOpener opener;
+ auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
+ if (!builder) {
+ LOG(ERROR) << "Could not create metadata builder: " << image_path;
+ return false;
+ }
+
+ auto new_p = builder->AddPartition("_temp_for_verify", 0);
+ if (!new_p) {
+ LOG(ERROR) << "Could not add temporary partition: " << image_path;
+ return false;
+ }
+
+ auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
+ if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
+ LOG(ERROR) << "Could not fill partition extents: " << image_path;
+ return false;
+ }
+
+ auto old_p = builder->FindPartition(name);
+ if (!old_p) {
+ LOG(ERROR) << "Could not find metadata for " << image_path;
+ return false;
+ }
+
+ if (!CompareExtents(old_p, new_p)) {
+ LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ImageManager::IsImageDisabled(const std::string& name) {
+ if (!MetadataExists(metadata_dir_)) {
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ return false;
+ }
+
+ auto partition = FindPartition(*metadata.get(), name);
+ if (!partition) {
+ return false;
+ }
+
+ return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
+}
+
std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
const std::chrono::milliseconds& timeout_ms,
const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d09751..7472949 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,6 +119,7 @@
ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
ASSERT_TRUE(manager_->BackingImageExists(base_name_));
ASSERT_TRUE(manager_->DisableImage(base_name_));
+ ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
ASSERT_TRUE(manager_->RemoveDisabledImages());
ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
}
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c87000..00dd661 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,6 +131,9 @@
virtual bool RemoveAllImages() = 0;
virtual bool UnmapImageIfExists(const std::string& name);
+
+ // Returns whether DisableImage() was called.
+ virtual bool IsImageDisabled(const std::string& name) = 0;
};
class ImageManager final : public IImageManager {
@@ -162,6 +165,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages();
@@ -174,6 +178,9 @@
// Writes |bytes| zeros at the beginning of the passed image
FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
+ // Validate that all images still have the same block map.
+ bool ValidateImageMaps();
+
private:
ImageManager(const std::string& metadata_dir, const std::string& data_dir,
const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 4eb3ad5..30b2c61 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,6 +20,7 @@
#include <string>
#include <libfiemap/split_fiemap_writer.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
namespace android {
@@ -34,5 +35,9 @@
bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
bool RemoveAllMetadata(const std::string& dir);
+bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
+ android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
+ uint64_t partition_size);
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index e913d50..85dbb36 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -35,19 +35,6 @@
namespace android {
namespace fs_mgr {
-std::string GetAvbPropertyDescriptor(const std::string& key,
- const std::vector<VBMetaData>& vbmeta_images) {
- size_t value_size;
- for (const auto& vbmeta : vbmeta_images) {
- const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
- key.size(), &value_size);
- if (value != nullptr) {
- return {value, value_size};
- }
- }
- return "";
-}
-
// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
// See the following link for more details:
// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
@@ -130,64 +117,6 @@
return true;
}
-std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
- const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
- bool found = false;
- const uint8_t* desc_partition_name;
- auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
-
- for (const auto& vbmeta : vbmeta_images) {
- size_t num_descriptors;
- std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
- avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
-
- if (!descriptors || num_descriptors < 1) {
- continue;
- }
-
- for (size_t n = 0; n < num_descriptors && !found; n++) {
- AvbDescriptor desc;
- if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
- LWARNING << "Descriptor[" << n << "] is invalid";
- continue;
- }
- if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
- desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
- if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
- hash_desc.get())) {
- continue;
- }
- if (hash_desc->partition_name_len != partition_name.length()) {
- continue;
- }
- // Notes that desc_partition_name is not NUL-terminated.
- std::string hash_partition_name((const char*)desc_partition_name,
- hash_desc->partition_name_len);
- if (hash_partition_name == partition_name) {
- found = true;
- }
- }
- }
-
- if (found) break;
- }
-
- if (!found) {
- LERROR << "Hash descriptor not found: " << partition_name;
- return nullptr;
- }
-
- hash_desc->partition_name = partition_name;
-
- const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
- hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
-
- const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
- hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
-
- return hash_desc;
-}
-
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index e8f7c39..7941c70 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -37,12 +37,6 @@
: partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
};
-std::string GetAvbPropertyDescriptor(const std::string& key,
- const std::vector<VBMetaData>& vbmeta_images);
-
-std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
- const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
-
// AvbHashtreeDescriptor to dm-verity table setup.
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index 1da7117..a288876 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -37,6 +37,7 @@
#include "avb_ops.h"
#include "avb_util.h"
+#include "fs_avb/fs_avb_util.h"
#include "sha.h"
#include "util.h"
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index 1c14cc0..5326226 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,6 +74,64 @@
return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
}
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+ bool found = false;
+ const uint8_t* desc_partition_name;
+ auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+ for (const auto& vbmeta : vbmeta_images) {
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ for (size_t n = 0; n < num_descriptors && !found; n++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+ LWARNING << "Descriptor[" << n << "] is invalid";
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+ desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+ if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+ hash_desc.get())) {
+ continue;
+ }
+ if (hash_desc->partition_name_len != partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hash_partition_name((const char*)desc_partition_name,
+ hash_desc->partition_name_len);
+ if (hash_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+
+ if (found) break;
+ }
+
+ if (!found) {
+ LERROR << "Hash descriptor not found: " << partition_name;
+ return nullptr;
+ }
+
+ hash_desc->partition_name = partition_name;
+
+ const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+ hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+ const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+ hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+ return hash_desc;
+}
+
// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
VBMetaData&& vbmeta) {
@@ -84,5 +142,18 @@
return GetHashDescriptor(avb_partition_name, vbmeta_images);
}
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images) {
+ size_t value_size;
+ for (const auto& vbmeta : vbmeta_images) {
+ const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+ key.size(), &value_size);
+ if (value != nullptr) {
+ return {value, value_size};
+ }
+ }
+ return "";
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index 3f37bd7..1b15db7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -43,9 +43,15 @@
std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
const std::string& avb_partition_name, VBMetaData&& vbmeta);
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
// Gets the hash descriptor for avb_partition_name from the vbmeta.
std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
VBMetaData&& vbmeta);
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
index 5d2ce3d..3e945a4 100755
--- a/fs_mgr/libfs_avb/run_tests.sh
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -1,8 +1,13 @@
#!/bin/sh
#
# Run host tests
-atest libfs_avb_test # Tests public libfs_avb APIs.
-atest libfs_avb_internal_test # Tests libfs_avb private APIs.
+atest --host libfs_avb_test # Tests public libfs_avb APIs.
+
+# Tests libfs_avb private APIs.
+# The tests need more time to finish, so increase the timeout to 5 mins.
+# The default timeout is only 60 seconds.
+atest --host libfs_avb_internal_test -- --test-arg \
+ com.android.tradefed.testtype.HostGTest:native-test-timeout:5m
# Run device tests
atest libfs_avb_device_test # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 6f874a6..2e34920 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -23,6 +23,7 @@
#include <libavb/libavb.h>
#include "avb_util.h"
+#include "fs_avb/fs_avb_util.h"
#include "fs_avb_test_util.h"
// Target classes or functions to test:
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 04bcbda..cf0d22b 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "liblp_test"
}
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "liblp_test"
+ }
]
}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 72827eb..1759cf9 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
@@ -31,6 +32,7 @@
using ::testing::ElementsAre;
using ::testing::NiceMock;
using ::testing::Return;
+using android::base::GetProperty;
class Environment : public ::testing::Environment {
public:
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index e67fb33..d123304 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -20,7 +20,10 @@
#include <sys/syscall.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
@@ -38,6 +41,7 @@
using ::testing::_;
using ::testing::Return;
using unique_fd = android::base::unique_fd;
+using android::base::GetProperty;
// Our tests assume a 128KiB disk with two 512 byte metadata slots.
static const size_t kDiskSize = 131072;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8b269cd..6db8f13 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -275,6 +275,24 @@
],
}
+cc_test {
+ name: "vts_ota_config_test",
+ srcs: [
+ "vts_ota_config_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_options: {
+ min_shipping_api_level: 33,
+ },
+ auto_gen_config: true,
+ require_root: true,
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 37319fe..9d2b877 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -2,3 +2,4 @@
balsini@google.com
dvander@google.com
elsk@google.com
+akailash@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 532f66d..5daa84d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -197,6 +197,9 @@
// user-space snapshots
bool userspace_snapshots = 9;
+
+ // io_uring support
+ bool io_uring_enabled = 10;
}
// Next: 10
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 20030b9..746feeb 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -475,10 +475,7 @@
std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
}
- merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
- for (auto block : other_ops) {
- merge_op_blocks->emplace_back(block);
- }
+ merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
num_total_data_ops_ = merge_op_blocks->size();
if (header_.num_merge_ops > 0) {
@@ -553,6 +550,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::vector<CowOperation>::iterator op_iter_;
@@ -563,6 +563,15 @@
op_iter_ = ops_->begin();
}
+bool CowOpIter::RDone() {
+ return op_iter_ == ops_->begin();
+}
+
+void CowOpIter::Prev() {
+ CHECK(!RDone());
+ op_iter_--;
+}
+
bool CowOpIter::Done() {
return op_iter_ == ops_->end();
}
@@ -588,6 +597,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -606,6 +618,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -626,6 +641,15 @@
block_iter_ = merge_op_blocks->begin() + start;
}
+bool CowMergeOpIter::RDone() {
+ return block_iter_ == merge_op_blocks_->begin();
+}
+
+void CowMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_iter_--;
+}
+
bool CowMergeOpIter::Done() {
return block_iter_ == merge_op_blocks_->end();
}
@@ -652,6 +676,15 @@
block_riter_ = merge_op_blocks->rbegin();
}
+bool CowRevMergeOpIter::RDone() {
+ return block_riter_ == merge_op_blocks_->rbegin();
+}
+
+void CowRevMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_riter_--;
+}
+
bool CowRevMergeOpIter::Done() {
return block_riter_ == merge_op_blocks_->rend() - start_;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index d5b4335..8e6bbd9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -92,7 +92,7 @@
public:
virtual ~ICowOpIter() {}
- // True if there are more items to read, false otherwise.
+ // True if there are no more items to read forward, false otherwise.
virtual bool Done() = 0;
// Read the current operation.
@@ -100,6 +100,12 @@
// Advance to the next item.
virtual void Next() = 0;
+
+ // Advance to the previous item.
+ virtual void Prev() = 0;
+
+ // True if there are no more items to read backwards, false otherwise
+ virtual bool RDone() = 0;
};
class CowReader final : public ICowReader {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 41c6ef5..11da568 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -396,6 +396,17 @@
DM_USER,
};
+ // Add new public entries above this line.
+
+ // Helpers for failure injection.
+ using MergeConsistencyChecker =
+ std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
+
+ void set_merge_consistency_checker(MergeConsistencyChecker checker) {
+ merge_consistency_checker_ = checker;
+ }
+ MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
+
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -410,6 +421,7 @@
FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
FRIEND_TEST(SnapshotUpdateTest, AddPartition);
+ FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
@@ -526,6 +538,9 @@
// Unmap a COW and remove it from a MetadataBuilder.
void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
+ // Remove invalid snapshots if any
+ void RemoveInvalidSnapshots(LockedFile* lock);
+
// Unmap and remove all known snapshots.
bool RemoveAllSnapshots(LockedFile* lock);
@@ -797,6 +812,9 @@
// userspace snapshots.
bool UpdateUsesUserSnapshots(LockedFile* lock);
+ // Check if io_uring API's need to be used
+ bool UpdateUsesIouring(LockedFile* lock);
+
// Wrapper around libdm, with diagnostics.
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
@@ -811,6 +829,7 @@
std::unique_ptr<SnapuserdClient> snapuserd_client_;
std::unique_ptr<LpMetadata> old_partition_metadata_;
std::optional<bool> is_snapshot_userspace_;
+ MergeConsistencyChecker merge_consistency_checker_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 548ba00..167ff8c 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -155,6 +155,7 @@
}
StringSink sink;
bool success = true;
+ uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
while (!iter->Done()) {
const CowOperation& op = iter->Get();
@@ -187,9 +188,26 @@
}
}
+ if (op.type == kCowCopyOp) {
+ copy_ops++;
+ } else if (op.type == kCowReplaceOp) {
+ replace_ops++;
+ } else if (op.type == kCowZeroOp) {
+ zero_ops++;
+ } else if (op.type == kCowXorOp) {
+ xor_ops++;
+ }
+
iter->Next();
}
+ if (!opt.silent) {
+ auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
+ std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
+ << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
+ << " Xor_ops: " << xor_ops << std::endl;
+ }
+
return success;
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index e6e17bd..a83f535 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -87,6 +87,8 @@
static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
static constexpr auto kUpdateStateCheckInterval = 2s;
+MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status);
+
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
SnapshotManager::~SnapshotManager() {}
@@ -116,7 +118,9 @@
}
SnapshotManager::SnapshotManager(IDeviceInfo* device)
- : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
+ : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {
+ merge_consistency_checker_ = android::snapshot::CheckMergeConsistency;
+}
static std::string GetCowName(const std::string& snapshot_name) {
return snapshot_name + "-cow";
@@ -214,7 +218,10 @@
if (!file) return false;
UpdateState state = ReadUpdateState(file.get());
- if (state == UpdateState::None) return true;
+ if (state == UpdateState::None) {
+ RemoveInvalidSnapshots(file.get());
+ return true;
+ }
if (state == UpdateState::Initiated) {
LOG(INFO) << "Update has been initiated, now canceling";
@@ -1329,14 +1336,20 @@
const SnapshotStatus& status) {
CHECK(lock);
+ return merge_consistency_checker_(name, status);
+}
+
+MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
if (!status.compression_enabled()) {
// Do not try to verify old-style COWs yet.
return MergeFailureCode::Ok;
}
+ auto& dm = DeviceMapper::Instance();
+
std::string cow_image_name = GetMappedCowDeviceName(name, status);
std::string cow_image_path;
- if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
+ if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
return MergeFailureCode::GetCowPathConsistencyCheck;
}
@@ -1400,9 +1413,11 @@
}
SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
- CHECK(update_status.state() == UpdateState::Merging);
+ CHECK(update_status.state() == UpdateState::Merging ||
+ update_status.state() == UpdateState::MergeFailed);
CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
+ update_status.set_state(UpdateState::Merging);
update_status.set_merge_phase(MergePhase::SECOND_PHASE);
if (!WriteSnapshotUpdateStatus(lock, update_status)) {
return MergeFailureCode::WriteStatus;
@@ -1455,6 +1470,14 @@
}
RemoveAllUpdateState(lock);
+
+ if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
+ if (snapuserd_client_) {
+ snapuserd_client_->DetachSnapuserd();
+ snapuserd_client_->CloseConnection();
+ snapuserd_client_ = nullptr;
+ }
+ }
}
void SnapshotManager::AcknowledgeMergeFailure(MergeFailureCode failure_code) {
@@ -1665,6 +1688,9 @@
if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {
snapuserd_argv->emplace_back("-user_snapshot");
+ if (UpdateUsesIouring(lock.get())) {
+ snapuserd_argv->emplace_back("-io_uring");
+ }
}
size_t num_cows = 0;
@@ -1880,6 +1906,33 @@
return true;
}
+void SnapshotManager::RemoveInvalidSnapshots(LockedFile* lock) {
+ std::vector<std::string> snapshots;
+
+ // Remove the stale snapshot metadata
+ //
+ // We make sure that all the three cases
+ // are valid before removing the snapshot metadata:
+ //
+ // 1: dm state is active
+ // 2: Root fs is not mounted off as a snapshot device
+ // 3: Snapshot slot suffix should match current device slot
+ if (!ListSnapshots(lock, &snapshots, device_->GetSlotSuffix()) || snapshots.empty()) {
+ return;
+ }
+
+ // We indeed have some invalid snapshots
+ for (const auto& name : snapshots) {
+ if (dm_.GetState(name) == DmDeviceState::ACTIVE && !IsSnapshotDevice(name)) {
+ if (!DeleteSnapshot(lock, name)) {
+ LOG(ERROR) << "Failed to delete invalid snapshot: " << name;
+ } else {
+ LOG(INFO) << "Invalid snapshot: " << name << " deleted";
+ }
+ }
+ }
+}
+
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
std::vector<std::string> snapshots;
if (!ListSnapshots(lock, &snapshots)) {
@@ -2042,6 +2095,11 @@
return update_status.compression_enabled();
}
+bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ return update_status.io_uring_enabled();
+}
+
bool SnapshotManager::UpdateUsesUserSnapshots() {
// This and the following function is constantly
// invoked during snapshot merge. We want to avoid
@@ -2857,6 +2915,7 @@
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
+ status.set_io_uring_enabled(old_status.io_uring_enabled());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -3176,19 +3235,32 @@
status.set_compression_enabled(cow_creator.compression_enabled);
if (cow_creator.compression_enabled) {
if (!device()->IsTestDevice()) {
+ bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release = android::base::GetProperty(
+ "ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ userSnapshotsEnabled = false;
+ }
+
// Userspace snapshots is enabled only if compression is enabled
- status.set_userspace_snapshots(IsUserspaceSnapshotsEnabled());
- if (IsUserspaceSnapshotsEnabled()) {
+ status.set_userspace_snapshots(userSnapshotsEnabled);
+ if (userSnapshotsEnabled) {
is_snapshot_userspace_ = true;
- LOG(INFO) << "User-space snapshots enabled";
+ status.set_io_uring_enabled(IsIouringEnabled());
+ LOG(INFO) << "Userspace snapshots enabled";
} else {
is_snapshot_userspace_ = false;
- LOG(INFO) << "User-space snapshots disabled";
+ LOG(INFO) << "Userspace snapshots disabled";
}
// Terminate stale daemon if any
std::unique_ptr<SnapuserdClient> snapuserd_client =
- SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+ SnapuserdClient::Connect(kSnapuserdSocket, 5s);
if (snapuserd_client) {
snapuserd_client->DetachSnapuserd();
snapuserd_client->CloseConnection();
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index c1a5af7..a648384 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -199,6 +199,7 @@
bool UnmapImageIfExists(const std::string& name) override {
return impl_->UnmapImageIfExists(name);
}
+ bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
private:
std::unique_ptr<android::fiemap::IImageManager> impl_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 14f2d45..36abf71 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -46,14 +46,14 @@
#include "partition_cow_creator.h"
#include "utility.h"
-#include <android-base/properties.h>
-
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
// class itself.
#include <libsnapshot/mock_device_info.h>
#include <libsnapshot/mock_snapshot.h>
DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
+DEFINE_string(force_iouring_disable, "",
+ "Force testing mode (iouring_disabled) - disable io_uring");
namespace android {
namespace snapshot {
@@ -1394,6 +1394,93 @@
}
}
+// Test that a transient merge consistency check failure can resume properly.
+TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
+ if (!ShouldUseCompression()) {
+ // b/179111359
+ GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+ }
+
+ auto old_sys_size = GetSize(sys_);
+ auto old_prd_size = GetSize(prd_);
+
+ // Grow |sys| but shrink |prd|.
+ SetSize(sys_, old_sys_size * 2);
+ sys_->set_estimate_cow_size(8_MiB);
+ SetSize(prd_, old_prd_size / 2);
+ prd_->set_estimate_cow_size(1_MiB);
+
+ AddOperationForPartitions();
+
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+ ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+ ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
+
+ sync();
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // After reboot, init does first stage mount.
+ auto init = NewManagerForFirstStageMount("_b");
+ ASSERT_NE(init, nullptr);
+ ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Check that the target partitions have the same content.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
+ auto old_checker = init->merge_consistency_checker();
+
+ init->set_merge_consistency_checker(
+ [](const std::string&, const SnapshotStatus&) -> MergeFailureCode {
+ return MergeFailureCode::WrongMergeCountConsistencyCheck;
+ });
+
+ // Initiate the merge and wait for it to be completed.
+ ASSERT_TRUE(init->InitiateMerge());
+ ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+ {
+ // Check that the merge phase is FIRST_PHASE until at least one call
+ // to ProcessUpdateState() occurs.
+ ASSERT_TRUE(AcquireLock());
+ auto local_lock = std::move(lock_);
+ auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
+ ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
+ }
+
+ // Merge should have failed.
+ ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
+
+ // Simulate shutting down the device and creating partitions again.
+ ASSERT_TRUE(UnmapAll());
+
+ // Restore the checker.
+ init->set_merge_consistency_checker(std::move(old_checker));
+
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+
+ // Complete the merge.
+ ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+ // Check that the target partitions have the same content after the merge.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name))
+ << "Content of " << name << " changes after the merge";
+ }
+}
+
// Test that if new system partitions uses empty space in super, that region is not snapshotted.
TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
GTEST_SKIP() << "b/141889746";
@@ -2324,8 +2411,15 @@
// fit in super, but not |prd|.
constexpr uint64_t partition_size = 3788_KiB;
SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, 18_MiB);
- AddOperationForPartitions({sys_});
+ // Make sure |prd| does not fit in super at all. On VABC, this means we
+ // fake an extra large COW for |vnd| to fill up super.
+ vnd_->set_estimate_cow_size(30_MiB);
+ prd_->set_estimate_cow_size(30_MiB);
+
+ AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -2335,8 +2429,25 @@
GTEST_SKIP() << "Test does not apply to userspace snapshots";
}
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ // Test that partitions prioritize using space in super.
+ auto tgt = MetadataBuilder::New(*opener_, "super", 1);
+ ASSERT_NE(tgt, nullptr);
+ ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
+ ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+ ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
ASSERT_TRUE(UnmapAll());
class DmStatusFailure final : public DeviceMapperWrapper {
@@ -2682,10 +2793,22 @@
}
}
+ if (FLAGS_force_iouring_disable == "iouring_disabled") {
+ if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
+ return testing::AssertionFailure()
+ << "Failed to disable property: snapuserd.test.io_uring.disabled";
+ }
+ }
+
int ret = RUN_ALL_TESTS();
if (FLAGS_force_config == "dmsnap") {
android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
}
+
+ if (FLAGS_force_iouring_disable == "iouring_disabled") {
+ android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
+ }
+
return ret;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 84bcb94..bc2bceb 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -86,7 +86,9 @@
"libsnapshot_cow",
"libz",
"libext4_utils",
+ "liburing",
],
+ include_dirs: ["bionic/libc/kernel"],
}
cc_binary {
@@ -182,7 +184,10 @@
"libfs_mgr",
"libdm",
"libext4_utils",
+ "liburing",
+ "libgflags",
],
+ include_dirs: ["bionic/libc/kernel"],
header_libs: [
"libstorage_literals_headers",
"libfiemap_headers",
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
index b86a802..484a9c4 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
@@ -932,7 +932,6 @@
ASSERT_EQ(area_sz, 2);
- size_t new_chunk = 263;
// Verify the partially filled area
void* buffer = snapuserd_->GetExceptionBuffer(1);
loff_t offset = 0;
@@ -941,7 +940,6 @@
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, i);
offset += sizeof(struct disk_exception);
- new_chunk += 1;
}
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index ddb1f79..2f7775c 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -28,11 +28,23 @@
DEFINE_bool(socket_handoff, false,
"If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
+DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
namespace android {
namespace snapshot {
bool Daemon::IsUserspaceSnapshotsEnabled() {
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release =
+ android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ return false;
+ }
+
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
@@ -51,7 +63,12 @@
// is applied will check for the property. This is ok as the system
// properties are valid at this point. We can't do this during first
// stage init and hence use the command line flags to get the information.
- if (!IsDmSnapshotTestingEnabled() && (FLAGS_user_snapshot || IsUserspaceSnapshotsEnabled())) {
+ bool user_snapshots = FLAGS_user_snapshot;
+ if (!user_snapshots) {
+ user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
+ }
+
+ if (user_snapshots) {
LOG(INFO) << "Starting daemon for user-space snapshots.....";
return StartServerForUserspaceSnapshots(arg_start, argc, argv);
} else {
@@ -75,6 +92,11 @@
MaskAllSignalsExceptIntAndTerm();
+ user_server_.SetServerRunning();
+ if (FLAGS_io_uring) {
+ user_server_.SetIouringEnabled();
+ }
+
if (FLAGS_socket_handoff) {
return user_server_.RunForSocketHandoff();
}
@@ -165,7 +187,10 @@
}
void Daemon::Interrupt() {
- if (IsUserspaceSnapshotsEnabled()) {
+ // TODO: We cannot access system property during first stage init.
+ // Until we remove the dm-snapshot code, we will have this check
+ // and verify it through a temp variable.
+ if (user_server_.IsServerRunning()) {
user_server_.Interrupt();
} else {
server_.Interrupt();
@@ -173,7 +198,7 @@
}
void Daemon::ReceivedSocketSignal() {
- if (IsUserspaceSnapshotsEnabled()) {
+ if (user_server_.IsServerRunning()) {
user_server_.ReceivedSocketSignal();
} else {
server_.ReceivedSocketSignal();
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 95d95cd..692cb74 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -16,6 +16,10 @@
#include "snapuserd_core.h"
+#include <sys/utsname.h>
+
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
namespace android {
@@ -35,7 +39,21 @@
}
bool SnapshotHandler::InitializeWorkers() {
- for (int i = 0; i < kNumWorkerThreads; i++) {
+ int num_worker_threads = kNumWorkerThreads;
+
+ // We will need multiple worker threads only during
+ // device boot after OTA. For all other purposes,
+ // one thread is sufficient. We don't want to consume
+ // unnecessary memory especially during OTA install phase
+ // when daemon will be up during entire post install phase.
+ //
+ // During boot up, we need multiple threads primarily for
+ // update-verification.
+ if (is_socket_present_) {
+ num_worker_threads = 1;
+ }
+
+ for (int i = 0; i < num_worker_threads; i++) {
std::unique_ptr<Worker> wt =
std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
misc_name_, base_path_merge_, GetSharedPtr());
@@ -288,6 +306,136 @@
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) {
@@ -513,5 +661,41 @@
return ra_state;
}
+bool SnapshotHandler::IsIouringSupported() {
+ struct utsname uts;
+ unsigned int major, minor;
+
+ if (android::base::GetBoolProperty("snapuserd.test.io_uring.force_disable", false)) {
+ SNAP_LOG(INFO) << "io_uring disabled for testing";
+ return false;
+ }
+
+ if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+ SNAP_LOG(ERROR) << "Could not parse the kernel version from uname. "
+ << " io_uring not supported";
+ return false;
+ }
+
+ // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
+ // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
+ if (major >= 5) {
+ if (major == 5 && minor < 6) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ // During selinux init transition, libsnapshot will propagate the
+ // status of io_uring enablement. As properties are not initialized,
+ // we cannot query system property.
+ if (is_io_uring_enabled_) {
+ return true;
+ }
+
+ // Finally check the system property
+ return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
} // namespace snapshot
} // namespace android
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 1953316..83d40f6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -39,6 +39,7 @@
#include <libdm/dm.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
+#include <liburing.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
@@ -98,6 +99,7 @@
void InitializeRAIter();
bool RAIterDone();
void RAIterNext();
+ void RAResetIter(uint64_t num_blocks);
const CowOperation* GetRAOpIter();
void InitializeBuffer();
@@ -113,6 +115,19 @@
bool ReconstructDataFromCow();
void CheckOverlap(const CowOperation* cow_op);
+ bool ReadAheadAsyncIO();
+ bool ReapIoCompletions(int pending_ios_to_complete);
+ bool ReadXorData(size_t block_index, size_t xor_op_index,
+ std::vector<const CowOperation*>& xor_op_vec);
+ void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
+ std::vector<const CowOperation*>& xor_op_vec, void* buffer,
+ loff_t& buffer_offset);
+ void UpdateScratchMetadata();
+
+ bool ReadAheadSyncIO();
+ bool InitializeIouring();
+ void FinalizeIouring();
+
void* read_ahead_buffer_;
void* metadata_buffer_;
@@ -131,7 +146,23 @@
std::unordered_set<uint64_t> dest_blocks_;
std::unordered_set<uint64_t> source_blocks_;
bool overlap_;
+ std::vector<uint64_t> blocks_;
+ int total_blocks_merged_ = 0;
+ std::unique_ptr<uint8_t[]> ra_temp_buffer_;
+ std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
BufferSink bufsink_;
+
+ uint64_t total_ra_blocks_completed_ = 0;
+ bool read_ahead_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+ std::unique_ptr<struct io_uring> ring_;
};
class Worker {
@@ -184,15 +215,20 @@
// Merge related ops
bool Merge();
- bool MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
- bool MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
+ bool AsyncMerge();
+ bool SyncMerge();
+ bool MergeOrderedOps();
+ bool MergeOrderedOpsAsync();
+ bool MergeReplaceZeroOps();
int PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec = nullptr);
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
+ bool InitializeIouring();
+ void FinalizeIouring();
+
std::unique_ptr<CowReader> reader_;
BufferSink bufsink_;
XorSink xorsink_;
@@ -208,6 +244,20 @@
unique_fd base_path_merge_fd_;
unique_fd ctrl_fd_;
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ size_t ra_block_index_ = 0;
+ uint64_t blocks_merged_in_group_ = 0;
+ bool merge_async_ = false;
+ // Queue depth of 8 seems optimal. We don't want
+ // to have a huge depth as it may put more memory pressure
+ // on the kernel worker threads given that we use
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
+ std::unique_ptr<struct io_uring> ring_;
+
std::shared_ptr<SnapshotHandler> snapuserd_;
};
@@ -281,6 +331,7 @@
void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
+ void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
bool MergeInitiated() { return merge_initiated_; }
double GetMergePercentage() { return merge_completion_percentage_; }
@@ -292,6 +343,8 @@
bool GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer);
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
+ bool IsIouringSupported();
+
private:
bool ReadMetadata();
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
@@ -304,6 +357,11 @@
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
@@ -351,7 +409,10 @@
bool merge_initiated_ = false;
bool attached_ = false;
bool is_socket_present_;
+ bool is_io_uring_enabled_ = false;
bool scratch_space_ = false;
+
+ std::unique_ptr<struct io_uring> ring_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index fa055b7..c26a2cd 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -24,15 +24,14 @@
using android::base::unique_fd;
int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
do {
- if (!cowop_iter->Done() && num_ops) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ if (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
}
@@ -42,12 +41,12 @@
replace_zero_vec->push_back(cow_op);
}
- cowop_iter->Next();
+ cowop_iter_->Next();
num_ops -= 1;
nr_consecutive = 1;
- while (!cowop_iter->Done() && num_ops) {
- const CowOperation* op = &cowop_iter->Get();
+ while (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
}
@@ -63,7 +62,7 @@
nr_consecutive += 1;
num_ops -= 1;
- cowop_iter->Next();
+ cowop_iter_->Next();
}
}
} while (0);
@@ -71,28 +70,30 @@
return nr_consecutive;
}
-bool Worker::MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
- // Flush every 2048 ops. Since all ops are independent and there is no
+bool Worker::MergeReplaceZeroOps() {
+ // Flush every 8192 ops. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
- // of ops merged in COW file for every 2048 ops. If there is a crash,
+ // of ops merged in COW file for every 8192 ops. If there is a crash,
// we will end up replaying some of the COW ops which were already merged.
// That is ok.
//
- // Why 2048 ops ? We can probably increase this to bigger value but just
- // need to ensure that merge makes forward progress if there are
- // crashes repeatedly which is highly unlikely.
- int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 8;
+ // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
+ // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
+ // if there is a crash merge should always make forward progress.
+ int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
int num_ops_merged = 0;
- while (!cowop_iter->Done()) {
+ SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
+
+ while (!cowop_iter_->Done()) {
int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
std::vector<const CowOperation*> replace_zero_vec;
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter, &replace_zero_vec);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
if (linear_blocks == 0) {
// Merge complete
- CHECK(cowop_iter->Done());
+ CHECK(cowop_iter_->Done());
break;
}
@@ -117,8 +118,8 @@
size_t io_size = linear_blocks * BLOCK_SZ;
// Merge - Write the contents back to base device
- int ret = pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(), io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(),
+ io_size, source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR)
<< "Merge: ReplaceZeroOps: Failed to write to backing device while merging "
@@ -128,7 +129,7 @@
num_ops_merged += linear_blocks;
- if (num_ops_merged == total_ops_merged_per_commit) {
+ if (num_ops_merged >= total_ops_merged_per_commit) {
// Flush the data
if (fsync(base_path_merge_fd_.get()) < 0) {
SNAP_LOG(ERROR) << "Merge: ReplaceZeroOps: Failed to fsync merged data";
@@ -172,16 +173,15 @@
return true;
}
-bool Worker::MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeOrderedOpsAsync() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- size_t block_index = 0;
- SNAP_LOG(INFO) << "MergeOrderedOps started....";
+ SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -190,11 +190,192 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
- snapuserd_->SetMergeFailed(block_index);
return false;
}
- snapuserd_->SetMergeInProgress(block_index);
+ snapuserd_->SetMergeInProgress(ra_block_index_);
+
+ loff_t offset = 0;
+ int num_ops = snapuserd_->GetTotalBlocksToMerge();
+
+ int pending_sqe = queue_depth_;
+ int pending_ios_to_submit = 0;
+ bool flush_required = false;
+ blocks_merged_in_group_ = 0;
+
+ SNAP_LOG(DEBUG) << "Merging copy-ops of size: " << num_ops;
+ while (num_ops) {
+ uint64_t source_offset;
+
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
+
+ if (linear_blocks != 0) {
+ size_t io_size = (linear_blocks * BLOCK_SZ);
+
+ // Get an SQE entry from the ring and populate the I/O variables
+ struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
+ if (!sqe) {
+ SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
+ return false;
+ }
+
+ io_uring_prep_write(sqe, base_path_merge_fd_.get(),
+ (char*)read_ahead_buffer + offset, io_size, source_offset);
+
+ offset += io_size;
+ num_ops -= linear_blocks;
+ blocks_merged_in_group_ += linear_blocks;
+
+ pending_sqe -= 1;
+ pending_ios_to_submit += 1;
+ // These flags are important - We need to make sure that the
+ // blocks are linked and are written in the same order as
+ // populated. This is because of overlapping block writes.
+ //
+ // If there are no dependency, we can optimize this further by
+ // allowing parallel writes; but for now, just link all the SQ
+ // entries.
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
+ }
+
+ // Ring is full or no more COW ops to be merged in this batch
+ if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
+ // If this is a last set of COW ops to be merged in this batch, we need
+ // to sync the merged data. We will try to grab an SQE entry
+ // and set the FSYNC command; additionally, make sure that
+ // the fsync is done after all the I/O operations queued
+ // in the ring is completed by setting IOSQE_IO_DRAIN.
+ //
+ // If there is no space in the ring, we will flush it later
+ // by explicitly calling fsync() system call.
+ if (num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
+ if (pending_sqe != 0) {
+ struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
+ if (!sqe) {
+ // very unlikely but let's continue and not fail the
+ // merge - we will flush it later
+ SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
+ flush_required = true;
+ } else {
+ io_uring_prep_fsync(sqe, base_path_merge_fd_.get(), 0);
+ // Drain the queue before fsync
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
+ pending_sqe -= 1;
+ flush_required = false;
+ pending_ios_to_submit += 1;
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
+ }
+ } else {
+ flush_required = true;
+ }
+ }
+
+ // Submit the IO for all the COW ops in a single syscall
+ int ret = io_uring_submit(ring_.get());
+ if (ret != pending_ios_to_submit) {
+ SNAP_PLOG(ERROR)
+ << "io_uring_submit failed for read-ahead: "
+ << " io submit: " << ret << " expected: " << pending_ios_to_submit;
+ return false;
+ }
+
+ int pending_ios_to_complete = pending_ios_to_submit;
+ pending_ios_to_submit = 0;
+
+ bool status = true;
+
+ // Reap I/O completions
+ while (pending_ios_to_complete) {
+ struct io_uring_cqe* cqe;
+
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
+ ret = io_uring_wait_cqe(ring_.get(), &cqe);
+ if (ret) {
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+ status = false;
+ break;
+ }
+
+ if (cqe->res < 0) {
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed with res: " << cqe->res;
+ status = false;
+ break;
+ }
+
+ io_uring_cqe_seen(ring_.get(), cqe);
+ pending_ios_to_complete -= 1;
+ }
+
+ if (!status) {
+ return false;
+ }
+
+ pending_sqe = queue_depth_;
+ }
+
+ if (linear_blocks == 0) {
+ break;
+ }
+ }
+
+ // Verify all ops are merged
+ CHECK(num_ops == 0);
+
+ // Flush the data
+ if (flush_required && (fsync(base_path_merge_fd_.get()) < 0)) {
+ SNAP_LOG(ERROR) << " Failed to fsync merged data";
+ return false;
+ }
+
+ // Merge is done and data is on disk. Update the COW Header about
+ // the merge completion
+ if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
+ SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
+ return false;
+ }
+
+ SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
+
+ // Mark the block as merge complete
+ snapuserd_->SetMergeCompleted(ra_block_index_);
+
+ // Notify RA thread that the merge thread is ready to merge the next
+ // window
+ snapuserd_->NotifyRAForMergeReady();
+
+ // Get the next block
+ ra_block_index_ += 1;
+ }
+
+ return true;
+}
+
+bool Worker::MergeOrderedOps() {
+ void* mapped_addr = snapuserd_->GetMappedAddr();
+ void* read_ahead_buffer =
+ static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
+
+ SNAP_LOG(INFO) << "MergeOrderedOps started....";
+
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
+ if (!IsOrderedOp(*cow_op)) {
+ break;
+ }
+
+ SNAP_LOG(DEBUG) << "Waiting for merge begin...";
+ // Wait for RA thread to notify that the merge window
+ // is ready for merging.
+ if (!snapuserd_->WaitForMergeBegin()) {
+ snapuserd_->SetMergeFailed(ra_block_index_);
+ return false;
+ }
+
+ snapuserd_->SetMergeInProgress(ra_block_index_);
loff_t offset = 0;
int num_ops = snapuserd_->GetTotalBlocksToMerge();
@@ -202,7 +383,7 @@
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
if (linear_blocks == 0) {
break;
}
@@ -211,12 +392,13 @@
// Write to the base device. Data is already in the RA buffer. Note
// that XOR ops is already handled by the RA thread. We just write
// the contents out.
- int ret = pwrite(base_path_merge_fd_.get(), (char*)read_ahead_buffer + offset, io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(),
+ (char*)read_ahead_buffer + offset, io_size,
+ source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR) << "Failed to write to backing device while merging "
<< " at offset: " << source_offset << " io_size: " << io_size;
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -230,7 +412,7 @@
// Flush the data
if (fsync(base_path_merge_fd_.get()) < 0) {
SNAP_LOG(ERROR) << " Failed to fsync merged data";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -238,39 +420,87 @@
// the merge completion
if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
// Mark the block as merge complete
- snapuserd_->SetMergeCompleted(block_index);
+ snapuserd_->SetMergeCompleted(ra_block_index_);
// Notify RA thread that the merge thread is ready to merge the next
// window
snapuserd_->NotifyRAForMergeReady();
// Get the next block
- block_index += 1;
+ ra_block_index_ += 1;
}
return true;
}
-bool Worker::Merge() {
- std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
+bool Worker::AsyncMerge() {
+ if (!MergeOrderedOpsAsync()) {
+ SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
+ // Reset the iter so that we retry the merge
+ while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ blocks_merged_in_group_ -= 1;
+ }
- // Start with Copy and Xor ops
- if (!MergeOrderedOps(cowop_iter)) {
- SNAP_LOG(ERROR) << "Merge failed for ordered ops";
- snapuserd_->MergeFailed();
return false;
}
- SNAP_LOG(INFO) << "MergeOrderedOps completed...";
+ SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed";
+ return true;
+}
+
+bool Worker::SyncMerge() {
+ if (!MergeOrderedOps()) {
+ SNAP_LOG(ERROR) << "Merge failed for ordered ops";
+ return false;
+ }
+
+ SNAP_LOG(INFO) << "MergeOrderedOps completed";
+ return true;
+}
+
+bool Worker::Merge() {
+ cowop_iter_ = reader_->GetMergeOpIter();
+
+ bool retry = false;
+ bool ordered_ops_merge_status;
+
+ // Start Async Merge
+ if (merge_async_) {
+ ordered_ops_merge_status = AsyncMerge();
+ if (!ordered_ops_merge_status) {
+ FinalizeIouring();
+ retry = true;
+ merge_async_ = false;
+ }
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async merge, we
+ // will directly enter here (aka devices with 4.x kernels)
+ const bool sync_merge_required = (retry || !merge_async_);
+
+ if (sync_merge_required) {
+ ordered_ops_merge_status = SyncMerge();
+ if (!ordered_ops_merge_status) {
+ // Merge failed. Device will continue to be mounted
+ // off snapshots; merge will be retried during
+ // next reboot
+ SNAP_LOG(ERROR) << "Merge failed for ordered ops";
+ snapuserd_->MergeFailed();
+ return false;
+ }
+ }
// Replace and Zero ops
- if (!MergeReplaceZeroOps(cowop_iter)) {
+ if (!MergeReplaceZeroOps()) {
SNAP_LOG(ERROR) << "Merge failed for replace/zero ops";
snapuserd_->MergeFailed();
return false;
@@ -281,6 +511,31 @@
return true;
}
+bool Worker::InitializeIouring() {
+ if (!snapuserd_->IsIouringSupported()) {
+ return false;
+ }
+
+ ring_ = std::make_unique<struct io_uring>();
+
+ int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
+ if (ret) {
+ LOG(ERROR) << "Merge: io_uring_queue_init failed with ret: " << ret;
+ return false;
+ }
+
+ merge_async_ = true;
+
+ LOG(INFO) << "Merge: io_uring initialized with queue depth: " << queue_depth_;
+ return true;
+}
+
+void Worker::FinalizeIouring() {
+ if (merge_async_) {
+ io_uring_queue_exit(ring_.get());
+ }
+}
+
bool Worker::RunMergeThread() {
SNAP_LOG(DEBUG) << "Waiting for merge begin...";
if (!snapuserd_->WaitForMergeBegin()) {
@@ -296,14 +551,17 @@
return false;
}
+ InitializeIouring();
+
if (!Merge()) {
return false;
}
+ FinalizeIouring();
CloseFds();
reader_->CloseCowFd();
- SNAP_LOG(INFO) << "Merge finish";
+ SNAP_LOG(INFO) << "Snapshot-Merge completed";
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 9e8ccfb..fa2866f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -183,25 +183,316 @@
return true;
}
-bool ReadAhead::ReadAheadIOStart() {
- // Check if the data has to be constructed from the COW file.
- // This will be true only once during boot up after a crash
- // during merge.
- if (snapuserd_->ShouldReconstructDataFromCow()) {
- return ReconstructDataFromCow();
- }
+/*
+ * With io_uring, the data flow is slightly different.
+ *
+ * The data flow is as follows:
+ *
+ * 1: Queue the I/O requests to be read from backing source device.
+ * This is done by retrieving the SQE entry from ring and populating
+ * the SQE entry. Note that the I/O is not submitted yet.
+ *
+ * 2: Once the ring is full (aka queue_depth), we will submit all
+ * the queued I/O request with a single system call. This essentially
+ * cuts down "queue_depth" number of system calls to a single system call.
+ *
+ * 3: Once the I/O is submitted, user-space thread will now work
+ * on processing the XOR Operations. This happens in parallel when
+ * I/O requests are submitted to the kernel. This is ok because, for XOR
+ * operations, we first need to retrieve the compressed data form COW block
+ * device. Thus, we have offloaded the backing source I/O to the kernel
+ * and user-space is parallely working on fetching the data for XOR operations.
+ *
+ * 4: After the XOR operations are read from COW device, poll the completion
+ * queue for all the I/O submitted. If the I/O's were already completed,
+ * then user-space thread will just read the CQE requests from the ring
+ * without doing any system call. If none of the I/O were completed yet,
+ * user-space thread will do a system call and wait for I/O completions.
+ *
+ * Flow diagram:
+ * SQ-RING
+ * SQE1 <----------- Fetch SQE1 Entry ---------- |SQE1||SQE2|SQE3|
+ *
+ * SQE1 ------------ Populate SQE1 Entry ------> |SQE1-X||SQE2|SQE3|
+ *
+ * SQE2 <----------- Fetch SQE2 Entry ---------- |SQE1-X||SQE2|SQE3|
+ *
+ * SQE2 ------------ Populate SQE2 Entry ------> |SQE1-X||SQE2-X|SQE3|
+ *
+ * SQE3 <----------- Fetch SQE3 Entry ---------- |SQE1-X||SQE2-X|SQE3|
+ *
+ * SQE3 ------------ Populate SQE3 Entry ------> |SQE1-X||SQE2-X|SQE3-X|
+ *
+ * Submit-IO ---------------------------------> |SQE1-X||SQE2-X|SQE3-X|
+ * | |
+ * | Process I/O entries in kernel
+ * | |
+ * Retrieve XOR |
+ * data from COW |
+ * | |
+ * | |
+ * Fetch CQ completions
+ * | CQ-RING
+ * |CQE1-X||CQE2-X|CQE3-X|
+ * |
+ * CQE1 <------------Fetch CQE1 Entry |CQE1||CQE2-X|CQE3-X|
+ * CQE2 <------------Fetch CQE2 Entry |CQE1||CQE2-|CQE3-X|
+ * CQE3 <------------Fetch CQE3 Entry |CQE1||CQE2-|CQE3-|
+ * |
+ * |
+ * Continue Next set of operations in the RING
+ */
- std::vector<uint64_t> blocks;
-
+bool ReadAhead::ReadAheadAsyncIO() {
int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
loff_t buffer_offset = 0;
- int total_blocks_merged = 0;
+ total_blocks_merged_ = 0;
overlap_ = false;
dest_blocks_.clear();
source_blocks_.clear();
+ blocks_.clear();
std::vector<const CowOperation*> xor_op_vec;
- auto ra_temp_buffer = std::make_unique<uint8_t[]>(snapuserd_->GetBufferDataSize());
+ int pending_sqe = queue_depth_;
+ int pending_ios_to_submit = 0;
+
+ size_t xor_op_index = 0;
+ size_t block_index = 0;
+
+ loff_t offset = 0;
+
+ bufsink_.ResetBufferOffset();
+
+ // Number of ops to be merged in this window. This is a fixed size
+ // except for the last window wherein the number of ops can be less
+ // than the size of the RA window.
+ while (num_ops) {
+ uint64_t source_offset;
+ struct io_uring_sqe* sqe;
+
+ int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);
+
+ if (linear_blocks != 0) {
+ size_t io_size = (linear_blocks * BLOCK_SZ);
+
+ // Get an SQE entry from the ring and populate the I/O variables
+ sqe = io_uring_get_sqe(ring_.get());
+ if (!sqe) {
+ SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during read-ahead";
+ return false;
+ }
+
+ io_uring_prep_read(sqe, backing_store_fd_.get(),
+ (char*)ra_temp_buffer_.get() + buffer_offset, io_size,
+ source_offset);
+
+ buffer_offset += io_size;
+ num_ops -= linear_blocks;
+ total_blocks_merged_ += linear_blocks;
+
+ pending_sqe -= 1;
+ pending_ios_to_submit += 1;
+ sqe->flags |= IOSQE_ASYNC;
+ }
+
+ // pending_sqe == 0 : Ring is full
+ //
+ // num_ops == 0 : All the COW ops in this batch are processed - Submit
+ // pending I/O requests in the ring
+ //
+ // linear_blocks == 0 : All the COW ops processing is done. Submit
+ // pending I/O requests in the ring
+ if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
+ // Submit the IO for all the COW ops in a single syscall
+ int ret = io_uring_submit(ring_.get());
+ if (ret != pending_ios_to_submit) {
+ SNAP_PLOG(ERROR) << "io_uring_submit failed for read-ahead: "
+ << " io submit: " << ret << " expected: " << pending_ios_to_submit;
+ return false;
+ }
+
+ int pending_ios_to_complete = pending_ios_to_submit;
+ pending_ios_to_submit = 0;
+
+ bool xor_processing_required = (xor_op_vec.size() > 0);
+
+ // Read XOR data from COW file in parallel when I/O's are in-flight
+ if (xor_processing_required && !ReadXorData(block_index, xor_op_index, xor_op_vec)) {
+ SNAP_LOG(ERROR) << "ReadXorData failed";
+ return false;
+ }
+
+ // Fetch I/O completions
+ if (!ReapIoCompletions(pending_ios_to_complete)) {
+ SNAP_LOG(ERROR) << "ReapIoCompletions failed";
+ return false;
+ }
+
+ // Retrieve XOR'ed data
+ if (xor_processing_required) {
+ ProcessXorData(block_index, xor_op_index, xor_op_vec, ra_temp_buffer_.get(),
+ offset);
+ }
+
+ // All the I/O in the ring is processed.
+ pending_sqe = queue_depth_;
+ }
+
+ if (linear_blocks == 0) {
+ break;
+ }
+ }
+
+ // Done with merging ordered ops
+ if (RAIterDone() && total_blocks_merged_ == 0) {
+ return true;
+ }
+
+ CHECK(blocks_.size() == total_blocks_merged_);
+
+ UpdateScratchMetadata();
+
+ return true;
+}
+
+void ReadAhead::UpdateScratchMetadata() {
+ loff_t metadata_offset = 0;
+
+ struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
+ (char*)ra_temp_meta_buffer_.get() + metadata_offset);
+
+ bm->new_block = 0;
+ bm->file_offset = 0;
+
+ loff_t file_offset = snapuserd_->GetBufferDataOffset();
+
+ for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
+ uint64_t new_block = blocks_[block_index];
+ // Track the metadata blocks which are stored in scratch space
+ bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
+ metadata_offset);
+
+ bm->new_block = new_block;
+ bm->file_offset = file_offset;
+
+ metadata_offset += sizeof(struct ScratchMetadata);
+ file_offset += BLOCK_SZ;
+ }
+
+ // This is important - explicitly set the contents to zero. This is used
+ // when re-constructing the data after crash. This indicates end of
+ // reading metadata contents when re-constructing the data
+ bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
+ metadata_offset);
+ bm->new_block = 0;
+ bm->file_offset = 0;
+}
+
+bool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) {
+ bool status = true;
+
+ // Reap I/O completions
+ while (pending_ios_to_complete) {
+ struct io_uring_cqe* cqe;
+
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
+ int ret = io_uring_wait_cqe(ring_.get(), &cqe);
+ if (ret) {
+ SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
+ status = false;
+ break;
+ }
+
+ if (cqe->res < 0) {
+ SNAP_LOG(ERROR) << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
+ status = false;
+ break;
+ }
+
+ io_uring_cqe_seen(ring_.get(), cqe);
+ pending_ios_to_complete -= 1;
+ }
+
+ return status;
+}
+
+void ReadAhead::ProcessXorData(size_t& block_xor_index, size_t& xor_index,
+ std::vector<const CowOperation*>& xor_op_vec, void* buffer,
+ loff_t& buffer_offset) {
+ loff_t xor_buf_offset = 0;
+
+ while (block_xor_index < blocks_.size()) {
+ void* bufptr = static_cast<void*>((char*)buffer + buffer_offset);
+ uint64_t new_block = blocks_[block_xor_index];
+
+ if (xor_index < xor_op_vec.size()) {
+ const CowOperation* xor_op = xor_op_vec[xor_index];
+
+ // Check if this block is an XOR op
+ if (xor_op->new_block == new_block) {
+ // Pointer to the data read from base device
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
+ // Get the xor'ed data read from COW device
+ uint8_t* xor_data = reinterpret_cast<uint8_t*>((char*)bufsink_.GetPayloadBufPtr() +
+ xor_buf_offset);
+
+ for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
+ buffer[byte_offset] ^= xor_data[byte_offset];
+ }
+
+ // Move to next XOR op
+ xor_index += 1;
+ xor_buf_offset += BLOCK_SZ;
+ }
+ }
+
+ buffer_offset += BLOCK_SZ;
+ block_xor_index += 1;
+ }
+
+ bufsink_.ResetBufferOffset();
+}
+
+bool ReadAhead::ReadXorData(size_t block_index, size_t xor_op_index,
+ std::vector<const CowOperation*>& xor_op_vec) {
+ // Process the XOR ops in parallel - We will be reading data
+ // from COW file for XOR ops processing.
+ while (block_index < blocks_.size()) {
+ uint64_t new_block = blocks_[block_index];
+
+ if (xor_op_index < xor_op_vec.size()) {
+ const CowOperation* xor_op = xor_op_vec[xor_op_index];
+ if (xor_op->new_block == new_block) {
+ if (!reader_->ReadData(*xor_op, &bufsink_)) {
+ SNAP_LOG(ERROR)
+ << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
+ return false;
+ }
+
+ xor_op_index += 1;
+ bufsink_.UpdateBufferOffset(BLOCK_SZ);
+ }
+ }
+ block_index += 1;
+ }
+ return true;
+}
+
+bool ReadAhead::ReadAheadSyncIO() {
+ int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
+ loff_t buffer_offset = 0;
+ total_blocks_merged_ = 0;
+ overlap_ = false;
+ dest_blocks_.clear();
+ source_blocks_.clear();
+ blocks_.clear();
+ std::vector<const CowOperation*> xor_op_vec;
+
+ bufsink_.ResetBufferOffset();
// Number of ops to be merged in this window. This is a fixed size
// except for the last window wherein the number of ops can be less
@@ -209,7 +500,7 @@
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks, xor_op_vec);
+ int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);
if (linear_blocks == 0) {
// No more blocks to read
SNAP_LOG(DEBUG) << " Read-ahead completed....";
@@ -220,7 +511,7 @@
// Read from the base device consecutive set of blocks in one shot
if (!android::base::ReadFullyAtOffset(backing_store_fd_,
- (char*)ra_temp_buffer.get() + buffer_offset, io_size,
+ (char*)ra_temp_buffer_.get() + buffer_offset, io_size,
source_offset)) {
SNAP_PLOG(ERROR) << "Ordered-op failed. Read from backing store: "
<< backing_store_device_ << "at block :" << source_offset / BLOCK_SZ
@@ -233,21 +524,19 @@
}
buffer_offset += io_size;
- total_blocks_merged += linear_blocks;
+ total_blocks_merged_ += linear_blocks;
num_ops -= linear_blocks;
}
// Done with merging ordered ops
- if (RAIterDone() && total_blocks_merged == 0) {
+ if (RAIterDone() && total_blocks_merged_ == 0) {
return true;
}
loff_t metadata_offset = 0;
- auto ra_temp_meta_buffer = std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
-
struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
- (char*)ra_temp_meta_buffer.get() + metadata_offset);
+ (char*)ra_temp_meta_buffer_.get() + metadata_offset);
bm->new_block = 0;
bm->file_offset = 0;
@@ -255,12 +544,15 @@
loff_t file_offset = snapuserd_->GetBufferDataOffset();
loff_t offset = 0;
- CHECK(blocks.size() == total_blocks_merged);
+ CHECK(blocks_.size() == total_blocks_merged_);
size_t xor_index = 0;
- for (size_t block_index = 0; block_index < blocks.size(); block_index++) {
- void* bufptr = static_cast<void*>((char*)ra_temp_buffer.get() + offset);
- uint64_t new_block = blocks[block_index];
+ BufferSink bufsink;
+ bufsink.Initialize(BLOCK_SZ * 2);
+
+ for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
+ void* bufptr = static_cast<void*>((char*)ra_temp_buffer_.get() + offset);
+ uint64_t new_block = blocks_[block_index];
if (xor_index < xor_op_vec.size()) {
const CowOperation* xor_op = xor_op_vec[xor_index];
@@ -268,17 +560,16 @@
// Check if this block is an XOR op
if (xor_op->new_block == new_block) {
// Read the xor'ed data from COW
- if (!reader_->ReadData(*xor_op, &bufsink_)) {
+ if (!reader_->ReadData(*xor_op, &bufsink)) {
SNAP_LOG(ERROR)
<< " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
snapuserd_->ReadAheadIOFailed();
return false;
}
-
// Pointer to the data read from base device
uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
// Get the xor'ed data read from COW device
- uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink_.GetPayloadBufPtr());
+ uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());
// Retrieve the original data
for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
@@ -292,7 +583,7 @@
offset += BLOCK_SZ;
// Track the metadata blocks which are stored in scratch space
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer.get() +
+ bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
metadata_offset);
bm->new_block = new_block;
@@ -308,11 +599,54 @@
// This is important - explicitly set the contents to zero. This is used
// when re-constructing the data after crash. This indicates end of
// reading metadata contents when re-constructing the data
- bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer.get() +
+ bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
metadata_offset);
bm->new_block = 0;
bm->file_offset = 0;
+ return true;
+}
+
+bool ReadAhead::ReadAheadIOStart() {
+ // Check if the data has to be constructed from the COW file.
+ // This will be true only once during boot up after a crash
+ // during merge.
+ if (snapuserd_->ShouldReconstructDataFromCow()) {
+ return ReconstructDataFromCow();
+ }
+
+ bool retry = false;
+ bool ra_status;
+
+ // Start Async read-ahead
+ if (read_ahead_async_) {
+ ra_status = ReadAheadAsyncIO();
+ if (!ra_status) {
+ SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - Falling back synchronous I/O";
+ FinalizeIouring();
+ RAResetIter(total_blocks_merged_);
+ retry = true;
+ read_ahead_async_ = false;
+ }
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async operations, we
+ // will directly enter here (aka devices with 4.x kernels)
+
+ const bool ra_sync_required = (retry || !read_ahead_async_);
+
+ if (ra_sync_required) {
+ ra_status = ReadAheadSyncIO();
+ if (!ra_status) {
+ SNAP_LOG(ERROR) << "ReadAheadSyncIO failed";
+ return false;
+ }
+ }
+
+ SNAP_LOG(DEBUG) << "Read-ahead: total_ra_blocks_merged: " << total_ra_blocks_completed_;
+
// Wait for the merge to finish for the previous RA window. We shouldn't
// be touching the scratch space until merge is complete of previous RA
// window. If there is a crash during this time frame, merge should resume
@@ -322,22 +656,23 @@
}
// Copy the data to scratch space
- memcpy(metadata_buffer_, ra_temp_meta_buffer.get(), snapuserd_->GetBufferMetadataSize());
- memcpy(read_ahead_buffer_, ra_temp_buffer.get(), total_blocks_merged * BLOCK_SZ);
+ memcpy(metadata_buffer_, ra_temp_meta_buffer_.get(), snapuserd_->GetBufferMetadataSize());
+ memcpy(read_ahead_buffer_, ra_temp_buffer_.get(), total_blocks_merged_ * BLOCK_SZ);
- offset = 0;
+ loff_t offset = 0;
std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
read_ahead_buffer_map.clear();
- for (size_t block_index = 0; block_index < blocks.size(); block_index++) {
+ for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + offset);
- uint64_t new_block = blocks[block_index];
+ uint64_t new_block = blocks_[block_index];
read_ahead_buffer_map[new_block] = bufptr;
offset += BLOCK_SZ;
}
- snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged);
+ total_ra_blocks_completed_ += total_blocks_merged_;
+ snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
// Flush the data only if we have a overlapping blocks in the region
// Notify the Merge thread to resume merging this window
@@ -350,6 +685,33 @@
return true;
}
+bool ReadAhead::InitializeIouring() {
+ if (!snapuserd_->IsIouringSupported()) {
+ return false;
+ }
+
+ ring_ = std::make_unique<struct io_uring>();
+
+ int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
+ if (ret) {
+ SNAP_LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
+ return false;
+ }
+
+ // For xor ops processing
+ bufsink_.Initialize(PAYLOAD_BUFFER_SZ * 2);
+ read_ahead_async_ = true;
+
+ SNAP_LOG(INFO) << "Read-ahead: io_uring initialized with queue depth: " << queue_depth_;
+ return true;
+}
+
+void ReadAhead::FinalizeIouring() {
+ if (read_ahead_async_) {
+ io_uring_queue_exit(ring_.get());
+ }
+}
+
bool ReadAhead::RunThread() {
if (!InitializeFds()) {
return false;
@@ -363,14 +725,18 @@
InitializeRAIter();
+ InitializeIouring();
+
while (!RAIterDone()) {
if (!ReadAheadIOStart()) {
break;
}
}
+ FinalizeIouring();
CloseFds();
reader_->CloseCowFd();
+
SNAP_LOG(INFO) << " ReadAhead thread terminating....";
return true;
}
@@ -423,6 +789,13 @@
cowop_iter_->Next();
}
+void ReadAhead::RAResetIter(uint64_t num_blocks) {
+ while (num_blocks && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ num_blocks -= 1;
+ }
+}
+
const CowOperation* ReadAhead::GetRAOpIter() {
const CowOperation* cow_op = &cowop_iter_->Get();
return cow_op;
@@ -434,8 +807,9 @@
metadata_buffer_ =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferMetadataOffset());
read_ahead_buffer_ = static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- // For xor ops
- bufsink_.Initialize(PAYLOAD_BUFFER_SZ);
+
+ ra_temp_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferDataSize());
+ ra_temp_meta_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
}
} // 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 a79e3e1..82b2b25 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -293,7 +293,6 @@
void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
- handler->snapuserd()->SetSocketPresent(is_socket_present_);
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -471,6 +470,9 @@
return nullptr;
}
+ snapuserd->SetSocketPresent(is_socket_present_);
+ snapuserd->SetIouringEnabled(io_uring_enabled_);
+
if (!snapuserd->InitializeWorkers()) {
LOG(ERROR) << "Failed to initialize workers";
return nullptr;
@@ -599,8 +601,13 @@
return false;
}
- // We must re-initialize property service access, since we launched before
- // second-stage init.
+ // This initialization of system property is important. When daemon is
+ // launched post selinux transition (before init second stage),
+ // bionic libc initializes system property as part of __libc_init_common();
+ // however that initialization fails silently given that fact that we don't
+ // have /dev/__properties__ setup which is created at init second stage.
+ //
+ // At this point, we have the handlers setup and is safe to setup property.
__system_properties_init();
if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
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 c645456..34e7941 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -84,6 +84,8 @@
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
int num_partitions_merge_complete_ = 0;
+ bool is_server_running_ = false;
+ bool io_uring_enabled_ = false;
std::mutex lock_;
@@ -136,6 +138,10 @@
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
+ void SetServerRunning() { is_server_running_ = true; }
+ bool IsServerRunning() { return is_server_running_; }
+ void SetIouringEnabled() { io_uring_enabled_ = true; }
+ bool IsIouringEnabled() { return io_uring_enabled_; }
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 1c3e04b..d670f1e 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+
#include <fcntl.h>
#include <linux/fs.h>
#include <linux/memfd.h>
@@ -27,6 +30,7 @@
#include <string_view>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <fs_mgr/file_wait.h>
#include <gtest/gtest.h>
@@ -38,6 +42,8 @@
#include "snapuserd_core.h"
+DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
+
namespace android {
namespace snapshot {
@@ -857,5 +863,23 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+
+ gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+ android::base::SetProperty("ctl.stop", "snapuserd");
+
+ if (FLAGS_force_config == "iouring_disabled") {
+ if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
+ return testing::AssertionFailure()
+ << "Failed to disable property: snapuserd.test.io_uring.disabled";
+ }
+ }
+
+ int ret = RUN_ALL_TESTS();
+
+ if (FLAGS_force_config == "iouring_disabled") {
+ android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
+ }
+
+ return ret;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 6dec1e2..d4e1d7c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -531,6 +531,13 @@
{
std::unique_lock<std::mutex> lock(blk_state->m_lock);
+ // We may have fallback from Async-merge to synchronous merging
+ // on the existing block. There is no need to reset as the
+ // merge is already in progress.
+ if (blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS) {
+ return;
+ }
+
CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_PENDING);
// First set the state to RA_READY so that in-flight I/O will drain
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index e3e3af8..71fe124 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -267,8 +267,8 @@
return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
<< kUserDataDevice;
}
- uint64_t next_consume =
- std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+ uint64_t next_consume = std::min(available_space_ - max_free_space,
+ (uint64_t)std::numeric_limits<off_t>::max());
off_t allocated = 0;
while (next_consume > 0 && free_space_ > max_free_space) {
int status = fallocate(big_file_->fd, 0, allocated, next_consume);
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 89d6145..f01bec9 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -192,6 +192,10 @@
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
+bool IsIouringEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
std::string GetOtherPartitionName(const std::string& name) {
auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index a032b68..0ef3234 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -135,6 +135,8 @@
bool IsDmSnapshotTestingEnabled();
+bool IsIouringEnabled();
+
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
new file mode 100644
index 0000000..afc2d81
--- /dev/null
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -0,0 +1,23 @@
+//
+// 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 <android-base/properties.h>
+#include <gtest/gtest.h>
+
+TEST(VAB, Enabled) {
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 335da9e..d82d566 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -88,3 +88,32 @@
test_suites: ["general-tests"],
}
+
+cc_test {
+ name: "vts_fs_test",
+ test_suites: [
+ "vts",
+ "device-tests",
+ ],
+ test_options: {
+ min_shipping_api_level: 29,
+ },
+ require_root: true,
+ auto_gen_config: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "vts_fs_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ "libfstab",
+ "libgmock",
+ "libgtest",
+ ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index d631d7a..6c881c0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -192,7 +192,6 @@
lhs.nonremovable == rhs.nonremovable &&
lhs.vold_managed == rhs.vold_managed &&
lhs.recovery_only == rhs.recovery_only &&
- lhs.verify == rhs.verify &&
lhs.no_emulated_sd == rhs.no_emulated_sd &&
lhs.no_trim == rhs.no_trim &&
lhs.file_encryption == rhs.file_encryption &&
@@ -200,7 +199,6 @@
lhs.slot_select == rhs.slot_select &&
lhs.late_mount == rhs.late_mount &&
lhs.no_fail == rhs.no_fail &&
- lhs.verify_at_boot == rhs.verify_at_boot &&
lhs.quota == rhs.quota &&
lhs.avb == rhs.avb &&
lhs.logical == rhs.logical &&
@@ -409,7 +407,7 @@
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source none0 swap defaults wait,check,nonremovable,recoveryonly,verifyatboot,verify
+source none0 swap defaults wait,check,nonremovable,recoveryonly
source none1 swap defaults avb,noemulatedsd,notrim,formattable,nofail
source none2 swap defaults first_stage_mount,latemount,quota,logical
source none3 swap defaults checkpoint=block
@@ -430,8 +428,6 @@
flags.check = true;
flags.nonremovable = true;
flags.recovery_only = true;
- flags.verify_at_boot = true;
- flags.verify = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
@@ -947,7 +943,7 @@
ASSERT_LE(1U, fstab.size());
auto entry = fstab.begin();
- EXPECT_EQ("adiantum", entry->metadata_encryption);
+ EXPECT_EQ("adiantum", entry->metadata_encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
@@ -964,8 +960,8 @@
ASSERT_LE(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("aes-256-xts:wrappedkey_v0", entry->metadata_encryption_options);
+ auto parts = android::base::Split(entry->metadata_encryption_options, ":");
EXPECT_EQ(2U, parts.size());
EXPECT_EQ("aes-256-xts", parts[0]);
EXPECT_EQ("wrappedkey_v0", parts[1]);
@@ -1108,3 +1104,76 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ(0, entry->readahead_size_kb);
}
+
+TEST(fs_mgr, TransformFstabForDsu) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+system /system ext4 ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
new file mode 100644
index 0000000..b5fac53
--- /dev/null
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -0,0 +1,111 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+static int GetVsrLevel() {
+ return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
+TEST(fs, ErofsSupported) {
+ // S and higher for this test.
+ if (GetVsrLevel() < __ANDROID_API_S__) {
+ GTEST_SKIP();
+ }
+
+ struct utsname uts;
+ ASSERT_EQ(uname(&uts), 0);
+
+ unsigned int major, minor;
+ ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+
+ // EROFS support only required in 5.10+
+ if (major < 5 || (major == 5 && minor < 10)) {
+ GTEST_SKIP();
+ }
+
+ std::string fs;
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
+ EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
+}
+
+TEST(fs, PartitionTypes) {
+ android::fs_mgr::Fstab fstab;
+ ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+ auto& dm = android::dm::DeviceMapper::Instance();
+
+ std::string super_bdev, userdata_bdev;
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
+
+ int vsr_level = GetVsrLevel();
+
+ for (const auto& entry : fstab) {
+ std::string parent_bdev = entry.blk_device;
+ while (true) {
+ auto basename = android::base::Basename(parent_bdev);
+ if (!android::base::StartsWith(basename, "dm-")) {
+ break;
+ }
+
+ auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);
+ if (!parent || *parent == parent_bdev) {
+ break;
+ }
+ parent_bdev = *parent;
+ }
+
+ if (parent_bdev == userdata_bdev ||
+ android::base::StartsWith(parent_bdev, "/dev/block/loop")) {
+ if (entry.flags & MS_RDONLY) {
+ // APEXes should not be F2FS.
+ EXPECT_NE(entry.fs_type, "f2fs");
+ }
+ continue;
+ }
+
+ if (vsr_level < __ANDROID_API_T__) {
+ continue;
+ }
+ if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
+ // Only check for dynamic partitions at this VSR level.
+ continue;
+ }
+
+ if (entry.flags & MS_RDONLY) {
+ EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+ } else {
+ EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
+ }
+ }
+}
+
+TEST(fs, NoDtFstab) {
+ if (GetVsrLevel() < __ANDROID_API_Q__) {
+ GTEST_SKIP();
+ }
+
+ android::fs_mgr::Fstab fstab;
+ EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 24777c8..f180006 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -22,9 +22,15 @@
"libutils",
"libbase",
- // Need latest HealthInfo definition from headers of this shared
- // library. Clients don't need to link to this.
+ // Need HealthInfo definition from headers of these shared
+ // libraries. Clients don't need to link to these.
"android.hardware.health@2.1",
+ "android.hardware.health-V1-ndk",
+ ],
+ whole_static_libs: [
+ // Need to translate HIDL to AIDL to support legacy APIs in
+ // BatteryMonitor.
+ "android.hardware.health-translate-ndk",
],
header_libs: ["libhealthd_headers"],
export_header_lib_headers: ["libhealthd_headers"],
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 377acb7..a7571a2 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -31,10 +31,12 @@
#include <memory>
#include <optional>
+#include <aidl/android/hardware/health/HealthInfo.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/hardware/health/2.1/types.h>
+#include <android/hardware/health/translate-ndk.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
@@ -52,10 +54,54 @@
using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-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;
+using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryStatus;
+using aidl::android::hardware::health::HealthInfo;
+
+namespace {
+
+// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
+// Skips storageInfo and diskStats.
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V1_0::HealthInfo* out) {
+ out->chargerAcOnline = in.chargerAcOnline;
+ out->chargerUsbOnline = in.chargerUsbOnline;
+ out->chargerWirelessOnline = in.chargerWirelessOnline;
+ out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
+ out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
+ out->batteryStatus =
+ static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
+ out->batteryHealth =
+ static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
+ out->batteryPresent = in.batteryPresent;
+ out->batteryLevel = in.batteryLevel;
+ out->batteryVoltage = in.batteryVoltageMillivolts;
+ out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
+ out->batteryCurrent = in.batteryCurrentMicroamps;
+ out->batteryCycleCount = in.batteryCycleCount;
+ out->batteryFullCharge = in.batteryFullChargeUah;
+ out->batteryChargeCounter = in.batteryChargeCounterUah;
+ out->batteryTechnology = in.batteryTechnology;
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_0::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
+ // Skip storageInfo and diskStats
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_1::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
+ in.batteryCapacityLevel);
+ out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+ out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+}
+
+} // namespace
namespace android {
@@ -74,17 +120,14 @@
return std::nullopt;
}
-static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
- *health_info_2_1 = HealthInfo_2_1{};
-
- // HIDL enum values are zero initialized, so they need to be initialized
- // properly.
- health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED;
- 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;
+static void initHealthInfo(HealthInfo* health_info) {
+ *health_info = {
+ .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
+ .batteryChargeTimeToFullNowSeconds =
+ (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
+ .batteryStatus = BatteryStatus::UNKNOWN,
+ .batteryHealth = BatteryHealth::UNKNOWN,
+ };
}
BatteryMonitor::BatteryMonitor()
@@ -92,21 +135,31 @@
mBatteryDevicePresent(false),
mBatteryFixedCapacity(0),
mBatteryFixedTemperature(0),
- mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
+ mHealthInfo(std::make_unique<HealthInfo>()) {
initHealthInfo(mHealthInfo.get());
}
BatteryMonitor::~BatteryMonitor() {}
-const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
- return getHealthInfo_2_0().legacy;
+HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
+ HealthInfo_1_0 health_info_1_0;
+ translateToHidl(*mHealthInfo, &health_info_1_0);
+ return health_info_1_0;
}
-const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
- return getHealthInfo_2_1().legacy;
+HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
+ HealthInfo_2_0 health_info_2_0;
+ translateToHidl(*mHealthInfo, &health_info_2_0);
+ return health_info_2_0;
}
-const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
+HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
+ HealthInfo_2_1 health_info_2_1;
+ translateToHidl(*mHealthInfo, &health_info_2_1);
+ return health_info_2_1;
+}
+
+const HealthInfo& BatteryMonitor::getHealthInfo() const {
return *mHealthInfo;
}
@@ -174,45 +227,48 @@
return *ret;
}
-int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
+static int readFromFile(const String8& path, std::string* buf) {
+ buf->clear();
if (android::base::ReadFileToString(path.c_str(), buf)) {
*buf = android::base::Trim(*buf);
}
return buf->length();
}
-BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
+static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
static SysfsStringEnumMap<int> supplyTypeMap[] = {
- {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
- {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
- {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
- {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
- {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+ {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+ {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
{NULL, 0},
};
std::string buf;
- if (readFromFile(path, &buf) <= 0)
- return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ if (readFromFile(path, &buf) <= 0) {
+ return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
- *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
}
-bool BatteryMonitor::getBooleanField(const String8& path) {
+static bool getBooleanField(const String8& path) {
std::string buf;
bool value = false;
@@ -223,7 +279,7 @@
return value;
}
-int BatteryMonitor::getIntField(const String8& path) {
+static int getIntField(const String8& path) {
std::string buf;
int value = 0;
@@ -233,7 +289,7 @@
return value;
}
-bool BatteryMonitor::isScopedPowerSupply(const char* name) {
+static bool isScopedPowerSupply(const char* name) {
constexpr char kScopeDevice[] = "Device";
String8 path;
@@ -245,32 +301,31 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
-
if (!mHealthdConfig->batteryPresentPath.isEmpty())
- props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+ mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
- props.batteryPresent = mBatteryDevicePresent;
+ mHealthInfo->batteryPresent = mBatteryDevicePresent;
- props.batteryLevel = mBatteryFixedCapacity ?
- mBatteryFixedCapacity :
- getIntField(mHealthdConfig->batteryCapacityPath);
- props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+ mHealthInfo->batteryLevel = mBatteryFixedCapacity
+ ? mBatteryFixedCapacity
+ : getIntField(mHealthdConfig->batteryCapacityPath);
+ mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
- props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath);
+ mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
if (!mHealthdConfig->batteryFullChargePath.isEmpty())
- props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
+ mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
- props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+ mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
- props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ mHealthInfo->batteryChargeCounterUah =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
- mHealthInfo->legacy.batteryCurrentAverage =
+ mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
@@ -281,9 +336,9 @@
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
- props.batteryTemperature = mBatteryFixedTemperature ?
- mBatteryFixedTemperature :
- getIntField(mHealthdConfig->batteryTemperaturePath);
+ mHealthInfo->batteryTemperatureTenthsCelsius =
+ mBatteryFixedTemperature ? mBatteryFixedTemperature
+ : getIntField(mHealthdConfig->batteryTemperaturePath);
std::string buf;
@@ -291,13 +346,13 @@
mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
- props.batteryStatus = getBatteryStatus(buf.c_str());
+ mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
- props.batteryHealth = getBatteryHealth(buf.c_str());
+ mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- props.batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = String8(buf.c_str());
double MaxPower = 0;
@@ -311,17 +366,26 @@
mChargerNames[i].string());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
- props.chargerAcOnline = true;
+ mHealthInfo->chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB:
- props.chargerUsbOnline = true;
+ mHealthInfo->chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
- props.chargerWirelessOnline = true;
+ mHealthInfo->chargerWirelessOnline = true;
+ break;
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+ mHealthInfo->chargerDockOnline = true;
break;
default:
- KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
- mChargerNames[i].string());
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
+ mChargerNames[i].string());
+ if (access(path.string(), R_OK) == 0)
+ mHealthInfo->chargerDockOnline = true;
+ else
+ KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+ mChargerNames[i].string());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
@@ -340,38 +404,34 @@
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
if (MaxPower < power) {
- props.maxChargingCurrent = ChargingCurrent;
- props.maxChargingVoltage = ChargingVoltage;
+ mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
+ mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
MaxPower = power;
}
}
}
}
-void BatteryMonitor::logValues(void) {
- logValues(*mHealthInfo, *mHealthdConfig);
-}
-
-void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
- const struct healthd_config& healthd_config) {
+static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
char dmesgline[256];
size_t len;
- const HealthInfo_1_0& props = health_info.legacy.legacy;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
- props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
- abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
- props.batteryHealth, props.batteryStatus);
+ props.batteryLevel, props.batteryVoltageMillivolts,
+ props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
+ abs(props.batteryTemperatureTenthsCelsius / 10),
+ abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
+ props.batteryStatus);
len = strlen(dmesgline);
if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
- props.batteryCurrent);
+ props.batteryCurrentMicroamps);
}
if (!healthd_config.batteryFullChargePath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
- props.batteryFullCharge);
+ props.batteryFullChargeUah);
}
if (!healthd_config.batteryCycleCountPath.isEmpty()) {
@@ -382,17 +442,28 @@
len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
}
- snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
+ snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
- props.chargerWirelessOnline ? "w" : "");
+ props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}
+void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
+ const struct healthd_config& healthd_config) {
+ HealthInfo aidl_health_info;
+ (void)android::h2a::translate(health_info, &aidl_health_info);
+ doLogValues(aidl_health_info, healthd_config);
+}
+
+void BatteryMonitor::logValues(void) {
+ doLogValues(*mHealthInfo, *mHealthdConfig);
+}
+
bool BatteryMonitor::isChargerOnline() {
- const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
- return props.chargerAcOnline | props.chargerUsbOnline |
- props.chargerWirelessOnline;
+ const HealthInfo& props = *mHealthInfo;
+ return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
+ props.chargerDockOnline;
}
int BatteryMonitor::getChargeStatus() {
@@ -475,19 +546,19 @@
void BatteryMonitor::dumpState(int fd) {
int v;
char vs[128];
- const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+ const HealthInfo& props = *mHealthInfo;
- snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
- props.chargerAcOnline, props.chargerUsbOnline,
- props.chargerWirelessOnline, props.maxChargingCurrent,
- props.maxChargingVoltage);
+ snprintf(vs, sizeof(vs),
+ "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+ props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
+ props.chargerDockOnline, props.maxChargingCurrentMicroamps,
+ props.maxChargingVoltageMicrovolts);
write(fd, vs, strlen(vs));
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
props.batteryStatus, props.batteryHealth, props.batteryPresent);
write(fd, vs, strlen(vs));
- snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
- props.batteryLevel, props.batteryVoltage,
- props.batteryTemperature);
+ snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
+ props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
@@ -509,7 +580,7 @@
}
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
- snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
+ snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
@@ -519,7 +590,7 @@
}
if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
- snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
+ snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
}
@@ -537,13 +608,13 @@
while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
- std::vector<String8>::iterator itIgnoreName;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
- itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
- hc->ignorePowerSupplyNames.end(), String8(name));
+ std::vector<String8>::iterator itIgnoreName =
+ find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
+ String8(name));
if (itIgnoreName != hc->ignorePowerSupplyNames.end())
continue;
@@ -554,6 +625,7 @@
case ANDROID_POWER_SUPPLY_TYPE_AC:
case ANDROID_POWER_SUPPLY_TYPE_USB:
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+ case ANDROID_POWER_SUPPLY_TYPE_DOCK:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
@@ -691,6 +763,17 @@
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
break;
}
+
+ // Look for "is_dock" file
+ path.clear();
+ path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0) {
+ path.clear();
+ path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path.string(), R_OK) == 0)
+ mChargerNames.add(String8(name));
+
+ }
}
}
diff --git a/healthd/OWNERS b/healthd/OWNERS
index d3f8758..e64c33d 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1,2 +1 @@
elsk@google.com
-hridya@google.com
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
index 5893d10..17e363d 100644
--- a/healthd/TEST_MAPPING
+++ b/healthd/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libhealthd_charger_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libhealthd_charger_test"
+ }
]
}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 4484fa6..3e73fcd 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -94,9 +94,18 @@
gr_flip();
}
-void HealthdDraw::blank_screen(bool blank) {
+void HealthdDraw::blank_screen(bool blank, int drm) {
if (!graphics_available) return;
- gr_fb_blank(blank);
+ gr_fb_blank(blank, drm);
+}
+
+/* support screen rotation for foldable phone */
+void HealthdDraw::rotate_screen(int drm) {
+ if (!graphics_available) return;
+ if (drm == 0)
+ gr_rotate(GRRotation::RIGHT /* landscape mode */);
+ else
+ gr_rotate(GRRotation::NONE /* Portrait mode */);
}
void HealthdDraw::clear_screen(void) {
@@ -139,6 +148,8 @@
void HealthdDraw::determine_xy(const animation::text_field& field,
const int length, int* x, int* y) {
*x = field.pos_x;
+ screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
+ screen_height_ = gr_fb_height();
int str_len_px = length * field.font->char_width;
if (field.pos_x == CENTER_VAL) {
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 0b48ce8..3d4abbd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -31,8 +31,12 @@
// Redraws screen.
void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
+ // According to the index of Direct Rendering Manager,
// Blanks screen if true, unblanks if false.
- virtual void blank_screen(bool blank);
+ virtual void blank_screen(bool blank, int drm);
+
+ // Rotate screen.
+ virtual void rotate_screen(int drm);
static std::unique_ptr<HealthdDraw> Create(animation *anim);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 0f9779c..9fe85d4 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -327,7 +327,7 @@
#if !defined(__ANDROID_VNDK__)
if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
- healthd_draw_->blank_screen(true);
+ healthd_draw_->blank_screen(true, static_cast<int>(drm_));
screen_blanked_ = true;
}
#endif
@@ -337,7 +337,7 @@
if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
reset_animation(&batt_anim_);
next_screen_transition_ = -1;
- healthd_draw_->blank_screen(true);
+ healthd_draw_->blank_screen(true, static_cast<int>(drm_));
screen_blanked_ = true;
LOGV("[%" PRId64 "] animation done\n", now);
if (configuration_->ChargerIsOnline()) {
@@ -348,8 +348,17 @@
disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
+ /* turn off all screen */
+ if (screen_switch_ == SCREEN_SWITCH_ENABLE) {
+ healthd_draw_->blank_screen(true, 0 /* drm */);
+ healthd_draw_->blank_screen(true, 1 /* drm */);
+ healthd_draw_->rotate_screen(static_cast<int>(drm_));
+ screen_blanked_ = true;
+ screen_switch_ = SCREEN_SWITCH_DISABLE;
+ }
+
if (screen_blanked_) {
- healthd_draw_->blank_screen(false);
+ healthd_draw_->blank_screen(false, static_cast<int>(drm_));
screen_blanked_ = false;
}
@@ -452,7 +461,26 @@
return 0;
}
+int Charger::SetSwCallback(int code, int value) {
+ if (code > SW_MAX) return -1;
+ if (code == SW_LID) {
+ if ((screen_switch_ == SCREEN_SWITCH_DEFAULT) || ((value != 0) && (drm_ == DRM_INNER)) ||
+ ((value == 0) && (drm_ == DRM_OUTER))) {
+ screen_switch_ = SCREEN_SWITCH_ENABLE;
+ drm_ = (value != 0) ? DRM_OUTER : DRM_INNER;
+ keys_[code].pending = true;
+ }
+ }
+
+ return 0;
+}
+
void Charger::UpdateInputState(input_event* ev) {
+ if (ev->type == EV_SW && ev->code == SW_LID) {
+ SetSwCallback(ev->code, ev->value);
+ return;
+ }
+
if (ev->type != EV_KEY) return;
SetKeyCallback(ev->code, ev->value);
}
@@ -511,10 +539,26 @@
key->pending = false;
}
+void Charger::ProcessHallSensor(int code) {
+ key_state* key = &keys_[code];
+
+ if (code == SW_LID) {
+ if (key->pending) {
+ reset_animation(&batt_anim_);
+ kick_animation(&batt_anim_);
+ RequestDisableSuspend();
+ }
+ }
+
+ key->pending = false;
+}
+
void Charger::HandleInputState(int64_t now) {
ProcessKey(KEY_POWER, now);
if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
+
+ ProcessHallSensor(SW_LID);
}
void Charger::HandlePowerSupplyState(int64_t now) {
@@ -743,9 +787,14 @@
batt_anim_.frames[i].surface = scale_frames[i];
}
}
+ drm_ = DRM_INNER;
+ screen_switch_ = SCREEN_SWITCH_DEFAULT;
ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
std::placeholders::_2));
+ (void)ev_sync_sw_state(
+ std::bind(&Charger::SetSwCallback, this, std::placeholders::_1, std::placeholders::_2));
+
next_screen_transition_ = -1;
next_key_check_ = -1;
next_pwr_check_ = -1;
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 3cda727..8cbf5ea 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -25,6 +25,10 @@
#include <healthd/healthd.h>
+namespace aidl::android::hardware::health {
+class HealthInfo;
+} // namespace aidl::android::hardware::health
+
namespace android {
namespace hardware {
namespace health {
@@ -48,7 +52,8 @@
ANDROID_POWER_SUPPLY_TYPE_AC,
ANDROID_POWER_SUPPLY_TYPE_USB,
ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
- ANDROID_POWER_SUPPLY_TYPE_BATTERY
+ ANDROID_POWER_SUPPLY_TYPE_BATTERY,
+ ANDROID_POWER_SUPPLY_TYPE_DOCK
};
BatteryMonitor();
@@ -58,9 +63,10 @@
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
- const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
- const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
- const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
+ android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;
+ android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;
+ android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;
+ const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;
void updateValues(void);
void logValues(void);
@@ -75,13 +81,7 @@
bool mBatteryDevicePresent;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
- std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
-
- int readFromFile(const String8& path, std::string* buf);
- PowerSupplyType readPowerSupplyType(const String8& path);
- bool getBooleanField(const String8& path);
- int getIntField(const String8& path);
- bool isScopedPowerSupply(const char* name);
+ std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
};
}; // namespace android
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 216e5ad..28e1fb5 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -44,6 +44,17 @@
aidl::android::hardware::health::BatteryStatus battery_status;
};
+enum DirectRenderManager {
+ DRM_INNER,
+ DRM_OUTER,
+};
+
+enum SrceenSwitch {
+ SCREEN_SWITCH_DEFAULT,
+ SCREEN_SWITCH_DISABLE,
+ SCREEN_SWITCH_ENABLE,
+};
+
// Configuration interface for charger. This includes:
// - HalHealthLoop APIs that interests charger.
// - configuration values that used to be provided by sysprops
@@ -85,9 +96,11 @@
void InitDefaultAnimationFrames();
void UpdateScreenState(int64_t now);
int SetKeyCallback(int code, int value);
+ int SetSwCallback(int code, int value);
void UpdateInputState(input_event* ev);
void SetNextKeyCheck(key_state* key, int64_t timeout);
void ProcessKey(int code, int64_t now);
+ void ProcessHallSensor(int code);
void HandleInputState(int64_t now);
void HandlePowerSupplyState(int64_t now);
int InputCallback(int fd, unsigned int epevents);
@@ -102,6 +115,9 @@
int64_t next_pwr_check_ = 0;
int64_t wait_batt_level_timestamp_ = 0;
+ DirectRenderManager drm_;
+ SrceenSwitch screen_switch_;
+
key_state keys_[KEY_MAX + 1] = {};
animation batt_anim_;
diff --git a/init/Android.bp b/init/Android.bp
index 66427dc..f91265e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,12 +162,15 @@
"libavb",
"libc++fs",
"libcgrouprc_format",
+ "libfsverity_init",
"liblmkd_utils",
+ "libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libsigningutils",
"libsnapshot_cow",
"libsnapshot_init",
"libxml2",
@@ -175,9 +178,9 @@
"update_metadata-protos",
],
shared_libs: [
- "libbacktrace",
"libbase",
"libbootloader_message",
+ "libcrypto",
"libcutils",
"libdl",
"libext4_utils",
@@ -191,7 +194,9 @@
"libprocessgroup",
"libprocessgroup_setup",
"libselinux",
+ "libunwindstack",
"libutils",
+ "libziparchive",
],
bootstrap: true,
visibility: [":__subpackages__"],
@@ -347,7 +352,6 @@
"libgsi",
"liblzma",
"libunwindstack_no_dex",
- "libbacktrace_no_dex",
"libmodprobe",
"libext2_uuid",
"libprotobuf-cpp-lite",
@@ -527,8 +531,8 @@
cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
}
-cc_binary {
- name: "host_init_verifier",
+cc_defaults {
+ name: "init_host_defaults",
host_supported: true,
cflags: [
"-Wall",
@@ -551,7 +555,6 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
],
- srcs: init_common_sources + init_host_sources,
proto: {
type: "lite",
},
@@ -569,6 +572,26 @@
},
}
+cc_binary {
+ name: "host_init_verifier",
+ defaults: ["init_host_defaults"],
+ srcs: init_common_sources + init_host_sources,
+}
+
+cc_library_host_static {
+ name: "libinit_host",
+ defaults: ["init_host_defaults"],
+ srcs: init_common_sources,
+ export_include_dirs: ["."],
+ proto: {
+ export_proto_headers: true,
+ },
+ visibility: [
+ // host_apex_verifier performs a subset of init.rc validation
+ "//system/apex/tools",
+ ],
+}
+
sh_binary {
name: "extra_free_kbytes.sh",
src: "extra_free_kbytes.sh",
diff --git a/init/OWNERS b/init/OWNERS
index 9e70e7d..4604d06 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1,2 @@
dvander@google.com
+jiyong@google.com
diff --git a/init/README.md b/init/README.md
index c102b1f..13c6ebd 100644
--- a/init/README.md
+++ b/init/README.md
@@ -804,13 +804,18 @@
`init.svc.<name>`
> State of a named service ("stopped", "stopping", "running", "restarting")
-`dev.mnt.blk.<mount_point>`
+`dev.mnt.dev.<mount_point>`, `dev.mnt.blk.<mount_point>`, `dev.mnt.rootdisk.<mount_point>`
> Block device base name associated with a *mount_point*.
The *mount_point* has / replaced by . and if referencing the root mount point
- "/", it will use "/root", specifically `dev.mnt.blk.root`.
- Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
- `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
- characteristics in a device agnostic manner.
+ "/", it will use "/root".
+ `dev.mnt.dev.<mount_point>` indicates a block device attached to filesystems.
+ (e.g., dm-N or sdaN/mmcblk0pN to access `/sys/fs/ext4/${dev.mnt.dev.<mount_point>}/`)
+
+ `dev.mnt.blk.<mount_point>` indicates the disk partition to the above block device.
+ (e.g., sdaN / mmcblk0pN to access `/sys/class/block/${dev.mnt.blk.<mount_point>}/`)
+
+ `dev.mnt.rootdisk.<mount_point>` indicates the root disk to contain the above disk partition.
+ (e.g., sda / mmcblk0 to access `/sys/class/block/${dev.mnt.rootdisk.<mount_point>}/queue`)
Init responds to properties that begin with `ctl.`. These properties take the format of
`ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The
@@ -1034,7 +1039,7 @@
/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
as 2). Note that the decision to boot normally into Android instead of booting
into recovery mode is made if androidboot.force_normal_boot=1 is present in the
-kernel commandline.
+kernel commandline, or in bootconfig with Android S and later.
Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 2401da3..3c7107a 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -147,6 +147,12 @@
Ueventd will additionally log all messages sent to stderr from the external program to the serial
console after the external program has exited.
+If the kernel command-line argument `firmware_class.path` is set, this path
+will be used first by the kernel to search for the firmware files. If found,
+ueventd will not be called at all. See the
+[kernel documentation](https://www.kernel.org/doc/html/v5.10/driver-api/firmware/fw_search_path.html)
+for more details on this feature.
+
## Coldboot
--------
Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 03b9eaa..fa1627c 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -9,5 +9,16 @@
{
"name": "MicrodroidHostTestCases"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "CtsInitTestCases"
+ },
+ {
+ "name": "init_kill_services_test"
+ },
+ {
+ "name": "MicrodroidHostTestCases"
+ }
]
}
diff --git a/init/action.h b/init/action.h
index 1534bf9..eddc384 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,6 +22,8 @@
#include <variant>
#include <vector>
+#include <android-base/strings.h>
+
#include "builtins.h"
#include "keyword_map.h"
#include "result.h"
@@ -79,6 +81,7 @@
static void set_function_map(const BuiltinFunctionMap* function_map) {
function_map_ = function_map;
}
+ bool IsFromApex() const { return base::StartsWith(filename_, "/apex/"); }
private:
void ExecuteCommand(const Command& command) const;
diff --git a/init/action_manager.h b/init/action_manager.h
index b6f93d9..2746a7c 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -37,6 +37,10 @@
size_t CheckAllCommands();
void AddAction(std::unique_ptr<Action> action);
+ template <class UnaryPredicate>
+ void RemoveActionIf(UnaryPredicate predicate) {
+ actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end());
+ }
void QueueEventTrigger(const std::string& trigger);
void QueuePropertyChange(const std::string& name, const std::string& value);
void QueueAllPropertyActions();
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index b7db9b6..f46fb09 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -140,6 +140,20 @@
static void bootchart_thread_main() {
LOG(INFO) << "Bootcharting started";
+ // Unshare the mount namespace of this thread so that the init process itself can switch
+ // the mount namespace later while this thread is still running.
+ // Otherwise, setns() call invoked as part of `enter_default_mount_ns` fails with EINVAL.
+ //
+ // Note that after unshare()'ing the mount namespace from the main thread, this thread won't
+ // receive mount/unmount events from the other mount namespace unless the events are happening
+ // from under a sharable mount.
+ //
+ // The bootchart thread is safe to unshare the mount namespace because it only reads from /proc
+ // and write to /data which are not private mounts.
+ if (unshare(CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Cannot create mount namespace";
+ return;
+ }
// Open log files.
auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
if (!stat_log) return;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index cc445be..9e1d93c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -117,7 +117,7 @@
android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
template <typename T>
- operator android::base::expected<T, ResultError<int>>() {
+ operator android::base::expected<T, ResultError<android::base::Errno>>() {
if (ignore_error_) {
return {};
}
@@ -1288,7 +1288,8 @@
return Error() << "glob pattern '" << glob_pattern << "' failed";
}
std::vector<std::string> configs;
- Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true);
+ Parser parser =
+ CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
for (size_t i = 0; i < glob_result.gl_pathc; i++) {
std::string path = glob_result.gl_pathv[i];
// Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
@@ -1306,58 +1307,11 @@
}
globfree(&glob_result);
- // Compare all files /apex/path.#rc and /apex/path.rc with the same "/apex/path" prefix,
- // choosing the one with the highest # that doesn't exceed the system's SDK.
- // (.rc == .0rc for ranking purposes)
- //
int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
- std::map<std::string, std::pair<std::string, int>> script_map;
-
- for (const auto& c : configs) {
- int sdk = 0;
- const std::vector<std::string> parts = android::base::Split(c, ".");
- std::string base;
- if (parts.size() < 2) {
- continue;
- }
-
- // parts[size()-1], aka the suffix, should be "rc" or "#rc"
- // any other pattern gets discarded
-
- const auto& suffix = parts[parts.size() - 1];
- if (suffix == "rc") {
- sdk = 0;
- } else {
- char trailer[9] = {0};
- int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
- if (r != 2) {
- continue;
- }
- if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
- continue;
- }
- }
-
- if (sdk < 0 || sdk > active_sdk) {
- continue;
- }
-
- base = parts[0];
- for (unsigned int i = 1; i < parts.size() - 1; i++) {
- base = base + "." + parts[i];
- }
-
- // is this preferred over what we already have
- auto it = script_map.find(base);
- if (it == script_map.end() || it->second.second < sdk) {
- script_map[base] = std::make_pair(c, sdk);
- }
- }
-
bool success = true;
- for (const auto& m : script_map) {
- success &= parser.ParseConfigFile(m.second.first);
+ for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
+ success &= parser.ParseConfigFile(c);
}
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 2057b55..009b639 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -56,24 +56,24 @@
]
jw = jiffy_record['jiffy_to_wallclock']
- print "process: baseline experiment (delta)"
- print " - Unit is ms (a jiffy is %d ms on the system)" % jw
- print "------------------------------------"
+ print("process: baseline experiment (delta)")
+ print(" - Unit is ms (a jiffy is %d ms on the system)" % jw)
+ print("------------------------------------")
for p in processes_of_interest:
# e.g., 32-bit system doesn't have zygote64
if p in process_map1 and p in process_map2:
- print "%s: %d %d (%+d)" % (
+ print("%s: %d %d (%+d)" % (
p, process_map1[p]['start_time'] * jw,
process_map2[p]['start_time'] * jw,
(process_map2[p]['start_time'] -
- process_map1[p]['start_time']) * jw)
+ process_map1[p]['start_time']) * jw))
# Print the last tick for the bootanimation process
- print "bootanimation ends at: %d %d (%+d)" % (
+ print("bootanimation ends at: %d %d (%+d)" % (
process_map1['/system/bin/bootanimation']['last_tick'] * jw,
process_map2['/system/bin/bootanimation']['last_tick'] * jw,
(process_map2['/system/bin/bootanimation']['last_tick'] -
- process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+ process_map1['/system/bin/bootanimation']['last_tick']) * jw))
def parse_proc_file(pathname, process_map, jiffy_record=None):
# Uncompress bootchart.tgz
@@ -83,7 +83,7 @@
f = tf.extractfile('proc_ps.log')
# Break proc_ps into chunks based on timestamps
- blocks = f.read().split('\n\n')
+ blocks = f.read().decode('utf-8').split('\n\n')
for b in blocks:
lines = b.split('\n')
if not lines[0]:
@@ -133,7 +133,7 @@
def main():
if len(sys.argv) != 3:
- print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+ print("Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0])
sys.exit(1)
process_map1 = {}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c7b7b0c..d050ed7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -27,6 +27,7 @@
#include <sys/utsname.h>
#include <unistd.h>
+#include <chrono>
#include <filesystem>
#include <string>
#include <vector>
@@ -107,6 +108,39 @@
cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
+static void Copy(const char* src, const char* dst) {
+ if (link(src, dst) == 0) {
+ LOG(INFO) << "hard linking " << src << " to " << dst << " succeeded";
+ return;
+ }
+ PLOG(FATAL) << "hard linking " << src << " to " << dst << " failed";
+}
+
+// Move snapuserd before switching root, so that it is available at the same path
+// after switching root.
+void PrepareSwitchRoot() {
+ constexpr const char* src = "/system/bin/snapuserd";
+ constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+
+ if (access(dst, X_OK) == 0) {
+ LOG(INFO) << dst << " already exists and it can be executed";
+ return;
+ }
+
+ if (access(src, F_OK) != 0) {
+ PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
+ return;
+ }
+
+ auto dst_dir = android::base::Dirname(dst);
+ std::error_code ec;
+ if (access(dst_dir.c_str(), F_OK) != 0) {
+ if (!fs::create_directories(dst_dir, ec)) {
+ LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
+ }
+ }
+ Copy(src, dst);
+}
} // namespace
std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -123,7 +157,7 @@
}
#define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
+bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
@@ -172,7 +206,8 @@
}
Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
- bool retval = m.LoadListedModules(!want_console);
+ bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
+ : m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
@@ -254,6 +289,9 @@
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
+
+ // First stage init stores Mainline sepolicy here.
+ CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
SetStdioToDevNull(argv);
@@ -282,11 +320,13 @@
}
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
+ auto want_parallel =
+ bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos;
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
- module_count)) {
+ want_parallel, module_count)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
@@ -301,12 +341,11 @@
<< module_elapse_time.count() << " ms";
}
-
bool created_devices = false;
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
if (!IsRecoveryMode()) {
created_devices = DoCreateDevices();
- if (!created_devices){
+ if (!created_devices) {
LOG(ERROR) << "Failed to create device nodes early";
}
}
@@ -349,10 +388,11 @@
if (ForceNormalBoot(cmdline, bootconfig)) {
mkdir("/first_stage_ramdisk", 0755);
+ PrepareSwitchRoot();
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
- LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+ PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index ada5a47..042988e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -191,8 +191,6 @@
auto& dm = android::dm::DeviceMapper::Instance();
if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) {
root_entry->fs_mgr_flags.avb = true;
- } else {
- root_entry->fs_mgr_flags.verify = true;
}
return true;
}
diff --git a/init/init.cpp b/init/init.cpp
index e3596cb..4955bc5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -48,7 +48,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
@@ -58,6 +57,7 @@
#include <processgroup/processgroup.h>
#include <processgroup/setup.h>
#include <selinux/android.h>
+#include <unwindstack/AndroidUnwinder.h>
#include "action_parser.h"
#include "builtins.h"
@@ -85,6 +85,10 @@
#include "system/core/init/property_service.pb.h"
#include "util.h"
+#ifndef RECOVERY
+#include "com_android_apex.h"
+#endif // RECOVERY
+
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -253,12 +257,14 @@
} shutdown_state;
static void UnwindMainThreadStack() {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ LOG(ERROR) << __FUNCTION__
+ << "sys.powerctl: Failed to unwind callstack: " << data.GetErrorString();
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+ for (const auto& frame : data.frames) {
+ LOG(ERROR) << "sys.powerctl: " << unwinder.FormatFrame(frame);
}
}
@@ -291,13 +297,59 @@
return parser;
}
-// parser that only accepts new services
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
- Parser parser;
+#ifndef RECOVERY
+template <typename T>
+struct LibXmlErrorHandler {
+ T handler_;
+ template <typename Handler>
+ LibXmlErrorHandler(Handler&& handler) : handler_(std::move(handler)) {
+ xmlSetGenericErrorFunc(nullptr, &ErrorHandler);
+ }
+ ~LibXmlErrorHandler() { xmlSetGenericErrorFunc(nullptr, nullptr); }
+ static void ErrorHandler(void*, const char* msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ char* formatted;
+ if (vasprintf(&formatted, msg, args) >= 0) {
+ LOG(ERROR) << formatted;
+ }
+ free(formatted);
+ va_end(args);
+ }
+};
- parser.AddSectionParser(
- "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
- from_apex));
+template <typename Handler>
+LibXmlErrorHandler(Handler&&) -> LibXmlErrorHandler<Handler>;
+#endif // RECOVERY
+
+// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`.
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) {
+ Parser parser;
+ auto subcontext = GetSubcontext();
+#ifndef RECOVERY
+ if (subcontext) {
+ const auto apex_info_list_file = "/apex/apex-info-list.xml";
+ auto error_handler = LibXmlErrorHandler([&](const auto& error_message) {
+ LOG(ERROR) << "Failed to read " << apex_info_list_file << ":" << error_message;
+ });
+ const auto apex_info_list = com::android::apex::readApexInfoList(apex_info_list_file);
+ if (apex_info_list.has_value()) {
+ std::vector<std::string> subcontext_apexes;
+ for (const auto& info : apex_info_list->getApexInfo()) {
+ if (info.hasPreinstalledModulePath() &&
+ subcontext->PathMatchesSubcontext(info.getPreinstalledModulePath())) {
+ subcontext_apexes.push_back(info.getModuleName());
+ }
+ }
+ subcontext->SetApexList(std::move(subcontext_apexes));
+ }
+ }
+#endif // RECOVERY
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, subcontext, std::nullopt,
+ /*from_apex=*/true));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
+
return parser;
}
@@ -578,12 +630,29 @@
HandlePowerctlMessage("shutdown,container");
}
+static constexpr std::chrono::milliseconds kDiagnosticTimeout = 10s;
+
static void HandleSignalFd() {
signalfd_siginfo siginfo;
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
- if (bytes_read != sizeof(siginfo)) {
- PLOG(ERROR) << "Failed to read siginfo from signal_fd";
- return;
+ auto started = std::chrono::steady_clock::now();
+ for (;;) {
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read < 0 && errno == EAGAIN) {
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::duration<double> waited = now - started;
+ if (waited >= kDiagnosticTimeout) {
+ LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";
+ started = now;
+ }
+
+ std::this_thread::sleep_for(100ms);
+ continue;
+ }
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+ return;
+ }
+ break;
}
switch (siginfo.ssi_signo) {
@@ -639,7 +708,7 @@
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
if (signal_fd == -1) {
PLOG(FATAL) << "failed to create signalfd";
}
@@ -778,6 +847,10 @@
InstallRebootSignalHandlers();
}
+ // No threads should be spin up until signalfd
+ // is registered. If the threads are indeed required,
+ // each of these threads _should_ make sure SIGCHLD signal
+ // is blocked. See b/223076262
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
@@ -937,7 +1010,7 @@
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
- auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
+ auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
@@ -977,6 +1050,13 @@
for (const auto& function : *pending_functions) {
(*function)();
}
+ } else if (Service::is_exec_service_running()) {
+ std::chrono::duration<double> waited =
+ std::chrono::steady_clock::now() - Service::exec_service_started();
+ if (waited >= kDiagnosticTimeout) {
+ LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
+ << " without SIGCHLD";
+ }
}
if (!IsShuttingDown()) {
HandleControlMessages();
diff --git a/init/init.h b/init/init.h
index 4f686cb..5220535 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,7 @@
namespace init {
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex);
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
bool start_waiting_for_property(const char *name, const char *value);
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 8550ec8..0dc6ff6 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -42,34 +42,34 @@
using ActionManagerCommand = std::function<void(ActionManager&)>;
void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
- const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
- ActionManager am;
-
+ const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+ ServiceList* service_list) {
Action::set_function_map(&test_function_map);
Parser parser;
parser.AddSectionParser("service",
std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
ASSERT_TRUE(parser.ParseConfig(init_script_file));
for (const auto& command : commands) {
- command(am);
+ command(*action_manager);
}
- while (am.HasMoreCommands()) {
- am.ExecuteOneCommand();
+ while (action_manager->HasMoreCommands()) {
+ action_manager->ExecuteOneCommand();
}
}
void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
- const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
+ const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+ ServiceList* service_list) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
- TestInit(tf.path, test_function_map, commands, service_list);
+ TestInit(tf.path, test_function_map, commands, action_manager, service_list);
}
TEST(init, SimpleEventTrigger) {
@@ -91,8 +91,9 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, test_function_map, commands, &service_list);
+ TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
EXPECT_TRUE(expect_true);
}
@@ -154,8 +155,10 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, test_function_map, commands, &service_list);
+ TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
+ EXPECT_EQ(3, num_executed);
}
TEST(init, OverrideService) {
@@ -169,8 +172,9 @@
)init";
+ ActionManager action_manager;
ServiceList service_list;
- TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+ TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
auto service = service_list.begin()->get();
@@ -236,13 +240,100 @@
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
+ ActionManager action_manager;
ServiceList service_list;
-
- TestInit(start.path, test_function_map, commands, &service_list);
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
EXPECT_EQ(6, num_executed);
}
+BuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) {
+ auto execute_command = [&num_executed](const BuiltinArguments& args) {
+ EXPECT_EQ(2U, args.size());
+ EXPECT_EQ(++num_executed, std::stoi(args[1]));
+ return Result<void>{};
+ };
+ auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {
+ EXPECT_EQ(2U, args.size());
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, nullptr));
+ if (!parser.ParseConfig(args[1])) {
+ return Error() << "Failed to load";
+ }
+ return Result<void>{};
+ };
+ auto trigger_command = [&action_manager](const BuiltinArguments& args) {
+ EXPECT_EQ(2U, args.size());
+ LOG(INFO) << "Queue event trigger: " << args[1];
+ action_manager.QueueEventTrigger(args[1]);
+ return Result<void>{};
+ };
+ BuiltinFunctionMap test_function_map = {
+ {"execute", {1, 1, {false, execute_command}}},
+ {"load", {1, 1, {false, load_command}}},
+ {"trigger", {1, 1, {false, trigger_command}}},
+ };
+ return test_function_map;
+}
+
+TEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) {
+ // "start" script loads "lazy" script. Even though "lazy" scripts
+ // defines "on boot" action, it's not executed by the current "boot"
+ // event because it's already processed.
+ TemporaryFile lazy;
+ ASSERT_TRUE(lazy.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", lazy.fd));
+
+ TemporaryFile start;
+ // clang-format off
+ std::string start_script = "on boot\n"
+ "load " + std::string(lazy.path) + "\n"
+ "execute 1";
+ // clang-format on
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ ActionManager action_manager;
+ ServiceList service_list;
+ BuiltinFunctionMap test_function_map =
+ GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+ EXPECT_EQ(1, num_executed);
+}
+
+TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) {
+ // "start" script loads "lazy" script and then triggers "next" event
+ // which executes "on next" action loaded by the previous command.
+ TemporaryFile lazy;
+ ASSERT_TRUE(lazy.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on next\nexecute 2", lazy.fd));
+
+ TemporaryFile start;
+ // clang-format off
+ std::string start_script = "on boot\n"
+ "load " + std::string(lazy.path) + "\n"
+ "execute 1\n"
+ "trigger next";
+ // clang-format on
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ ActionManager action_manager;
+ ServiceList service_list;
+ BuiltinFunctionMap test_function_map =
+ GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+ TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+ EXPECT_EQ(2, num_executed);
+}
+
TEST(init, RejectsCriticalAndOneshotService) {
if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
GTEST_SKIP() << "Test only valid for devices launching with R or later";
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index f0d8d45..227ce2f 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <algorithm>
+#include <filesystem>
#include <string>
#include <utility>
#include <vector>
@@ -32,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fstab/fstab.h>
@@ -39,6 +41,9 @@
#include "epoll.h"
+using android::base::Basename;
+using android::base::StringPrintf;
+
namespace android {
namespace init {
@@ -67,41 +72,82 @@
return MountHandlerEntry(fields[0], fields[1], fields[2]);
}
+// return sda25 for dm-4, sda25 for sda25, or mmcblk0p24 for mmcblk0p24
+std::string GetDiskPart(std::string blockdev) {
+ if (blockdev.find('/') != std::string::npos) return {};
+
+ while (android::base::StartsWith(blockdev, "dm-")) {
+ auto& dm = dm::DeviceMapper::Instance();
+ std::optional<std::string> parent = dm.GetParentBlockDeviceByPath("/dev/block/" + blockdev);
+ if (parent) {
+ blockdev = android::base::Basename(*parent);
+ } else {
+ return {};
+ }
+ }
+ return blockdev;
+}
+
+// return sda for sda25, or mmcblk0 for mmcblk0p24
+std::string GetRootDisk(std::string blockdev) {
+ if (blockdev.empty()) return {};
+ if (blockdev.find('/') != std::string::npos) return {};
+
+ std::error_code ec;
+ for (const auto& entry : std::filesystem::directory_iterator("/sys/block", ec)) {
+ const std::string path = entry.path().string();
+ if (std::filesystem::exists(StringPrintf("%s/%s", path.c_str(), blockdev.c_str()))) {
+ return Basename(path);
+ }
+ }
+ return {};
+}
+
void SetMountProperty(const MountHandlerEntry& entry, bool add) {
static constexpr char devblock[] = "/dev/block/";
if (!android::base::StartsWith(entry.blk_device, devblock)) return;
- std::string value;
+ auto target = entry.blk_device.substr(strlen(devblock));
+ std::string diskpart, rootdisk;
if (add) {
- value = entry.blk_device.substr(strlen(devblock));
- if (android::base::StartsWith(value, "sd")) {
- // All sd partitions inherit their queue characteristics
- // from the whole device reference. Strip partition number.
- auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
- if (it != value.end()) value.erase(it, value.end());
- }
- auto queue = "/sys/block/" + value + "/queue";
+ diskpart = GetDiskPart(target);
+ rootdisk = GetRootDisk(diskpart);
+
struct stat sb;
- if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
- if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) rootdisk = "";
// Clear the noise associated with loopback and APEX.
- if (android::base::StartsWith(value, "loop")) value = "";
- if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+ if (android::base::StartsWith(target, "loop")) rootdisk = "";
+ if (android::base::StartsWith(entry.mount_point, "/apex/")) rootdisk = "";
}
auto mount_prop = entry.mount_point;
if (mount_prop == "/") mount_prop = "/root";
std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
auto dev_mount_prop = "dev.mnt.dev" + mount_prop;
- // Set property even if its value does not change to trigger 'on property:'
+ auto rootdisk_mount_prop = "dev.mnt.rootdisk" + mount_prop;
+ // Set property even if its rootdisk does not change to trigger 'on property:'
// handling, except for clearing non-existent or already clear property.
// Goal is reduction of empty properties and associated triggers.
- if (value.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
- android::base::SetProperty(blk_mount_prop, value);
- if (!value.empty()) {
- android::base::SetProperty(dev_mount_prop, entry.blk_device.substr(strlen(devblock)));
- } else {
+ if (rootdisk.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
+
+ if (rootdisk.empty()) {
+ android::base::SetProperty(blk_mount_prop, "");
android::base::SetProperty(dev_mount_prop, "");
+ android::base::SetProperty(rootdisk_mount_prop, "");
+ return;
}
+
+ // 1. dm-N
+ // dev.mnt.dev.data = dm-N
+ // dev.mnt.blk.data = sdaN or mmcblk0pN
+ // dev.mnt.rootdisk.data = sda or mmcblk0
+ //
+ // 2. sdaN or mmcblk0pN
+ // dev.mnt.dev.data = sdaN or mmcblk0pN
+ // dev.mnt.blk.data = sdaN or mmcblk0pN
+ // dev.mnt.rootdisk.data = sda or mmcblk0
+ android::base::SetProperty(dev_mount_prop, target);
+ android::base::SetProperty(blk_mount_prop, diskpart);
+ android::base::SetProperty(rootdisk_mount_prop, rootdisk);
}
} // namespace
diff --git a/init/parser.cpp b/init/parser.cpp
index 5c18551..abc2017 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -18,6 +18,8 @@
#include <dirent.h>
+#include <map>
+
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -154,6 +156,58 @@
return true;
}
+std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk) {
+ std::vector<std::string> filtered_configs;
+
+ std::map<std::string, std::pair<std::string, int>> script_map;
+ for (const auto& c : configs) {
+ int sdk = 0;
+ const std::vector<std::string> parts = android::base::Split(c, ".");
+ std::string base;
+ if (parts.size() < 2) {
+ continue;
+ }
+
+ // parts[size()-1], aka the suffix, should be "rc" or "#rc"
+ // any other pattern gets discarded
+
+ const auto& suffix = parts[parts.size() - 1];
+ if (suffix == "rc") {
+ sdk = 0;
+ } else {
+ char trailer[9] = {0};
+ int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
+ if (r != 2) {
+ continue;
+ }
+ if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
+ continue;
+ }
+ }
+
+ if (sdk < 0 || sdk > active_sdk) {
+ continue;
+ }
+
+ base = parts[0];
+ for (unsigned int i = 1; i < parts.size() - 1; i++) {
+ base = base + "." + parts[i];
+ }
+
+ // is this preferred over what we already have
+ auto it = script_map.find(base);
+ if (it == script_map.end() || it->second.second < sdk) {
+ script_map[base] = std::make_pair(c, sdk);
+ }
+ }
+
+ for (const auto& m : script_map) {
+ filtered_configs.push_back(m.second.first);
+ }
+ return filtered_configs;
+}
+
bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 95b0cd7..2f4108f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,6 +76,12 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ // Compare all files */path.#rc and */path.rc with the same path prefix.
+ // Keep the one with the highest # that doesn't exceed the system's SDK.
+ // (.rc == .0rc for ranking purposes)
+ std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk);
+
// Host init verifier check file permissions.
bool ParseConfigFileInsecure(const std::string& path);
diff --git a/init/perfboot.py b/init/perfboot.py
index 4b23ad2..968df38 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,7 +39,7 @@
import argparse
import atexit
-import cStringIO
+import io
import glob
import inspect
import logging
@@ -102,7 +102,7 @@
self._wait_cpu_cool_down(self._product, self._temp_paths)
else:
if self._waited:
- print 'Waiting for %d seconds' % self._interval
+ print('Waiting for %d seconds' % self._interval)
time.sleep(self._interval)
self._waited = True
@@ -119,9 +119,9 @@
threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(
self._product)
if threshold is None:
- print 'No CPU temperature threshold is set for ' + self._product
- print ('Just wait %d seconds' %
- IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+ print('No CPU temperature threshold is set for ' + self._product)
+ print(('Just wait %d seconds' %
+ IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT))
time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
return
while True:
@@ -129,8 +129,8 @@
if temp < threshold:
logging.info('Current CPU temperature %s' % temp)
return
- print 'Waiting until CPU temperature (%d) falls below %d' % (
- temp, threshold)
+ print('Waiting until CPU temperature (%d) falls below %d' % (
+ temp, threshold))
time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)
@@ -260,7 +260,7 @@
def get_values(record, tag):
"""Gets values that matches |tag| from |record|."""
- keys = [key for key in record.keys() if key[0] == tag]
+ keys = [key for key in list(record.keys()) if key[0] == tag]
return [record[k] for k in sorted(keys)]
@@ -304,7 +304,7 @@
with open(filename, 'w') as f:
f.write('\t'.join(labels) + '\n')
for record in record_list:
- line = cStringIO.StringIO()
+ line = io.StringIO()
invalid_line = False
for i, tag in enumerate(tags):
if i != 0:
@@ -319,7 +319,7 @@
logging.error('Invalid record found: ' + line.getvalue())
line.write('\n')
f.write(line.getvalue())
- print 'Wrote: ' + filename
+ print(('Wrote: ' + filename))
def median(data):
@@ -349,9 +349,9 @@
# Filter out invalid data.
end_times = [get_last_value(record, end_tag) for record in record_list
if get_last_value(record, end_tag) != 0]
- print 'mean:', int(round(mean(end_times))), 'ms'
- print 'median:', int(round(median(end_times))), 'ms'
- print 'standard deviation:', int(round(stddev(end_times))), 'ms'
+ print(('mean:', int(round(mean(end_times))), 'ms'))
+ print(('median:', int(round(median(end_times))), 'ms'))
+ print(('standard deviation:', int(round(stddev(end_times))), 'ms'))
def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
@@ -359,7 +359,7 @@
device.wait()
interval_adjuster.wait()
device.reboot()
- print 'Rebooted the device'
+ print('Rebooted the device, waiting for tag', end_tag)
record = {}
booted = False
while not booted:
@@ -372,7 +372,7 @@
stdout=subprocess.PIPE)
for line in readlines_unbuffered(p):
if t.is_timedout():
- print '*** Timed out ***'
+ print('*** Timed out ***')
return record
m = event_tags_re.search(line)
if not m:
@@ -381,8 +381,8 @@
event_time = int(m.group('time'))
pid = m.group('pid')
record[(tag, pid)] = event_time
- print 'Event log recorded: %s (%s) - %d ms' % (
- tag, pid, event_time)
+ print(('Event log recorded: %s (%s) - %d ms' % (
+ tag, pid, event_time)))
if tag == end_tag:
booted = True
t.cancel()
@@ -420,7 +420,7 @@
def install_apks(device, apk_dir):
for apk in glob.glob(os.path.join(apk_dir, '*.apk')):
- print 'Installing: ' + apk
+ print('Installing: ' + apk)
device.install(apk, replace=True)
@@ -452,7 +452,7 @@
event_tags_re = make_event_tags_re(event_tags)
for i in range(args.iterations):
- print 'Run #%d ' % i
+ print('Run #%d ' % i)
record = do_iteration(
device, interval_adjuster, event_tags_re, end_tag)
record_list.append(record)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 70e26ec..9f7c215 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1035,8 +1035,8 @@
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
// Api level properties of the board. The order of the properties must be kept.
- std::vector<std::string> BOARD_API_LEVEL_PROPS = {
- "ro.board.api_level", "ro.board.first_api_level", "ro.vendor.build.version.sdk"};
+ std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
+ "ro.board.first_api_level"};
// Api level properties of the device. The order of the properties must be kept.
std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
"ro.build.version.sdk"};
@@ -1107,6 +1107,7 @@
LoadPropertiesFromSecondStageRes(&properties);
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+ load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
// TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
// all updated.
// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
@@ -1171,6 +1172,9 @@
// Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
+ if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
+ }
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
&property_infos);
@@ -1194,6 +1198,7 @@
LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 6aa9912..4e4bfd8 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <linux/f2fs.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <mntent.h>
@@ -218,7 +219,7 @@
<< stat;
}
-static bool IsDataMounted() {
+static bool IsDataMounted(const std::string& fstype) {
std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
if (fp == nullptr) {
PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -227,7 +228,7 @@
mntent* mentry;
while ((mentry = getmntent(fp.get())) != nullptr) {
if (mentry->mnt_dir == "/data"s) {
- return true;
+ return fstype == "*" || mentry->mnt_type == fstype;
}
}
return false;
@@ -633,7 +634,7 @@
// If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
// worry about unmounting it.
- if (!IsDataMounted()) {
+ if (!IsDataMounted("*")) {
sync();
RebootSystem(cmd, reboot_target);
abort();
@@ -758,6 +759,16 @@
sem_post(&reboot_semaphore);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
+ if (IsDataMounted("f2fs")) {
+ uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;
+ unique_fd fd(TEMP_FAILURE_RETRY(open("/data", O_RDONLY)));
+ int ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
+ if (ret) {
+ PLOG(ERROR) << "Shutdown /data: ";
+ } else {
+ LOG(INFO) << "Shutdown /data";
+ }
+ }
RebootSystem(cmd, reboot_target);
abort();
}
@@ -881,7 +892,16 @@
sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
- // Remove services that were defined in an APEX.
+ ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
+ if (action->IsFromApex()) {
+ std::string trigger_name = action->BuildTriggersString();
+ LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
+ return true;
+ }
+ return false;
+ });
+ // Remove services that were defined in an APEX
ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
if (s->is_from_apex()) {
LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd..f8e1de0 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -26,8 +26,8 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
#include <cutils/android_reboot.h>
+#include <unwindstack/AndroidUnwinder.h>
#include "capabilities.h"
#include "reboot_utils.h"
@@ -157,13 +157,13 @@
// In the parent, let's try to get a backtrace then shutdown.
LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
- std::unique_ptr<Backtrace> backtrace(
- Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
- if (!backtrace->Unwind(0)) {
- LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack: " << data.GetErrorString();
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- LOG(ERROR) << backtrace->FormatFrameData(i);
+ for (const auto& frame : data.frames) {
+ LOG(ERROR) << unwinder.FormatFrame(frame);
}
if (init_fatal_panic) {
LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 28cd012..be8c554 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,26 +26,29 @@
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
-// The split policy is for supporting treble devices. It splits the SEPolicy across files on
-// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
-// portion of the policy). This is necessary to allow the system image to be updated independently
-// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
-// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
-// identical to the system image shipped on a vendor's device.
+// The split policy is for supporting treble devices and updateable apexes. It splits the SEPolicy
+// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
+// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
+// /odm/etc/selinux, and /dev/selinux (the apex portion of policy). This is necessary to allow
+// images to be updated independently of the vendor image, while maintaining contributions from
+// multiple partitions in the SEPolicy. This is especially important for VTS testing, where the
+// SEPolicy on the Google System Image may not be identical to the system image shipped on a
+// vendor's device.
// The split SEPolicy is loaded as described below:
// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
// /odm/etc/selinux/precompiled_sepolicy if odm parition is present. Stored along with this file
-// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
-// were used to compile this precompiled policy. The system partition contains a similar sha256
-// of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext and
-// product paritition contain sha256 hashes of their SEPolicy. The init loads this
+// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
+// that were used to compile this precompiled policy. The system partition contains a similar
+// sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
+// product, and apex contain sha256 hashes of their SEPolicy. Init loads this
// precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-// /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
-// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
-// have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
-// compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the
-// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
+// respectively.
+// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
+// them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
+// to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by
+// the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -58,19 +61,25 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <fstream>
+#include <CertUtils.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
+#include <fsverity_init.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
+#include <mini_keyctl_utils.h>
#include <selinux/android.h>
+#include <ziparchive/zip_archive.h>
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
@@ -247,6 +256,7 @@
precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
{"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
+ {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
};
for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -325,7 +335,7 @@
// * vendor -- policy needed due to logic contained in the vendor image,
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
// with newer versions of platform policy.
- // * (optional) policy needed due to logic on product, system_ext, or odm images.
+ // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
@@ -421,6 +431,12 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
+
+ // apex_sepolicy.cil is default but optional.
+ std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
+ if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
+ apex_policy_cil_file.clear();
+ }
const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
@@ -463,6 +479,9 @@
if (!odm_policy_cil_file.empty()) {
compile_args.push_back(odm_policy_cil_file.c_str());
}
+ if (!apex_policy_cil_file.empty()) {
+ compile_args.push_back(apex_policy_cil_file.c_str());
+ }
compile_args.push_back(nullptr);
if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -489,6 +508,197 @@
return true;
}
+constexpr const char* kSigningCertRelease =
+ "/system/etc/selinux/com.android.sepolicy.cert-release.der";
+constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
+const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
+const std::string kSepolicyZip = "SEPolicy.zip";
+const std::string kSepolicySignature = "SEPolicy.zip.sig";
+
+const std::string kTmpfsDir = "/dev/selinux/";
+
+// Files that are deleted after policy is compiled/loaded.
+const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
+// Files that need to persist because they are used by userspace processes.
+const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
+ "apex_service_contexts", "apex_seapp_contexts",
+ "apex_test"};
+
+Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
+ ZipEntry entry;
+ std::string dstPath = kTmpfsDir + fileName;
+
+ int ret = FindEntry(archive, fileName, &entry);
+ if (ret != 0) {
+ // All files are optional. If a file doesn't exist, return without error.
+ return {};
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd == -1) {
+ return Error() << "Failed to open " << dstPath;
+ }
+
+ ret = ExtractEntryToFile(archive, &entry, fd);
+ if (ret != 0) {
+ return Error() << "Failed to extract entry \"" << fileName << "\" ("
+ << entry.uncompressed_length << " bytes) to \"" << dstPath
+ << "\": " << ErrorCodeString(ret);
+ }
+
+ return {};
+}
+
+Result<void> GetPolicyFromApex(const std::string& dir) {
+ LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
+ unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
+ }
+
+ ZipArchiveHandle handle;
+ int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
+ /*assume_ownership=*/false);
+ if (ret < 0) {
+ return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
+ << ErrorCodeString(ret);
+ }
+
+ auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
+
+ for (const auto& file : kApexSepolicy) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ for (const auto& file : kApexSepolicyTmp) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ return {};
+}
+
+Result<void> LoadSepolicyApexCerts() {
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ return Error() << "Failed to find .fs-verity keyring id";
+ }
+
+ // TODO(b/199914227) the release key should always exist. Once it's checked in, start
+ // throwing an error here if it doesn't exist.
+ if (access(kSigningCertRelease, F_OK) == 0) {
+ LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
+ }
+ return {};
+}
+
+Result<void> SepolicyFsVerityCheck() {
+ return Error() << "TODO implementent support for fsverity SEPolicy.";
+}
+
+Result<void> SepolicyCheckSignature(const std::string& dir) {
+ std::string signature;
+ if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
+ return ErrnoError() << "Failed to read " << kSepolicySignature;
+ }
+
+ std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
+ if (!sepolicyZip) {
+ return Error() << "Failed to open " << kSepolicyZip;
+ }
+ sepolicyZip.seekg(0);
+ std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
+ std::istreambuf_iterator<char>());
+
+ auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
+ if (!releaseKey.ok()) {
+ return releaseKey.error();
+ }
+
+ return verifySignature(sepolicyStr, signature, *releaseKey);
+}
+
+Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
+ if (supportsFsVerity) {
+ auto fsVerityCheck = SepolicyFsVerityCheck();
+ if (fsVerityCheck.ok()) {
+ return fsVerityCheck;
+ }
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
+ // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
+ }
+
+ auto sepolicySignature = SepolicyCheckSignature(dir);
+ if (!sepolicySignature.ok()) {
+ return Error() << "Apex SEPolicy failed signature check";
+ }
+ return {};
+}
+
+void CleanupApexSepolicy() {
+ for (const auto& file : kApexSepolicyTmp) {
+ std::string path = kTmpfsDir + file;
+ unlink(path.c_str());
+ }
+}
+
+// Updatable sepolicy is shipped within an zip within an APEX. Because
+// it needs to be available before Apexes are mounted, apexd copies
+// the zip from the APEX and stores it in /metadata/sepolicy. If there is
+// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
+// loaded from /system/etc/selinux/apex. Init performs the following
+// steps on boot:
+//
+// 1. Validates the zip by checking its signature against a public key that is
+// stored in /system/etc/selinux.
+// 2. Extracts files from zip and stores them in /dev/selinux.
+// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
+// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
+// is used. This is the same flow as on-device compilation of policy for Treble.
+// 4. Cleans up files in /dev/selinux which are no longer needed.
+// 5. Restorecons the remaining files in /dev/selinux.
+// 6. Sets selinux into enforcing mode and continues normal booting.
+//
+void PrepareApexSepolicy() {
+ bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+ if (supportsFsVerity) {
+ auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
+ if (!loadSepolicyApexCerts.ok()) {
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
+ // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << loadSepolicyApexCerts.error();
+ }
+ }
+ // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
+ // /system.
+ auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
+ ? kSepolicyApexMetadataDir
+ : kSepolicyApexSystemDir;
+
+ auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
+ if (!sepolicyVerify.ok()) {
+ LOG(INFO) << "Error: " << sepolicyVerify.error();
+ // If signature verification fails, fall back to version on /system.
+ // This file doesn't need to be verified because it lives on the system partition which
+ // is signed and protected by verified boot.
+ dir = kSepolicyApexSystemDir;
+ }
+
+ auto apex = GetPolicyFromApex(dir);
+ if (!apex.ok()) {
+ // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
+ LOG(ERROR) << apex.error();
+ }
+}
+
void ReadPolicy(std::string* policy) {
PolicyFile policy_file;
@@ -552,6 +762,7 @@
void SelinuxRestoreContext() {
LOG(INFO) << "Running restorecon...";
selinux_android_restorecon("/dev", 0);
+ selinux_android_restorecon("/dev/console", 0);
selinux_android_restorecon("/dev/kmsg", 0);
if constexpr (WORLD_WRITABLE_KMSG) {
selinux_android_restorecon("/dev/kmsg_debug", 0);
@@ -740,9 +951,12 @@
LOG(INFO) << "Opening SELinux policy";
+ PrepareApexSepolicy();
+
// Read the policy before potentially killing snapuserd.
std::string policy;
ReadPolicy(&policy);
+ CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
@@ -760,6 +974,13 @@
snapuserd_helper = nullptr;
}
+ // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
+ // needed to transition files from tmpfs to *_contexts_file context should not be granted to
+ // any process after selinux is set into enforcing mode.
+ if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
+ PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
+ }
+
SelinuxSetEnforcement();
// We're in the kernel domain and want to transition to the init domain. File systems that
diff --git a/init/service.cpp b/init/service.cpp
index f7318cb..95a5328 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -127,6 +127,7 @@
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
+std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::vector<std::string>& args, bool from_apex)
@@ -287,6 +288,10 @@
if (flags_ & SVC_EXEC) UnSetExec();
+ if (name_ == "zygote" || name_ == "zygote64") {
+ removeAllEmptyProcessGroups();
+ }
+
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
@@ -388,6 +393,7 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
+ exec_service_started_ = std::chrono::steady_clock::now();
LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
<< " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
@@ -397,6 +403,122 @@
return {};
}
+static void ClosePipe(const std::array<int, 2>* pipe) {
+ for (const auto fd : *pipe) {
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+}
+
+Result<void> Service::CheckConsole() {
+ if (!(flags_ & SVC_CONSOLE)) {
+ return {};
+ }
+
+ if (proc_attr_.console.empty()) {
+ proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
+ }
+
+ // Make sure that open call succeeds to ensure a console driver is
+ // properly registered for the device node
+ int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
+ if (console_fd < 0) {
+ flags_ |= SVC_DISABLED;
+ return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
+ }
+ close(console_fd);
+ return {};
+}
+
+// Configures the memory cgroup properties for the service.
+void Service::ConfigureMemcg() {
+ if (swappiness_ != -1) {
+ if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+ PLOG(ERROR) << "setProcessGroupSwappiness failed";
+ }
+ }
+
+ if (soft_limit_in_bytes_ != -1) {
+ if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+ }
+ }
+
+ size_t computed_limit_in_bytes = limit_in_bytes_;
+ if (limit_percent_ != -1) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ long num_pages = sysconf(_SC_PHYS_PAGES);
+ if (page_size > 0 && num_pages > 0) {
+ size_t max_mem = SIZE_MAX;
+ if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+ max_mem = size_t(num_pages) * size_t(page_size);
+ }
+ computed_limit_in_bytes =
+ std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+ }
+ }
+
+ if (!limit_property_.empty()) {
+ // This ends up overwriting computed_limit_in_bytes but only if the
+ // property is defined.
+ computed_limit_in_bytes =
+ android::base::GetUintProperty(limit_property_, computed_limit_in_bytes, SIZE_MAX);
+ }
+
+ if (computed_limit_in_bytes != size_t(-1)) {
+ if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+ PLOG(ERROR) << "setProcessGroupLimit failed";
+ }
+ }
+}
+
+// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
+void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
+ const std::vector<Descriptor>& descriptors,
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
+ if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
+ LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
+ }
+
+ for (const auto& [key, value] : environment_vars_) {
+ setenv(key.c_str(), value.c_str(), 1);
+ }
+
+ for (const auto& descriptor : descriptors) {
+ descriptor.Publish();
+ }
+
+ if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
+ LOG(ERROR) << "failed to write pid to files: " << result.error();
+ }
+
+ // Wait until the cgroups have been created and until the cgroup controllers have been
+ // activated.
+ char byte = 0;
+ if (read((*pipefd)[0], &byte, 1) < 0) {
+ PLOG(ERROR) << "failed to read from notification channel";
+ }
+ pipefd.reset();
+ if (!byte) {
+ LOG(FATAL) << "Service '" << name_ << "' failed to start due to a fatal error";
+ _exit(EXIT_FAILURE);
+ }
+
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
+ // As requested, set our gid, supplemental gids, uid, context, and
+ // priority. Aborts on failure.
+ SetProcessAttributesAndCaps();
+
+ if (!ExpandArgsAndExecv(args_, sigstop_)) {
+ PLOG(ERROR) << "cannot execv('" << args_[0]
+ << "'). See the 'Debugging init' section of init's README.md for tips";
+ }
+}
+
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
@@ -428,20 +550,14 @@
return {};
}
- bool needs_console = (flags_ & SVC_CONSOLE);
- if (needs_console) {
- if (proc_attr_.console.empty()) {
- proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
- }
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
+ ClosePipe);
+ if (pipe(pipefd->data()) < 0) {
+ return ErrnoError() << "pipe()";
+ }
- // Make sure that open call succeeds to ensure a console driver is
- // properly registered for the device node
- int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
- if (console_fd < 0) {
- flags_ |= SVC_DISABLED;
- return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
- }
- close(console_fd);
+ if (Result<void> result = CheckConsole(); !result.ok()) {
+ return result;
}
struct stat sb;
@@ -513,38 +629,7 @@
if (pid == 0) {
umask(077);
-
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
- !result.ok()) {
- LOG(FATAL) << "Service '" << name_
- << "' failed to set up namespaces: " << result.error();
- }
-
- for (const auto& [key, value] : environment_vars_) {
- setenv(key.c_str(), value.c_str(), 1);
- }
-
- for (const auto& descriptor : descriptors) {
- descriptor.Publish();
- }
-
- if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
- LOG(ERROR) << "failed to write pid to files: " << result.error();
- }
-
- if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
- LOG(ERROR) << "failed to set task profiles";
- }
-
- // As requested, set our gid, supplemental gids, uid, context, and
- // priority. Aborts on failure.
- SetProcessAttributesAndCaps();
-
- if (!ExpandArgsAndExecv(args_, sigstop_)) {
- PLOG(ERROR) << "cannot execv('" << args_[0]
- << "'). See the 'Debugging init' section of init's README.md for tips";
- }
-
+ RunService(override_mount_namespace, descriptors, std::move(pipefd));
_exit(127);
}
@@ -571,53 +656,25 @@
limit_percent_ != -1 || !limit_property_.empty();
errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
if (errno != 0) {
- PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
- << ") failed for service '" << name_ << "'";
- } else if (use_memcg) {
- if (swappiness_ != -1) {
- if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
- PLOG(ERROR) << "setProcessGroupSwappiness failed";
- }
+ if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
+ return ErrnoError() << "sending notification failed";
}
+ return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+ << ") failed for service '" << name_ << "'";
+ }
- if (soft_limit_in_bytes_ != -1) {
- if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
- PLOG(ERROR) << "setProcessGroupSoftLimit failed";
- }
- }
-
- size_t computed_limit_in_bytes = limit_in_bytes_;
- if (limit_percent_ != -1) {
- long page_size = sysconf(_SC_PAGESIZE);
- long num_pages = sysconf(_SC_PHYS_PAGES);
- if (page_size > 0 && num_pages > 0) {
- size_t max_mem = SIZE_MAX;
- if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
- max_mem = size_t(num_pages) * size_t(page_size);
- }
- computed_limit_in_bytes =
- std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
- }
- }
-
- if (!limit_property_.empty()) {
- // This ends up overwriting computed_limit_in_bytes but only if the
- // property is defined.
- computed_limit_in_bytes = android::base::GetUintProperty(
- limit_property_, computed_limit_in_bytes, SIZE_MAX);
- }
-
- if (computed_limit_in_bytes != size_t(-1)) {
- if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
- PLOG(ERROR) << "setProcessGroupLimit failed";
- }
- }
+ if (use_memcg) {
+ ConfigureMemcg();
}
if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
}
+ if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
+ return ErrnoError() << "sending notification failed";
+ }
+
NotifyStateChange("running");
reboot_on_failure.Disable();
return {};
diff --git a/init/service.h b/init/service.h
index 3289f54..d233cbf 100644
--- a/init/service.h
+++ b/init/service.h
@@ -102,6 +102,9 @@
size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
static bool is_exec_service_running() { return is_exec_service_running_; }
+ static std::chrono::time_point<std::chrono::steady_clock> exec_service_started() {
+ return exec_service_started_;
+ }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
@@ -145,9 +148,17 @@
void KillProcessGroup(int signal, bool report_oneshot = false);
void SetProcessAttributesAndCaps();
void ResetFlagsForStart();
+ Result<void> CheckConsole();
+ void ConfigureMemcg();
+ void RunService(
+ const std::optional<MountNamespace>& override_mount_namespace,
+ const std::vector<Descriptor>& descriptors,
+ std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
static unsigned long next_start_order_;
static bool is_exec_service_running_;
+ static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
+ static pid_t exec_service_pid_;
std::string name_;
std::set<std::string> classnames_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 57c311a..9e914ee 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -27,6 +27,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
+#include <processgroup/processgroup.h>
#include <system/thread_defs.h>
#include "lmkd_service.h"
@@ -202,7 +203,7 @@
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
- if (svc->interfaces().count(fullname) > 0) {
+ if (svc->interfaces().count(fullname) > 0 && !service_->is_override()) {
return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
<< " but is already defined by " << svc->name();
}
@@ -395,7 +396,15 @@
Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
args.erase(args.begin());
- service_->task_profiles_ = std::move(args);
+ if (service_->task_profiles_.empty()) {
+ service_->task_profiles_ = std::move(args);
+ } else {
+ // Some task profiles might have been added during writepid conversions
+ service_->task_profiles_.insert(service_->task_profiles_.end(),
+ std::make_move_iterator(args.begin()),
+ std::make_move_iterator(args.end()));
+ args.clear();
+ }
return {};
}
@@ -521,8 +530,37 @@
return {};
}
+// Convert legacy paths used to migrate processes between cgroups using writepid command.
+// We can't get these paths from TaskProfiles because profile definitions are changing
+// when we migrate to cgroups v2 while these hardcoded paths stay the same.
+static std::optional<const std::string> ConvertTaskFileToProfile(const std::string& file) {
+ static const std::map<const std::string, const std::string> map = {
+ {"/dev/stune/top-app/tasks", "MaxPerformance"},
+ {"/dev/stune/foreground/tasks", "HighPerformance"},
+ {"/dev/cpuset/camera-daemon/tasks", "CameraServiceCapacity"},
+ {"/dev/cpuset/foreground/tasks", "ProcessCapacityHigh"},
+ {"/dev/cpuset/system-background/tasks", "ServiceCapacityLow"},
+ {"/dev/stune/nnapi-hal/tasks", "NNApiHALPerformance"},
+ {"/dev/blkio/background/tasks", "LowIoPriority"},
+ };
+ auto iter = map.find(file);
+ return iter == map.end() ? std::nullopt : std::make_optional<const std::string>(iter->second);
+}
+
Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
args.erase(args.begin());
+ // Convert any cgroup writes into appropriate task_profiles
+ for (auto iter = args.begin(); iter != args.end();) {
+ auto task_profile = ConvertTaskFileToProfile(*iter);
+ if (task_profile) {
+ LOG(WARNING) << "'writepid " << *iter << "' is converted into 'task_profiles "
+ << task_profile.value() << "' for service " << service_->name();
+ service_->task_profiles_.push_back(task_profile.value());
+ iter = args.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
service_->writepid_files_ = std::move(args);
return {};
}
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f2383d7..eed5c65 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <grp.h>
+#include <map>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
@@ -190,8 +191,6 @@
// Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
fcntl(fd, F_SETFL, flags);
- LOG(INFO) << "Opened file '" << name << "', flags " << flags;
-
return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
}
@@ -307,6 +306,16 @@
} else {
LOG(ERROR) << "cpuset cgroup controller is not mounted!";
}
+
+ // Issue a warning whenever writepid is being used with a cgroup. This can't be done during
+ // command parsing because cgroups might not be configured at the time or parsing.
+ for (const auto& file : *files) {
+ if (CgroupGetControllerFromPath(file, nullptr)) {
+ LOG(WARNING) << "writepid usage with cgroups path '" << file
+ << "' is obsolete, please use task_profiles!";
+ }
+ }
+
std::string pid_str = std::to_string(getpid());
for (const auto& file : *files) {
if (!WriteStringToFile(pid_str, file)) {
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index e11510e..5deaf31 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -32,6 +32,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
+#include <fs_avb/fs_avb.h>
#include <libsnapshot/snapshot.h>
#include <private/android_filesystem_config.h>
#include <procinfo/process_map.h>
@@ -247,6 +248,56 @@
}
}
+/*
+ * Before starting init second stage, we will wait
+ * for snapuserd daemon to be up and running; bionic libc
+ * may read /system/etc/selinux/plat_property_contexts file
+ * before invoking main() function. This will happen if
+ * init initializes property during second stage. Any access
+ * to /system without snapuserd daemon will lead to a deadlock.
+ *
+ * Thus, we do a simple probe by reading system partition. This
+ * read will eventually be serviced by daemon confirming that
+ * daemon is up and running. Furthermore, we are still in the kernel
+ * domain and sepolicy has not been enforced yet. Thus, access
+ * to these device mapper block devices are ok even though
+ * we may see audit logs.
+ */
+bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
+ std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
+ if (fd < 0) {
+ PLOG(ERROR) << "open " << dev << " failed";
+ return false;
+ }
+
+ void* addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&addr, page_size, page_size) < 0) {
+ PLOG(ERROR) << "posix_memalign with page size " << page_size;
+ return false;
+ }
+
+ std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+ int iter = 0;
+ while (iter < 10) {
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0));
+ if (n < 0) {
+ // Wait for sometime before retry
+ std::this_thread::sleep_for(100ms);
+ } else if (n == page_size) {
+ return true;
+ } else {
+ LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size;
+ }
+
+ iter += 1;
+ }
+
+ return false;
+}
+
void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
auto fd = GetRamdiskSnapuserdFd();
if (!fd) {
@@ -268,6 +319,13 @@
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+
+ if (!TestSnapuserdIsReady()) {
+ PLOG(FATAL) << "snapuserd daemon failed to launch";
+ } else {
+ LOG(INFO) << "snapuserd daemon is up and running";
+ }
+
return;
}
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index be22afd..557d105 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -56,6 +56,7 @@
private:
void RelaunchFirstStageSnapuserd();
void ExecSnapuserd();
+ bool TestSnapuserdIsReady();
std::unique_ptr<SnapshotManager> sm_;
BlockDevInitializer block_dev_init_;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 7aa4a9d..bb3967e 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -250,7 +250,14 @@
Fork();
}
-bool Subcontext::PathMatchesSubcontext(const std::string& path) {
+bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
+ static const std::string kApexDir = "/apex/";
+ if (StartsWith(path, kApexDir)) {
+ auto begin = kApexDir.size();
+ auto end = path.find('/', begin);
+ auto apex_name = path.substr(begin, end - begin);
+ return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
+ }
for (const auto& prefix : path_prefixes_) {
if (StartsWith(path, prefix)) {
return true;
@@ -259,6 +266,10 @@
return false;
}
+void Subcontext::SetApexList(std::vector<std::string>&& apex_list) {
+ apex_list_ = std::move(apex_list);
+}
+
Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
Restart();
diff --git a/init/subcontext.h b/init/subcontext.h
index cb4138e..8acc032 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -46,7 +46,8 @@
Result<void> Execute(const std::vector<std::string>& args);
Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
void Restart();
- bool PathMatchesSubcontext(const std::string& path);
+ bool PathMatchesSubcontext(const std::string& path) const;
+ void SetApexList(std::vector<std::string>&& apex_list);
const std::string& context() const { return context_; }
pid_t pid() const { return pid_; }
@@ -56,6 +57,7 @@
Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
std::vector<std::string> path_prefixes_;
+ std::vector<std::string> apex_list_;
std::string context_;
pid_t pid_;
android::base::unique_fd socket_;
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 575b67f..86fad80 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -78,7 +78,8 @@
auto new_mount_path = new_root + mount_path;
mkdir(new_mount_path.c_str(), 0755);
if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
- PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+ PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
+ << "'" << new_mount_path << "'";
}
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 68c6b51..586e2cf 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -298,21 +298,31 @@
static UeventdConfiguration GetConfiguration() {
auto hardware = android::base::GetProperty("ro.hardware", "");
- std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
- "/ueventd." + hardware + ".rc"};
+
+ struct LegacyPathInfo {
+ std::string legacy_path;
+ std::string preferred;
+ };
+ std::vector<LegacyPathInfo> legacy_paths{
+ {"/vendor/ueventd.rc", "/vendor/etc/ueventd.rc"},
+ {"/odm/ueventd.rc", "/odm/etc/ueventd.rc"},
+ {"/ueventd." + hardware + ".rc", "another ueventd.rc file"}};
std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
- if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
+ if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
// TODO: Remove these legacy paths once Android S is no longer supported.
- canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+ for (const auto& info : legacy_paths) {
+ canonical.push_back(info.legacy_path);
+ }
} else {
// Warn if newer device is using legacy paths.
- for (const auto& path : legacy_paths) {
- if (access(path.c_str(), F_OK) == 0) {
+ for (const auto& info : legacy_paths) {
+ if (access(info.legacy_path.c_str(), F_OK) == 0) {
LOG(FATAL_WITHOUT_ABORT)
<< "Legacy ueventd configuration file detected and will not be parsed: "
- << path;
+ << info.legacy_path << ". Please move your configuration to "
+ << info.preferred << " instead.";
}
}
}
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..af6cf50 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -465,12 +465,6 @@
return FscryptAction::kNone;
}
- // Special-case /data/media/obb per b/64566063
- if (dir == "/data/media/obb") {
- // Try to set policy on this directory, but if it is non-empty this may fail.
- return FscryptAction::kAttempt;
- }
-
// Only set policy on first level /data directories
// To make this less restrictive, consider using a policy file.
// However this is overkill for as long as the policy is simply
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index e512ab7..cca7d93 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libcutils_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libcutils_test"
+ }
]
}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index e9497a8..a6835fc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -211,6 +211,7 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/snapuserd" },
{ 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" },
+ { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/fsck.f2fs" },
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 9a2305c..0575ccf 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -30,6 +30,8 @@
extern appid_t multiuser_get_app_id(uid_t uid);
extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+extern uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id);
+extern uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid);
extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 3f5e41f..a5ffb03 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -34,24 +34,6 @@
extern int qtaguid_untagSocket(int sockfd);
/*
- * For the given uid, switch counter sets.
- * The kernel only keeps a limited number of sets.
- * 2 for now.
- */
-extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
-
-/*
- * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freed.
- * The delete data also affects active tagged sockets, which are
- * then untagged.
- * The calling process can only operate on its own tags.
- * Unless it is part of the happy AID_NET_BW_ACCT group.
- * In which case it can clobber everything.
- */
-extern int qtaguid_deleteTagData(int tag, uid_t uid);
-
-/*
* Enable/disable qtaguid functionnality at a lower level.
* When pacified, the kernel will accept commands but do nothing.
*/
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 97e93f8..98ae0d4 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_THERMAL (1 << 27)
+#define ATRACE_TAG_LAST ATRACE_TAG_THERMAL
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
@@ -208,6 +209,37 @@
}
/**
+ * Trace the beginning of an asynchronous event. In addition to the name and a
+ * cookie as in ATRACE_ASYNC_BEGIN/ATRACE_ASYNC_END, a track name argument is
+ * provided, which is the name of the row where this async event should be
+ * recorded. The track name, name, and cookie used to begin an event must be
+ * used to end it.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
+ atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
+ const char* name, int32_t cookie) {
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
+ atrace_async_for_track_begin_body(track_name, name, cookie);
+ }
+}
+
+/**
+ * Trace the end of an asynchronous event.
+ * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
+ atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
+ const char* name, int32_t cookie) {
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_for_track_end_body(const char*, const char*, int32_t);
+ atrace_async_for_track_end_body(track_name, name, cookie);
+ }
+}
+
+/**
* Trace an instantaneous context. name is used to identify the context.
*
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
@@ -226,17 +258,18 @@
/**
* Trace an instantaneous context. name is used to identify the context.
- * trackName is the name of the row where the event should be recorded.
+ * track_name is the name of the row where the event should be recorded.
*
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
* in the timeline (rather than a span, in the case of begin/end events).
*/
#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
atrace_instant_for_track(ATRACE_TAG, trackName, name)
-static inline void atrace_instant_for_track(uint64_t tag, const char* trackName, const char* name) {
+static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
+ const char* name) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
void atrace_instant_for_track_body(const char*, const char*);
- atrace_instant_for_track_body(trackName, name);
+ atrace_instant_for_track_body(track_name, name);
}
}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 23d1415..8bb8652 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,11 @@
#define AID_UWB 1083 /* UWB subsystem */
#define AID_THREAD_NETWORK 1084 /* Thread Network subsystem */
#define AID_DICED 1085 /* Android's DICE daemon */
+#define AID_DMESGD 1086 /* dmesg parsing daemon for kernel report collection */
+#define AID_JC_WEAVER 1087 /* Javacard Weaver HAL - to manage omapi ARA rules */
+#define AID_JC_STRONGBOX 1088 /* Javacard Strongbox HAL - to manage omapi ARA rules */
+#define AID_JC_IDENTITYCRED 1089 /* Javacard Identity Cred HAL - to manage omapi ARA rules */
+#define AID_SDK_SANDBOX 1090 /* SDK sandbox virtual UID */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -209,6 +214,10 @@
*/
#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
+/* use the ranges below to determine whether a process is sdk sandbox */
+#define AID_SDK_SANDBOX_PROCESS_START 20000 /* start of uids allocated to sdk sandbox processes */
+#define AID_SDK_SANDBOX_PROCESS_END 29999 /* end of uids allocated to sdk sandbox processes */
+
/* use the ranges below to determine whether a process is isolated */
#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
index 7ef3854..56a39a6 100644
--- a/libcutils/include/private/android_projectid_config.h
+++ b/libcutils/include/private/android_projectid_config.h
@@ -49,3 +49,13 @@
#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
+
+/* Start of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_START 50000
+/* End of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_END 59999
+
+/* Start of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_START 60000
+/* End of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_END 69999
diff --git a/libcutils/multiuser.cpp b/libcutils/multiuser.cpp
index 0fd3d0c..967f991 100644
--- a/libcutils/multiuser.cpp
+++ b/libcutils/multiuser.cpp
@@ -29,6 +29,25 @@
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
+uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id) {
+ int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET) + sdk_sandbox_offset;
+ } else {
+ return -1;
+ }
+}
+
+uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid) {
+ appid_t app_id = multiuser_get_app_id(uid);
+ int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
+ if (app_id >= AID_SDK_SANDBOX_PROCESS_START && app_id <= AID_SDK_SANDBOX_PROCESS_END) {
+ return uid - sdk_sandbox_offset;
+ } else {
+ return -1;
+ }
+}
+
gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
diff --git a/libcutils/multiuser_test.cpp b/libcutils/multiuser_test.cpp
index 4b0fd13..b57223d 100644
--- a/libcutils/multiuser_test.cpp
+++ b/libcutils/multiuser_test.cpp
@@ -18,6 +18,7 @@
#include <gtest/gtest.h>
static constexpr auto ERR_GID = static_cast<gid_t>(-1);
+static constexpr auto ERR_UID = static_cast<uid_t>(-1);
TEST(MultiuserTest, TestMerge) {
EXPECT_EQ(0U, multiuser_get_uid(0, 0));
@@ -30,6 +31,40 @@
EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
}
+TEST(MultiuserTest, TestSdkSandboxUid) {
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 0));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 1000));
+ EXPECT_EQ(20000U, multiuser_get_sdk_sandbox_uid(0, 10000));
+ EXPECT_EQ(25000U, multiuser_get_sdk_sandbox_uid(0, 15000));
+ EXPECT_EQ(29999U, multiuser_get_sdk_sandbox_uid(0, 19999));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 50000));
+
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 0));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 1000));
+ EXPECT_EQ(1020000U, multiuser_get_sdk_sandbox_uid(10, 10000));
+ EXPECT_EQ(1025000U, multiuser_get_sdk_sandbox_uid(10, 15000));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 20000));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSdkSandboxUidConvertation) {
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(0));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(10000));
+ EXPECT_EQ(10000U, multiuser_convert_sdk_sandbox_to_app_uid(20000));
+ EXPECT_EQ(15000U, multiuser_convert_sdk_sandbox_to_app_uid(25000));
+ EXPECT_EQ(19999U, multiuser_convert_sdk_sandbox_to_app_uid(29999));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(50000));
+
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1001000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1010000));
+ EXPECT_EQ(1010000U, multiuser_convert_sdk_sandbox_to_app_uid(1020000));
+ EXPECT_EQ(1015000U, multiuser_convert_sdk_sandbox_to_app_uid(1025000));
+ EXPECT_EQ(1019999U, multiuser_convert_sdk_sandbox_to_app_uid(1029999));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1050000));
+}
+
TEST(MultiuserTest, TestSplitUser) {
EXPECT_EQ(0U, multiuser_get_user_id(0));
EXPECT_EQ(0U, multiuser_get_user_id(1000));
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index 2fe877c..a987b85 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -34,8 +34,6 @@
public:
int (*netdTagSocket)(int, uint32_t, uid_t);
int (*netdUntagSocket)(int);
- int (*netdSetCounterSet)(uint32_t, uid_t);
- int (*netdDeleteTagData)(uint32_t, uid_t);
};
int stubTagSocket(int, uint32_t, uid_t) {
@@ -46,16 +44,8 @@
return -EREMOTEIO;
}
-int stubSetCounterSet(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
-int stubDeleteTagData(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
@@ -73,15 +63,6 @@
ALOGE("load netdUntagSocket handler failed: %s", dlerror());
}
- handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
- if (!handler.netdSetCounterSet) {
- ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
- }
-
- handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
- if (!handler.netdDeleteTagData) {
- ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
- }
return handler;
}
@@ -114,13 +95,3 @@
ALOGV("Untagging socket %d", sockfd);
return getHandler().netdUntagSocket(sockfd);
}
-
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
- ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
- return getHandler().netdSetCounterSet(counterSetNum, uid);
-}
-
-int qtaguid_deleteTagData(int tag, uid_t uid) {
- ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
- return getHandler().netdDeleteTagData(tag, uid);
-}
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index ef7c72d..8901e4a 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -131,29 +131,41 @@
// Write trace events to container trace file. Note that we need to amend tid and time information
// here comparing to normal ftrace, where those informations are added by kernel.
-#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, \
+ track_name, name, value) { \
char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+ const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
int pid = getpid(); \
int tid = gettid(); \
uint64_t ts = gettime(CLOCK_MONOTONIC); \
uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
int len = snprintf( \
buf, sizeof(buf), \
- ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
- pid, tid, ts, tts, name, value); \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%s" value_format, \
+ pid, tid, ts, tts, track_name, track_name_sep, name, value); \
if (len >= (int) sizeof(buf)) { \
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
/* Truncate the name to make the message fit. */ \
if (name_len > 0) { \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
len = snprintf( \
- buf, sizeof(buf), \
- ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
- pid, tid, ts, tts, name_len, name, value); \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%.*s" value_format, \
+ pid, tid, ts, tts, track_name, track_name_sep, name_len, name, value); \
} else { \
- /* Data is still too long. Drop it. */ \
- ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
- len = 0; \
+ int track_name_len = 0; \
+ if (track_name[0] != '\0') { \
+ track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+ } \
+ if (track_name_len <= 0){ \
+ /* Data is still too long. Drop it. */ \
+ len = 0; \
+ } else { \
+ /* Truncate the trackName and name to make the message fit. */ \
+ len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s|%.1s" value_format, \
+ pid, tid, ts, tts, track_name_len, track_name, name, value); \
+ } \
} \
} \
if (len > 0) { \
@@ -161,10 +173,10 @@
} \
}
-#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, track_name, name, value) { \
pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
if (atrace_container_sock_fd != -1) { \
- WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+ WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, track_name, name, value); \
} \
pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
}
@@ -172,93 +184,115 @@
void atrace_begin_body(const char* name)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+ WRITE_MSG_IN_CONTAINER("B", "|", "%s", "", name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("B|%d|", "%s", name, "");
+ WRITE_MSG("B|%d|", "%s", "", name, "");
}
void atrace_end_body()
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+ WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "", "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("E|%d", "%s", "", "");
+ WRITE_MSG("E|%d", "%s", "", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+ WRITE_MSG_IN_CONTAINER("S", "|", "|%d", "", name, cookie);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+ WRITE_MSG_IN_CONTAINER("F", "|", "|%d", "", name, cookie);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("T", "|", "|%d", track_name, name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("U", "|", "|%d", track_name, name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
}
void atrace_instant_body(const char* name) {
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("I", "|", "%s", name, "");
+ WRITE_MSG_IN_CONTAINER("I", "|", "%s", "", name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("I|%d|", "%s", name, "");
+ WRITE_MSG("I|%d|", "%s", "", name, "");
}
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("N", "|", "|%s", trackName, name);
+ WRITE_MSG_IN_CONTAINER("N", "|", "%s", track_name, name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("N|%d|", "|%s", name, trackName);
+ WRITE_MSG("N|%d|", "%s", track_name, name, "");
}
void atrace_int_body(const char* name, int32_t value)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, "", name, value);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, "", name, value);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
}
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 25c86f4..eacc8ee 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -71,38 +71,46 @@
void atrace_begin_body(const char* name)
{
- WRITE_MSG("B|%d|", "%s", name, "");
+ WRITE_MSG("B|%d|", "%s", "", name, "");
}
void atrace_end_body()
{
- WRITE_MSG("E|%d", "%s", "", "");
+ WRITE_MSG("E|%d", "%s", "", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+ WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+ WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
}
void atrace_instant_body(const char* name) {
- WRITE_MSG("I|%d|", "%s", name, "");
+ WRITE_MSG("I|%d|", "%s", "", name, "");
}
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
- WRITE_MSG("N|%d|", "|%s", trackName, name);
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
+ WRITE_MSG("N|%d|", "%s", track_name, name, "");
}
void atrace_int_body(const char* name, int32_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 3b459e0..94945ec 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -185,21 +185,36 @@
}
}
-#define WRITE_MSG(format_begin, format_end, name, value) { \
+#define WRITE_MSG(format_begin, format_end, track_name, name, value) { \
char buf[ATRACE_MESSAGE_LENGTH] __attribute__((uninitialized)); \
+ const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
int pid = getpid(); \
- int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
- name, value); \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s%s%s" format_end, pid, \
+ track_name, track_name_sep, name, value); \
if (len >= (int) sizeof(buf)) { \
- /* Given the sizeof(buf), and all of the current format buffers, \
- * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
/* Truncate the name to make the message fit. */ \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
- len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
- name_len, name, value); \
+ if (name_len > 0) { \
+ len = snprintf(buf, sizeof(buf), format_begin "%s%s%.*s" format_end, pid, \
+ track_name, track_name_sep, name_len, name, value); \
+ } else { \
+ int track_name_len = 0; \
+ if (track_name[0] != '\0') { \
+ track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+ } \
+ if (track_name_len <= 0) { \
+ /* Data is still too long. Drop it. */ \
+ len = 0; \
+ } else { \
+ /* Truncate the trackName and name to make the message fit */ \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s|%.1s" format_end, pid, \
+ track_name_len, track_name, name, value); \
+ } \
+ } \
} \
- write(atrace_marker_fd, buf, len); \
+ if (len > 0) { \
+ write(atrace_marker_fd, buf, len); \
+ } \
}
#endif // __TRACE_DEV_INC
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index ff6d202..841674a 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,6 +195,226 @@
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_normal) {
+ atrace_async_for_track_begin_body("fake_track", "fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("T|%d|fake_track|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {
+ const int name_size = 5;
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string track_name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += track_name + "|name|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify name truncation
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ track_name += '*';
+ expected = android::base::StringPrintf("T|%d|", getpid());
+ expected += track_name + "|nam|12345";
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+ expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("T|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+ expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+ name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
+ atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("U|%d|fake_track|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
+ const int name_size = 5;
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string track_name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += track_name + "|name|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify name truncation
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ track_name += '*';
+ expected = android::base::StringPrintf("U|%d|", getpid());
+ expected += track_name + "|nam|12345";
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+ expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("U|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+ expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+ name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
TEST_F(TraceDevTest, atrace_instant_body_normal) {
atrace_instant_body("fake_name");
@@ -255,43 +475,100 @@
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_instant_for_track_body_exact) {
- const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_track_name) {
+ const int name_size = 5;
std::string expected = android::base::StringPrintf("N|%d|", getpid());
- std::string trackName = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize);
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ std::string track_name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size);
+ atrace_instant_for_track_body(track_name.c_str(), "name");
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- expected += trackName + "|name";
+ expected += track_name + "|name";
ASSERT_STREQ(expected.c_str(), actual.c_str());
- // Add a single character and verify we get the exact same value as before.
+ // Add a single character and verify name truncation
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
- trackName += '*';
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ track_name += '*';
+ expected = android::base::StringPrintf("N|%d|", getpid());
+ expected += track_name + "|nam";
+ atrace_instant_for_track_body(track_name.c_str(), "name");
EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated) {
- const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_track_name) {
std::string expected = android::base::StringPrintf("N|%d|", getpid());
- std::string trackName = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body(track_name.c_str(), "name");
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize;
- expected += android::base::StringPrintf("%.*s|name", expected_len, trackName.c_str());
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+ expected += android::base::StringPrintf("%.*s|n", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("N|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size);
+ atrace_instant_for_track_body("track_name", name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name;
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_instant_for_track_body("track_name", name.c_str());
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("N|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body("track_name", name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+ expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("N|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body(track_name.c_str(), name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+ expected +=
+ android::base::StringPrintf("%.*s|%.1s", expected_len, track_name.c_str(), name.c_str());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index b01a0ec..c2a379b 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,9 +28,12 @@
void atrace_end_body() { }
void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
+ int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
+ int32_t /*cookie*/) {}
void atrace_instant_body(const char* /*name*/) {}
-void atrace_instant_for_track_body(const char* /*trackName*/,
- const char* /*name*/) {}
+void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
void atrace_init() {}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index a3d643e..f523d4e 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -27,12 +27,8 @@
darwin: {
enabled: false,
},
- linux_glibc: {
+ host_linux: {
cflags: [
- "-O2",
- "-g",
- "-W",
- "-Wall",
"-D_LARGEFILE64_SOURCE",
],
},
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index f31b5f1..16103d2 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -44,4 +44,9 @@
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
}
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
index 526b1e4..888593e 100644
--- a/libmodprobe/TEST_MAPPING
+++ b/libmodprobe/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libmodprobe_tests"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libmodprobe_tests"
+ }
]
}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index c934860..5d79d6a 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,17 +16,21 @@
#pragma once
+#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
+#include <android-base/thread_annotations.h>
+
class Modprobe {
public:
Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
bool use_blocklist = true);
+ bool LoadModulesParallel(int num_threads);
bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
const std::string& parameters = "");
@@ -66,7 +70,9 @@
std::vector<std::string> module_load_;
std::unordered_map<std::string, std::string> module_options_;
std::set<std::string> module_blocklist_;
+ std::mutex module_loaded_lock_;
std::unordered_set<std::string> module_loaded_;
+ std::unordered_set<std::string> module_loaded_paths_;
int module_count_ = 0;
bool blocklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1a9d364..3054d2b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -21,8 +21,10 @@
#include <sys/syscall.h>
#include <algorithm>
+#include <map>
#include <set>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -437,6 +439,97 @@
return module_blocklist_.count(canonical_name) > 0;
}
+// Another option to load kernel modules. load in independent modules in parallel
+// and then load modules which only have soft dependency, third update dependency list of other
+// remaining modules, repeat these steps until all modules are loaded.
+bool Modprobe::LoadModulesParallel(int num_threads) {
+ bool ret = true;
+ std::map<std::string, std::set<std::string>> mod_with_deps;
+ std::map<std::string, std::set<std::string>> mod_with_softdeps;
+
+ // Get dependencies
+ for (const auto& module : module_load_) {
+ auto dependencies = GetDependencies(MakeCanonical(module));
+
+ for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
+ mod_with_deps[module].emplace(*dep);
+ }
+ }
+
+ // Get soft dependencies
+ for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
+ mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ }
+
+ // Get soft post dependencies
+ for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
+ mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ }
+
+ while (!mod_with_deps.empty()) {
+ std::vector<std::thread> threads;
+ std::vector<std::string> mods_path_to_load;
+ std::vector<std::string> mods_with_softdep_to_load;
+ std::mutex vector_lock;
+
+ // Find independent modules and modules only having soft dependencies
+ for (const auto& [it_mod, it_dep] : mod_with_deps) {
+ if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
+ mods_path_to_load.emplace_back(*(it_dep.begin()));
+ } else if (it_dep.size() == 1) {
+ mods_with_softdep_to_load.emplace_back(it_mod);
+ }
+ }
+
+ // Load independent modules in parallel
+ auto thread_function = [&] {
+ std::unique_lock lk(vector_lock);
+ while (!mods_path_to_load.empty()) {
+ auto mod_path_to_load = std::move(mods_path_to_load.back());
+ mods_path_to_load.pop_back();
+
+ lk.unlock();
+ ret &= Insmod(mod_path_to_load, "");
+ lk.lock();
+ }
+ };
+
+ std::generate_n(std::back_inserter(threads), num_threads,
+ [&] { return std::thread(thread_function); });
+
+ // Wait for the threads.
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ // Since we cannot assure if these soft dependencies tree are overlap,
+ // we loaded these modules one by one.
+ for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
+ dep++) {
+ ret &= LoadWithAliases(*dep, true);
+ }
+
+ std::lock_guard guard(module_loaded_lock_);
+ // Remove loaded module form mod_with_deps and soft dependencies of other modules
+ for (const auto& module_loaded : module_loaded_) {
+ mod_with_deps.erase(module_loaded);
+
+ for (auto& [mod, softdeps] : mod_with_softdeps) {
+ softdeps.erase(module_loaded);
+ }
+ }
+
+ // Remove loaded module form dependencies of other modules which are not loaded yet
+ for (const auto& module_loaded_path : module_loaded_paths_) {
+ for (auto& [mod, deps] : mod_with_deps) {
+ deps.erase(module_loaded_path);
+ }
+ }
+ }
+
+ return ret;
+}
+
bool Modprobe::LoadListedModules(bool strict) {
auto ret = true;
for (const auto& module : module_load_) {
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index fb1f5e7..94a1dc4 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -54,6 +54,8 @@
if (ret != 0) {
if (errno == EEXIST) {
// Module already loaded
+ std::lock_guard guard(module_loaded_lock_);
+ module_loaded_paths_.emplace(path_name);
module_loaded_.emplace(canonical_name);
return true;
}
@@ -62,6 +64,8 @@
}
LOG(INFO) << "Loaded kernel module " << path_name;
+ std::lock_guard guard(module_loaded_lock_);
+ module_loaded_paths_.emplace(path_name);
module_loaded_.emplace(canonical_name);
module_count_++;
return true;
@@ -74,6 +78,7 @@
PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
return false;
}
+ std::lock_guard guard(module_loaded_lock_);
module_loaded_.erase(canonical_name);
return true;
}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index f960b61..f92a2b6 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -188,10 +188,11 @@
EXPECT_TRUE(modules_loaded == expected_after_remove);
- m = Modprobe({dir.path});
- EXPECT_FALSE(m.LoadWithAliases("test4", true));
- while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
- EXPECT_TRUE(m.LoadListedModules());
+ Modprobe m2({dir.path});
+
+ EXPECT_FALSE(m2.LoadWithAliases("test4", true));
+ while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front()));
+ EXPECT_TRUE(m2.LoadListedModules());
GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
for (auto i = expected_modules_blocklist_enabled.begin();
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index 51773f9..d69a7fb 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libpackagelistparser_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libpackagelistparser_test"
+ }
]
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c68552d..7b0e0d3 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -73,3 +73,29 @@
],
min_sdk_version: "29",
}
+
+cc_test {
+ name: "task_profiles_test",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wexit-time-destructors",
+ "-Wno-unused-parameter",
+ ],
+ srcs: [
+ "task_profiles_test.cpp",
+ ],
+ header_libs: [
+ "libcutils_headers",
+ "libprocessgroup_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libprocessgroup",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 352847a..8c00326 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -231,7 +231,8 @@
const ACgroupController* controller = ACgroupFile_getController(i);
if (ACgroupController_getFlags(controller) &
CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
- std::string str = std::string("+") + ACgroupController_getName(controller);
+ std::string str("+");
+ str.append(ACgroupController_getName(controller));
if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
return -errno;
}
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index be34f95..39b9f3f 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -35,6 +35,8 @@
#ifndef __ANDROID_VNDK__
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+
static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
bool UsePerAppMemcg();
@@ -65,6 +67,7 @@
bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
void removeAllProcessGroups(void);
+void removeAllEmptyProcessGroups(void);
// Provides the path for an attribute in a specific process group
// Returns false in case of error, true in case of success
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0320b02..267e62c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -69,6 +69,23 @@
return true;
}
+static bool CgroupGetMemcgAppsPath(std::string* path) {
+ CgroupController controller = CgroupMap::GetInstance().FindController("memory");
+
+ if (!controller.HasValue()) {
+ return false;
+ }
+
+ if (path) {
+ *path = controller.path();
+ if (controller.version() == 1) {
+ *path += "/apps";
+ }
+ }
+
+ return true;
+}
+
bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name) {
auto controller = CgroupMap::GetInstance().FindControllerByPath(path);
@@ -85,7 +102,7 @@
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -100,7 +117,7 @@
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -126,17 +143,37 @@
}
void DropTaskProfilesResourceCaching() {
- TaskProfiles::GetInstance().DropResourceCaching();
+ TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);
+ TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);
}
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
- return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
+}
+
+bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
}
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
}
+// C wrapper for SetProcessProfiles.
+// No need to have this in the header file because this function is specifically for crosvm. Crosvm
+// which is written in Rust has its own declaration of this foreign function and doesn't rely on the
+// header. See
+// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
+extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
+ const char* profiles[]) {
+ std::vector<std::string> profiles_;
+ profiles_.reserve(num_profiles);
+ for (size_t i = 0; i < num_profiles; i++) {
+ profiles_.emplace_back(profiles[i]);
+ }
+ return SetProcessProfiles(uid, pid, profiles_);
+}
+
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
return StringPrintf("%s/uid_%d", cgroup, uid);
}
@@ -163,7 +200,7 @@
return ret;
}
-static bool RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
bool empty = true;
if (uid != NULL) {
@@ -178,6 +215,21 @@
}
auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
+ if (empty_only) {
+ struct stat st;
+ auto procs_file = StringPrintf("%s/%s", path.c_str(),
+ PROCESSGROUP_CGROUP_PROCS_FILE);
+ if (stat(procs_file.c_str(), &st) == -1) {
+ PLOG(ERROR) << "Failed to get stats for " << procs_file;
+ continue;
+ }
+ if (st.st_size > 0) {
+ // skip non-empty groups
+ LOG(VERBOSE) << "Skipping non-empty group " << path;
+ empty = false;
+ continue;
+ }
+ }
LOG(VERBOSE) << "Removing " << path;
if (rmdir(path.c_str()) == -1) {
if (errno != EBUSY) {
@@ -190,23 +242,21 @@
return empty;
}
-void removeAllProcessGroups() {
- LOG(VERBOSE) << "removeAllProcessGroups()";
-
+void removeAllProcessGroupsInternal(bool empty_only) {
std::vector<std::string> cgroups;
- std::string path;
+ std::string path, memcg_apps_path;
if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
cgroups.push_back(path);
}
- if (CgroupGetControllerPath("memory", &path)) {
- cgroups.push_back(path + "/apps");
+ if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
+ cgroups.push_back(memcg_apps_path);
}
for (std::string cgroup_root_path : cgroups) {
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -219,7 +269,7 @@
}
auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- if (!RemoveUidProcessGroups(path)) {
+ if (!RemoveUidProcessGroups(path, empty_only)) {
LOG(VERBOSE) << "Skip removing " << path;
continue;
}
@@ -232,6 +282,16 @@
}
}
+void removeAllProcessGroups() {
+ LOG(VERBOSE) << "removeAllProcessGroups()";
+ removeAllProcessGroupsInternal(false);
+}
+
+void removeAllEmptyProcessGroups() {
+ LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
+ removeAllProcessGroupsInternal(true);
+}
+
/**
* Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
* the user root. Ownership for the newly created cgroup and all of its files must thus be
@@ -292,7 +352,8 @@
// This happens when process is already dead
return 0;
}
- PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+ << initialPid;
return -1;
}
@@ -405,10 +466,11 @@
int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- std::string memory_path;
- CgroupGetControllerPath("memory", &memory_path);
- memory_path += "/apps";
- if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
+ std::string memcg_apps_path;
+ if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
+ RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+ return -1;
+ }
}
return err;
@@ -436,11 +498,11 @@
struct stat cgroup_stat;
mode_t cgroup_mode = 0750;
- gid_t cgroup_uid = AID_SYSTEM;
- uid_t cgroup_gid = AID_SYSTEM;
+ uid_t cgroup_uid = AID_SYSTEM;
+ gid_t cgroup_gid = AID_SYSTEM;
int ret = 0;
- if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
+ if (stat(cgroup.c_str(), &cgroup_stat) < 0) {
PLOG(ERROR) << "Failed to get stats for " << cgroup;
} else {
cgroup_mode = cgroup_stat.st_mode;
@@ -485,10 +547,12 @@
return -EINVAL;
}
- if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- CgroupGetControllerPath("memory", &cgroup);
- cgroup += "/apps";
- int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
+ if (std::string memcg_apps_path;
+ isMemoryCgroupSupported() && UsePerAppMemcg() && CgroupGetMemcgAppsPath(&memcg_apps_path)) {
+ // Note by bvanassche: passing 'false' as fourth argument below implies that the v1
+ // hierarchy is used. It is not clear to me whether the above conditions guarantee that the
+ // v1 hierarchy is used.
+ int ret = createProcessGroupInternal(uid, initialPid, memcg_apps_path, false);
if (ret != 0) {
return ret;
}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0634220..3e4393d 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -3,7 +3,7 @@
{
"Controller": "blkio",
"Path": "/dev/blkio",
- "Mode": "0755",
+ "Mode": "0775",
"UID": "system",
"GID": "system"
},
@@ -32,16 +32,13 @@
],
"Cgroups2": {
"Path": "/sys/fs/cgroup",
- "Mode": "0755",
+ "Mode": "0775",
"UID": "system",
"GID": "system",
"Controllers": [
{
"Controller": "freezer",
- "Path": ".",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
+ "Path": "."
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b668dcb..f5533c2 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -16,14 +16,21 @@
"File": "top-app/cpus"
},
{
+ "Name": "MemStats",
+ "Controller": "memory",
+ "File": "memory.stat"
+ },
+ {
"Name": "MemLimit",
"Controller": "memory",
- "File": "memory.limit_in_bytes"
+ "File": "memory.limit_in_bytes",
+ "FileV2": "memory.max"
},
{
"Name": "MemSoftLimit",
"Controller": "memory",
- "File": "memory.soft_limit_in_bytes"
+ "File": "memory.soft_limit_in_bytes",
+ "FileV2": "memory.low"
},
{
"Name": "MemSwappiness",
@@ -31,6 +38,26 @@
"File": "memory.swappiness"
},
{
+ "Name": "MemUsage",
+ "Controller": "memory",
+ "File": "memory.usage_in_bytes"
+ },
+ {
+ "Name": "MemAndSwapUsage",
+ "Controller": "memory",
+ "File": "memory.memsw.usage_in_bytes"
+ },
+ {
+ "Name": "MemPressureLevel",
+ "Controller": "memory",
+ "File": "memory.pressure_level"
+ },
+ {
+ "Name": "MemCgroupEventControl",
+ "Controller": "memory",
+ "File": "cgroup.event_control"
+ },
+ {
"Name": "UClampMin",
"Controller": "cpu",
"File": "cpu.uclamp.min"
@@ -197,6 +224,19 @@
]
},
{
+ "Name": "VMCompilationPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpu",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
"Name": "CpuPolicySpread",
"Actions": [
{
@@ -408,7 +448,6 @@
}
]
},
-
{
"Name": "LowIoPriority",
"Actions": [
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 1de4395..ebcd9b5 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -25,11 +25,13 @@
repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
}
-// Next: 4
+// Next: 6
message Attribute {
string name = 1 [json_name = "Name"];
string controller = 2 [json_name = "Controller"];
string file = 3 [json_name = "File"];
+ string filev2 = 4 [json_name = "FileV2"];
+ string optional = 5 [json_name = "Optional"];
}
// Next: 3
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3121d24..3831ef2 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -147,12 +147,17 @@
static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* descriptors,
const Json::Value& cgroup, const std::string& name,
const std::string& root_path, int cgroups_version) {
+ const std::string cgroup_path = cgroup["Path"].asString();
std::string path;
if (!root_path.empty()) {
- path = root_path + "/" + cgroup["Path"].asString();
+ path = root_path;
+ if (cgroup_path != ".") {
+ path += "/";
+ path += cgroup_path;
+ }
} else {
- path = cgroup["Path"].asString();
+ path = cgroup_path;
}
uint32_t controller_flags = 0;
@@ -263,8 +268,18 @@
return false;
}
- result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- nullptr);
+ // The memory_recursiveprot mount option has been introduced by kernel commit
+ // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
+ // mount with that option enabled. If mounting fails because the kernel is too old,
+ // retry without that mount option.
+ if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ "memory_recursiveprot") < 0) {
+ LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
+ if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr) < 0) {
+ PLOG(ERROR) << "Failed to mount cgroup v2";
+ }
+ }
// selinux permissions change after mounting, so it's ok to change mode and owner now
if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 3834f91..e1c5934 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -51,6 +51,69 @@
static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
"/etc/task_profiles/task_profiles_%u.json";
+class FdCacheHelper {
+ public:
+ enum FdState {
+ FDS_INACCESSIBLE = -1,
+ FDS_APP_DEPENDENT = -2,
+ FDS_NOT_CACHED = -3,
+ };
+
+ static void Cache(const std::string& path, android::base::unique_fd& fd);
+ static void Drop(android::base::unique_fd& fd);
+ static void Init(const std::string& path, android::base::unique_fd& fd);
+ static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
+
+ private:
+ static bool IsAppDependentPath(const std::string& path);
+};
+
+void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
+ // file descriptors for app-dependent paths can't be cached
+ if (IsAppDependentPath(path)) {
+ // file descriptor is not cached
+ fd.reset(FDS_APP_DEPENDENT);
+ return;
+ }
+ // file descriptor can be cached later on request
+ fd.reset(FDS_NOT_CACHED);
+}
+
+void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
+ if (fd != FDS_NOT_CACHED) {
+ return;
+ }
+
+ if (access(path.c_str(), W_OK) != 0) {
+ // file is not accessible
+ fd.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (tmp_fd < 0) {
+ PLOG(ERROR) << "Failed to cache fd '" << path << "'";
+ fd.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ fd = std::move(tmp_fd);
+}
+
+void FdCacheHelper::Drop(android::base::unique_fd& fd) {
+ if (fd == FDS_NOT_CACHED) {
+ return;
+ }
+
+ fd.reset(FDS_NOT_CACHED);
+}
+
+bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
+ return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
+IProfileAttribute::~IProfileAttribute() = default;
+
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
file_name_ = file_name;
@@ -66,11 +129,12 @@
return true;
}
+ const std::string& file_name =
+ controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
- file_name_.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
}
return true;
}
@@ -122,6 +186,12 @@
return true;
}
+#else
+
+bool SetTimerSlackAction::ExecuteForTask(int) const {
+ return true;
+};
+
#endif
bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -137,6 +207,17 @@
}
if (!WriteStringToFile(value_, path)) {
+ if (access(path.c_str(), F_OK) < 0) {
+ if (optional_) {
+ return true;
+ } else {
+ LOG(ERROR) << "No such cgroup attribute: " << path;
+ return false;
+ }
+ }
+ // The PLOG() statement below uses the error code stored in `errno` by
+ // WriteStringToFile() because access() only overwrites `errno` if it fails
+ // and because this code is only reached if the access() function returns 0.
PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
return false;
}
@@ -144,57 +225,11 @@
return true;
}
-void CachedFdProfileAction::EnableResourceCaching() {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (fd_ != FDS_NOT_CACHED) {
- return;
- }
-
- std::string tasks_path = GetPath();
-
- if (access(tasks_path.c_str(), W_OK) != 0) {
- // file is not accessible
- fd_.reset(FDS_INACCESSIBLE);
- return;
- }
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
- fd_.reset(FDS_INACCESSIBLE);
- return;
- }
-
- fd_ = std::move(fd);
-}
-
-void CachedFdProfileAction::DropResourceCaching() {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (fd_ == FDS_NOT_CACHED) {
- return;
- }
-
- fd_.reset(FDS_NOT_CACHED);
-}
-
-bool CachedFdProfileAction::IsAppDependentPath(const std::string& path) {
- return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
-}
-
-void CachedFdProfileAction::InitFd(const std::string& path) {
- // file descriptors for app-dependent paths can't be cached
- if (IsAppDependentPath(path)) {
- // file descriptor is not cached
- fd_.reset(FDS_APP_DEPENDENT);
- return;
- }
- // file descriptor can be cached later on request
- fd_.reset(FDS_NOT_CACHED);
-}
-
SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
- InitFd(controller_.GetTasksFilePath(path_));
+ FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
+ // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+ FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
}
bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
@@ -232,7 +267,40 @@
return false;
}
+ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
+ int id) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (FdCacheHelper::IsCached(fd_[cache_type])) {
+ // fd is cached, reuse it
+ if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return ProfileAction::FAIL;
+ }
+ return ProfileAction::SUCCESS;
+ }
+
+ if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
+ // no permissions to access the file, ignore
+ return ProfileAction::SUCCESS;
+ }
+
+ if (cache_type == ResourceCacheType::RCT_TASK &&
+ fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ PLOG(ERROR) << "Application profile can't be applied to a thread";
+ return ProfileAction::FAIL;
+ }
+
+ return ProfileAction::UNUSED;
+}
+
bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
+ }
+
+ // fd was not cached or cached fd can't be used
std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
@@ -248,28 +316,12 @@
}
bool SetCgroupAction::ExecuteForTask(int tid) const {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- if (IsFdValid()) {
- // fd is cached, reuse it
- if (!AddTidToCgroup(tid, fd_, controller()->name())) {
- LOG(ERROR) << "Failed to add task into cgroup";
- return false;
- }
- return true;
+ CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
}
- if (fd_ == FDS_INACCESSIBLE) {
- // no permissions to access the file, ignore
- return true;
- }
-
- if (fd_ == FDS_APP_DEPENDENT) {
- // application-dependent path can't be used with tid
- PLOG(ERROR) << "Application profile can't be applied to a thread";
- return false;
- }
-
- // fd was not cached because cached fd can't be used
+ // fd was not cached or cached fd can't be used
std::string tasks_path = controller()->GetTasksFilePath(path_);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
@@ -284,14 +336,59 @@
return true;
}
-WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
- bool logfailures)
- : path_(path), value_(value), logfailures_(logfailures) {
- InitFd(path_);
+void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
+ // include regex evaluations
+ if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+ return;
+ }
+ switch (cache_type) {
+ case (ProfileAction::RCT_TASK):
+ FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
+ break;
+ case (ProfileAction::RCT_PROCESS):
+ // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
+ FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
+ break;
+ default:
+ LOG(ERROR) << "Invalid cache type is specified!";
+ break;
+ }
}
-bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path,
- bool logfailures) {
+void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ FdCacheHelper::Drop(fd_[cache_type]);
+}
+
+WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
+ const std::string& value, bool logfailures)
+ : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
+ FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);
+ if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);
+}
+
+bool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,
+ int uid, int pid, bool logfailures) const {
+ std::string value(value_);
+
+ value = StringReplace(value, "<uid>", std::to_string(uid), true);
+ value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+ CacheUseResult result = UseCachedFd(cache_type, value);
+
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
+ }
+
+ std::string path;
+ if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
+ path = task_path_;
+ } else {
+ path = proc_path_;
+ }
+
// Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
// O_TRUNC which causes kernfs_mutex contention
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
@@ -309,55 +406,101 @@
return true;
}
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
+ const std::string& value) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
- std::string value(value_);
- std::string path(path_);
-
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(pid), true);
- path = StringReplace(path, "<uid>", std::to_string(uid), true);
- path = StringReplace(path, "<pid>", std::to_string(pid), true);
-
- return WriteValueToFile(value, path, logfailures_);
-}
-
-bool WriteFileAction::ExecuteForTask(int tid) const {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- std::string value(value_);
- int uid = getuid();
-
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(tid), true);
-
- if (IsFdValid()) {
+ if (FdCacheHelper::IsCached(fd_[cache_type])) {
// fd is cached, reuse it
- if (!WriteStringToFd(value, fd_)) {
- if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
- return false;
+ bool ret = WriteStringToFd(value, fd_[cache_type]);
+
+ if (!ret && logfailures_) {
+ if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << task_path_;
+ } else {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << proc_path_;
+ }
}
- return true;
+ return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;
}
- if (fd_ == FDS_INACCESSIBLE) {
+ if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
// no permissions to access the file, ignore
- return true;
+ return ProfileAction::SUCCESS;
}
- if (fd_ == FDS_APP_DEPENDENT) {
+ if (cache_type == ResourceCacheType::RCT_TASK &&
+ fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
// application-dependent path can't be used with tid
PLOG(ERROR) << "Application profile can't be applied to a thread";
+ return ProfileAction::FAIL;
+ }
+ return ProfileAction::UNUSED;
+}
+
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ if (!proc_path_.empty()) {
+ return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);
+ }
+
+ DIR* d;
+ struct dirent* de;
+ char proc_path[255];
+ int t_pid;
+
+ sprintf(proc_path, "/proc/%d/task", pid);
+ if (!(d = opendir(proc_path))) {
return false;
}
- return WriteValueToFile(value, path_, logfailures_);
+ while ((de = readdir(d))) {
+ if (de->d_name[0] == '.') {
+ continue;
+ }
+
+ t_pid = atoi(de->d_name);
+
+ if (!t_pid) {
+ continue;
+ }
+
+ WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);
+ }
+
+ closedir(d);
+
+ return true;
+}
+
+bool WriteFileAction::ExecuteForTask(int tid) const {
+ return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);
+}
+
+void WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+ return;
+ }
+ switch (cache_type) {
+ case (ProfileAction::RCT_TASK):
+ FdCacheHelper::Cache(task_path_, fd_[cache_type]);
+ break;
+ case (ProfileAction::RCT_PROCESS):
+ if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);
+ break;
+ default:
+ LOG(ERROR) << "Invalid cache type is specified!";
+ break;
+ }
+}
+
+void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ FdCacheHelper::Drop(fd_[cache_type]);
}
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& profile : profiles_) {
- if (!profile->ExecuteForProcess(uid, pid)) {
- PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
- }
+ profile->ExecuteForProcess(uid, pid);
}
return true;
}
@@ -369,15 +512,15 @@
return true;
}
-void ApplyProfileAction::EnableResourceCaching() {
+void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
for (const auto& profile : profiles_) {
- profile->EnableResourceCaching();
+ profile->EnableResourceCaching(cache_type);
}
}
-void ApplyProfileAction::DropResourceCaching() {
+void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
for (const auto& profile : profiles_) {
- profile->DropResourceCaching();
+ profile->DropResourceCaching(cache_type);
}
}
@@ -389,6 +532,7 @@
bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& element : elements_) {
if (!element->ExecuteForProcess(uid, pid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
@@ -401,39 +545,40 @@
}
for (const auto& element : elements_) {
if (!element->ExecuteForTask(tid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
return true;
}
-void TaskProfile::EnableResourceCaching() {
+void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
if (res_cached_) {
return;
}
for (auto& element : elements_) {
- element->EnableResourceCaching();
+ element->EnableResourceCaching(cache_type);
}
res_cached_ = true;
}
-void TaskProfile::DropResourceCaching() {
+void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
if (!res_cached_) {
return;
}
for (auto& element : elements_) {
- element->DropResourceCaching();
+ element->DropResourceCaching(cache_type);
}
res_cached_ = false;
}
-void TaskProfiles::DropResourceCaching() const {
+void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
for (auto& iter : profiles_) {
- iter.second->DropResourceCaching();
+ iter.second->DropResourceCaching(cache_type);
}
}
@@ -457,8 +602,7 @@
android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
- LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
- << "] failed";
+ LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
}
}
}
@@ -493,12 +637,19 @@
std::string name = attr[i]["Name"].asString();
std::string controller_name = attr[i]["Controller"].asString();
std::string file_attr = attr[i]["File"].asString();
+ std::string file_v2_attr = attr[i]["FileV2"].asString();
+
+ if (!file_v2_attr.empty() && file_attr.empty()) {
+ LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
+ return false;
+ }
auto controller = cg_map.FindController(controller_name);
if (controller.HasValue()) {
auto iter = attributes_.find(name);
if (iter == attributes_.end()) {
- attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
+ attributes_[name] =
+ std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
} else {
iter->second->Reset(controller, file_attr);
}
@@ -513,7 +664,7 @@
std::string profile_name = profile_val["Name"].asString();
const Json::Value& actions = profile_val["Actions"];
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(profile_name);
for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
const Json::Value& action_val = actions[act_idx];
@@ -543,11 +694,12 @@
} else if (action_name == "SetAttribute") {
std::string attr_name = params_val["Name"].asString();
std::string attr_value = params_val["Value"].asString();
+ bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
auto iter = attributes_.find(attr_name);
if (iter != attributes_.end()) {
- profile->Add(
- std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
+ profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
+ attr_value, optional));
} else {
LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
}
@@ -570,12 +722,14 @@
}
} else if (action_name == "WriteFile") {
std::string attr_filepath = params_val["FilePath"].asString();
+ std::string attr_procfilepath = params_val["ProcFilePath"].asString();
std::string attr_value = params_val["Value"].asString();
+ // FilePath and Value are mandatory
if (!attr_filepath.empty() && !attr_value.empty()) {
std::string attr_logfailures = params_val["LogFailures"].asString();
bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
- profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
- logfailures));
+ profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,
+ attr_value, logfailures));
} else if (attr_filepath.empty()) {
LOG(WARNING) << "WriteFile: invalid parameter: "
<< "empty filepath";
@@ -623,7 +777,7 @@
}
}
if (ret) {
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
profile->Add(std::make_unique<ApplyProfileAction>(profiles));
profiles_[aggregateprofile_name] = profile;
}
@@ -641,7 +795,7 @@
return nullptr;
}
-const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
auto iter = attributes_.find(name);
if (iter != attributes_.end()) {
@@ -651,34 +805,43 @@
}
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
- const std::vector<std::string>& profiles) {
- for (const auto& name : profiles) {
- TaskProfile* profile = GetProfile(name);
- if (profile != nullptr) {
- if (!profile->ExecuteForProcess(uid, pid)) {
- PLOG(WARNING) << "Failed to apply " << name << " process profile";
- }
- } else {
- PLOG(WARNING) << "Failed to find " << name << "process profile";
- }
- }
- return true;
-}
-
-bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
- bool use_fd_cache) {
+ const std::vector<std::string>& profiles, bool use_fd_cache) {
+ bool success = true;
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
if (use_fd_cache) {
- profile->EnableResourceCaching();
+ profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
+ }
+ if (!profile->ExecuteForProcess(uid, pid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ success = false;
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << " process profile";
+ success = false;
+ }
+ }
+ return success;
+}
+
+bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
+ bool use_fd_cache) {
+ bool success = true;
+ for (const auto& name : profiles) {
+ TaskProfile* profile = GetProfile(name);
+ if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching(ProfileAction::RCT_TASK);
}
if (!profile->ExecuteForTask(tid)) {
PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << "task profile";
+ PLOG(WARNING) << "Failed to find " << name << " task profile";
+ success = false;
}
}
- return true;
+ return success;
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 278892d..df08f65 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,33 +26,55 @@
#include <android-base/unique_fd.h>
#include <cgroup_map.h>
-class ProfileAttribute {
+class IProfileAttribute {
public:
- ProfileAttribute(const CgroupController& controller, const std::string& file_name)
- : controller_(controller), file_name_(file_name) {}
+ virtual ~IProfileAttribute() = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual const CgroupController* controller() const = 0;
+ virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+};
- const CgroupController* controller() const { return &controller_; }
- const std::string& file_name() const { return file_name_; }
- void Reset(const CgroupController& controller, const std::string& file_name);
+class ProfileAttribute : public IProfileAttribute {
+ public:
+ // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is
+ // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for
+ // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.
+ ProfileAttribute(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name)
+ : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}
+ ~ProfileAttribute() = default;
- bool GetPathForTask(int tid, std::string* path) const;
+ const CgroupController* controller() const override { return &controller_; }
+ const std::string& file_name() const override { return file_name_; }
+ void Reset(const CgroupController& controller, const std::string& file_name) override;
+
+ bool GetPathForTask(int tid, std::string* path) const override;
private:
CgroupController controller_;
std::string file_name_;
+ std::string file_v2_name_;
};
// Abstract profile element
class ProfileAction {
public:
+ enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };
+
virtual ~ProfileAction() {}
+ virtual const char* Name() const = 0;
+
// Default implementations will fail
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
virtual bool ExecuteForTask(int) const { return false; };
- virtual void EnableResourceCaching() {}
- virtual void DropResourceCaching() {}
+ virtual void EnableResourceCaching(ResourceCacheType) {}
+ virtual void DropResourceCaching(ResourceCacheType) {}
+
+ protected:
+ enum CacheUseResult { SUCCESS, FAIL, UNUSED };
};
// Profile actions
@@ -60,22 +82,21 @@
public:
SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetClamps"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
protected:
int boost_;
int clamp_;
};
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
class SetTimerSlackAction : public ProfileAction {
public:
SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetTimerSlack"; }
+ bool ExecuteForTask(int tid) const override;
private:
unsigned long slack_;
@@ -83,107 +104,83 @@
static bool IsTimerSlackSupported(int tid);
};
-#else
-
-class SetTimerSlackAction : public ProfileAction {
- public:
- SetTimerSlackAction(unsigned long) noexcept {}
-
- virtual bool ExecuteForTask(int) const { return true; }
-};
-
-#endif
-
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
- : attribute_(attribute), value_(value) {}
+ SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional)
+ : attribute_(attribute), value_(value), optional_(optional) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetAttribute"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
private:
- const ProfileAttribute* attribute_;
+ const IProfileAttribute* attribute_;
std::string value_;
-};
-
-// Abstract profile element for cached fd
-class CachedFdProfileAction : public ProfileAction {
- public:
- virtual void EnableResourceCaching();
- virtual void DropResourceCaching();
-
- protected:
- enum FdState {
- FDS_INACCESSIBLE = -1,
- FDS_APP_DEPENDENT = -2,
- FDS_NOT_CACHED = -3,
- };
-
- android::base::unique_fd fd_;
- mutable std::mutex fd_mutex_;
-
- static bool IsAppDependentPath(const std::string& path);
-
- void InitFd(const std::string& path);
- bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
-
- virtual const std::string GetPath() const = 0;
+ bool optional_;
};
// Set cgroup profile element
-class SetCgroupAction : public CachedFdProfileAction {
+class SetCgroupAction : public ProfileAction {
public:
SetCgroupAction(const CgroupController& c, const std::string& p);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetCgroup"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
const CgroupController* controller() const { return &controller_; }
- protected:
- const std::string GetPath() const override { return controller_.GetTasksFilePath(path_); }
-
private:
CgroupController controller_;
std::string path_;
+ android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
+ mutable std::mutex fd_mutex_;
static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+ CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
};
// Write to file action
-class WriteFileAction : public CachedFdProfileAction {
+class WriteFileAction : public ProfileAction {
public:
- WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
+ WriteFileAction(const std::string& task_path, const std::string& proc_path,
+ const std::string& value, bool logfailures);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
-
- protected:
- const std::string GetPath() const override { return path_; }
+ const char* Name() const override { return "WriteFile"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
private:
- std::string path_, value_;
+ std::string task_path_, proc_path_, value_;
bool logfailures_;
+ android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
+ mutable std::mutex fd_mutex_;
- static bool WriteValueToFile(const std::string& value, const std::string& path,
- bool logfailures);
+ bool WriteValueToFile(const std::string& value, ResourceCacheType cache_type, int uid, int pid,
+ bool logfailures) const;
+ CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
};
class TaskProfile {
public:
- TaskProfile() : res_cached_(false) {}
+ TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}
+ const std::string& Name() const { return name_; }
void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
void MoveTo(TaskProfile* profile);
bool ExecuteForProcess(uid_t uid, pid_t pid) const;
bool ExecuteForTask(int tid) const;
- void EnableResourceCaching();
- void DropResourceCaching();
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
private:
+ const std::string name_;
bool res_cached_;
std::vector<std::unique_ptr<ProfileAction>> elements_;
};
@@ -194,10 +191,11 @@
ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
: profiles_(profiles) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching();
- virtual void DropResourceCaching();
+ const char* Name() const override { return "ApplyProfileAction"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -209,14 +207,15 @@
static TaskProfiles& GetInstance();
TaskProfile* GetProfile(const std::string& name) const;
- const ProfileAttribute* GetAttribute(const std::string& name) const;
- void DropResourceCaching() const;
- bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+ const IProfileAttribute* GetAttribute(const std::string& name) const;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
+ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+ bool use_fd_cache);
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
private:
std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
- std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+ std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
TaskProfiles();
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
new file mode 100644
index 0000000..09ac44c
--- /dev/null
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 "task_profiles.h"
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <mntent.h>
+#include <processgroup/processgroup.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+
+using ::android::base::ERROR;
+using ::android::base::LogFunction;
+using ::android::base::LogId;
+using ::android::base::LogSeverity;
+using ::android::base::SetLogger;
+using ::android::base::VERBOSE;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+namespace {
+
+bool IsCgroupV2Mounted() {
+ std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
+ if (!mnts) {
+ LOG(ERROR) << "Failed to open /proc/mounts";
+ return false;
+ }
+ struct mntent* mnt;
+ while ((mnt = getmntent(mnts.get()))) {
+ if (strcmp(mnt->mnt_fsname, "cgroup2") == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+class ScopedLogCapturer {
+ public:
+ struct log_args {
+ LogId log_buffer_id;
+ LogSeverity severity;
+ std::string tag;
+ std::string file;
+ unsigned int line;
+ std::string message;
+ };
+
+ // Constructor. Installs a new logger and saves the currently active logger.
+ ScopedLogCapturer() {
+ saved_severity_ = SetMinimumLogSeverity(android::base::VERBOSE);
+ saved_logger_ = SetLogger([this](LogId log_buffer_id, LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ if (saved_logger_) {
+ saved_logger_(log_buffer_id, severity, tag, file, line, message);
+ }
+ log_.emplace_back(log_args{.log_buffer_id = log_buffer_id,
+ .severity = severity,
+ .tag = tag,
+ .file = file,
+ .line = line,
+ .message = message});
+ });
+ }
+ // Destructor. Restores the original logger and log level.
+ ~ScopedLogCapturer() {
+ SetLogger(std::move(saved_logger_));
+ SetMinimumLogSeverity(saved_severity_);
+ }
+ ScopedLogCapturer(const ScopedLogCapturer&) = delete;
+ ScopedLogCapturer& operator=(const ScopedLogCapturer&) = delete;
+ // Returns the logged lines.
+ const std::vector<log_args>& Log() const { return log_; }
+
+ private:
+ LogSeverity saved_severity_;
+ LogFunction saved_logger_;
+ std::vector<log_args> log_;
+};
+
+// cgroup attribute at the top level of the cgroup hierarchy.
+class ProfileAttributeMock : public IProfileAttribute {
+ public:
+ ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
+ ~ProfileAttributeMock() override = default;
+ void Reset(const CgroupController& controller, const std::string& file_name) override {
+ CHECK(false);
+ }
+ const CgroupController* controller() const override {
+ CHECK(false);
+ return {};
+ }
+ const std::string& file_name() const override { return file_name_; }
+ bool GetPathForTask(int tid, std::string* path) const override {
+#ifdef __ANDROID__
+ CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+ CHECK_GT(path->length(), 0);
+ if (path->rbegin()[0] != '/') {
+ *path += "/";
+ }
+#else
+ // Not Android.
+ *path = "/sys/fs/cgroup/";
+#endif
+ *path += file_name_;
+ return true;
+ };
+
+ private:
+ const std::string file_name_;
+};
+
+struct TestParam {
+ const char* attr_name;
+ const char* attr_value;
+ bool optional_attr;
+ bool result;
+ LogSeverity log_severity;
+ const char* log_prefix;
+ const char* log_suffix;
+};
+
+class SetAttributeFixture : public TestWithParam<TestParam> {
+ public:
+ ~SetAttributeFixture() = default;
+};
+
+TEST_P(SetAttributeFixture, SetAttribute) {
+ // Treehugger runs host tests inside a container without cgroupv2 support.
+ if (!IsCgroupV2Mounted()) {
+ GTEST_SKIP();
+ return;
+ }
+ const TestParam params = GetParam();
+ ScopedLogCapturer captured_log;
+ ProfileAttributeMock pa(params.attr_name);
+ SetAttributeAction a(&pa, params.attr_value, params.optional_attr);
+ EXPECT_EQ(a.ExecuteForProcess(getuid(), getpid()), params.result);
+ auto log = captured_log.Log();
+ if (params.log_prefix || params.log_suffix) {
+ ASSERT_EQ(log.size(), 1);
+ EXPECT_EQ(log[0].severity, params.log_severity);
+ if (params.log_prefix) {
+ EXPECT_EQ(log[0].message.find(params.log_prefix), 0);
+ }
+ if (params.log_suffix) {
+ EXPECT_NE(log[0].message.find(params.log_suffix), std::string::npos);
+ }
+ } else {
+ ASSERT_EQ(log.size(), 0);
+ }
+}
+
+// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
+// exists }.
+INSTANTIATE_TEST_SUITE_P(
+ SetAttributeTestSuite, SetAttributeFixture,
+ Values(
+ // Test that attempting to write into a non-existing cgroup attribute fails and also
+ // that an error message is logged.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "No such cgroup attribute"},
+ // Test that attempting to write into an optional non-existing cgroup attribute
+ // results in the return value 'true' and also that no messages are logged.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = true,
+ .result = true},
+ // Test that attempting to write an invalid value into an existing optional cgroup
+ // attribute fails and also that it causes an error
+ // message to be logged.
+ TestParam{.attr_name = "cgroup.procs",
+ .attr_value = "-1",
+ .optional_attr = true,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "Failed to write",
+ .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"},
+ // Test that attempting to write into an existing optional read-only cgroup
+ // attribute fails and also that it causes an error message to be logged.
+ TestParam{
+ .attr_name = "cgroup.controllers",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "Failed to write",
+ .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
+
+} // namespace
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
deleted file mode 100644
index 64db095..0000000
--- a/libqtaguid/Android.bp
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_headers {
- name: "libqtaguid_headers",
- vendor_available: false,
- host_supported: false,
- export_include_dirs: ["include"],
- target: {
- linux_bionic: {
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libqtaguid",
- vendor_available: false,
- host_supported: false,
- target: {
- android: {
- srcs: [
- "qtaguid.c",
- ],
- sanitize: {
- misc_undefined: ["integer"],
- },
- },
- },
-
- shared_libs: ["liblog"],
- header_libs: [
- "libqtaguid_headers",
- ],
- export_header_lib_headers: ["libqtaguid_headers"],
- local_include_dirs: ["include"],
-
- cflags: [
- "-Werror",
- "-Wall",
- "-Wextra",
- ],
-}
diff --git a/libqtaguid/include/qtaguid/qtaguid.h b/libqtaguid/include/qtaguid/qtaguid.h
deleted file mode 100644
index 72285e5..0000000
--- a/libqtaguid/include/qtaguid/qtaguid.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#ifndef __LEGACY_QTAGUID_H
-#define __LEGACY_QTAGUID_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Set tags (and owning UIDs) for network sockets. The socket must be untagged
- * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
- * module will keep a reference to it even after close.
- */
-extern int legacy_tagSocket(int sockfd, int tag, uid_t uid);
-
-/*
- * Untag a network socket before closing.
- */
-extern int legacy_untagSocket(int sockfd);
-
-/*
- * For the given uid, switch counter sets.
- * The kernel only keeps a limited number of sets.
- * 2 for now.
- */
-extern int legacy_setCounterSet(int counterSetNum, uid_t uid);
-
-/*
- * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freeded.
- * The delete data also affects active tagged socketd, which are
- * then untagged.
- * The calling process can only operate on its own tags.
- * Unless it is part of the happy AID_NET_BW_ACCT group.
- * In which case it can clobber everything.
- */
-extern int legacy_deleteTagData(int tag, uid_t uid);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LEGACY_QTAGUID_H */
diff --git a/libqtaguid/qtaguid.c b/libqtaguid/qtaguid.c
deleted file mode 100644
index cd38bad..0000000
--- a/libqtaguid/qtaguid.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-** Copyright 2011, 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 LOG_NDEBUG 0
-
-#define LOG_TAG "qtaguid"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <qtaguid/qtaguid.h>
-
-static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
-static const int CTRL_MAX_INPUT_LEN = 128;
-
-/*
- * One per proccess.
- * Once the device is open, this process will have its socket tags tracked.
- * And on exit or untimely death, all socket tags will be removed.
- * A process can only open /dev/xt_qtaguid once.
- * It should not close it unless it is really done with all the socket tags.
- * Failure to open it will be visible when socket tagging will be attempted.
- */
-static int resTrackFd = -1;
-pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
-
-/* Only call once per process. */
-void legacy_resTrack(void) {
- resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
-}
-
-/*
- * Returns:
- * 0 on success.
- * -errno on failure.
- */
-static int write_ctrl(const char* cmd) {
- int fd, res, savedErrno;
-
- ALOGV("write_ctrl(%s)", cmd);
-
- fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
- if (fd < 0) {
- return -errno;
- }
-
- res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd)));
- if (res < 0) {
- savedErrno = errno;
- } else {
- savedErrno = 0;
- }
- if (res < 0) {
- // ALOGV is enough because all the callers also log failures
- ALOGV("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
- }
- close(fd);
- return -savedErrno;
-}
-
-int legacy_tagSocket(int sockfd, int tag, uid_t uid) {
- char lineBuf[CTRL_MAX_INPUT_LEN];
- int res;
- uint64_t kTag = ((uint64_t)tag << 32);
-
- pthread_once(&resTrackInitDone, legacy_resTrack);
-
- snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
-
- ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
-
- res = write_ctrl(lineBuf);
- if (res < 0) {
- ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag,
- tag, uid, res);
- }
-
- return res;
-}
-
-int legacy_untagSocket(int sockfd) {
- char lineBuf[CTRL_MAX_INPUT_LEN];
- int res;
-
- ALOGV("Untagging socket %d", sockfd);
-
- snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd);
- res = write_ctrl(lineBuf);
- if (res < 0) {
- ALOGI("Untagging socket %d failed errno=%d", sockfd, res);
- }
-
- return res;
-}
-
-int legacy_setCounterSet(int counterSetNum, uid_t uid) {
- char lineBuf[CTRL_MAX_INPUT_LEN];
- int res;
-
- ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
-
- snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid);
- res = write_ctrl(lineBuf);
- return res;
-}
-
-int legacy_deleteTagData(int tag, uid_t uid) {
- char lineBuf[CTRL_MAX_INPUT_LEN];
- int cnt = 0, res = 0;
- uint64_t kTag = (uint64_t)tag << 32;
-
- ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
-
- pthread_once(&resTrackInitDone, legacy_resTrack);
-
- snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
- res = write_ctrl(lineBuf);
- if (res < 0) {
- ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
- kTag, tag, uid, cnt, errno);
- }
-
- return res;
-}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 3f9aeb2..02bfee6 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -96,12 +96,14 @@
cc_fuzz {
name: "sparse_fuzzer",
- host_supported: false,
+ host_supported: true,
srcs: [
"sparse_fuzzer.cpp",
],
static_libs: [
"libsparse",
+ "libbase",
+ "libz",
"liblog",
],
}
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index f3ec655..7c52c3f 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -263,21 +263,6 @@
int sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);
/**
- * sparse_file_read_buf - read a buffer into a sparse file cookie
- *
- * @s - sparse file cookie
- * @buf - buffer to read from
- * @crc - verify the crc of a file in the Android sparse file format
- *
- * Reads a buffer into a sparse file cookie. The buffer must remain
- * valid until the sparse file cookie is freed. If crc is true, the
- * crc of the sparse file will be verified.
- *
- * Returns 0 on success, negative errno on error.
- */
-int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
-
-/**
* sparse_file_import - import an existing sparse file
*
* @fd - file descriptor to read from
@@ -296,6 +281,7 @@
* sparse_file_import_buf - import an existing sparse file from a buffer
*
* @buf - buffer to read from
+ * @len - length of buffer
* @verbose - print verbose errors while reading the sparse file
* @crc - verify the crc of a file in the Android sparse file format
*
@@ -305,7 +291,7 @@
*
* Returns a new sparse file cookie on success, NULL on error.
*/
-struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
+struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc);
/**
* sparse_file_import_auto - import an existing sparse or normal file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b2c5407..cb5d730 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -54,6 +54,8 @@
#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
+#define FILL_ZERO_BUFSIZE (2 * 1024 * 1024)
+
#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
struct output_file_ops {
@@ -391,13 +393,29 @@
ret = out->ops->write(out, data, len);
if (ret < 0) return -1;
if (zero_len) {
- ret = out->ops->write(out, out->zero_buf, zero_len);
- if (ret < 0) return -1;
+ uint64_t len = zero_len;
+ uint64_t write_len;
+ while (len) {
+ write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+ ret = out->ops->write(out, out->zero_buf, write_len);
+ if (ret < 0) {
+ return ret;
+ }
+ len -= write_len;
+ }
}
if (out->use_crc) {
out->crc32 = sparse_crc32(out->crc32, data, len);
- if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
+ if (zero_len) {
+ uint64_t len = zero_len;
+ uint64_t write_len;
+ while (len) {
+ write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+ out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
+ len -= write_len;
+ }
+ }
}
out->cur_out_ptr += rnd_up_len;
@@ -460,12 +478,12 @@
uint64_t write_len;
/* Initialize fill_buf with the fill_val */
- for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
+ for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) {
out->fill_buf[i] = fill_val;
}
while (len) {
- write_len = std::min(len, (uint64_t)out->block_size);
+ write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
@@ -512,13 +530,15 @@
out->crc32 = 0;
out->use_crc = crc;
- out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
+ // don't use sparse format block size as it can takes up to 32GB
+ out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1));
if (!out->zero_buf) {
error_errno("malloc zero_buf");
return -ENOMEM;
}
- out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
+ // don't use sparse format block size as it can takes up to 32GB
+ out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1));
if (!out->fill_buf) {
error_errno("malloc fill_buf");
ret = -ENOMEM;
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index b0b3b22..8811a52 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -120,7 +120,7 @@
"output offset", "output blocks", "type", "hash"])
offset = 0
- for i in xrange(1, total_chunks + 1):
+ for i in range(1, total_chunks + 1):
header_bin = FH.read(12)
header = struct.unpack("<2H2I", header_bin)
chunk_type = header[0]
@@ -159,7 +159,7 @@
if showhash:
h = hashlib.sha1()
data = fill_bin * (blk_sz / 4);
- for block in xrange(chunk_sz):
+ for block in range(chunk_sz):
h.update(data)
curhash = h.hexdigest()
elif chunk_type == 0xCAC3:
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 42f331f..663c812 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -1,16 +1,29 @@
#include "include/sparse/sparse.h"
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < 2 * sizeof(wchar_t)) return 0;
+static volatile int count;
- int64_t blocksize = 4096;
- struct sparse_file* file = sparse_file_new(size, blocksize);
- if (!file) {
+int WriteCallback(void* priv __attribute__((__unused__)), const void* data, size_t len) {
+ if (!data) {
+ return 0;
+ }
+ if (len == 0) {
return 0;
}
- unsigned int block = 1;
- sparse_file_add_data(file, &data, size, block);
- sparse_file_destroy(file);
+ const char* p = (const char*)data;
+ // Just to make sure the data is accessible
+ // We only check the head and tail to save time
+ count += *p;
+ count += *(p+len-1);
return 0;
}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ struct sparse_file* file = sparse_file_import_buf((char*)data, size, true, false);
+ if (!file) {
+ return 0;
+ }
+ int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);
+ sparse_file_destroy(file);
+ return result;
+}
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 9e564dc..028b6be 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -58,14 +58,15 @@
class SparseFileSource {
public:
- /* Seeks the source ahead by the given offset. */
- virtual void Seek(int64_t offset) = 0;
+ /* Seeks the source ahead by the given offset.
+ * Return 0 if successful. */
+ virtual int Seek(int64_t offset) = 0;
/* Return the current offset. */
virtual int64_t GetOffset() = 0;
- /* Set the current offset. Return 0 if successful. */
- virtual int SetOffset(int64_t offset) = 0;
+ /* Rewind to beginning. Return 0 if successful. */
+ virtual int Rewind() = 0;
/* Adds the given length from the current offset of the source to the file at the given block.
* Return 0 if successful. */
@@ -88,12 +89,14 @@
SparseFileFdSource(int fd) : fd(fd) {}
~SparseFileFdSource() override {}
- void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
+ int Seek(int64_t off) override {
+ return lseek64(fd, off, SEEK_CUR) != -1 ? 0 : -errno;
+ }
int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
- int SetOffset(int64_t offset) override {
- return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
+ int Rewind() override {
+ return lseek64(fd, 0, SEEK_SET) == 0 ? 0 : -errno;
}
int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
@@ -120,39 +123,74 @@
class SparseFileBufSource : public SparseFileSource {
private:
+ char* buf_start;
+ char* buf_end;
char* buf;
int64_t offset;
+ int AccessOkay(int64_t len) {
+ if (len <= 0) return -EINVAL;
+ if (buf < buf_start) return -EOVERFLOW;
+ if (buf >= buf_end) return -EOVERFLOW;
+ if (len > buf_end - buf) return -EOVERFLOW;
+
+ return 0;
+ }
+
public:
- SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
+ SparseFileBufSource(char* buf, uint64_t len) {
+ this->buf = buf;
+ this->offset = 0;
+ this->buf_start = buf;
+ this->buf_end = buf + len;
+ }
~SparseFileBufSource() override {}
- void Seek(int64_t off) override {
+ int Seek(int64_t off) override {
+ int ret = AccessOkay(off);
+ if (ret < 0) {
+ return ret;
+ }
buf += off;
offset += off;
+ return 0;
}
int64_t GetOffset() override { return offset; }
- int SetOffset(int64_t off) override {
- buf += off - offset;
- offset = off;
+ int Rewind() override {
+ buf = buf_start;
+ offset = 0;
return 0;
}
int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
+ int ret = AccessOkay(len);
+ if (ret < 0) {
+ return ret;
+ }
return sparse_file_add_data(s, buf, len, block);
}
int ReadValue(void* ptr, int len) override {
+ int ret = AccessOkay(len);
+ if (ret < 0) {
+ return ret;
+ }
memcpy(ptr, buf, len);
- Seek(len);
+ buf += len;
+ offset += len;
return 0;
}
int GetCrc32(uint32_t* crc32, int64_t len) override {
+ int ret = AccessOkay(len);
+ if (ret < 0) {
+ return ret;
+ }
*crc32 = sparse_crc32(*crc32, buf, len);
- Seek(len);
+ buf += len;
+ offset += len;
return 0;
}
};
@@ -175,7 +213,7 @@
SparseFileSource* source, unsigned int blocks, unsigned int block,
uint32_t* crc32) {
int ret;
- int64_t len = blocks * s->block_size;
+ int64_t len = (int64_t)blocks * s->block_size;
if (chunk_size % s->block_size != 0) {
return -EINVAL;
@@ -196,7 +234,10 @@
return ret;
}
} else {
- source->Seek(len);
+ ret = source->Seek(len);
+ if (ret < 0) {
+ return ret;
+ }
}
return 0;
@@ -379,7 +420,10 @@
/* Skip the remaining bytes in a header that is longer than
* we expected.
*/
- source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+ ret = source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
+ if (ret < 0) {
+ return ret;
+ }
}
for (i = 0; i < sparse_header.total_chunks; i++) {
@@ -392,7 +436,10 @@
/* Skip the remaining bytes in a header that is longer than
* we expected.
*/
- source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+ ret = source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
+ if (ret < 0) {
+ return ret;
+ }
}
ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
@@ -544,11 +591,6 @@
}
}
-int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
- SparseFileBufSource source(buf);
- return sparse_file_read_sparse(s, &source, crc);
-}
-
static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
bool crc) {
int ret;
@@ -580,6 +622,14 @@
return nullptr;
}
+ if (!sparse_header.blk_sz || (sparse_header.blk_sz % 4)) {
+ return nullptr;
+ }
+
+ if (!sparse_header.total_blks) {
+ return nullptr;
+ }
+
len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
s = sparse_file_new(sparse_header.blk_sz, len);
if (!s) {
@@ -587,7 +637,7 @@
return nullptr;
}
- ret = source->SetOffset(0);
+ ret = source->Rewind();
if (ret < 0) {
verbose_error(verbose, ret, "seeking");
sparse_file_destroy(s);
@@ -610,8 +660,8 @@
return sparse_file_import_source(&source, verbose, crc);
}
-struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
- SparseFileBufSource source(buf);
+struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc) {
+ SparseFileBufSource source(buf, len);
return sparse_file_import_source(&source, verbose, crc);
}
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 89b8c2a..92f1e6a 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "libstatspull_lazy_test"
}
+ ],
+ "hwasan-postsubmit" : [
+ {
+ "name" : "libstatspull_lazy_test"
+ }
]
}
\ No newline at end of file
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index f07e32b..4ffa98d 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -44,6 +44,11 @@
],
},
},
+ min_sdk_version: "apex_inherit",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ]
}
rust_library {
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 174125e..09b2623 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -68,7 +68,7 @@
}
/// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
- pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
+ pub fn set_additive_fields(&mut self, additive_fields: &mut [i32]) {
// Safety: Metadata::new ensures that self.metadata is a valid object.
unsafe {
AStatsManager_PullAtomMetadata_setAdditiveFields(
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index 13afc00..03506cd 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "libstatssocket_lazy_test"
}
+ ],
+ "hwasan-presubmit" : [
+ {
+ "name" : "libstatssocket_lazy_test"
+ }
]
}
\ No newline at end of file
diff --git a/libsync/OWNERS b/libsync/OWNERS
index e75b15b..8f69e50 100644
--- a/libsync/OWNERS
+++ b/libsync/OWNERS
@@ -1,3 +1,2 @@
chrisforbes@google.com
-hridya@google.com
jessehall@google.com
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index 1b6060a..a3c23b2 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -59,12 +59,14 @@
/*
* Structure for describing YCbCr formats for consumption by applications.
- * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*.
*
* Buffer chroma subsampling is defined in the format.
* e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
*
- * Buffers must have a 8 bit depth.
+ * Buffers must have a byte aligned channel depth or a byte aligned packed
+ * channel depth (e.g. 10 bits packed into 16 bits for
+ * HAL_PIXEL_FORMAT_YCbCr_P010).
*
* y, cb, and cr point to the first byte of their respective planes.
*
@@ -75,8 +77,8 @@
* cstride is the stride of the chroma planes.
*
* chroma_step is the distance in bytes from one chroma pixel value to the
- * next. This is 2 bytes for semiplanar (because chroma values are interleaved
- * and each chroma value is one byte) and 1 for planar.
+ * next. This is `2 * channel depth` bytes for semiplanar (because chroma
+ * values are interleaved) and `1 * channel depth` bytes for planar.
*/
struct android_ycbcr {
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 3883317..9ae73d0 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -30,11 +30,9 @@
export_include_dirs: ["include"],
target: {
android: {
- cflags: [
- "-g",
- "-DUSE_LIBLOG",
- ],
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
+ srcs: ["usbhost_jni.cpp"],
},
darwin: {
enabled: false,
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 7e62542..01cd68b 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -21,6 +21,7 @@
extern "C" {
#endif
+#include <stddef.h>
#include <stdint.h>
#include <linux/version.h>
diff --git a/libusbhost/include/usbhost/usbhost_jni.h b/libusbhost/include/usbhost/usbhost_jni.h
new file mode 100644
index 0000000..4885d45
--- /dev/null
+++ b/libusbhost/include/usbhost/usbhost_jni.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+/**
+ * Reads USB descriptors from `fd`.
+ *
+ * Returns a byte[] on success,
+ * or returns NULL and logs an appropriate error on failure.
+ */
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd);
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 3bed0e3..d8f15cd 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -18,20 +18,9 @@
#define _GNU_SOURCE
#endif
-// #define DEBUG 1
-#if DEBUG
+#include <usbhost/usbhost.h>
-#ifdef USE_LIBLOG
-#define LOG_TAG "usbhost"
-#include "log/log.h"
-#define D ALOGD
-#else
-#define D printf
-#endif
-
-#else
-#define D(...)
-#endif
+#include "usbhost_private.h"
#include <stdio.h>
#include <stdlib.h>
@@ -48,12 +37,19 @@
#include <errno.h>
#include <ctype.h>
#include <poll.h>
-#include <pthread.h>
#include <linux/usbdevice_fs.h>
-#include <asm/byteorder.h>
-#include "usbhost/usbhost.h"
+// #define DEBUG 1
+#if defined(DEBUG)
+#if defined(__BIONIC__)
+#define D ALOGD
+#else
+#define D printf
+#endif
+#else
+#define D(...)
+#endif
#define DEV_DIR "/dev"
#define DEV_BUS_DIR DEV_DIR "/bus"
@@ -76,8 +72,6 @@
int wddbus;
};
-#define MAX_DESCRIPTORS_LENGTH 4096
-
struct usb_device {
char dev_name[64];
unsigned char desc[MAX_DESCRIPTORS_LENGTH];
diff --git a/libusbhost/usbhost_jni.cpp b/libusbhost/usbhost_jni.cpp
new file mode 100644
index 0000000..0da83dc
--- /dev/null
+++ b/libusbhost/usbhost_jni.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 <usbhost/usbhost_jni.h>
+
+#include "usbhost_private.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd) {
+ if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+ ALOGE("usb_jni_read_descriptors(%d): lseek() failed: %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ jbyte buf[MAX_DESCRIPTORS_LENGTH];
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
+ if (n == -1) {
+ ALOGE("usb_jni_read_descriptors: read failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ jbyteArray result = env->NewByteArray(n);
+ if (result) env->SetByteArrayRegion(result, 0, n, buf);
+ return result;
+}
diff --git a/libusbhost/usbhost_private.h b/libusbhost/usbhost_private.h
new file mode 100644
index 0000000..72d7938
--- /dev/null
+++ b/libusbhost/usbhost_private.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#define LOG_TAG "usbhost"
+#include <log/log.h>
+
+// Somewhat arbitrary: Sony has reported needing more than 4KiB (but less
+// than 8KiB), and some frameworks code had 16KiB without any explanation,
+// so we went with the largest of those.
+#define MAX_DESCRIPTORS_LENGTH (16 * 1024)
diff --git a/libutils/Android.bp b/libutils/Android.bp
index e6d9a4c..f663671 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -28,31 +28,23 @@
min_sdk_version: "apex_inherit",
header_libs: [
- "liblog_headers",
- "libsystem_headers",
+ "libbase_headers",
"libcutils_headers",
+ "liblog_headers",
"libprocessgroup_headers",
+ "libsystem_headers",
],
export_header_lib_headers: [
- "liblog_headers",
- "libsystem_headers",
+ "libbase_headers",
"libcutils_headers",
+ "liblog_headers",
"libprocessgroup_headers",
+ "libsystem_headers",
],
export_include_dirs: ["include"],
target: {
- android: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
- },
- linux_glibc: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
- },
linux_bionic: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
enabled: true,
},
windows: {
@@ -98,7 +90,6 @@
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libprocessgroup",
"libvndksupport",
],
@@ -129,6 +120,9 @@
enabled: true,
},
},
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
}
cc_library {
@@ -177,6 +171,8 @@
"//apex_available:platform",
],
min_sdk_version: "apex_inherit",
+
+ afdo: true,
}
cc_library {
@@ -184,6 +180,7 @@
defaults: ["libutils_defaults"],
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
+ min_sdk_version: "29",
srcs: [
"CallStack.cpp",
@@ -191,7 +188,7 @@
shared_libs: [
"libutils",
- "libbacktrace",
+ "libunwindstack",
],
target: {
@@ -297,13 +294,14 @@
srcs: [
"BitSet_test.cpp",
+ "Errors_test.cpp",
"FileMap_test.cpp",
"LruCache_test.cpp",
"Mutex_test.cpp",
"SharedBuffer_test.cpp",
"Singleton_test.cpp",
- "String8_test.cpp",
"String16_test.cpp",
+ "String8_test.cpp",
"StrongPointer_test.cpp",
"Timers_test.cpp",
"Unicode_test.cpp",
@@ -362,6 +360,7 @@
"-Wall",
"-Werror",
],
+ header_libs: ["libutils_headers"],
}
cc_test_library {
@@ -374,6 +373,7 @@
"-Werror",
],
shared_libs: ["libutils_test_singleton1"],
+ header_libs: ["libutils_headers"],
}
cc_benchmark {
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index fe6f33d..f19ba6a 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -20,7 +20,7 @@
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
#define CALLSTACK_WEAK // Don't generate weak definitions.
#include <utils/CallStack.h>
@@ -39,14 +39,25 @@
}
void CallStack::update(int32_t ignoreDepth, pid_t tid) {
+ if (ignoreDepth < 0) {
+ ignoreDepth = 0;
+ }
+
mFrameLines.clear();
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
- if (!backtrace->Unwind(ignoreDepth)) {
- ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ std::optional<pid_t> tid_val;
+ if (tid != -1) {
+ *tid_val = tid;
}
- for (size_t i = 0; i < backtrace->NumFrames(); i++) {
- mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str()));
+ if (!unwinder.Unwind(tid_val, data)) {
+ ALOGW("%s: Failed to unwind callstack: %s", __FUNCTION__, data.GetErrorString().c_str());
+ }
+ for (size_t i = ignoreDepth; i < data.frames.size(); i++) {
+ auto& frame = data.frames[i];
+ frame.num -= ignoreDepth;
+ mFrameLines.push_back(String8(unwinder.FormatFrame(frame).c_str()));
}
}
diff --git a/libutils/Errors_test.cpp b/libutils/Errors_test.cpp
new file mode 100644
index 0000000..0d13bb0
--- /dev/null
+++ b/libutils/Errors_test.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 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 "utils/ErrorsMacros.h"
+
+#include <android-base/result.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+using android::base::Error;
+using android::base::Result;
+
+status_t success_or_fail(bool success) {
+ if (success)
+ return OK;
+ else
+ return PERMISSION_DENIED;
+}
+
+TEST(errors, unwrap_or_return) {
+ auto f = [](bool success, int* val) -> status_t {
+ OR_RETURN(success_or_fail(success));
+ *val = 10;
+ return OK;
+ };
+
+ int val;
+ status_t s = f(true, &val);
+ EXPECT_EQ(OK, s);
+ EXPECT_EQ(10, val);
+
+ val = 0; // reset
+ status_t q = f(false, &val);
+ EXPECT_EQ(PERMISSION_DENIED, q);
+ EXPECT_EQ(0, val);
+}
+
+TEST(errors, unwrap_or_return_result) {
+ auto f = [](bool success) -> Result<std::string, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return "hello";
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ EXPECT_EQ("hello", *r);
+
+ auto s = f(false);
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(PERMISSION_DENIED, s.error().code());
+ EXPECT_EQ("PERMISSION_DENIED", s.error().message());
+}
+
+TEST(errors, unwrap_or_return_result_int) {
+ auto f = [](bool success) -> Result<int, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return 10;
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ EXPECT_EQ(10, *r);
+
+ auto s = f(false);
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(PERMISSION_DENIED, s.error().code());
+ EXPECT_EQ("PERMISSION_DENIED", s.error().message());
+}
+
+TEST(errors, unwrap_or_fatal) {
+ OR_FATAL(success_or_fail(true));
+
+ EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "PERMISSION_DENIED");
+}
+
+TEST(errors, result_in_status) {
+ auto f = [](bool success) -> Result<std::string, StatusT> {
+ if (success)
+ return "OK";
+ else
+ return Error<StatusT>(PERMISSION_DENIED) << "custom error message";
+ };
+
+ auto g = [&](bool success) -> status_t {
+ std::string val = OR_RETURN(f(success));
+ EXPECT_EQ("OK", val);
+ return OK;
+ };
+
+ status_t a = g(true);
+ EXPECT_EQ(OK, a);
+
+ status_t b = g(false);
+ EXPECT_EQ(PERMISSION_DENIED, b);
+}
+
+TEST(errors, conversion_promotion) {
+ constexpr size_t successVal = 10ull;
+ auto f = [&](bool success) -> Result<size_t, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+TEST(errors, conversion_promotion_bool) {
+ constexpr size_t successVal = true;
+ auto f = [&](bool success) -> Result<bool, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+TEST(errors, conversion_promotion_char) {
+ constexpr char successVal = 'a';
+ auto f = [&](bool success) -> Result<unsigned char, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+struct IntContainer {
+ // Implicit conversion from int is desired
+ IntContainer(int val) : val_(val) {}
+ int val_;
+};
+
+TEST(errors, conversion_construct) {
+ constexpr int successVal = 10;
+ auto f = [&](bool success) -> Result<IntContainer, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value().val_, successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
index c4d917b..8b16947 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -298,8 +298,8 @@
}
TEST_F(LruCacheTest, Callback) {
- LruCache<SimpleKey, StringValue> cache(100);
EntryRemovedCallback callback;
+ LruCache<SimpleKey, StringValue> cache(100);
cache.setOnEntryRemovedListener(&callback);
cache.put(1, "one");
@@ -313,8 +313,8 @@
}
TEST_F(LruCacheTest, CallbackOnClear) {
- LruCache<SimpleKey, StringValue> cache(100);
EntryRemovedCallback callback;
+ LruCache<SimpleKey, StringValue> cache(100);
cache.setOnEntryRemovedListener(&callback);
cache.put(1, "one");
@@ -326,8 +326,8 @@
}
TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
- LruCache<KeyWithPointer, StringValue> cache(1);
InvalidateKeyCallback callback;
+ LruCache<KeyWithPointer, StringValue> cache(1);
cache.setOnEntryRemovedListener(&callback);
KeyWithPointer key1;
key1.ptr = new int(1);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 0518927..4ddac3d 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -50,12 +50,7 @@
// log all reference counting operations
#define PRINT_REFS 0
-// Continue after logging a stack trace if ~RefBase discovers that reference
-// count has never been incremented. Normally we conspicuously crash in that
-// case.
-#define DEBUG_REFBASE_DESTRUCTION 1
-
-#if !defined(_WIN32) && !defined(__APPLE__)
+#if defined(__linux__)
// CallStack is only supported on linux type platforms.
#define CALLSTACK_ENABLED 1
#else
@@ -751,17 +746,19 @@
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
// We never acquired a strong reference on this object.
-#if DEBUG_REFBASE_DESTRUCTION
- // Treating this as fatal is prone to causing boot loops. For debugging, it's
- // better to treat as non-fatal.
- ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+
+ // TODO: make this fatal, but too much code in Android manages RefBase with
+ // new/delete manually (or using other mechanisms such as std::make_unique).
+ // However, this is dangerous because it's also common for code to use the
+ // sp<T>(T*) constructor, assuming that if the object is around, it is already
+ // owned by an sp<>.
+ ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+ "object.",
+ mRefs->mWeak.load(), this);
#if CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
-#else
- LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
-#endif
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 419b2de..3690389 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -313,7 +313,7 @@
if (n > 0) {
size_t oldLength = length();
- if (n > std::numeric_limits<size_t>::max() - 1 ||
+ if (static_cast<size_t>(n) > std::numeric_limits<size_t>::max() - 1 ||
oldLength > std::numeric_limits<size_t>::max() - n - 1) {
return NO_MEMORY;
}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 3bf5779..4dacdc6 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -84,14 +84,6 @@
delete t;
setpriority(PRIO_PROCESS, 0, prio);
- // A new thread will be in its parent's sched group by default,
- // so we just need to handle the background case.
- // currently set to system_background group which is different
- // from background group for app.
- if (prio >= ANDROID_PRIORITY_BACKGROUND) {
- SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true);
- }
-
if (name) {
androidSetThreadName(name);
free(name);
@@ -307,27 +299,16 @@
int androidSetThreadPriority(pid_t tid, int pri)
{
int rc = 0;
- int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
if (curr_pri == pri) {
return rc;
}
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
- }
-
- if (rc) {
- lasterr = errno;
- }
-
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
rc = INVALID_OPERATION;
} else {
- errno = lasterr;
+ errno = 0;
}
return rc;
diff --git a/libutils/Unicode_test.cpp b/libutils/Unicode_test.cpp
index b92eef8..8b994d9 100644
--- a/libutils/Unicode_test.cpp
+++ b/libutils/Unicode_test.cpp
@@ -100,7 +100,7 @@
0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character
};
- char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL
+ char16_t output[1 + 1 + 1 + 2 + 1]; // Room for null
utf8_to_utf16(str, sizeof(str), output, sizeof(output) / sizeof(output[0]));
@@ -114,8 +114,7 @@
<< "should be first half of surrogate U+10000";
EXPECT_EQ(0xDC00, output[4])
<< "should be second half of surrogate U+10000";
- EXPECT_EQ(NULL, output[5])
- << "should be NULL terminated";
+ EXPECT_EQ(0, output[5]) << "should be null terminated";
}
TEST_F(UnicodeTest, strstr16EmptyTarget) {
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index c97a19b..d951b8b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -279,14 +279,12 @@
ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
{
- ALOG_ASSERT((index+count)<=size(),
- "[%p] remove: index=%d, count=%d, size=%d",
- this, (int)index, (int)count, (int)size());
-
- if ((index+count) > size())
- return BAD_VALUE;
- _shrink(index, count);
- return index;
+ size_t end;
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(index, count, &end), "overflow: index=%zu count=%zu",
+ index, count);
+ if (end > size()) return BAD_VALUE;
+ _shrink(index, count);
+ return index;
}
void VectorImpl::finish_vector()
diff --git a/libutils/Vector_test.cpp b/libutils/Vector_test.cpp
index 5336c40..6d90eaa 100644
--- a/libutils/Vector_test.cpp
+++ b/libutils/Vector_test.cpp
@@ -136,4 +136,13 @@
}
}
+TEST_F(VectorTest, removeItemsAt_overflow) {
+ android::Vector<int> v;
+ for (int i = 0; i < 666; i++) v.add(i);
+
+ ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, 666), "overflow");
+ ASSERT_DEATH(v.removeItemsAt(666, SIZE_MAX), "overflow");
+ ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, SIZE_MAX), "overflow");
+}
+
} // namespace android
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 7a4a345..fe4d4f5 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -20,7 +20,6 @@
#include <memory>
#include <android/log.h>
-#include <backtrace/backtrace_constants.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -59,7 +58,7 @@
// Immediately collect the stack traces for the specified thread.
// The default is to dump the stack of the current call.
- void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
+ void update(int32_t ignoreDepth = 1, pid_t tid = -1);
// Dump a stack trace to the log using the supplied logtag.
void log(const char* logtag,
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 6002567..3221899 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -71,19 +71,17 @@
#define CONSTEXPR
#endif
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
+/* TEMP_FAILURE_RETRY is not available on macOS, but still useful there. */
#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ __typeof__(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
#endif
#if defined(_WIN32)
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index d14d223..22fb36d 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -34,15 +34,13 @@
* All error codes are negative values.
*/
-// Win32 #defines NO_ERROR as well. It has the same value, so there's no
-// real conflict, though it's a bit awkward.
-#ifdef _WIN32
-# undef NO_ERROR
-#endif
-
enum {
OK = 0, // Preferred constant for checking success.
+#ifndef NO_ERROR
+ // Win32 #defines NO_ERROR as well. It has the same value, so there's no
+ // real conflict, though it's a bit awkward.
NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
+#endif
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
@@ -76,10 +74,4 @@
// Human readable name of error
std::string statusToString(status_t status);
-// Restore define; enumeration is in "android" namespace, so the value defined
-// there won't work for Win32 code in a different namespace.
-#ifdef _WIN32
-# define NO_ERROR 0L
-#endif
-
} // namespace android
diff --git a/libutils/include/utils/ErrorsMacros.h b/libutils/include/utils/ErrorsMacros.h
new file mode 100644
index 0000000..fdc46e6
--- /dev/null
+++ b/libutils/include/utils/ErrorsMacros.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2021 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 "Errors.h"
+
+// It would have been better if this file (ErrorsMacros.h) is entirely in utils/Errors.h. However
+// that is infeasible as some (actually many) are using utils/Errors.h via the implicit include path
+// `system/core/include` [1]. Since such users are not guaranteed to specify the dependency to
+// libbase_headers, the following headers from libbase_headers can't be found.
+// [1] build/soong/cc/config/global.go#commonGlobalIncludes
+#include <android-base/errors.h>
+#include <android-base/result.h>
+#include <log/log_main.h>
+
+#include <assert.h>
+
+namespace android {
+
+// StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating
+// Result<T, E> and Error<E> template classes. This is required to distinguish status_t from
+// other integer-based error code types like errno, and also to provide utility functions like
+// print().
+struct StatusT {
+ StatusT() : val_(OK) {}
+ StatusT(status_t s) : val_(s) {}
+ const status_t& value() const { return val_; }
+ operator status_t() const { return val_; }
+ std::string print() const { return statusToString(val_); }
+
+ status_t val_;
+};
+
+
+namespace base {
+// TODO(b/221235365) StatusT fulfill ResultError contract and cleanup.
+
+// Unlike typical ResultError types, the underlying code should be a status_t
+// instead of a StatusT. We also special-case message generation.
+template<>
+struct ResultError<StatusT, false> {
+ ResultError(status_t s) : val_(s) {
+ LOG_FATAL_IF(s == OK, "Result error should not hold success");
+ }
+
+ template <typename T>
+ operator expected<T, ResultError<StatusT, false>>() const {
+ return unexpected(*this);
+ }
+
+ std::string message() const { return statusToString(val_); }
+ status_t code() const { return val_; }
+
+ private:
+ const status_t val_;
+};
+
+template<>
+struct ResultError<StatusT, true> {
+ template <typename T>
+ ResultError(T&& message, status_t s) : val_(s), message_(std::forward<T>(message)) {
+ LOG_FATAL_IF(s == OK, "Result error should not hold success");
+ }
+
+ ResultError(status_t s) : val_(s) {}
+
+ template <typename T>
+ operator expected<T, ResultError<StatusT, true>>() const {
+ return unexpected(*this);
+ }
+
+ status_t code() const { return val_; }
+
+ std::string message() const { return statusToString(val_) + message_; }
+ private:
+ const status_t val_;
+ std::string message_;
+};
+
+// Specialization of android::base::OkOrFail<V> for V = status_t. This is used to use the OR_RETURN
+// and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h
+// for the detailed contract.
+template <>
+struct OkOrFail<status_t> {
+ static_assert(std::is_same_v<status_t, int>);
+ // Tests if status_t is a success value of not.
+ static bool IsOk(const status_t& s) { return s == OK; }
+
+ // Unwrapping status_t in the success case is just asserting that it is actually a success.
+ // We don't return OK because it would be redundant.
+ static void Unwrap([[maybe_unused]] status_t&& s) { assert(IsOk(s)); }
+
+ // Consumes status_t when it's a fail value
+ static OkOrFail<status_t> Fail(status_t&& s) {
+ assert(!IsOk(s));
+ return OkOrFail<status_t>{s};
+ }
+ status_t val_;
+
+ // And converts back into status_t. This is used when OR_RETURN is used in a function whose
+ // return type is status_t.
+ operator status_t() && { return val_; }
+
+ // Or converts into Result<T, StatusT>. This is used when OR_RETURN is used in a function whose
+ // return type is Result<T, StatusT>.
+
+ template <typename T>
+ operator Result<T, StatusT>() && {
+ return ResultError<StatusT>(std::move(val_));
+ }
+
+ template<typename T>
+ operator Result<T, StatusT, false>() && {
+ return ResultError<StatusT, false>(std::move(val_));
+ }
+
+ // Since user defined conversion can be followed by numeric conversion,
+ // we have to specialize all conversions to results holding numeric types to
+ // avoid conversion ambiguities with the constructor of expected.
+#pragma push_macro("SPECIALIZED_CONVERSION")
+#define SPECIALIZED_CONVERSION(type)\
+ operator Result<type, StatusT>() && { return ResultError<StatusT>(std::move(val_)); }\
+ operator Result<type, StatusT, false>() && { return ResultError<StatusT, false>(std::move(val_));}
+
+ SPECIALIZED_CONVERSION(int)
+ SPECIALIZED_CONVERSION(short int)
+ SPECIALIZED_CONVERSION(unsigned short int)
+ SPECIALIZED_CONVERSION(unsigned int)
+ SPECIALIZED_CONVERSION(long int)
+ SPECIALIZED_CONVERSION(unsigned long int)
+ SPECIALIZED_CONVERSION(long long int)
+ SPECIALIZED_CONVERSION(unsigned long long int)
+ SPECIALIZED_CONVERSION(bool)
+ SPECIALIZED_CONVERSION(char)
+ SPECIALIZED_CONVERSION(unsigned char)
+ SPECIALIZED_CONVERSION(signed char)
+ SPECIALIZED_CONVERSION(wchar_t)
+ SPECIALIZED_CONVERSION(char16_t)
+ SPECIALIZED_CONVERSION(char32_t)
+ SPECIALIZED_CONVERSION(float)
+ SPECIALIZED_CONVERSION(double)
+ SPECIALIZED_CONVERSION(long double)
+#undef SPECIALIZED_CONVERSION
+#pragma pop_macro("SPECIALIZED_CONVERSION")
+ // String representation of the error value.
+ static std::string ErrorMessage(const status_t& s) { return statusToString(s); }
+};
+} // namespace base
+
+
+// These conversions make StatusT directly comparable to status_t in order to
+// avoid calling code whenever comparisons are desired.
+
+template <bool include_message>
+bool operator==(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
+ return (l.code() == r);
+}
+template <bool include_message>
+bool operator==(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
+ return (l == r.code());
+}
+
+template <bool include_message>
+bool operator!=(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
+ return (l.code() != r);
+}
+template <bool include_message>
+bool operator!=(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
+ return (l != r.code());
+}
+
+} // namespace android
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index fc67656..5cf6b47 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -42,7 +42,9 @@
{
public:
// Create a Thread object, but doesn't create or start the associated
- // thread. See the run() method.
+ // thread. See the run() method. This object must be used with RefBase/sp,
+ // like any other RefBase object, because they are conventionally promoted
+ // from bare pointers (Thread::run is particularly problematic here).
explicit Thread(bool canCallJava = true);
virtual ~Thread();
diff --git a/libvndksupport/TEST_MAPPING b/libvndksupport/TEST_MAPPING
new file mode 100644
index 0000000..c9b3b20
--- /dev/null
+++ b/libvndksupport/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libvndksupport-tests"
+ }
+ ]
+}
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index ba700cb..0159395 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -31,4 +31,6 @@
"libvndksupport",
"libbase",
],
+
+ test_suites: ["general-tests"],
}
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index fcdc86a..7b02b51 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "propertyinfoserializer_tests"
+ }
]
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index ce2ec0e..fe23b62 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,5 +1,7 @@
LOCAL_PATH:= $(call my-dir)
+$(eval $(call declare-1p-copy-files,system/core/rootdir,))
+
#######################################
# init-debug.rc
include $(CLEAR_VARS)
@@ -77,7 +79,11 @@
EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
- EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+ ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw
+ else
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+ endif
endif
# Put it here instead of in init.rc module definition,
@@ -110,9 +116,6 @@
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
endif
-ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
- LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
-endif
# For /odm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
@@ -152,6 +155,9 @@
# via /odm/lib/modules directly.
LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+# For /system_dlkm partition
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
@@ -197,15 +203,9 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
- $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(UBSAN_RUNTIME_LIBRARY) \
- $(TSAN_RUNTIME_LIBRARY) \
- $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_UBSAN_RUNTIME_LIBRARY) \
- $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := \
+ $(SANITIZER_STEMS) \
+ $(2ND_SANITIZER_STEMS)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
deleted file mode 100644
index cfc59a7..0000000
--- a/rootdir/avb/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "q-gsi_avbpubkey",
- srcs: [
- "q-gsi.avbpubkey",
- ],
-}
-
-filegroup {
- name: "r-gsi_avbpubkey",
- srcs: [
- "r-gsi.avbpubkey",
- ],
-}
-
-filegroup {
- name: "s-gsi_avbpubkey",
- srcs: [
- "s-gsi.avbpubkey",
- ],
-}
-
-filegroup {
- name: "qcar-gsi_avbpubkey",
- srcs: [
- "qcar-gsi.avbpubkey",
- ],
-}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 647cfa2..8cf3172 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -15,19 +15,6 @@
endif
#######################################
-# q-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := q-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
# q-developer-gsi.avbpubkey
include $(CLEAR_VARS)
@@ -41,19 +28,6 @@
include $(BUILD_PREBUILT)
#######################################
-# r-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := r-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
# r-developer-gsi.avbpubkey
include $(CLEAR_VARS)
@@ -67,19 +41,6 @@
include $(BUILD_PREBUILT)
#######################################
-# s-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := s-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
-#######################################
# s-developer-gsi.avbpubkey
include $(CLEAR_VARS)
@@ -92,17 +53,4 @@
include $(BUILD_PREBUILT)
-#######################################
-# qcar-gsi.avbpubkey
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := qcar-gsi.avbpubkey
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_CLASS := ETC
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
-
-include $(BUILD_PREBUILT)
-
my_gsi_avb_keys_path :=
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
deleted file mode 100644
index 5ed7543..0000000
--- a/rootdir/avb/q-gsi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/rootdir/avb/qcar-gsi.avbpubkey b/rootdir/avb/qcar-gsi.avbpubkey
deleted file mode 100644
index ce56646..0000000
--- a/rootdir/avb/qcar-gsi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
deleted file mode 100644
index 2609b30..0000000
--- a/rootdir/avb/r-gsi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
deleted file mode 100644
index 9065fb8..0000000
--- a/rootdir/avb/s-gsi.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index e4d3d5e..1c7b716 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "CtsBionicTestCases"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "CtsBionicTestCases"
+ }
]
}
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index d62c41d..780ace5 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -20,6 +20,8 @@
"libicuuc.so",
// resolv
"libnetd_resolv.so",
+ // netd
+ "libnetd_updatable.so",
// nn
"libneuralnetworks.so",
// statsd
diff --git a/rootdir/init.no_zygote.rc b/rootdir/init.no_zygote.rc
new file mode 100644
index 0000000..8e0afcb
--- /dev/null
+++ b/rootdir/init.no_zygote.rc
@@ -0,0 +1,2 @@
+# This is an empty file that does not specify any zygote services.
+# It exists to support targets that do not include a zygote.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 42545d9..caed4e1 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,6 +86,10 @@
mkdir /dev/sys/fs 0755 system system
mkdir /dev/sys/block 0755 system system
+ # Create location for fs_mgr to store abbreviated output from filesystem
+ # checker programs.
+ mkdir /dev/fscklogs 0770 root system
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -97,22 +101,18 @@
exec_start boringssl_self_test_apex64
service boringssl_self_test32 /system/bin/boringssl_self_test32
- setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
reboot_on_failure reboot,boringssl-self-check-failed
stdio_to_kmsg
service boringssl_self_test64 /system/bin/boringssl_self_test64
- setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
reboot_on_failure reboot,boringssl-self-check-failed
stdio_to_kmsg
service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
- setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
reboot_on_failure reboot,boringssl-self-check-failed
stdio_to_kmsg
service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
- setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
reboot_on_failure reboot,boringssl-self-check-failed
stdio_to_kmsg
@@ -142,11 +142,21 @@
chown system system /dev/stune/background/tasks
chown system system /dev/stune/top-app/tasks
chown system system /dev/stune/rt/tasks
+ chown system system /dev/stune/cgroup.procs
+ chown system system /dev/stune/foreground/cgroup.procs
+ chown system system /dev/stune/background/cgroup.procs
+ chown system system /dev/stune/top-app/cgroup.procs
+ chown system system /dev/stune/rt/cgroup.procs
chmod 0664 /dev/stune/tasks
chmod 0664 /dev/stune/foreground/tasks
chmod 0664 /dev/stune/background/tasks
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
+ chmod 0664 /dev/stune/cgroup.procs
+ chmod 0664 /dev/stune/foreground/cgroup.procs
+ chmod 0664 /dev/stune/background/cgroup.procs
+ chmod 0664 /dev/stune/top-app/cgroup.procs
+ chmod 0664 /dev/stune/rt/cgroup.procs
# cpuctl hierarchy for devices using utilclamp
mkdir /dev/cpuctl/foreground
@@ -172,6 +182,14 @@
chown system system /dev/cpuctl/system/tasks
chown system system /dev/cpuctl/system-background/tasks
chown system system /dev/cpuctl/dex2oat/tasks
+ chown system system /dev/cpuctl/cgroup.procs
+ chown system system /dev/cpuctl/foreground/cgroup.procs
+ chown system system /dev/cpuctl/background/cgroup.procs
+ chown system system /dev/cpuctl/top-app/cgroup.procs
+ chown system system /dev/cpuctl/rt/cgroup.procs
+ chown system system /dev/cpuctl/system/cgroup.procs
+ chown system system /dev/cpuctl/system-background/cgroup.procs
+ chown system system /dev/cpuctl/dex2oat/cgroup.procs
chmod 0664 /dev/cpuctl/tasks
chmod 0664 /dev/cpuctl/foreground/tasks
chmod 0664 /dev/cpuctl/background/tasks
@@ -180,12 +198,22 @@
chmod 0664 /dev/cpuctl/system/tasks
chmod 0664 /dev/cpuctl/system-background/tasks
chmod 0664 /dev/cpuctl/dex2oat/tasks
+ chmod 0664 /dev/cpuctl/cgroup.procs
+ chmod 0664 /dev/cpuctl/foreground/cgroup.procs
+ chmod 0664 /dev/cpuctl/background/cgroup.procs
+ chmod 0664 /dev/cpuctl/top-app/cgroup.procs
+ chmod 0664 /dev/cpuctl/rt/cgroup.procs
+ chmod 0664 /dev/cpuctl/system/cgroup.procs
+ chmod 0664 /dev/cpuctl/system-background/cgroup.procs
+ chmod 0664 /dev/cpuctl/dex2oat/cgroup.procs
# Create a cpu group for NNAPI HAL processes
mkdir /dev/cpuctl/nnapi-hal
chown system system /dev/cpuctl/nnapi-hal
chown system system /dev/cpuctl/nnapi-hal/tasks
+ chown system system /dev/cpuctl/nnapi-hal/cgroup.procs
chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+ chmod 0664 /dev/cpuctl/nnapi-hal/cgroup.procs
write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
@@ -193,19 +221,25 @@
mkdir /dev/cpuctl/camera-daemon
chown system system /dev/cpuctl/camera-daemon
chown system system /dev/cpuctl/camera-daemon/tasks
+ chown system system /dev/cpuctl/camera-daemon/cgroup.procs
chmod 0664 /dev/cpuctl/camera-daemon/tasks
+ chmod 0664 /dev/cpuctl/camera-daemon/cgroup.procs
# Create an stune group for camera-specific processes
mkdir /dev/stune/camera-daemon
chown system system /dev/stune/camera-daemon
chown system system /dev/stune/camera-daemon/tasks
+ chown system system /dev/stune/camera-daemon/cgroup.procs
chmod 0664 /dev/stune/camera-daemon/tasks
+ chmod 0664 /dev/stune/camera-daemon/cgroup.procs
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal/tasks
+ chown system system /dev/stune/nnapi-hal/cgroup.procs
chmod 0664 /dev/stune/nnapi-hal/tasks
+ chmod 0664 /dev/stune/nnapi-hal/cgroup.procs
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
@@ -217,8 +251,12 @@
chown system system /dev/blkio/background
chown system system /dev/blkio/tasks
chown system system /dev/blkio/background/tasks
+ chown system system /dev/blkio/cgroup.procs
+ chown system system /dev/blkio/background/cgroup.procs
chmod 0664 /dev/blkio/tasks
chmod 0664 /dev/blkio/background/tasks
+ chmod 0664 /dev/blkio/cgroup.procs
+ chmod 0664 /dev/blkio/background/cgroup.procs
write /dev/blkio/blkio.weight 1000
write /dev/blkio/background/blkio.weight 200
write /dev/blkio/background/blkio.bfq.weight 10
@@ -367,6 +405,13 @@
chown system system /dev/cpuset/top-app/tasks
chown system system /dev/cpuset/restricted/tasks
chown system system /dev/cpuset/camera-daemon/tasks
+ chown system system /dev/cpuset/cgroup.procs
+ chown system system /dev/cpuset/foreground/cgroup.procs
+ chown system system /dev/cpuset/background/cgroup.procs
+ chown system system /dev/cpuset/system-background/cgroup.procs
+ chown system system /dev/cpuset/top-app/cgroup.procs
+ chown system system /dev/cpuset/restricted/cgroup.procs
+ chown system system /dev/cpuset/camera-daemon/cgroup.procs
# set system-background to 0775 so SurfaceFlinger can touch it
chmod 0775 /dev/cpuset/system-background
@@ -378,6 +423,13 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
chmod 0664 /dev/cpuset/camera-daemon/tasks
+ chmod 0664 /dev/cpuset/foreground/cgroup.procs
+ chmod 0664 /dev/cpuset/background/cgroup.procs
+ chmod 0664 /dev/cpuset/system-background/cgroup.procs
+ chmod 0664 /dev/cpuset/top-app/cgroup.procs
+ chmod 0664 /dev/cpuset/restricted/cgroup.procs
+ chmod 0664 /dev/cpuset/cgroup.procs
+ chmod 0664 /dev/cpuset/camera-daemon/cgroup.procs
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
@@ -395,10 +447,6 @@
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
- # Create location for fs_mgr to store abbreviated output from filesystem
- # checker programs.
- mkdir /dev/fscklogs 0770 root system
-
# pstore/ramoops previous console log
mount pstore pstore /sys/fs/pstore nodev noexec nosuid
chown system log /sys/fs/pstore
@@ -486,6 +534,10 @@
# /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
+ # APEXes are ready to use. apex-ready is a public trigger similar to apexd.status=ready which
+ # is a system-private property.
+ trigger apex-ready
+
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
@@ -772,9 +824,15 @@
# directory used for on-device refresh metrics file.
mkdir /data/misc/odrefresh 0777 system system
# directory used for on-device signing key blob
- mkdir /data/misc/odsign 0700 root root
+ mkdir /data/misc/odsign 0710 root system
+ # directory used for odsign metrics
+ mkdir /data/misc/odsign/metrics 0770 root system
+
# Directory for VirtualizationService temporary image files.
- mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
+ # Delete any stale files owned by the old virtualizationservice uid (b/230056726).
+ chmod 0770 /data/misc/virtualizationservice
+ exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice
+ mkdir /data/misc/virtualizationservice 0770 system system
mkdir /data/preloads 0775 system system encryption=None
@@ -782,7 +840,6 @@
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
mkdir /data/local/traces 0777 shell shell
- mkdir /data/data 0771 system system encryption=None
mkdir /data/app-private 0771 system system encryption=Require
mkdir /data/app-ephemeral 0771 system system encryption=Require
mkdir /data/app-asec 0700 root root encryption=Require
@@ -857,13 +914,6 @@
mkdir /data/user 0711 system system encryption=None
mkdir /data/user_de 0711 system system encryption=None
- # Unlink /data/user/0 if we previously symlink it to /data/data
- rm /data/user/0
-
- # Bind mount /data/user/0 to /data/data
- mkdir /data/user/0 0700 system system encryption=None
- mount none /data/data /data/user/0 bind rec
-
# A tmpfs directory, which will contain all apps CE DE data directory that
# bind mount from the original source.
mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000
@@ -875,8 +925,10 @@
mkdir /data_mirror/data_ce/null 0700 root root
mkdir /data_mirror/data_de/null 0700 root root
- # Bind mount CE and DE data directory to mirror's default volume directory
- mount none /data/user /data_mirror/data_ce/null bind rec
+ # Bind mount CE and DE data directory to mirror's default volume directory.
+ # The 'slave' option (MS_SLAVE) is needed to cause the later bind mount of
+ # /data/data onto /data/user/0 to propagate to /data_mirror/data_ce/null/0.
+ mount none /data/user /data_mirror/data_ce/null bind rec slave
mount none /data/user_de /data_mirror/data_de/null bind rec
# Create mirror directory for jit profiles
@@ -909,10 +961,11 @@
wait_for_prop apexd.status activated
perform_apex_config
- # Special-case /data/media/obb per b/64566063
mkdir /data/media 0770 media_rw media_rw encryption=None
exec - media_rw media_rw -- /system/bin/chattr +F /data/media
- mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
+
+ # Create directories for boot animation.
+ mkdir /data/bootanim 0755 system system encryption=None
exec_start derive_sdk
@@ -953,12 +1006,6 @@
# module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
- # If there is no post-fs-data action in the init.<device>.rc file, you
- # must uncomment this line, otherwise encrypted filesystems
- # won't work.
- # Set indication (checked by vold) that we have finished this action
- #setprop vold.post_fs_data_done 1
-
# sys.memfd_use set to false by default, which keeps it disabled
# until it is confirmed that apps and vendor processes don't make
# IOCTLs on ashmem fds any more.
@@ -1030,9 +1077,11 @@
mkdir /dev/sys/fs/by-name 0755 system system
symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
- # to access dm-<num> sysfs
+ # dev.mnt.dev.data=dm-N, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0, or
+ # dev.mnt.dev.data=sdaN/mmcblk0pN, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0
mkdir /dev/sys/block/by-name 0755 system system
- symlink /sys/devices/virtual/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+ symlink /sys/class/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+ symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
@@ -1042,10 +1091,14 @@
write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
write /dev/sys/fs/by-name/userdata/iostat_enable 1
+ # set readahead multiplier for POSIX_FADV_SEQUENTIAL files
+ write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 16
+
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
- # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+ # this requires enabling selinux entry for sda/mmcblk0 in vendor side
write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
+ write /dev/sys/block/by-name/rootdisk/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
@@ -1074,10 +1127,6 @@
chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
- # Assume SMP uses shared cpufreq policy for all CPUs
- chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
- chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-
chown system system /sys/class/leds/vibrator/trigger
chown system system /sys/class/leds/vibrator/activate
chown system system /sys/class/leds/vibrator/brightness
@@ -1245,9 +1294,22 @@
on userspace-reboot-resume
trigger userspace-reboot-fs-remount
trigger post-fs-data
+ trigger apex-ready
trigger zygote-start
trigger early-boot
trigger boot
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
setprop sys.init.userspace_reboot.in_progress ""
+
+# Multi-Gen LRU Experiment
+on property:persist.device_config.mglru_native.lru_gen_config=none
+ write /sys/kernel/mm/lru_gen/enabled 0
+on property:persist.device_config.mglru_native.lru_gen_config=core
+ write /sys/kernel/mm/lru_gen/enabled 1
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
+ write /sys/kernel/mm/lru_gen/enabled 3
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young
+ write /sys/kernel/mm/lru_gen/enabled 5
+on property:persist.device_config.mglru_native.lru_gen_config=all
+ write /sys/kernel/mm/lru_gen/enabled 7
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 3101974..a140c8c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,9 +67,8 @@
# CDMA radio interface MUX
/dev/ppp 0660 radio vpn
-# Virtualization is managed by VirtualizationService.
-/dev/kvm 0600 virtualizationservice root
-/dev/vhost-vsock 0600 virtualizationservice root
+/dev/kvm 0600 system system
+/dev/vhost-vsock 0600 system system
# sysfs properties
/sys/devices/platform/trusty.* trusty_version 0440 root log
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../build/soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index 0a26aba..52a7f74 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -244,25 +244,6 @@
any_changed = true;
}
avb_ops_user_free(ops);
- } else {
- // Not using AVB - assume VB1.0.
-
- // read all fstab entries at once from all sources
- android::fs_mgr::Fstab fstab;
- if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
- printf("Failed to read fstab\n");
- suggest_run_adb_root();
- return 0;
- }
-
- // Loop through entries looking for ones that verity manages.
- for (const auto& entry : fstab) {
- if (entry.fs_mgr_flags.verify) {
- if (set_verity_enabled_state(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
- any_changed = true;
- }
- }
- }
}
if (!any_changed) any_changed = overlayfs_setup(enable);
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index f339553..ca522b7 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -34,184 +34,71 @@
full list for a release by running `toybox` directly.
-## Android 2.3 (Gingerbread)
+## Android 13 ("T")
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-umount uptime vmstat watchprops wipe
-
-
-## Android 4.0 (IceCreamSandwich)
-
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.1-4.3 (JellyBean)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
-printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
-sendevent setconsole setenforce setprop setsebool sleep smd start stop
-sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.4 (KitKat)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
-notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
-schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
-stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 5.0 (Lollipop)
-
-BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
-
-toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
-getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
-lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
-nohup notify ps readlink renice restorecon rmmod route runcon schedtop
-sendevent setenforce setprop setsebool smd start stop swapoff swapon
-top touch umount uptime vmstat watchprops wipe
-
-
-## Android 6.0 (Marshmallow)
-
-BSD: dd du grep
-
-toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
-newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
-chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
-env expand expr fallocate false find free getenforce getprop groups
-head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
-logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
-more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
-pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate umount uname uniq unix2dos usleep
-vmstat wc which whoami xargs yes
-
-
-## Android 7.0 (Nougat)
-
-BSD: dd grep
-
-toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
-sendevent start stop top
-
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
-chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
-dos2unix **du** echo env expand expr fallocate false find **flock** free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
-lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
-mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
-pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
-**uptime** usleep vmstat wc which whoami xargs **xxd** yes
-
-
-## Android 8.0 (Oreo)
-
-BSD: dd grep
+BSD: fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
-toolbox: getevent newfs\_msdos
-
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
-dos2unix du echo env expand expr fallocate false **file** find flock free
-getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
-losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
-mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
-nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
-readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
-seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
-**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
-sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
-tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
-vmstat wc which whoami xargs xxd yes **zcat**
-
-
-## Android 9.0 (Pie)
-
-BSD: dd grep
-
-bzip2: bzcat bzip2 bunzip2
+gavinhoward/bc: bc
one-true-awk: awk
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop setprop start stop
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
-dos2unix du echo env expand expr fallocate false file find flock **fmt** free
-getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
-lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
-modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
-patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
-renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
-tail tar taskset tee time timeout top touch tr true truncate tty ulimit
-umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
-which whoami xargs xxd yes zcat
+toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
+chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
+dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
+microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
+mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
+od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
+printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
+restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl **uclampset** ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
-## Android 10 ("Q")
+## Android 12 ("S")
-BSD: grep fsck\_msdos newfs\_msdos
+BSD: fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
+gavinhoward/bc: bc
+
one-true-awk: awk
-toolbox: getevent getprop
+toolbox: getevent getprop setprop start stop
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
-false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
-getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
-**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
-**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
-losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
-mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
-mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
-paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
-printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
-**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
-true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
-**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
-wc which whoami xargs xxd yes zcat
+toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
+dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
+microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
+mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
+od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
+printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
+restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee **test** time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
+unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
+vmstat watch wc which whoami xargs xxd yes zcat
+
## Android 11 ("R")
@@ -245,34 +132,182 @@
usleep uudecode uuencode uuidgen vconfig **vi** vmstat watch wc which
whoami xargs xxd yes zcat
-## Android ("S")
-BSD: fsck\_msdos newfs\_msdos
+## Android 10 ("Q")
+
+BSD: grep fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
-gavinhoward/bc: bc
+one-true-awk: awk
+
+toolbox: getevent getprop
+
+toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
+false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
+getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
+**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
+**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
+losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
+paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
+printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
+**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
+true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
+**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
+wc which whoami xargs xxd yes zcat
+
+
+## Android 9.0 (Pie)
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
-toolbox: getevent getprop setprop start stop
+toolbox: getevent getprop newfs\_msdos
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
-chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
-dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
-expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
-fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
-help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
-inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
-log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
-microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
-mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
-od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
-printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
-restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
-seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee **test** time timeout top touch tr traceroute
-traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
-unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
-vmstat watch wc which whoami xargs xxd yes zcat
+toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock **fmt** free
+getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
+lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
+modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
+patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
+renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
+tail tar taskset tee time timeout top touch tr true truncate tty ulimit
+umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
+which whoami xargs xxd yes zcat
+
+
+## Android 8.0 (Oreo)
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent newfs\_msdos
+
+toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
+dos2unix du echo env expand expr fallocate false **file** find flock free
+getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
+losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
+mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
+seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
+**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
+vmstat wc which whoami xargs xxd yes **zcat**
+
+
+## Android 7.0 (Nougat)
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
+dos2unix **du** echo env expand expr fallocate false find **flock** free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
+lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
+**uptime** usleep vmstat wc which whoami xargs **xxd** yes
+
+
+## Android 6.0 (Marshmallow)
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+## Android 5.0 (Lollipop)
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.4 (KitKat)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.1-4.3 (JellyBean)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.0 (IceCreamSandwich)
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+## Android 2.3 (Gingerbread)
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index fb855f7..8cc8b59 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -153,7 +153,7 @@
mUidm.init(is_charger_on(status));
// register listener after init uid_monitor
- aidl_health_callback = std::make_shared<HealthInfoCallback>(&mUidm);
+ aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);
ret = health->registerCallback(aidl_health_callback);
if (!ret.isOk()) {
LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index c72af40..278499f 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -223,6 +223,9 @@
case APPLOADER_ERR_INVALID_VERSION:
LOG(ERROR) << "Error: invalid application version";
break;
+ case APPLOADER_ERR_POLICY_VIOLATION:
+ LOG(ERROR) << "Error: loading denied by policy engine";
+ break;
default:
LOG(ERROR) << "Unrecognized error: " << resp.error;
break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 6cda7c1..306596e 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -56,6 +56,7 @@
APPLOADER_ERR_ALREADY_EXISTS,
APPLOADER_ERR_INTERNAL,
APPLOADER_ERR_INVALID_VERSION,
+ APPLOADER_ERR_POLICY_VIOLATION,
};
/**
diff --git a/trusty/apploader/fuzz/Android.bp b/trusty/apploader/fuzz/Android.bp
index e37dab1..c961b36 100644
--- a/trusty/apploader/fuzz/Android.bp
+++ b/trusty/apploader/fuzz/Android.bp
@@ -25,7 +25,10 @@
"-DTRUSTY_APP_PORT=\"com.android.trusty.apploader\"",
"-DTRUSTY_APP_UUID=\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\"",
"-DTRUSTY_APP_FILENAME=\"apploader.syms.elf\"",
- ]
+ ],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
}
// Fuzz app package sent to apploader.
@@ -37,4 +40,7 @@
shared_libs: [
"libdmabufheap",
],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
}
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index ba57191..4780943 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -25,6 +25,9 @@
"-DTRUSTY_APP_UUID=\"7dee2364-c036-425b-b086-df0f6c233c1b\"",
"-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
}
@@ -36,6 +39,9 @@
shared_libs: [
"libdmabufheap",
],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
// The initial corpus for this fuzzer was derived by dumping messages from/to
// HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.
diff --git a/trusty/gatekeeper/fuzz/Android.bp b/trusty/gatekeeper/fuzz/Android.bp
index d084cb6..67d0c0f 100644
--- a/trusty/gatekeeper/fuzz/Android.bp
+++ b/trusty/gatekeeper/fuzz/Android.bp
@@ -25,6 +25,9 @@
"-DTRUSTY_APP_UUID=\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\"",
"-DTRUSTY_APP_FILENAME=\"gatekeeper.syms.elf\"",
],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
// The initial corpus for this fuzzer was derived by dumping messages from
// the `secure_env` emulator interface for cuttlefish while enrolling a new
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index d787f7a..443a670 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -17,9 +17,10 @@
#define LOG_TAG "android.hardware.keymaster@3.0-impl.trusty"
-#include <authorization_set.h>
#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster_tags.h>
#include <trusty_keymaster/TrustyKeymaster3Device.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index e68ba82..9c7e781 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -18,9 +18,10 @@
#define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <authorization_set.h>
#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster_tags.h>
#include <trusty_keymaster/TrustyKeymaster4Device.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index 8d7ee00..5f24bc6 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -25,6 +25,9 @@
"-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
"-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
],
+ fuzz_config: {
+ cc: ["trong@google.com"],
+ },
// The initial corpus for this fuzzer was derived by dumping messages from
// the `secure_env` emulator interface for cuttlefish while running the
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
index 5fd628f..c8d8932 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -27,6 +27,7 @@
using ::keymaster::TrustyKeymaster;
using ::ndk::ScopedAStatus;
using secureclock::TimeStampToken;
+using ::std::array;
using ::std::optional;
using ::std::shared_ptr;
using ::std::vector;
@@ -77,8 +78,13 @@
const optional<TimeStampToken>& timestampToken) override;
ScopedAStatus earlyBootEnded() override;
- ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
- std::vector<uint8_t>* ephemeralKeyBlob) override;
+ ScopedAStatus convertStorageKeyToEphemeral(const vector<uint8_t>& storageKeyBlob,
+ vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
+ ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) override;
+ ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
protected:
std::shared_ptr<TrustyKeymaster> impl_;
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
new file mode 100644
index 0000000..6671355
--- /dev/null
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit" : [
+ {
+ "name" : "vts_treble_vintf_framework_test"
+ }
+ ],
+ "hwasan-presubmit" : [
+ {
+ "name" : "vts_treble_vintf_framework_test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 68a7912..44780e8 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -306,7 +306,7 @@
}
ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
- const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
+ const vector<uint8_t>& storageKeyBlob, vector<uint8_t>* ephemeralKeyBlob) {
keymaster::ExportKeyRequest request(impl_->message_version());
request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
request.key_format = KM_KEY_FORMAT_RAW;
@@ -321,4 +321,17 @@
return ScopedAStatus::ok();
}
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* /* challenge */) {
+ 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::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 5664829..099f189 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -71,9 +71,10 @@
} // namespace
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
- info->versionNumber = 1;
+ info->versionNumber = 2;
info->rpcAuthorName = "Google";
info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ info->uniqueId = "Trusty: My password is ******";
return ScopedAStatus::ok();
}
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 7ca5050..0b995a2 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IKeyMintDevice/default</fqname>
</hal>
<hal format="aidl">
@@ -13,6 +14,7 @@
</hal>
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IRemotelyProvisionedComponent/default</fqname>
</hal>
</manifest>
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index df6b0f8..e2f376c 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -342,6 +342,19 @@
return 0;
}
+static int provision_ids(void) {
+ keymaster::SetAttestationIdsRequest req(4 /* ver */);
+ keymaster::EmptyKeymasterResponse rsp(4 /* ver */);
+
+ req.brand.Reinitialize("trusty", 6);
+ req.device.Reinitialize("trusty", 6);
+ req.product.Reinitialize("trusty", 6);
+ req.manufacturer.Reinitialize("trusty", 6);
+ req.model.Reinitialize("trusty", 6);
+
+ return trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
+}
+
int main(int argc, char** argv) {
int ret = 0;
@@ -353,10 +366,22 @@
ret = trusty_keymaster_connect();
if (ret) {
fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
- } else {
- ret = parse_xml_file(argv[optind]);
- trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
}
- return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ ret = parse_xml_file(argv[optind]);
+ if (ret) {
+ fprintf(stderr, "parse_xml_file failed %d\n", ret);
+ trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
+ }
+
+ ret = provision_ids();
+ if (ret) {
+ fprintf(stderr, "provision_ids failed %d\n", ret);
+ trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
}
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
new file mode 100644
index 0000000..bc1dcf6
--- /dev/null
+++ b/trusty/libtrusty-rs/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+ name: "libtrusty-rs",
+ crate_name: "trusty",
+ srcs: [
+ "src/lib.rs"
+ ],
+ rustlibs: [
+ "libnix",
+ "liblibc",
+ ],
+}
+
+rust_test {
+ name: "libtrusty-rs-tests",
+ crate_name: "trusty_test",
+ srcs: ["tests/test.rs"],
+ rustlibs: [
+ "libtrusty-rs",
+ "liblibc",
+ ]
+}
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
new file mode 100644
index 0000000..28ea075
--- /dev/null
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -0,0 +1,224 @@
+// 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.
+
+//! Functionality for communicating with Trusty services.
+//!
+//! This crate provides the [`TipcChannel`] type, which allows you to establish a
+//! connection to a Trusty service and then communicate with that service.
+//!
+//! # Usage
+//!
+//! To connect to a Trusty service you need two things:
+//!
+//! * The filesystem path to the Trusty IPC device. This is usually
+//! `/dev/trusty-ipc-dev0`, which is exposed in the constant [`DEFAULT_DEVICE`].
+//! * The port name defined by the service, e.g. `com.android.ipc-unittest.srv.echo`.
+//!
+//! Pass these values to [`TipcChannel::connect`] to establish a connection to a
+//! service.
+//!
+//! Once connected use the [`send`][TipcChannel::send] and [`recv`][TipcChannel::recv]
+//! methods to communicate with the service. Messages are passed as byte buffers, and
+//! each Trusty service has its own protocol for what data messages are expected to
+//! contain. Consult the documentation for the service you are communicating with to
+//! determine how to format outgoing messages and interpret incoming ones.
+//!
+//! The connection is closed automatically when [`TipcChannel`] is dropped.
+//!
+//! # Examples
+//!
+//! This example is a simplified version of the echo test from `tipc-test-rs`:
+//!
+//! ```no_run
+//! use trusty::{DEFAULT_DEVICE, TipcChannel};
+//! use std::io::{Read, Write};
+//!
+//! let mut chann = TipcChannel::connect(
+//! DEFAULT_DEVICE,
+//! "com.android.ipc-unittest.srv.echo",
+//! ).unwrap();
+//!
+//! chann.send("Hello, world!".as_bytes()).unwrap();
+//!
+//! let mut read_buf = Vec::new();
+//! let read_len = stream.recv(&mut read_buf).unwrap();
+//!
+//! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();
+//! assert_eq!("Hello, world!", response);
+//!
+//! // The connection is closed here.
+//! ```
+
+use crate::sys::tipc_connect;
+use std::ffi::CString;
+use std::fs::File;
+use std::io::prelude::*;
+use std::io::{ErrorKind, Result};
+use std::os::unix::prelude::AsRawFd;
+use std::path::Path;
+
+mod sys;
+
+/// The default filesystem path for the Trusty IPC device.
+pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0";
+
+/// The maximum size an incoming TIPC message can be.
+///
+/// This can be used to pre-allocate buffer space in order to ensure that your
+/// read buffer can always hold an incoming message.
+pub const MAX_MESSAGE_SIZE: usize = 4096;
+
+/// A channel for communicating with a Trusty service.
+///
+/// See the [crate-level documentation][crate] for usage details and examples.
+#[derive(Debug)]
+pub struct TipcChannel(File);
+
+impl TipcChannel {
+ /// Attempts to establish a connection to the specified Trusty service.
+ ///
+ /// The first argument is the path of the Trusty device in the local filesystem,
+ /// e.g. `/dev/trusty-ipc-dev0`. The second argument is the name of the service
+ /// to connect to, e.g. `com.android.ipc-unittest.srv.echo`.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `service` contains any intermediate `NUL`
+ /// bytes. This is handled with a panic because the service names are all
+ /// hard-coded constants, and so such an error should always be indicative of a
+ /// bug in the calling code.
+ pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
+ let file = File::options().read(true).write(true).open(device)?;
+
+ let srv_name = CString::new(service).expect("Service name contained null bytes");
+ unsafe {
+ tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
+ }
+
+ Ok(TipcChannel(file))
+ }
+
+ /// Sends a message to the connected service.
+ ///
+ /// The entire contents of `buf` will be sent as a single message to the
+ /// connected service.
+ pub fn send(&mut self, buf: &[u8]) -> Result<()> {
+ let write_len = self.0.write(buf)?;
+
+ // Verify that the expected number of bytes were written. The entire message
+ // should always be written with a single `write` call, or an error should have
+ // been returned if the message couldn't be written. An assertion failure here
+ // potentially means a bug in the kernel driver.
+ assert_eq!(
+ buf.len(),
+ write_len,
+ "Failed to send full message ({} of {} bytes written)",
+ write_len,
+ buf.len(),
+ );
+
+ Ok(())
+ }
+
+ /// Reads the next incoming message.
+ ///
+ /// Attempts to read the next incoming message from the connected service if any
+ /// exist. If the initial capacity of `buf` is not enough to hold the incoming
+ /// message the function repeatedly attempts to reserve additional space until
+ /// it is able to fully read the message.
+ ///
+ /// Blocks until there is an incoming message if there is not already a message
+ /// ready to be received.
+ ///
+ /// # Errors
+ ///
+ /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
+ /// then the error is ignored and the operation will be tried again.
+ ///
+ /// If this function encounters an error with the error code `EMSGSIZE` then
+ /// additional space will be reserved in `buf` and the operation will be tried
+ /// again.
+ ///
+ /// If any other read error is encountered then this function immediately
+ /// returns the error to the caller, and the length of `buf` is set to 0.
+ pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {
+ // If no space has been allocated in the buffer reserve enough space to hold any
+ // incoming message.
+ if buf.capacity() == 0 {
+ buf.reserve(MAX_MESSAGE_SIZE);
+ }
+
+ loop {
+ // Resize the vec to make its full capacity available to write into.
+ buf.resize(buf.capacity(), 0);
+
+ match self.0.read(buf.as_mut_slice()) {
+ Ok(len) => {
+ buf.truncate(len);
+ return Ok(());
+ }
+
+ Err(err) => {
+ if let Some(libc::EMSGSIZE) = err.raw_os_error() {
+ // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity
+ // to contain the maximum message size. This should never happen, but if it
+ // does we don't want to hang by looping infinitely.
+ assert!(
+ buf.capacity() < MAX_MESSAGE_SIZE,
+ "Received `EMSGSIZE` error when buffer capacity was already at maximum",
+ );
+
+ // If we didn't have enough space to hold the incoming message, reserve
+ // enough space to fit the maximum message size regardless of how much
+ // capacity the buffer already had.
+ buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());
+ } else if err.kind() == ErrorKind::Interrupted {
+ // If we get an interrupted error the operation can be retried as-is, i.e.
+ // we don't need to allocate additional space.
+ continue;
+ } else {
+ buf.truncate(0);
+ return Err(err);
+ }
+ }
+ }
+ }
+ }
+
+ /// Reads the next incoming message without allocating.
+ ///
+ /// Returns the number of bytes in the received message, or any error that
+ /// occurred when reading the message.
+ ///
+ /// Blocks until there is an incoming message if there is not already a message
+ /// ready to be received.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large
+ /// enough to contain the incoming message. Use
+ /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to
+ /// determine if you need to increase the size of `buf`. If error code
+ /// `EMSGSIZE` is returned the incoming message will not be dropped, and a
+ /// subsequent call to `recv_no_alloc` can still read it.
+ ///
+ /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
+ /// operation should be retried if there is nothing else to do.
+ pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {
+ self.0.read(buf)
+ }
+
+ // TODO: Add method that is equivalent to `tipc_send`, i.e. that supports
+ // sending shared memory buffers.
+}
diff --git a/trusty/libtrusty-rs/src/sys.rs b/trusty/libtrusty-rs/src/sys.rs
new file mode 100644
index 0000000..f1c8c5f
--- /dev/null
+++ b/trusty/libtrusty-rs/src/sys.rs
@@ -0,0 +1,31 @@
+// NOTE: The ioctl definitions are sequestered into this module because the
+// `ioctl_*!` macros provided by the nix crate generate public functions that we
+// don't want to be part of this crate's public API.
+//
+// NOTE: We are manually re-declaring the types and constants here instead of using
+// bindgen and a separate `-sys` crate because the defines used for the ioctl
+// numbers (`TIPC_IOC_CONNECT` and `TIPC_IOC_SEND_MSG`) can't currently be
+// translated by bindgen.
+
+use std::os::raw::c_char;
+
+const TIPC_IOC_MAGIC: u8 = b'r';
+
+// NOTE: We use `ioctl_write_ptr_bad!` here due to an error in how the ioctl
+// code is defined in `trusty/ipc.h`.
+//
+// If we were to do `ioctl_write_ptr!(TIPC_IOC_MAGIC, 0x80, c_char)` it would
+// generate a function that takes a `*const c_char` data arg and would use
+// `size_of::<c_char>()` when generating the ioctl number. However, in
+// `trusty/ipc.h` the definition for `TIPC_IOC_CONNECT` declares the ioctl with
+// `char*`, meaning we need to use `size_of::<*const c_char>()` to generate an
+// ioctl number that matches what Trusty expects.
+//
+// To maintain compatibility with the `trusty/ipc.h` and the kernel driver we
+// use `ioctl_write_ptr_bad!` and manually use `request_code_write!` to generate
+// the ioctl number using the correct size.
+nix::ioctl_write_ptr_bad!(
+ tipc_connect,
+ nix::request_code_write!(TIPC_IOC_MAGIC, 0x80, std::mem::size_of::<*const c_char>()),
+ c_char
+);
diff --git a/trusty/libtrusty-rs/tests/test.rs b/trusty/libtrusty-rs/tests/test.rs
new file mode 100644
index 0000000..6bff479
--- /dev/null
+++ b/trusty/libtrusty-rs/tests/test.rs
@@ -0,0 +1,86 @@
+use trusty::{TipcChannel, DEFAULT_DEVICE};
+
+const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo";
+
+#[test]
+fn recv_no_alloc() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 32];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message from the TA. The response message will be the
+ // same as the message we just sent.
+ let mut recv_buf = [0u8; 32];
+ let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();
+
+ assert_eq!(
+ send_buf.len(),
+ read_len,
+ "Received data was wrong size (expected {} bytes, received {})",
+ send_buf.len(),
+ read_len,
+ );
+ assert_eq!(send_buf, recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_small_buf() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a long message to the echo service so that we can test receiving a long
+ // message.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Attempt to receive the response message with a buffer that is too small to
+ // contain the message.
+ let mut recv_buf = [0u8; 32];
+ let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();
+
+ assert_eq!(
+ Some(libc::EMSGSIZE),
+ err.raw_os_error(),
+ "Unexpected error err when receiving incoming message: {:?}",
+ err,
+ );
+}
+
+#[test]
+fn recv_empty_vec() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message. `recv_buf` is initially empty, and `recv` is
+ // responsible for allocating enough space to hold the message.
+ let mut recv_buf = Vec::new();
+ connection.recv(&mut recv_buf).unwrap();
+
+ assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_vec_existing_capacity() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message into a buffer that already has enough capacity
+ // to hold the message. No additional capacity should be allocated when
+ // receiving the message.
+ let mut recv_buf = Vec::with_capacity(2048);
+ connection.recv(&mut recv_buf).unwrap();
+
+ assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+ assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed");
+}
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
index 1fa6fe4..04e84c6 100644
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -23,15 +23,19 @@
/**
* enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
- * be mapped as nonsecure. Suitable for shared memory. The paired
- * fd must be a "memfd".
- * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will be
- * transitioned to "Secure" memory if Trusty is in TrustZone.
- * This transfer kind is suitable for donating video buffers or
- * other similar resources. The paired fd may need to come from a
- * platform-specific allocator for memory that may be
- * transitioned to "Secure".
+ * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it
+ * will be mapped as nonsecure. Suitable for shared memory.
+ * The paired fd must be a "dma_buf".
+ * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will
+ * be transitioned to "Secure" memory if Trusty is in
+ * TrustZone. This transfer kind is suitable for donating
+ * video buffers or other similar resources. The paired fd
+ * may need to come from a platform-specific allocator for
+ * memory that may be transitioned to "Secure".
+ * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be
+ * accessible only to Trusty. The paired fd may need to
+ * come from a platform-specific allocator that returns
+ * "Secure" buffers.
*
* Describes how the user would like the resource in question to be sent to
* Trusty. Options may be valid only for certain kinds of fds.
@@ -39,6 +43,7 @@
enum transfer_kind {
TRUSTY_SHARE = 0,
TRUSTY_LEND = 1,
+ TRUSTY_SEND_SECURE = 2,
};
/**
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 38d8685..94f26d8 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -35,7 +35,10 @@
"liblog",
"libhardware_legacy",
],
- header_libs: ["libcutils_headers"],
+ header_libs: [
+ "libcutils_headers",
+ "libgsi_headers",
+ ],
static_libs: [
"libfstab",
diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp
index 6c2fd36..3305d8d 100644
--- a/trusty/storage/proxy/checkpoint_handling.cpp
+++ b/trusty/storage/proxy/checkpoint_handling.cpp
@@ -18,9 +18,12 @@
#include "log.h"
#include <fstab/fstab.h>
+#include <unistd.h>
#include <cstring>
#include <string>
+#include <libgsi/libgsi.h>
+
namespace {
bool checkpointingDoneForever = false;
@@ -75,3 +78,15 @@
return 0;
}
+
+/**
+ * is_gsi_running() - Check if a GSI image is running via DSU.
+ *
+ * This function is equivalent to android::gsi::IsGsiRunning(), but this API is
+ * not yet vendor-accessible although the underlying metadata file is.
+ *
+ */
+bool is_gsi_running() {
+ /* TODO(b/210501710): Expose GSI image running state to vendor storageproxyd */
+ return !access(android::gsi::kGsiBootedIndicatorFile, F_OK);
+}
diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h
index f1bf27c..dfe2947 100644
--- a/trusty/storage/proxy/checkpoint_handling.h
+++ b/trusty/storage/proxy/checkpoint_handling.h
@@ -32,6 +32,8 @@
*/
int is_data_checkpoint_active(bool* active);
+bool is_gsi_running();
+
#ifdef __cplusplus
}
#endif
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c690a28..2620034 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -104,8 +104,11 @@
return -1;
}
- /* no-execute for user, no access for group and other */
- umask(S_IXUSR | S_IRWXG | S_IRWXO);
+ /*
+ * No access for group and other. We need execute access for user to create
+ * an accessible directory.
+ */
+ umask(S_IRWXG | S_IRWXO);
return 0;
}
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 2fde30f..c00c399 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <libgen.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -24,26 +25,29 @@
#include <sys/types.h>
#include <unistd.h>
-#include "log.h"
+#include "checkpoint_handling.h"
#include "ipc.h"
+#include "log.h"
#include "storage.h"
#define FD_TBL_SIZE 64
#define MAX_READ_SIZE 4096
+#define ALTERNATE_DATA_DIR "alternate/"
+
enum sync_state {
SS_UNUSED = -1,
SS_CLEAN = 0,
SS_DIRTY = 1,
};
-static int ssdir_fd = -1;
static const char *ssdir_name;
static enum sync_state fs_state;
-static enum sync_state dir_state;
static enum sync_state fd_state[FD_TBL_SIZE];
+static bool alternate_mode;
+
static struct {
struct storage_file_read_resp hdr;
uint8_t data[MAX_READ_SIZE];
@@ -53,10 +57,6 @@
{
uint32_t handle = fd;
- if (open_flags & O_CREAT) {
- dir_state = SS_DIRTY;
- }
-
if (handle < FD_TBL_SIZE) {
fd_state[fd] = SS_CLEAN; /* fd clean */
if (open_flags & O_TRUNC) {
@@ -187,7 +187,6 @@
goto err_response;
}
- dir_state = SS_DIRTY;
rc = unlink(path);
if (rc < 0) {
rc = errno;
@@ -211,11 +210,21 @@
return ipc_respond(msg, NULL, 0);
}
+static void sync_parent(const char* path) {
+ int parent_fd;
+ char* parent_path = dirname(path);
+ parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));
+ if (parent_fd >= 0) {
+ fsync(parent_fd);
+ close(parent_fd);
+ } else {
+ ALOGE("%s: failed to open parent directory \"%s\" for sync: %s\n", __func__, parent_path,
+ strerror(errno));
+ }
+}
-int storage_file_open(struct storage_msg *msg,
- const void *r, size_t req_len)
-{
- char *path = NULL;
+int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len) {
+ char* path = NULL;
const struct storage_file_open_req *req = r;
struct storage_file_open_resp resp = {0};
@@ -234,6 +243,24 @@
goto err_response;
}
+ /*
+ * TODO(b/210501710): Expose GSI image running state to vendor
+ * storageproxyd. We want to control data file paths in vendor_init, but we
+ * don't have access to the necessary property there yet. When we have
+ * access to that property we can set the root data path read-only and only
+ * allow creation of files in alternate/. Checking paths here temporarily
+ * until that is fixed.
+ *
+ * We are just checking for "/" instead of "alternate/" because we still
+ * want to still allow access to "persist/" in alternate mode (for now, this
+ * may change in the future).
+ */
+ if (alternate_mode && !strchr(req->name, '/')) {
+ ALOGE("%s: Cannot open root data file \"%s\" in alternate mode\n", __func__, req->name);
+ msg->result = STORAGE_ERR_ACCESS;
+ goto err_response;
+ }
+
int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
if (rc < 0) {
ALOGE("%s: asprintf failed\n", __func__);
@@ -247,6 +274,24 @@
open_flags |= O_TRUNC;
if (req->flags & STORAGE_FILE_OPEN_CREATE) {
+ /*
+ * Create the alternate parent dir if needed & allowed.
+ *
+ * TODO(b/210501710): Expose GSI image running state to vendor
+ * storageproxyd. This directory should be created by vendor_init, once
+ * it has access to the necessary bit of information.
+ */
+ if (strstr(req->name, ALTERNATE_DATA_DIR) == req->name) {
+ char* parent_path = dirname(path);
+ rc = mkdir(parent_path, S_IRWXU);
+ if (rc == 0) {
+ sync_parent(parent_path);
+ } else if (errno != EEXIST) {
+ ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
+ strerror(errno));
+ }
+ }
+
/* open or create */
if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
/* create exclusive */
@@ -279,6 +324,10 @@
msg->result = translate_errno(rc);
goto err_response;
}
+
+ if (open_flags & O_CREAT) {
+ sync_parent(path);
+ }
free(path);
/* at this point rc contains storage file fd */
@@ -467,17 +516,14 @@
int storage_init(const char *dirname)
{
+ /* If there is an active DSU image, use the alternate fs mode. */
+ alternate_mode = is_gsi_running();
+
fs_state = SS_CLEAN;
- dir_state = SS_CLEAN;
for (uint i = 0; i < FD_TBL_SIZE; i++) {
fd_state[i] = SS_UNUSED; /* uninstalled */
}
- ssdir_fd = open(dirname, O_RDONLY);
- if (ssdir_fd < 0) {
- ALOGE("failed to open ss root dir \"%s\": %s\n",
- dirname, strerror(errno));
- }
ssdir_name = dirname;
return 0;
}
@@ -501,25 +547,16 @@
}
}
- /* check if we need to sync the directory */
- if (dir_state == SS_DIRTY) {
- if (fs_state == SS_CLEAN) {
- rc = fsync(ssdir_fd);
- if (rc < 0) {
- ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
- return rc;
- }
- }
- dir_state = SS_CLEAN; /* set to clean */
- }
-
- /* check if we need to sync the whole fs */
+ /* check if we need to sync all filesystems */
if (fs_state == SS_DIRTY) {
- rc = syscall(SYS_syncfs, ssdir_fd);
- if (rc < 0) {
- ALOGE("syncfs failed: %s\n", strerror(errno));
- return rc;
- }
+ /*
+ * We sync all filesystems here because we don't know what filesystem
+ * needs syncing if there happen to be other filesystems symlinked under
+ * the root data directory. This should not happen in the normal case
+ * because our fd table is large enough to handle the few open files we
+ * use.
+ */
+ sync();
fs_state = SS_CLEAN;
}
diff --git a/trusty/test/driver/Android.bp b/trusty/test/driver/Android.bp
new file mode 100644
index 0000000..b813a04
--- /dev/null
+++ b/trusty/test/driver/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test {
+ name: "trusty_driver_test",
+ srcs: [
+ "**/*.py",
+ ],
+ test_suites: ["general-tests"],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ enabled: true,
+ },
+ },
+}
diff --git a/trusty/test/driver/trusty_driver_test.py b/trusty/test/driver/trusty_driver_test.py
new file mode 100644
index 0000000..608fd47
--- /dev/null
+++ b/trusty/test/driver/trusty_driver_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+#
+# Copyright 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.
+
+"""Test cases for Trusty Linux Driver."""
+
+import os
+import unittest
+
+def ReadFile(file_path):
+ with open(file_path, 'r') as f:
+ # Strip all trailing spaces, newline and null characters.
+ return f.read().rstrip(' \n\x00')
+
+def WriteFile(file_path, s):
+ with open(file_path, 'w') as f:
+ f.write(s)
+
+def IsTrustySupported():
+ return os.path.exists("/dev/trusty-ipc-dev0")
+
+@unittest.skipIf(not IsTrustySupported(), "Device does not support Trusty")
+class TrustyDriverTest(unittest.TestCase):
+ def testIrqDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/unbind", "trusty:irq")
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/bind", "trusty:irq")
+
+ def testLogDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-log/unbind", "trusty:log")
+ WriteFile("/sys/bus/platform/drivers/trusty-log/bind", "trusty:log")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testVirtioDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/unbind",
+ "trusty:virtio")
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/bind",
+ "trusty:virtio")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testTrustyDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty/unbind", "trusty")
+ WriteFile("/sys/bus/platform/drivers/trusty/bind", "trusty")
+
+ def testTrustyDriverVersion(self):
+ ver = ReadFile("/sys/bus/platform/devices/trusty/trusty_version")
+ self.assertTrue(ver.startswith("Project:"))
+
+ def testUntaintedLinux(self):
+ tainted = ReadFile("/proc/sys/kernel/tainted")
+ self.assertEqual(tainted, "0")
+
+ # stdcall test with shared memory buffers.
+ # Each test run takes up to 4 arguments:
+ # <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>
+ #
+ # Test single 4K shared memory object.
+ # Test 10 8MB objects, shared twice, each accessed twice. (8MB non-
+ # contiguous object is large enough to need several 4KB messages to
+ # describe)
+ # Test sharing 2 8MB objects 100 times without accessing it.
+ # Test 10 4K shared memory objects, shared 10 times, each accessed
+ # 10 times.
+ def testStdCall(self):
+ test = "/sys/devices/platform/trusty/trusty:test/trusty_test_run"
+ args = "0x1000 0x800000,10,2,2 0x800000,2,100,0 0x1000,10,10,10"
+ WriteFile(test, args)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index 74106ec..3a43774 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -13,7 +13,7 @@
# limitations under the License.
PRODUCT_PACKAGES += \
- spiproxyd \
- trusty_keymaster_set_attestation_key \
keymaster_soft_attestation_keys.xml \
-
+ spiproxyd \
+ trusty_driver_test \
+ trusty_keymaster_set_attestation_key \