Merge "Rectify Boottime property"
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 e0c138b..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"],
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 967b942..e3ea455 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -51,12 +51,9 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
@@ -623,9 +620,12 @@
}
// TODO: Use seccomp to lock ourselves down.
- unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
- if (!unwinder.Init()) {
- LOG(FATAL) << "Failed to init unwinder object.";
+
+ unwindstack::AndroidRemoteUnwinder unwinder(vm_pid, unwindstack::Regs::CurrentArch());
+ unwindstack::ErrorData error_data;
+ if (!unwinder.Initialize(error_data)) {
+ LOG(FATAL) << "Failed to initialize unwinder object: "
+ << unwindstack::GetErrorCodeString(error_data.code);
}
std::string amfd_data;
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 db30b8f..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 a5e2413..f4ba347 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -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,
@@ -377,6 +383,8 @@
#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([]() {
@@ -408,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([]() {
@@ -486,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;
@@ -2021,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 4c1f9eb..70e3022 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -31,12 +31,9 @@
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "debuggerd/handler.h"
#include "handler/fallback.h"
@@ -73,14 +70,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
+ // Use a normal cached object because the thread 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::AndroidLocalUnwinder unwinder(process_memory);
dump_backtrace_thread(output_fd, &unwinder, thread);
}
__linker_disable_fallback_allocator();
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index fd91038..3ff9710 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -37,6 +37,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/types.h"
@@ -57,7 +58,7 @@
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread) {
log_t log;
log.tfd = output_fd;
@@ -65,21 +66,17 @@
_LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
- unwinder->SetRegs(thread.registers.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- _LOG(&log, logtype::THREAD, " Error code: %s\n", unwinder->LastErrorCodeString());
- _LOG(&log, logtype::THREAD, " Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
- }
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder->Unwind(thread.registers.get(), data)) {
+ _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d: Error %s\n", thread.tid,
+ data.GetErrorString().c_str());
return;
}
- log_backtrace(&log, unwinder, " ");
+ log_backtrace(&log, unwinder, data, " ");
}
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
log_t log;
log.tfd = output_fd.get();
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index b2077ba..d8f74e0 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -21,9 +21,8 @@
#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "tombstone.pb.h"
@@ -43,10 +42,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;
}
@@ -103,7 +105,8 @@
constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
-void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone,
+ unwindstack::AndroidUnwinder* unwinder) const {
if (!CrashIsMine()) {
ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
return;
@@ -137,7 +140,6 @@
heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
- unwinder->SetDisplayBuildID(true);
std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index c20d090..531afea 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -30,16 +30,16 @@
// Forward delcaration
namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
}
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
void dump_backtrace_header(int output_fd);
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread);
void dump_backtrace_footer(int output_fd);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index a979370..0429643 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,9 +26,15 @@
#include "types.h"
#include "utility.h"
+// Forward delcarations
class Cause;
class Tombstone;
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+} // namespace unwindstack
+
class GwpAsanCrashData {
public:
GwpAsanCrashData() = delete;
@@ -52,7 +58,7 @@
// allocator crash state.
uintptr_t GetFaultAddress() const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+ void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
protected:
// Is GWP-ASan responsible for this crash.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 172ffe9..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -23,9 +23,15 @@
#include "scudo/interface.h"
+// Forward delcarations
class Cause;
class Tombstone;
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+} // namespace unwindstack
+
class ScudoCrashData {
public:
ScudoCrashData() = delete;
@@ -34,12 +40,12 @@
bool CrashIsMine() const;
- void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+ void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
private:
scudo_error_info error_info_ = {};
uintptr_t untagged_fault_addr_;
void FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const;
+ unwindstack::AndroidUnwinder* unwinder) const;
};
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7bf1688..be999e0 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,7 +37,7 @@
namespace unwindstack {
struct FrameData;
-class Unwinder;
+class AndroidUnwinder;
}
// The maximum number of frames to save when unwinding.
@@ -51,7 +51,7 @@
/* Creates a tombstone file and writes the crash dump to it. */
void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,
- unwindstack::Unwinder* unwinder,
+ unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data);
@@ -59,7 +59,7 @@
void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
siginfo_t* siginfo, ucontext_t* ucontext);
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 63e142f..25b03af 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -73,11 +73,13 @@
void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
class Memory;
+struct AndroidUnwinderData;
}
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+ unwindstack::AndroidUnwinderData& data, const char* prefix);
ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
unwindstack::Memory* memory);
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a4836d7..27fae25 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -17,8 +17,8 @@
#include "libdebuggerd/scudo.h"
#include "libdebuggerd/tombstone.h"
+#include "unwindstack/AndroidUnwinder.h"
#include "unwindstack/Memory.h"
-#include "unwindstack/Unwinder.h"
#include <android-base/macros.h>
#include <bionic/macros.h>
@@ -80,7 +80,7 @@
}
void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,
- unwindstack::Unwinder* unwinder) const {
+ unwindstack::AndroidUnwinder* unwinder) const {
MemoryError* memory_error = cause->mutable_memory_error();
HeapObject* heap_object = memory_error->mutable_heap();
@@ -102,7 +102,6 @@
heap_object->set_address(report->allocation_address);
heap_object->set_size(report->allocation_size);
- unwinder->SetDisplayBuildID(true);
heap_object->set_allocation_tid(report->allocation_tid);
for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
@@ -123,7 +122,8 @@
set_human_readable_cause(cause, untagged_fault_addr_);
}
-void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void ScudoCrashData::AddCauseProtos(Tombstone* tombstone,
+ unwindstack::AndroidUnwinder* unwinder) const {
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) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 14caaf6..5ca2c00 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,9 +36,9 @@
#include <async_safe/log.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/open_files_list.h"
@@ -101,12 +101,16 @@
}
}
- unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
- auto process_memory =
- unwindstack::Memory::CreateProcessMemoryCached(getpid());
- unwinder.SetProcessMemory(process_memory);
- if (!unwinder.Init()) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ // 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 thread is stopped, and there
+ // is no chance of data changing between reads.
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+ unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+ unwindstack::ErrorData error;
+ if (!unwinder.Initialize(error)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object: %s",
+ unwindstack::GetErrorCodeString(error.code));
return;
}
@@ -116,7 +120,8 @@
process_info, nullptr, nullptr);
}
-void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unwinder* unwinder,
+void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
+ unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
std::string* amfd_data) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bee4a67..159ebc8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -56,10 +56,11 @@
#include <private/android_filesystem_config.h>
#include <procinfo/process.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
@@ -189,7 +190,7 @@
error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
}
-static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ProcessInfo& process_info, const ThreadInfo& main_thread) {
#if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
@@ -245,9 +246,9 @@
}
}
-static void dump_abort_message(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_abort_message(Tombstone* tombstone,
+ std::shared_ptr<unwindstack::Memory>& process_memory,
const ProcessInfo& process_info) {
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
uintptr_t address = process_info.abort_msg_address;
if (address == 0) {
return;
@@ -348,7 +349,7 @@
f->set_build_id(frame.map_info->GetPrintableBuildID());
}
-static void dump_registers(unwindstack::Unwinder* unwinder,
+static void dump_registers(unwindstack::AndroidUnwinder* unwinder,
const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
bool memory_dump) {
if (regs == nullptr) {
@@ -402,27 +403,9 @@
});
}
-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);
+static void dump_thread_backtrace(std::vector<unwindstack::FrameData>& frames, Thread& thread) {
std::set<std::string> unreadable_elf_files;
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : frames) {
BacktraceFrame* f = thread.add_current_backtrace();
fill_in_backtrace_frame(f, frame);
if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
@@ -446,7 +429,7 @@
}
}
-static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const ThreadInfo& thread_info, bool memory_dump = false) {
Thread thread;
@@ -455,36 +438,29 @@
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- 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);
+ unwindstack::AndroidUnwinderData data;
+ // Indicate we want a copy of the initial registers.
+ data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+ bool unwind_ret;
+ if (thread_info.registers != nullptr) {
+ unwind_ret = unwinder->Unwind(thread_info.registers.get(), data);
} else {
- 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);
+ unwind_ret = unwinder->Unwind(thread_info.tid, data);
}
+ if (!unwind_ret) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Unwind failed for tid %d: Error %s",
+ thread_info.tid, data.GetErrorString().c_str());
+ } else {
+ dump_thread_backtrace(data.frames, thread);
+ }
+ dump_registers(unwinder, *data.saved_initial_regs, thread, memory_dump);
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
- unwindstack::Maps* maps = unwinder->GetMaps();
- std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
-
+static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
+ std::shared_ptr<unwindstack::Memory>& process_memory) {
for (const auto& map_info : *maps) {
auto* map = tombstone->add_memory_mappings();
map->set_begin_address(map_info->start());
@@ -593,7 +569,8 @@
}
static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
- unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+ std::shared_ptr<unwindstack::Memory>& process_memory,
+ uintptr_t fault_addr) {
if (tombstone.arch() != Architecture::ARM64) return;
fault_addr = untag_address(fault_addr);
@@ -604,8 +581,6 @@
// a valid address for us to dump tags from.
if (fault_addr < kBytesToRead / 2) return;
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
MemoryDump tag_dump;
@@ -614,7 +589,7 @@
// Attempt to read the first tag. If reading fails, this likely indicates the
// lowest touched page is inaccessible or not marked with PROT_MTE.
// Fast-forward over pages until one has tags, or we exhaust the search range.
- while (memory->ReadTag(start_address) < 0) {
+ while (process_memory->ReadTag(start_address) < 0) {
size_t page_size = sysconf(_SC_PAGE_SIZE);
size_t bytes_to_next_page = page_size - (start_address % page_size);
if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
@@ -626,7 +601,7 @@
std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
for (size_t i = 0; i < granules_to_read; ++i) {
- long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+ long tag = process_memory->ReadTag(start_address + i * kTagGranuleSize);
if (tag < 0) break;
mte_tags->push_back(static_cast<uint8_t>(tag));
}
@@ -636,7 +611,7 @@
}
}
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, const OpenFilesList* open_files) {
Tombstone result;
@@ -691,12 +666,12 @@
sig.set_has_fault_address(true);
uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
sig.set_fault_address(fault_addr);
- dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
+ dump_tags_around_fault_addr(&sig, result, unwinder->GetProcessMemory(), fault_addr);
}
*result.mutable_signal_info() = sig;
- dump_abort_message(&result, unwinder, process_info);
+ dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
// Dump the main thread, but save the memory around the registers.
dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
@@ -709,7 +684,7 @@
dump_probable_cause(&result, unwinder, process_info, main_thread);
- dump_mappings(&result, unwinder);
+ dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
// Only dump logs on debuggable devices.
if (android::base::GetBoolProperty("ro.debuggable", false)) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index ecd98a4..74a1423 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -39,6 +39,7 @@
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
@@ -483,10 +484,10 @@
return describe_end(value, desc);
}
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+ unwindstack::AndroidUnwinderData& data, const char* prefix) {
std::set<std::string> unreadable_elf_files;
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : data.frames) {
if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
unreadable_elf_files.emplace(frame.map_info->name());
}
@@ -509,7 +510,7 @@
}
}
- for (const auto& frame : unwinder->frames()) {
+ for (const auto& frame : data.frames) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
}
}
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 4e8fdf9..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 || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_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 4eb996e..0cb8e08 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -34,7 +34,15 @@
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 || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9ae2c37..5c07eb0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -130,12 +130,10 @@
"-Werror",
"-Wvla",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-Wthread-safety",
],
rtti: true,
- clang_cflags: [
- "-Wthread-safety",
- ],
}
cc_binary {
@@ -215,7 +213,7 @@
"-Werror",
"-Wunreachable-code",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-D_FILE_OFFSET_BITS=64"
+ "-D_FILE_OFFSET_BITS=64",
],
target: {
@@ -411,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/device/flashing.cpp b/fastboot/device/flashing.cpp
index 22f8363..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,30 +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) {
+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) {
// 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);
}
}
@@ -181,7 +189,7 @@
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;
}
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 e9bf9e9..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 init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm 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 39d86f9..79c3ac7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -186,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
};
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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index d13f4d1..7c7979b 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,7 +165,7 @@
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);
}
@@ -255,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)) {
@@ -267,20 +266,30 @@
const char* f2fs_fsck_forced_argv[] = {
F2FS_FSCK_BIN, "-f", "-c", "10000", "--debug-cache", blk_device.c_str()};
- if (should_force_check(*fs_stat)) {
- LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
- << realpath(blk_device);
- ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
- &status, false, LOG_KLOG | LOG_FILE, false, nullptr);
+ if (access(F2FS_FSCK_BIN, X_OK)) {
+ LINFO << "Not running " << F2FS_FSCK_BIN << " on " << realpath(blk_device)
+ << " (executable not in system image)";
} else {
- LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
- << realpath(blk_device);
- ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
- LOG_KLOG | LOG_FILE, false, nullptr);
- }
- 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;
+ if (should_force_check(*fs_stat)) {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
+ << realpath(blk_device);
+ ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+ &status, false, LOG_KLOG | LOG_FILE, false,
+ FSCK_LOG_FILE);
+ } else {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
+ << realpath(blk_device);
+ ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status,
+ false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
+ }
+ 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),
@@ -1913,6 +1922,7 @@
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
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 143e980..8c719c8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -287,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;
@@ -648,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 =
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b831d12..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;
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/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 8e6bbd9..f4d5c72 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -171,11 +171,11 @@
std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
- uint64_t merge_op_start_;
+ uint64_t merge_op_start_{};
std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
- uint64_t num_total_data_ops_;
- uint64_t num_ordered_ops_to_merge_;
- bool has_seq_ops_;
+ uint64_t num_total_data_ops_{};
+ uint64_t num_ordered_ops_to_merge_{};
+ bool has_seq_ops_{};
std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
ReaderFlags reader_flag_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 38b47d5..11da568 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -538,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);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 797d627..a83f535 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -218,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";
@@ -1903,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)) {
@@ -3205,15 +3235,27 @@
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;
status.set_io_uring_enabled(IsIouringEnabled());
- LOG(INFO) << "User-space snapshots enabled";
+ 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
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 04d228d..36abf71 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2411,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());
@@ -2422,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 {
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 c31772b..2f7775c 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -34,6 +34,17 @@
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);
}
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/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1dbee75..6c881c0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -943,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) {
@@ -960,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]);
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/init/Android.bp b/init/Android.bp
index c39d163..f91265e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -178,7 +178,6 @@
"update_metadata-protos",
],
shared_libs: [
- "libbacktrace",
"libbase",
"libbootloader_message",
"libcrypto",
@@ -195,6 +194,7 @@
"libprocessgroup",
"libprocessgroup_setup",
"libselinux",
+ "libunwindstack",
"libutils",
"libziparchive",
],
@@ -352,7 +352,6 @@
"libgsi",
"liblzma",
"libunwindstack_no_dex",
- "libbacktrace_no_dex",
"libmodprobe",
"libext2_uuid",
"libprotobuf-cpp-lite",
@@ -532,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",
@@ -556,7 +555,6 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
],
- srcs: init_common_sources + init_host_sources,
proto: {
type: "lite",
},
@@ -574,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/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/action_parser.cpp b/init/action_parser.cpp
index 52f6a1f..49fe24a 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -142,6 +142,14 @@
action_subcontext = subcontext_;
}
+ // We support 'on' for only Vendor APEXes from /{vendor, odm}.
+ // It is to prevent mainline modules from using 'on' triggers because events/properties are
+ // not stable for mainline modules.
+ // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.
+ if (StartsWith(filename, "/apex/") && !action_subcontext) {
+ return Error() << "ParseSection() failed: 'on' is supported for only Vendor APEXes.";
+ }
+
std::string event_trigger;
std::map<std::string, std::string> property_triggers;
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 0eb894b..9e1d93c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -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/epoll.cpp b/init/epoll.cpp
index 0580f86..74d8aac 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -23,8 +23,6 @@
#include <functional>
#include <map>
-#include <android-base/logging.h>
-
namespace android {
namespace init {
@@ -44,11 +42,8 @@
if (!events) {
return Error() << "Must specify events";
}
-
- Info info;
- info.events = events;
- info.handler = std::make_shared<decltype(handler)>(std::move(handler));
- auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
+ auto sp = std::make_shared<decltype(handler)>(std::move(handler));
+ auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(sp));
if (!inserted) {
return Error() << "Cannot specify two epoll handlers for a given FD";
}
@@ -89,14 +84,8 @@
}
std::vector<std::shared_ptr<Handler>> pending_functions;
for (int i = 0; i < num_events; ++i) {
- auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
- if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
- (ev[i].events & EPOLLIN) != ev[i].events) {
- // This handler wants to know about exception events, and just got one.
- // Log something informational.
- LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
- }
- pending_functions.emplace_back(info.handler);
+ auto sp = *reinterpret_cast<std::shared_ptr<Handler>*>(ev[i].data.ptr);
+ pending_functions.emplace_back(std::move(sp));
}
return pending_functions;
diff --git a/init/epoll.h b/init/epoll.h
index f58ae8d..0df5289 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -46,13 +46,8 @@
std::optional<std::chrono::milliseconds> timeout);
private:
- struct Info {
- std::shared_ptr<Handler> handler;
- uint32_t events;
- };
-
android::base::unique_fd epoll_fd_;
- std::map<int, Info> epoll_handlers_;
+ std::map<int, std::shared_ptr<Handler>> epoll_handlers_;
};
} // namespace init
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..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;
@@ -285,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 {
@@ -304,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";
}
}
@@ -352,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/init.cpp b/init/init.cpp
index 5a0b3a6..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;
}
@@ -661,8 +713,7 @@
PLOG(FATAL) << "failed to create signalfd";
}
- constexpr int flags = EPOLLIN | EPOLLPRI;
- if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
+ if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {
LOG(FATAL) << result.error();
}
}
@@ -796,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); };
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/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/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 c89c5ab..be8c554 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -762,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);
diff --git a/init/service.cpp b/init/service.cpp
index 2ebf87e..01dd685 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -288,6 +288,10 @@
if (flags_ & SVC_EXEC) UnSetExec();
+ if (name_ == "zygote" || name_ == "zygote64") {
+ removeAllEmptyProcessGroups();
+ }
+
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
@@ -491,10 +495,15 @@
// Wait until the cgroups have been created and until the cgroup controllers have been
// activated.
- if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
+ 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";
@@ -536,6 +545,10 @@
if ((flags_ & SVC_ONESHOT) && disabled) {
flags_ |= SVC_RESTART;
}
+
+ LOG(INFO) << "service '" << name_
+ << "' requested start, but it is already running (flags: " << flags_ << ")";
+
// It is not an error to try to start a service that is already running.
reboot_on_failure.Disable();
return {};
@@ -647,9 +660,14 @@
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 (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 (use_memcg) {
ConfigureMemcg();
}
@@ -657,7 +675,7 @@
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
}
- if (write((*pipefd)[1], "", 1) < 0) {
+ if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
return ErrnoError() << "sending notification failed";
}
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 35bd415..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"
@@ -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 263cb73..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>
@@ -305,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/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 6fc64df..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -95,10 +95,7 @@
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
- if (!service) {
- LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
- return pid;
- }
+ if (!service) return pid;
service->Reap(siginfo);
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..1801d17 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -61,6 +61,8 @@
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
+const std::string kDataDirPrefix("/data/");
+
void (*trigger_shutdown)(const std::string& command) = nullptr;
// DecodeUid() - decodes and returns the given string, which can be either the
@@ -458,58 +460,34 @@
return {};
}
-static FscryptAction FscryptInferAction(const std::string& dir) {
- const std::string prefix = "/data/";
-
- if (!android::base::StartsWith(dir, prefix)) {
- 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
- // to apply a global policy to all /data folders created via makedir
- if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
- return FscryptAction::kNone;
- }
-
- // Special case various directories that must not be encrypted,
- // often because their subdirectories must be encrypted.
- // This isn't a nice way to do this, see b/26641735
- std::vector<std::string> directories_to_exclude = {
- "lost+found", "system_ce", "system_de", "misc_ce", "misc_de",
- "vendor_ce", "vendor_de", "media", "data", "user",
- "user_de", "apex", "preloads", "app-staging", "gsi",
- };
- for (const auto& d : directories_to_exclude) {
- if ((prefix + d) == dir) {
- return FscryptAction::kNone;
+// Remove unnecessary slashes so that any later checks (e.g., the check for
+// whether the path is a top-level directory in /data) don't get confused.
+std::string CleanDirPath(const std::string& path) {
+ std::string result;
+ result.reserve(path.length());
+ // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/
+ for (char c : path) {
+ if (c != '/' || result.empty() || result.back() != '/') {
+ result += c;
}
}
- // Empty these directories if policy setting fails.
- std::vector<std::string> wipe_on_failure = {
- "rollback", "rollback-observer", // b/139193659
- };
- for (const auto& d : wipe_on_failure) {
- if ((prefix + d) == dir) {
- return FscryptAction::kDeleteIfNecessary;
- }
+ // Remove trailing slash, e.g. /data/foo/ => /data/foo
+ if (result.length() > 1 && result.back() == '/') {
+ result.pop_back();
}
- return FscryptAction::kRequire;
+ return result;
}
Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+ std::string path = CleanDirPath(args[1]);
+ const bool is_toplevel_data_dir =
+ StartsWith(path, kDataDirPrefix) &&
+ path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;
+ FscryptAction fscrypt_action =
+ is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;
mode_t mode = 0755;
Result<uid_t> uid = -1;
Result<gid_t> gid = -1;
- FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
- FscryptAction fscrypt_action = fscrypt_inferred_action;
std::string ref_option = "ref";
bool set_option_encryption = false;
bool set_option_key = false;
@@ -574,24 +552,17 @@
if (set_option_key && fscrypt_action == FscryptAction::kNone) {
return Error() << "Key option set but encryption action is none";
}
- const std::string prefix = "/data/";
- if (StartsWith(args[1], prefix) &&
- args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+ if (is_toplevel_data_dir) {
if (!set_option_encryption) {
- LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+ LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << path
<< " <mode> <uid> <gid> encryption=Require";
}
if (fscrypt_action == FscryptAction::kNone) {
- LOG(INFO) << "Not setting encryption policy on: " << args[1];
+ LOG(INFO) << "Not setting encryption policy on: " << path;
}
}
- if (fscrypt_action != fscrypt_inferred_action) {
- LOG(WARNING) << "Inferred action different from explicit one, expected "
- << static_cast<int>(fscrypt_inferred_action) << " but got "
- << static_cast<int>(fscrypt_action);
- }
- return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+ return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};
}
Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
diff --git a/init/util.h b/init/util.h
index bf53675..47d4ff5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -69,6 +69,7 @@
bool IsLegalPropertyName(const std::string& name);
Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+std::string CleanDirPath(const std::string& path);
struct MkdirOptions {
std::string target;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4..e8144c3 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -170,5 +170,18 @@
EXPECT_TRUE(is_dir(path1.c_str()));
}
+TEST(util, CleanDirPath) {
+ EXPECT_EQ("", CleanDirPath(""));
+ EXPECT_EQ("/", CleanDirPath("/"));
+ EXPECT_EQ("/", CleanDirPath("//"));
+ EXPECT_EQ("/foo", CleanDirPath("/foo"));
+ EXPECT_EQ("/foo", CleanDirPath("//foo"));
+ EXPECT_EQ("/foo", CleanDirPath("/foo/"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar/"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar////"));
+ EXPECT_EQ("/foo/bar", CleanDirPath("//foo//bar"));
+}
+
} // namespace init
} // namespace android
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/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 17a0070..3867f34 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -209,6 +209,38 @@
}
/**
+ * 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.
+ * The cookie here must be unique on the track_name level, not the name level.
+ */
+#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, cookie) \
+ atrace_async_for_track_end(ATRACE_TAG, track_name, cookie)
+static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
+ int32_t cookie) {
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_for_track_end_body(const char*, int32_t);
+ atrace_async_for_track_end_body(track_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
@@ -227,17 +259,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/trace-container.cpp b/libcutils/trace-container.cpp
index ef7c72d..eae6155 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("G", "|", "|%d", track_name, name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("H", "|", "|%d", "", track_name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("H|%d|", "|%" PRId32, "", track_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..1827e32 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("G|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
+ WRITE_MSG("H|%d|", "|%" PRId32, "", track_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..3dea5ff 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,6 +195,166 @@
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("G|%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("G|%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("G|%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("G|%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("G|%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("G|%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("G|%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", 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("H|%d|fake_track|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact) {
+ std::string expected = android::base::StringPrintf("H|%d|", getpid());
+ std::string track_name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+ atrace_async_for_track_end_body(track_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 + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the exact same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ track_name += '*';
+ atrace_async_for_track_end_body(track_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) {
+ std::string expected = android::base::StringPrintf("H|%d|", getpid());
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body(track_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() - 7;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, track_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 +415,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..e9f58c3 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,9 +28,11 @@
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*/, 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/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/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index c5badc9..39b9f3f 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -67,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 96b5537..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);
@@ -142,6 +159,21 @@
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);
}
@@ -168,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) {
@@ -183,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) {
@@ -195,17 +242,15 @@
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) {
@@ -224,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;
}
@@ -237,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
@@ -411,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;
@@ -446,7 +502,7 @@
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;
@@ -491,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/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b668dcb..8589a8d 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"
@@ -408,7 +435,6 @@
}
]
},
-
{
"Name": "LowIoPriority",
"Actions": [
@@ -621,6 +647,10 @@
"Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
},
{
+ "Name": "SCHED_SP_COMPUTE",
+ "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "LowIoPriority", "TimerSlackNormal" ]
+ },
+ {
"Name": "SCHED_SP_RT_APP",
"Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
},
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 dc7c368..e1c5934 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -129,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;
}
@@ -206,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;
}
@@ -625,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);
}
@@ -675,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;
}
@@ -786,6 +806,7 @@
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
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) {
@@ -794,16 +815,19 @@
}
if (!profile->ExecuteForProcess(uid, pid)) {
PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << "process profile";
+ PLOG(WARNING) << "Failed to find " << name << " process profile";
+ success = false;
}
}
- return true;
+ 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) {
@@ -812,10 +836,12 @@
}
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 9ee3781..df08f65 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -37,8 +37,12 @@
class ProfileAttribute : public IProfileAttribute {
public:
- ProfileAttribute(const CgroupController& controller, const std::string& file_name)
- : controller_(controller), file_name_(file_name) {}
+ // 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;
const CgroupController* controller() const override { return &controller_; }
@@ -50,6 +54,7 @@
private:
CgroupController controller_;
std::string file_name_;
+ std::string file_v2_name_;
};
// Abstract profile element
@@ -102,8 +107,8 @@
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const IProfileAttribute* 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) {}
const char* Name() const override { return "SetAttribute"; }
bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
@@ -112,6 +117,7 @@
private:
const IProfileAttribute* attribute_;
std::string value_;
+ bool optional_;
};
// Set cgroup profile element
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/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 3e24cc0..51580f7 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -38,24 +38,41 @@
#endif
void usage() {
- fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+ fprintf(stderr, "Usage: img2simg [-s] <raw_image_file> <sparse_image_file> [<block_size>]\n");
}
int main(int argc, char* argv[]) {
+ char *arg_in;
+ char *arg_out;
+ enum sparse_read_mode mode = SPARSE_READ_MODE_NORMAL;
+ int extra;
int in;
+ int opt;
int out;
int ret;
struct sparse_file* s;
unsigned int block_size = 4096;
off64_t len;
- if (argc < 3 || argc > 4) {
+ while ((opt = getopt(argc, argv, "s")) != -1) {
+ switch (opt) {
+ case 's':
+ mode = SPARSE_READ_MODE_HOLE;
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+ }
+
+ extra = argc - optind;
+ if (extra < 2 || extra > 3) {
usage();
exit(-1);
}
- if (argc == 4) {
- block_size = atoi(argv[3]);
+ if (extra == 3) {
+ block_size = atoi(argv[optind + 2]);
}
if (block_size < 1024 || block_size % 4 != 0) {
@@ -63,22 +80,24 @@
exit(-1);
}
- if (strcmp(argv[1], "-") == 0) {
+ arg_in = argv[optind];
+ if (strcmp(arg_in, "-") == 0) {
in = STDIN_FILENO;
} else {
- in = open(argv[1], O_RDONLY | O_BINARY);
+ in = open(arg_in, O_RDONLY | O_BINARY);
if (in < 0) {
- fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+ fprintf(stderr, "Cannot open input file %s\n", arg_in);
exit(-1);
}
}
- if (strcmp(argv[2], "-") == 0) {
+ arg_out = argv[optind + 1];
+ if (strcmp(arg_out, "-") == 0) {
out = STDOUT_FILENO;
} else {
- out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+ out = open(arg_out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
if (out < 0) {
- fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+ fprintf(stderr, "Cannot open output file %s\n", arg_out);
exit(-1);
}
}
@@ -93,7 +112,7 @@
}
sparse_file_verbose(s);
- ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
+ ret = sparse_file_read(s, in, mode, false);
if (ret) {
fprintf(stderr, "Failed to read file\n");
exit(-1);
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 235d15d..663c812 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -23,5 +23,7 @@
if (!file) {
return 0;
}
- return sparse_file_callback(file, false, false, WriteCallback, nullptr);
+ int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);
+ sparse_file_destroy(file);
+ return result;
}
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/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/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/libutils/Android.bp b/libutils/Android.bp
index 58af8e4..f663671 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -44,17 +44,7 @@
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: {
@@ -190,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",
@@ -197,7 +188,7 @@
shared_libs: [
"libutils",
- "libbacktrace",
+ "libunwindstack",
],
target: {
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/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/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/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/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/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/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/init.rc b/rootdir/init.rc
index 404d7ae..70a3736 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
@@ -447,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
@@ -538,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
@@ -688,8 +688,6 @@
copy /data/system/entropy.dat /dev/urandom
mkdir /data/vendor 0771 root root encryption=Require
- mkdir /data/vendor_ce 0771 root root encryption=None
- mkdir /data/vendor_de 0771 root root encryption=None
mkdir /data/vendor/hardware 0771 root root
# Start tombstoned early to be able to store tombstones.
@@ -738,6 +736,13 @@
# To handle userspace reboots as well as devices that use FDE, make sure
# that apexd is started cleanly here (set apexd.status="") and that it is
# restarted if it's already running.
+ #
+ # /data/apex uses encryption=None because direct I/O support is needed on
+ # APEX files, but some devices don't support direct I/O on encrypted files.
+ # Also, APEXes are public information, similar to the system image.
+ # /data/apex/decompressed and /data/apex/ota_reserved override this setting;
+ # they are encrypted so that files in them can be hard-linked into
+ # /data/rollback which is encrypted.
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
@@ -824,17 +829,24 @@
# 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
- # Directory for VirtualizationService temporary image files.
- mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
+ 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.
+ # 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
+
+ # /data/preloads uses encryption=None because it only contains preloaded
+ # files that are public information, similar to the system image.
mkdir /data/preloads 0775 system system encryption=None
# For security reasons, /data/local/tmp should always be empty.
# 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
@@ -872,7 +884,10 @@
chown system system /data/resource-cache
chmod 0771 /data/resource-cache
- # create the lost+found directories, so as to enforce our permissions
+ # Ensure that lost+found exists and has the correct permissions. Linux
+ # filesystems expect this directory to exist; it's where the fsck tool puts
+ # any recovered files that weren't present in any directory. It must be
+ # unencrypted, as fsck must be able to write to it.
mkdir /data/lost+found 0770 root root encryption=None
# create directory for DRM plug-ins - give drm the read/write access to
@@ -900,21 +915,26 @@
mkdir /data/system/heapdump 0700 system system
mkdir /data/system/users 0775 system system
- mkdir /data/system_de 0770 system system encryption=None
- mkdir /data/system_ce 0770 system system encryption=None
-
- mkdir /data/misc_de 01771 system misc encryption=None
+ # Create the parent directories of the user CE and DE storage directories.
+ # These parent directories must use encryption=None, since each of their
+ # subdirectories uses a different encryption policy (a per-user one), and
+ # encryption policies apply recursively. These directories should never
+ # contain any subdirectories other than the per-user ones. /data/media/obb
+ # is an exception that exists for legacy reasons.
+ mkdir /data/media 0770 media_rw media_rw encryption=None
mkdir /data/misc_ce 01771 system misc encryption=None
-
+ mkdir /data/misc_de 01771 system misc encryption=None
+ mkdir /data/system_ce 0770 system system encryption=None
+ mkdir /data/system_de 0770 system system encryption=None
mkdir /data/user 0711 system system encryption=None
mkdir /data/user_de 0711 system system encryption=None
+ mkdir /data/vendor_ce 0771 root root encryption=None
+ mkdir /data/vendor_de 0771 root root 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
+ # Set the casefold flag on /data/media. For upgrades, a restorecon can be
+ # needed first to relabel the directory from media_rw_data_file.
+ restorecon /data/media
+ exec - media_rw media_rw -- /system/bin/chattr +F /data/media
# A tmpfs directory, which will contain all apps CE DE data directory that
# bind mount from the original source.
@@ -927,8 +947,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
@@ -961,13 +983,8 @@
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
+ mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
exec_start derive_sdk
@@ -1008,12 +1025,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.
@@ -1099,6 +1110,9 @@
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)
# this requires enabling selinux entry for sda/mmcblk0 in vendor side
@@ -1132,10 +1146,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
@@ -1303,9 +1313,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/storaged/storaged.cpp b/storaged/storaged.cpp
index fb855f7..cefef6e 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();
@@ -333,7 +333,7 @@
first_write = false;
}
- if (benchmark_size) {
+ if (benchmark_size && benchmark_time_ns) {
int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
storage_info->update_perf_history(perf, system_clock::now());
}
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/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/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index 2400ccd..6671355 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"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/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
index 9440724..78e765e 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
@@ -52,11 +52,15 @@
}
ScopedAStatus TrustyKeyMintOperation::updateAad(
- const vector<uint8_t>& input, const optional<HardwareAuthToken>& /* authToken */,
+ const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
const optional<TimeStampToken>& /* timestampToken */) {
UpdateOperationRequest request(impl_->message_version());
request.op_handle = opHandle_;
request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());
+ if (authToken) {
+ auto tokenAsVec(authToken2AidlVec(*authToken));
+ request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+ }
UpdateOperationResponse response(impl_->message_version());
impl_->UpdateOperation(request, &response);
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");
+}