Merge "isBlockAligned to use uint64_t" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0c5543e..c365cac 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -359,6 +359,7 @@
"libdebuggerd/test/dump_memory_test.cpp",
"libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp",
+ "libdebuggerd/test/mte_stack_record_test.cpp",
"libdebuggerd/test/open_files_list_test.cpp",
"libdebuggerd/test/tombstone_proto_to_text_test.cpp",
],
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 8dd2b0d..c9235ee 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -662,6 +662,15 @@
info.pac_enabled_keys = -1;
}
+#if defined(__aarch64__)
+ struct iovec tls_iov = {
+ &info.tls,
+ sizeof(info.tls),
+ };
+ if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TLS, reinterpret_cast<void*>(&tls_iov)) == -1) {
+ info.tls = 0;
+ }
+#endif
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 08619b9..962097e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -86,7 +86,6 @@
#define ARCH_SUFFIX ""
#endif
-constexpr size_t kTagGranuleSize = 16;
constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger";
#define TIMEOUT(seconds, expr) \
@@ -742,6 +741,8 @@
}
#if defined(__aarch64__)
+constexpr size_t kTagGranuleSize = 16;
+
static uintptr_t CreateTagMapping() {
// Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
// of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index dfdfabd..074b095 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -73,5 +73,8 @@
void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
-
+#if defined(__aarch64__)
+void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
+ StackHistoryBuffer& shb_ob, bool nounwind = false);
+#endif
#endif // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index c799f24..f7fc2a3 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -41,6 +41,9 @@
siginfo_t* siginfo = nullptr;
std::unique_ptr<unwindstack::Regs> guest_registers;
+#if defined(__aarch64__)
+ uintptr_t tls; // This is currently used for MTE stack history buffer.
+#endif
};
// This struct is written into a pipe from inside the crashing process.
diff --git a/debuggerd/libdebuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
deleted file mode 100644
index 762bae5..0000000
--- a/debuggerd/libdebuggerd/test/host_signal_fixup.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-
-#include <signal.h>
-
-#if !defined(__BIONIC__)
-
-// In order to compile parts of debuggerd for the host, we need to
-// define these values.
-
-#if !defined(NSIGILL)
-#define NSIGILL ILL_BADSTK
-#endif
-
-#if !defined(BUS_MCEERR_AR)
-#define BUS_MCEERR_AR 4
-#endif
-#if !defined(BUS_MCEERR_AO)
-#define BUS_MCEERR_AO 5
-#endif
-#if !defined(NSIGBUS)
-#define NSIGBUS BUS_MCEERR_AO
-#endif
-
-#if !defined(NSIGFPE)
-#define NSIGFPE FPE_FLTSUB
-#endif
-
-#if !defined(NSIGSEGV)
-#define NSIGSEGV SEGV_ACCERR
-#endif
-
-#if !defined(TRAP_BRANCH)
-#define TRAP_BRANCH 3
-#endif
-#if !defined(TRAP_HWBKPT)
-#define TRAP_HWBKPT 4
-#endif
-#if !defined(NSIGTRAP)
-#define NSIGTRAP TRAP_HWBKPT
-#endif
-
-#if !defined(SI_DETHREAD)
-#define SI_DETHREAD (-7)
-#endif
-
-#endif
-
-#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
new file mode 100644
index 0000000..4b788f3
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+#if defined(__aarch64__)
+
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <optional>
+
+#include "bionic/mte.h"
+#include "bionic/page.h"
+#include "unwindstack/AndroidUnwinder.h"
+#include "unwindstack/Memory.h"
+
+#include <android-base/test_utils.h>
+#include "gtest/gtest.h"
+
+#include "libdebuggerd/tombstone.h"
+
+struct ScopedUnmap {
+ void* ptr;
+ size_t size;
+ ~ScopedUnmap() { munmap(ptr, size); }
+};
+
+class MteStackHistoryTest : public ::testing::TestWithParam<int> {
+ void SetUp() override {
+#if !defined(__aarch64__)
+ GTEST_SKIP();
+#endif
+ SKIP_WITH_HWASAN;
+ unwinder.emplace();
+ unwindstack::ErrorData E;
+ ASSERT_TRUE(unwinder->Initialize(E));
+ }
+
+ protected:
+ // std::optional so we don't construct it for the SKIP cases.
+ std::optional<unwindstack::AndroidLocalUnwinder> unwinder;
+};
+
+TEST(MteStackHistoryUnwindTest, TestOne) {
+#if !defined(__aarch64__)
+ GTEST_SKIP();
+#endif
+ SKIP_WITH_HWASAN;
+ size_t size = stack_mte_ringbuffer_size(0);
+ char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(0, nullptr));
+ ScopedUnmap s{data, size};
+
+ uintptr_t taggedfp = (1ULL << 56) | 1;
+ uintptr_t pc = reinterpret_cast<uintptr_t>(&memcpy);
+ memcpy(data, &pc, sizeof(pc));
+ memcpy(data + 8, &taggedfp, sizeof(taggedfp));
+
+ // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
+ void* tls[4] = {data + 16};
+
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::ErrorData E;
+ unwinder.Initialize(E);
+ StackHistoryBuffer shb;
+ dump_stack_history(&unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ false);
+ ASSERT_EQ(shb.entries_size(), 1);
+ const StackHistoryBufferEntry& e = shb.entries(0);
+ EXPECT_EQ(e.addr().pc(), pc);
+ EXPECT_EQ(e.addr().file_name(), "/apex/com.android.runtime/lib64/bionic/libc.so");
+ EXPECT_EQ(e.fp(), 1ULL);
+ EXPECT_EQ(e.tag(), 1ULL);
+}
+
+TEST_P(MteStackHistoryTest, TestEmpty) {
+ int size_cls = GetParam();
+ size_t size = stack_mte_ringbuffer_size(size_cls);
+ void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr);
+ ScopedUnmap s{data, size};
+ // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
+ void* tls[4] = {data};
+
+ StackHistoryBuffer shb;
+ dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
+ EXPECT_EQ(shb.entries_size(), 0);
+}
+
+TEST_P(MteStackHistoryTest, TestFull) {
+ int size_cls = GetParam();
+ size_t size = stack_mte_ringbuffer_size(size_cls);
+ char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));
+ ScopedUnmap s{data, size};
+ uintptr_t itr = 1;
+ for (char* d = data; d < &data[size]; d += 16) {
+ uintptr_t taggedfp = ((itr & 15) << 56) | itr;
+ uintptr_t pc = itr;
+ memcpy(d, &pc, sizeof(pc));
+ memcpy(d + 8, &taggedfp, sizeof(taggedfp));
+ ++itr;
+ }
+ // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
+ // Because the buffer is full, and we point at one past the last inserted element,
+ // due to wrap-around we point at the beginning of the buffer.
+ void* tls[4] = {data};
+
+ StackHistoryBuffer shb;
+ dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
+ EXPECT_EQ(static_cast<size_t>(shb.entries_size()), size / 16);
+ for (const auto& entry : shb.entries()) {
+ EXPECT_EQ(entry.addr().pc(), --itr);
+ EXPECT_EQ(entry.addr().pc(), entry.fp());
+ EXPECT_EQ(entry.addr().pc() & 15, entry.tag());
+ }
+}
+
+TEST_P(MteStackHistoryTest, TestHalfFull) {
+ int size_cls = GetParam();
+ size_t size = stack_mte_ringbuffer_size(size_cls);
+ size_t half_size = size / 2;
+
+ char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));
+ ScopedUnmap s{data, size};
+
+ uintptr_t itr = 1;
+ for (char* d = data; d < &data[half_size]; d += 16) {
+ uintptr_t taggedfp = ((itr & 15) << 56) | itr;
+ uintptr_t pc = itr;
+ memcpy(d, &pc, sizeof(pc));
+ memcpy(d + 8, &taggedfp, sizeof(taggedfp));
+ ++itr;
+ }
+ // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.
+ void* tls[4] = {&data[half_size]};
+
+ StackHistoryBuffer shb;
+ dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);
+ EXPECT_EQ(static_cast<size_t>(shb.entries_size()), half_size / 16);
+ for (const auto& entry : shb.entries()) {
+ EXPECT_EQ(entry.addr().pc(), --itr);
+ EXPECT_EQ(entry.addr().pc(), entry.fp());
+ EXPECT_EQ(entry.addr().pc() & 15, entry.tag());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(MteStackHistoryTestInstance, MteStackHistoryTest, testing::Range(0, 8));
+
+#endif
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
index a4c08a4..4fd2643 100644
--- a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -134,3 +134,31 @@
ProtoToString();
EXPECT_MATCH(text_, R"(CRASH_DETAIL_NAME: 'helloworld\\1\\255\\3')");
}
+
+TEST_F(TombstoneProtoToTextTest, stack_record) {
+ auto* cause = tombstone_->add_causes();
+ cause->set_human_readable("stack tag-mismatch on thread 123");
+ auto* stack = tombstone_->mutable_stack_history_buffer();
+ stack->set_tid(123);
+ {
+ auto* shb_entry = stack->add_entries();
+ shb_entry->set_fp(0x1);
+ shb_entry->set_tag(0xb);
+ auto* addr = shb_entry->mutable_addr();
+ addr->set_rel_pc(0x567);
+ addr->set_file_name("foo.so");
+ addr->set_build_id("ABC123");
+ }
+ {
+ auto* shb_entry = stack->add_entries();
+ shb_entry->set_fp(0x2);
+ shb_entry->set_tag(0xc);
+ auto* addr = shb_entry->mutable_addr();
+ addr->set_rel_pc(0x678);
+ addr->set_file_name("bar.so");
+ }
+ ProtoToString();
+ EXPECT_MATCH(text_, "stack tag-mismatch on thread 123");
+ EXPECT_MATCH(text_, "stack_record fp:0x1 tag:0xb pc:foo\\.so\\+0x567 \\(BuildId: ABC123\\)");
+ EXPECT_MATCH(text_, "stack_record fp:0x2 tag:0xc pc:bar\\.so\\+0x678");
+}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 3e8ab6e..b6fc4e2 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -27,6 +27,7 @@
#include <inttypes.h>
#include <signal.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
@@ -49,9 +50,11 @@
#include <android/log.h>
#include <android/set_abort_message.h>
-#include <bionic/macros.h>
-#include <bionic/reserved_signals.h>
#include <bionic/crash_detail_internal.h>
+#include <bionic/macros.h>
+#include <bionic/mte.h>
+#include <bionic/reserved_signals.h>
+#include <bionic/tls_defines.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -202,8 +205,117 @@
error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
}
+#if defined(__aarch64__)
+void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,
+ StackHistoryBuffer& shb_obj, bool nounwind) {
+ auto process_memory = unwinder->GetProcessMemory();
+ target_tls += sizeof(void*) * TLS_SLOT_STACK_MTE;
+ uintptr_t stack_mte;
+ if (!process_memory->ReadFully(target_tls, &stack_mte, sizeof(stack_mte))) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "dump_stack_history: failed to read TLS_SLOT_STACK_MTE: %m");
+ return;
+ }
+ if (stack_mte == 0) {
+ async_safe_format_log(ANDROID_LOG_DEBUG, LOG_TAG,
+ "dump_stack_history: stack history buffer is null");
+ return;
+ }
+ uintptr_t untagged_stack_mte = untag_address(stack_mte);
+ uintptr_t buf_size = stack_mte_ringbuffer_size_from_pointer(stack_mte);
+ if (buf_size == 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "dump_stack_history: empty size");
+ return;
+ }
+ uintptr_t buf_start = untagged_stack_mte & ~(buf_size - 1ULL);
+ std::vector<char> buf(buf_size);
+ if (!process_memory->ReadFully(buf_start, buf.data(), buf.size())) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "dump_stack_history: failed to read stack history: %m");
+ return;
+ }
+ uintptr_t original_off = untagged_stack_mte - buf_start;
+ if (original_off % 16 || original_off > buf_size) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "dump_stack_history: invalid offset: %" PRIuPTR, original_off);
+ return;
+ }
+
+ // The original_off is the next slot that would have been written, so the last
+ // slot that was written is the previous one.
+ for (uintptr_t idx = 16; idx <= buf_size; idx += 16) {
+ int64_t off = original_off - idx;
+ if (off < 0) off += buf_size;
+ uintptr_t pc, taggedfp;
+ memcpy(&pc, &(buf[off]), sizeof(pc));
+ memcpy(&taggedfp, &(buf[off + sizeof(pc)]), sizeof(taggedfp));
+
+ if (pc == 0) break;
+ uintptr_t fp = untag_address(taggedfp);
+ uintptr_t tag = taggedfp >> 56;
+
+ unwindstack::FrameData frame_data;
+
+ if (nounwind) {
+ frame_data.pc = pc;
+ } else {
+ // +4 is to counteract the "pc adjustment" in BuildFrameFromPcOnly.
+ // BuildFrameFromPcOnly assumes we are unwinding, so it needs to correct for that
+ // the PC is the return address. That is not the case here.
+ // It doesn't really matter, because either should be in the correct function, but
+ // this is more correct (and consistent with the nounwind case).
+ frame_data = unwinder->BuildFrameFromPcOnly(pc);
+ frame_data.pc += 4;
+ frame_data.rel_pc += 4;
+ }
+
+ StackHistoryBufferEntry* entry = shb_obj.add_entries();
+ fill_in_backtrace_frame(entry->mutable_addr(), frame_data);
+ entry->set_fp(fp);
+ entry->set_tag(tag);
+ }
+}
+
+static pid_t get_containing_thread(unwindstack::MapInfo* map_info, pid_t main_tid) {
+ if (map_info == nullptr) return 0;
+
+ std::string name = map_info->name();
+ if (name == "[stack]") {
+ return main_tid;
+ }
+ int tid;
+ if (sscanf(name.c_str(), "[anon:stack_and_tls:%d", &tid) != 1) {
+ return 0;
+ }
+ return tid;
+}
+
+static std::optional<std::string> maybe_stack_mte_cause(
+ Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const ThreadInfo& target_thread,
+ [[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads, uint64_t fault_addr) {
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ auto map_info = maps->Find(untag_address(fault_addr));
+ pid_t tid = get_containing_thread(map_info.get(), target_thread.tid);
+ if (!tid) {
+ return std::nullopt;
+ }
+ auto it = threads.find(tid);
+ if (it != threads.end()) {
+ StackHistoryBuffer* shb = tombstone->mutable_stack_history_buffer();
+ shb->set_tid(tid);
+ dump_stack_history(unwinder, it->second.tls, *shb);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "dump_probable_cause: unknown target thread %d", tid);
+ }
+ return StringPrintf("stack tag-mismatch on thread %u", tid);
+}
+
+#endif
+
static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
- const ProcessInfo& process_info, const ThreadInfo& target_thread) {
+ const ProcessInfo& process_info, const ThreadInfo& target_thread,
+ [[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads) {
#if defined(USE_SCUDO)
ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
if (scudo_crash_data.CrashIsMine()) {
@@ -247,7 +359,15 @@
} else {
cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);
}
- } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+ }
+#if defined(__aarch64__) && defined(SEGV_MTESERR)
+ else if (si->si_signo == SIGSEGV && si->si_code == SEGV_MTESERR) {
+ // If this was a heap MTE crash, it would have been handled by scudo. Checking whether it
+ // is a stack one.
+ cause = maybe_stack_mte_cause(tombstone, unwinder, target_thread, threads, fault_addr);
+ }
+#endif
+ else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
si->si_syscall);
}
@@ -787,7 +907,7 @@
}
}
- dump_probable_cause(&result, unwinder, process_info, target_thread);
+ dump_probable_cause(&result, unwinder, process_info, target_thread, threads);
dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 1900719..c3f9470 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -511,6 +511,19 @@
"order of likelihood.");
}
+ if (tombstone.has_stack_history_buffer()) {
+ for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) {
+ std::string stack_record_str = StringPrintf(
+ "stack_record fp:0x%" PRIx64 " tag:0x%" PRIx64 " pc:%s+0x%" PRIx64, shbe.fp(), shbe.tag(),
+ shbe.addr().file_name().c_str(), shbe.addr().rel_pc());
+ if (!shbe.addr().build_id().empty()) {
+ StringAppendF(&stack_record_str, " (BuildId: %s)", shbe.addr().build_id().c_str());
+ }
+
+ CBL("%s", stack_record_str.c_str());
+ }
+ }
+
for (const Cause& cause : tombstone.causes()) {
if (tombstone.causes_size() > 1) {
CBS("");
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index b662d36..444c973 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -22,6 +22,21 @@
reserved 3 to 999;
}
+message StackHistoryBufferEntry {
+ BacktraceFrame addr = 1;
+ uint64 fp = 2;
+ uint64 tag = 3;
+
+ reserved 4 to 999;
+}
+
+message StackHistoryBuffer {
+ uint64 tid = 1;
+ repeated StackHistoryBufferEntry entries = 2;
+
+ reserved 3 to 999;
+}
+
message Tombstone {
Architecture arch = 1;
Architecture guest_arch = 24;
@@ -53,7 +68,9 @@
uint32 page_size = 22;
bool has_been_16kb_mode = 23;
- reserved 26 to 999;
+ StackHistoryBuffer stack_history_buffer = 26;
+
+ reserved 27 to 999;
}
enum Architecture {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 774af28..bfe0768 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -186,6 +186,7 @@
"libprotobuf-cpp-lite",
"libsparse",
"libutils",
+ "libselinux",
],
static_libs: [
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7e4d5e5..84e4924 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -80,10 +80,8 @@
using namespace std::chrono_literals;
-bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
bool fs_mgr_is_device_unlocked();
-bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
bool fs_mgr_filesystem_available(const std::string& filesystem);
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index af5ae2d..9cfa93f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -104,6 +104,12 @@
// returned. Otherwise, it will use the current slot.
std::string fs_mgr_get_super_partition_name(int slot = -1);
+// Set readonly for the block device
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
+
+// Check if the block device has ext4 filesystem
+bool fs_mgr_is_ext4(const std::string& blk_device);
+
enum FsMgrUmountStatus : int {
SUCCESS = 0,
ERROR_UNKNOWN = 1 << 0,
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
index 5105da0..73cb175 100644
--- a/fs_mgr/libfstab/fstab_priv.h
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -39,9 +39,6 @@
void ImportBootconfigFromString(const std::string& bootconfig,
const std::function<void(std::string, std::string)>& fn);
-bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
- std::string* out);
-
void ImportKernelCmdlineFromString(const std::string& cmdline,
const std::function<void(std::string, std::string)>& fn);
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index 1696daf..21fe017 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -153,5 +153,8 @@
// Return the "other" slot for the given slot suffix.
std::string OtherSlotSuffix(const std::string& suffix);
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+ std::string* out);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index f297125..4828c4c 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -46,6 +46,7 @@
"libfstab",
"libsnapuserd_client",
"libz",
+ "libselinux",
],
header_libs: [
"libfiemap_headers",
@@ -91,6 +92,7 @@
"partition_cow_creator.cpp",
"return.cpp",
"utility.cpp",
+ "scratch_super.cpp",
],
}
@@ -351,6 +353,7 @@
],
srcs: [
"snapshotctl.cpp",
+ "scratch_super.cpp",
],
static_libs: [
"libbrotli",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index e0969f4..19f3e02 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "device_info.h"
+#include "scratch_super.h"
#include <android-base/logging.h>
#include <fs_mgr.h>
@@ -37,8 +38,24 @@
constexpr bool kIsRecovery = false;
#endif
+DeviceInfo::DeviceInfo() {
+ std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();
+ if (!scratch_device.empty()) {
+ std::string scratch_metadata =
+ android::snapshot::MapScratchOtaMetadataPartition(scratch_device);
+ if (!scratch_metadata.empty()) {
+ SetMetadataDir(scratch_metadata);
+ SetTempMetadata();
+ }
+ }
+}
+
std::string DeviceInfo::GetMetadataDir() const {
- return "/metadata/ota"s;
+ return metadata_dir_;
+}
+
+void DeviceInfo::SetMetadataDir(const std::string& value) {
+ metadata_dir_ = value;
}
std::string DeviceInfo::GetSlotSuffix() const {
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 9153abb..e93ec49 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -29,6 +29,7 @@
using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
public:
+ DeviceInfo();
std::string GetMetadataDir() const override;
std::string GetSlotSuffix() const override;
std::string GetOtherSlotSuffix() const override;
@@ -42,14 +43,19 @@
std::unique_ptr<IImageManager> OpenImageManager() const override;
bool IsFirstStageInit() const override;
android::dm::IDeviceMapper& GetDeviceMapper() override;
-
+ void SetMetadataDir(const std::string& value);
void set_first_stage_init(bool value) { first_stage_init_ = value; }
+ bool IsTempMetadata() const override { return temp_metadata_; }
+ void SetTempMetadata() { temp_metadata_ = true; }
private:
bool EnsureBootHal();
android::fs_mgr::PartitionOpener opener_;
bool first_stage_init_ = false;
+ // Default value
+ std::string metadata_dir_ = "/metadata/ota";
+ bool temp_metadata_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
std::unique_ptr<::android::hal::BootControlClient> boot_control_;
#endif
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index deb2d6e..7ae55db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -111,6 +111,7 @@
virtual bool IsFirstStageInit() const = 0;
virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
+ virtual bool IsTempMetadata() const = 0;
// Helper method for implementing OpenImageManager.
std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@@ -329,6 +330,10 @@
// might be needed to perform first-stage mounts.
static bool IsSnapshotManagerNeeded();
+ // Map the temp OTA metadata partition from super
+ static bool MapTempOtaMetadataPartitionIfNeeded(
+ const std::function<bool(const std::string&)>& init);
+
// Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath();
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 620b03c..1cd6651 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -79,7 +79,7 @@
: TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix);
}
- std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
+ std::string GetMetadataDir() const override { return metadata_dir_; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
@@ -120,6 +120,7 @@
void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
MergeStatus merge_status() const { return merge_status_; }
+ bool IsTempMetadata() const override { return temp_metadata_; }
private:
std::string slot_suffix_ = "_a";
@@ -129,6 +130,8 @@
bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_;
android::dm::IDeviceMapper* dm_ = nullptr;
+ std::string metadata_dir_ = "/metadata/ota/test";
+ bool temp_metadata_ = false;
};
class DeviceMapperWrapper : public android::dm::IDeviceMapper {
diff --git a/fs_mgr/libsnapshot/scratch_super.cpp b/fs_mgr/libsnapshot/scratch_super.cpp
new file mode 100644
index 0000000..805abf3
--- /dev/null
+++ b/fs_mgr/libsnapshot/scratch_super.cpp
@@ -0,0 +1,417 @@
+// Copyright (C) 2024 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+
+#include <libsnapshot/snapshot.h>
+
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
+#include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
+#include <algorithm>
+#include <filesystem>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "device_info.h"
+#include "scratch_super.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+namespace android {
+namespace snapshot {
+
+static bool UmountScratch() {
+ auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+ std::error_code ec;
+
+ if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {
+ LOG(ERROR) << "Failed to remove OTA directory: " << ec.message();
+ return false;
+ }
+
+ if (umount(kOtaMetadataMount) != 0) {
+ PLOG(ERROR) << "UmountScratch failed";
+ return false;
+ }
+
+ LOG(INFO) << "umount scratch_super success";
+ return true;
+}
+
+bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {
+ if (!UmountScratch()) {
+ return false;
+ }
+
+ std::unique_ptr<MetadataBuilder> builder;
+ const auto partition_name = android::base::Basename(kOtaMetadataMount);
+ const std::vector<int> slots = {0, 1};
+
+ if (info == nullptr) {
+ info = new android::snapshot::DeviceInfo();
+ }
+
+ std::string super_device;
+ if (info->IsTestDevice()) {
+ super_device = "super";
+ } else {
+ super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ }
+ const auto& opener = info->GetPartitionOpener();
+ std::string slot_suffix = info->GetSlotSuffix();
+ int slot = SlotNumberForSlotSuffix(slot_suffix);
+ // Walk both the slots and clean up metadata related to scratch space from
+ // both the slots.
+ for (auto slot : slots) {
+ std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
+ if (!builder) {
+ return false;
+ }
+
+ if (builder->FindPartition(partition_name) != nullptr) {
+ builder->RemovePartition(partition_name);
+ auto metadata = builder->Export();
+ if (!metadata) {
+ return false;
+ }
+ if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),
+ slot)) {
+ LOG(ERROR) << "UpdatePartitionTable failed for slot: " << slot;
+ return false;
+ }
+ if (DestroyLogicalPartition(partition_name)) {
+ LOG(INFO) << "CleanupScratchOtaMetadata success for slot: " << slot;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool SetupOTADirs() {
+ if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
+ PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
+ return false;
+ }
+ const auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+ if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {
+ PLOG(ERROR) << "mkdir " << ota_dir;
+ return false;
+ }
+
+ const auto snapshot_dir = ota_dir + "/" + "snapshots";
+ if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {
+ PLOG(ERROR) << "mkdir " << snapshot_dir;
+ return false;
+ }
+ if (setfscreatecon(nullptr)) {
+ PLOG(ERROR) << "setfscreatecon null";
+ return false;
+ }
+ return true;
+}
+
+static bool MountScratch(const std::string& device_path) {
+ if (access(device_path.c_str(), R_OK | W_OK)) {
+ LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+ return false;
+ }
+
+ std::string filesystem_candidate;
+ if (fs_mgr_is_ext4(device_path)) {
+ filesystem_candidate = "ext4";
+ } else {
+ LOG(ERROR) << "Scratch partition is not ext4";
+ return false;
+ }
+ if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
+ PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
+ return false;
+ }
+ if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {
+ PLOG(ERROR) << "create " << kOtaMetadataMount;
+ return false;
+ }
+
+ android::fs_mgr::FstabEntry entry;
+ entry.blk_device = device_path;
+ entry.mount_point = kOtaMetadataMount;
+ entry.flags = MS_NOATIME;
+ entry.flags |= MS_SYNCHRONOUS;
+ entry.fs_options = "nodiscard";
+ fs_mgr_set_blk_ro(device_path, false);
+ entry.fs_mgr_flags.check = true;
+
+ bool mounted = false;
+ entry.fs_type = filesystem_candidate.c_str();
+ if (fs_mgr_do_mount_one(entry) == 0) {
+ mounted = true;
+ }
+
+ if (setfscreatecon(nullptr)) {
+ PLOG(ERROR) << "setfscreatecon null";
+ return false;
+ }
+ if (!mounted) {
+ rmdir(kOtaMetadataMount);
+ return false;
+ }
+
+ return true;
+}
+
+static bool MakeScratchFilesystem(const std::string& scratch_device) {
+ std::string fs_type;
+ std::string command;
+ if (!access(kMkExt4, X_OK)) {
+ fs_type = "ext4";
+ command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kOtaMetadataMount;
+ } else {
+ LOG(ERROR) << "No supported mkfs command or filesystem driver available, supported "
+ "filesystems "
+ "are: f2fs, ext4";
+ return false;
+ }
+ command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
+ fs_mgr_set_blk_ro(scratch_device, false);
+ auto ret = system(command.c_str());
+ if (ret) {
+ LOG(ERROR) << "make " << fs_type << " filesystem on " << scratch_device
+ << " return=" << ret;
+ return false;
+ }
+ return true;
+}
+
+static bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,
+ std::string* scratch_device) {
+ const auto partition_name = android::base::Basename(kOtaMetadataMount);
+ auto& dm = DeviceMapper::Instance();
+ if (info == nullptr) {
+ info = new android::snapshot::DeviceInfo();
+ }
+
+ std::string super_device;
+ if (info->IsTestDevice()) {
+ super_device = "super";
+ } else {
+ super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+ }
+
+ bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
+ if (partition_exists) {
+ LOG(ERROR) << "Partition already exists: " << partition_name;
+ return false;
+ }
+
+ const auto& opener = info->GetPartitionOpener();
+ std::string slot_suffix = info->GetSlotSuffix();
+ int slot = SlotNumberForSlotSuffix(slot_suffix);
+ std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
+
+ if (!builder) {
+ LOG(ERROR) << "open " << super_device << " failed";
+ return false;
+ }
+
+ auto partition = builder->FindPartition(partition_name);
+ partition_exists = partition != nullptr;
+ if (partition_exists) {
+ LOG(ERROR) << "Partition exists in super metadata";
+ return false;
+ }
+
+ partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+ if (!partition) {
+ LOG(ERROR) << "AddPartition failed " << partition_name;
+ return false;
+ }
+
+ auto free_space = builder->AllocatableSpace() - builder->UsedSpace();
+ if (free_space < kOtaMetadataPartitionSize) {
+ LOG(ERROR) << "No space in super partition. Free space: " << free_space
+ << " Requested space: " << kOtaMetadataPartitionSize;
+ return false;
+ }
+
+ LOG(INFO) << "CreateDynamicScratch: free_space: " << free_space
+ << " scratch_size: " << kOtaMetadataPartitionSize << " slot_number: " << slot;
+
+ if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {
+ LOG(ERROR) << "ResizePartition failed: " << partition_name << " free_space: " << free_space
+ << " scratch_size: " << kOtaMetadataPartitionSize;
+ return false;
+ }
+
+ auto metadata = builder->Export();
+ CreateLogicalPartitionParams params;
+
+ if (!metadata ||
+ !UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {
+ LOG(ERROR) << "UpdatePartitionTable failed: " << partition_name;
+ return false;
+ }
+ params = {
+ .block_device = super_device,
+ .metadata_slot = slot,
+ .partition_name = partition_name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ .partition_opener = &info->GetPartitionOpener(),
+ };
+
+ if (!CreateLogicalPartition(params, scratch_device)) {
+ LOG(ERROR) << "CreateLogicalPartition failed";
+ return false;
+ }
+
+ LOG(INFO) << "Scratch device created successfully: " << *scratch_device << " slot: " << slot;
+ return true;
+}
+
+bool IsScratchOtaMetadataOnSuper() {
+ auto partition_name = android::base::Basename(kOtaMetadataMount);
+ auto source_slot = fs_mgr_get_slot_suffix();
+ auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+
+ const auto super_device =
+ kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);
+
+ auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);
+ if (!metadata) {
+ return false;
+ }
+ auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);
+ if (!partition) {
+ return false;
+ }
+
+ auto& dm = DeviceMapper::Instance();
+ if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {
+ LOG(INFO) << "Partition: " << partition_name << " is active";
+ return true;
+ }
+
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata = metadata.get(),
+ .partition = partition,
+ };
+
+ std::string scratch_path;
+ if (!CreateLogicalPartition(params, &scratch_path)) {
+ LOG(ERROR) << "Could not create logical partition: " << partition_name;
+ return false;
+ }
+ LOG(INFO) << "Scratch device: " << scratch_path << " created successfully";
+
+ return true;
+}
+
+std::string GetScratchOtaMetadataPartition() {
+ std::string device;
+ auto& dm = DeviceMapper::Instance();
+ auto partition_name = android::base::Basename(kOtaMetadataMount);
+
+ bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);
+ if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {
+ return device;
+ }
+ return "";
+}
+
+static bool ScratchAlreadyMounted(const std::string& mount_point) {
+ android::fs_mgr::Fstab fstab;
+ if (!ReadFstabFromProcMounts(&fstab)) {
+ return false;
+ }
+ for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+ if (entry->fs_type == "ext4") {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {
+ if (!ScratchAlreadyMounted(kOtaMetadataMount)) {
+ if (!MountScratch(scratch_device)) {
+ return "";
+ }
+ }
+
+ auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+ if (access(ota_dir.c_str(), F_OK) != 0) {
+ return "";
+ }
+ return ota_dir;
+}
+
+// Entry point to create a scratch device on super partition
+// This will create a 1MB space in super. The space will be
+// from the current active slot. Ext4 filesystem will be created
+// on this scratch device and all the OTA related directories
+// will be created.
+bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {
+ std::string scratch_device;
+
+ if (!CreateDynamicScratch(info, &scratch_device)) {
+ LOG(ERROR) << "CreateDynamicScratch failed";
+ return false;
+ }
+ if (!MakeScratchFilesystem(scratch_device)) {
+ LOG(ERROR) << "MakeScratchFilesystem failed";
+ return false;
+ }
+ if (!MountScratch(scratch_device)) {
+ LOG(ERROR) << "MountScratch failed";
+ return false;
+ }
+ if (!SetupOTADirs()) {
+ LOG(ERROR) << "SetupOTADirs failed";
+ return false;
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/scratch_super.h b/fs_mgr/libsnapshot/scratch_super.h
new file mode 100644
index 0000000..3e6fe70
--- /dev/null
+++ b/fs_mgr/libsnapshot/scratch_super.h
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+namespace android {
+namespace snapshot {
+
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+constexpr char kOtaMetadataFileContext[] = "u:object_r:ota_metadata_file:s0";
+constexpr char kOtaMetadataMount[] = "/mnt/scratch_ota_metadata_super";
+const size_t kOtaMetadataPartitionSize = uint64_t(1 * 1024 * 1024);
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+
+bool IsScratchOtaMetadataOnSuper();
+std::string GetScratchOtaMetadataPartition();
+std::string MapScratchOtaMetadataPartition(const std::string& device);
+bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info = nullptr);
+bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info = nullptr);
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 108fd90..6c3bedd 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -48,6 +48,7 @@
#include <libsnapshot/snapshot_stats.h>
#include "device_info.h"
#include "partition_cow_creator.h"
+#include "scratch_super.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"
@@ -117,7 +118,11 @@
info = new DeviceInfo();
}
- return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
+ auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
+ if (info->IsTempMetadata()) {
+ LOG(INFO) << "Using temp metadata from super";
+ }
+ return sm;
}
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
@@ -1110,6 +1115,13 @@
if (result.state == UpdateState::MergeFailed) {
AcknowledgeMergeFailure(result.failure_code);
}
+
+ if (result.state == UpdateState::MergeCompleted) {
+ if (device_->IsTempMetadata()) {
+ CleanupScratchOtaMetadataIfPresent();
+ }
+ }
+
if (result.state != UpdateState::Merging) {
// Either there is no merge, or the merge was finished, so no need
// to keep waiting.
@@ -2310,7 +2322,27 @@
}
bool SnapshotManager::IsSnapshotManagerNeeded() {
- return access(kBootIndicatorPath, F_OK) == 0;
+ if (access(kBootIndicatorPath, F_OK) == 0) {
+ return true;
+ }
+
+ if (IsScratchOtaMetadataOnSuper()) {
+ return true;
+ }
+
+ return false;
+}
+
+bool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(
+ const std::function<bool(const std::string&)>& init) {
+ auto device = android::snapshot::GetScratchOtaMetadataPartition();
+ if (!device.empty()) {
+ init(device);
+ if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {
+ return false;
+ }
+ }
+ return true;
}
std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
@@ -2397,6 +2429,12 @@
continue;
}
+ if (GetPartitionName(partition) ==
+ android::base::Basename(android::snapshot::kOtaMetadataMount)) {
+ LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
+ continue;
+ }
+
CreateLogicalPartitionParams params = {
.block_device = super_device,
.metadata = metadata.get(),
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 16c247f..46c3a35 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -47,6 +47,7 @@
#include <android/snapshot/snapshot.pb.h>
#include <libsnapshot/test_helpers.h>
#include "partition_cow_creator.h"
+#include "scratch_super.h"
#include "utility.h"
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
@@ -1342,6 +1343,15 @@
DynamicPartitionGroup* group_ = nullptr;
};
+TEST_F(SnapshotUpdateTest, SuperOtaMetadataTest) {
+ auto info = new TestDeviceInfo(fake_super);
+ ASSERT_TRUE(CreateScratchOtaMetadataOnSuper(info));
+ std::string scratch_device = GetScratchOtaMetadataPartition();
+ ASSERT_NE(scratch_device, "");
+ ASSERT_NE(MapScratchOtaMetadataPartition(scratch_device), "");
+ ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));
+}
+
// Test full update flow executed by update_engine. Some partitions uses super empty space,
// some uses images, and some uses both.
// Also test UnmapUpdateSnapshot unmaps everything.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 0158d4d..23c3ccf 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -16,7 +16,6 @@
#include <sysexits.h>
#include <unistd.h>
-
#include <chrono>
#include <filesystem>
#include <fstream>
@@ -46,6 +45,7 @@
#include <storage_literals/storage_literals.h>
#include "partition_cow_creator.h"
+#include "scratch_super.h"
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h>
@@ -57,6 +57,8 @@
using android::base::LogdLogger;
using android::base::StderrLogger;
using android::base::TeeLogger;
+using namespace android::dm;
+using namespace android::fs_mgr;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::FindPartition;
using android::fs_mgr::GetPartitionSize;
@@ -97,7 +99,7 @@
#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
class MapSnapshots {
public:
- MapSnapshots(std::string path = "");
+ MapSnapshots(std::string path = "", bool metadata_super = false);
bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
bool FinishSnapshotWrites();
@@ -122,15 +124,12 @@
std::vector<std::string> patchfiles_;
chromeos_update_engine::DeltaArchiveManifest manifest_;
+ bool metadata_super_ = false;
};
-MapSnapshots::MapSnapshots(std::string path) {
- sm_ = SnapshotManager::New();
- if (!sm_) {
- std::cout << "Failed to create snapshotmanager";
- exit(1);
- }
+MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
snapshot_dir_path_ = path + "/";
+ metadata_super_ = metadata_super;
}
std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
@@ -150,6 +149,12 @@
}
bool MapSnapshots::PrepareUpdate() {
+ if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
+ LOG(ERROR) << "Failed to create OTA metadata on super";
+ return false;
+ }
+ sm_ = SnapshotManager::New();
+
auto source_slot = fs_mgr_get_slot_suffix();
auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
@@ -234,14 +239,22 @@
bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
auto& dm = android::dm::DeviceMapper::Instance();
- std::string cow_device = partition_name + "-cow";
+
+ std::string cow_device = partition_name + "-cow-img";
+ if (metadata_super_) {
+ // If COW device exists on /data, then data wipe cannot be done.
+ if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
+ LOG(ERROR) << "COW device exists on /data: " << *cow_path;
+ return false;
+ }
+ }
+
+ cow_device = partition_name + "-cow";
if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
return true;
}
LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
- // If the COW device exists only on /data
- cow_device = partition_name + "-cow-img";
if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
LOG(ERROR) << "Failed to cow path: " << cow_device;
return false;
@@ -321,6 +334,12 @@
}
bool MapSnapshots::BeginUpdate() {
+ if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
+ LOG(ERROR) << "Failed to create OTA metadata on super";
+ return false;
+ }
+ sm_ = SnapshotManager::New();
+
lock_ = sm_->LockExclusive();
std::vector<std::string> snapshots;
sm_->ListSnapshots(lock_.get(), &snapshots);
@@ -470,10 +489,12 @@
}
bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+ sm_ = SnapshotManager::New();
return sm_->UnmapCowImage(name);
}
bool MapSnapshots::DeleteSnapshots() {
+ sm_ = SnapshotManager::New();
lock_ = sm_->LockExclusive();
if (!sm_->RemoveAllUpdateState(lock_.get())) {
LOG(ERROR) << "Remove All Update State failed";
@@ -583,13 +604,19 @@
}
if (argc < 3) {
- std::cerr << " apply-update <directory location where snapshot patches are present>"
+ std::cerr << " apply-update <directory location where snapshot patches are present> {-w}"
" Apply the snapshots to the COW block device\n";
return false;
}
std::string path = std::string(argv[2]);
- MapSnapshots cow(path);
+ bool metadata_on_super = false;
+ if (argc == 4) {
+ if (std::string(argv[3]) == "-w") {
+ metadata_on_super = true;
+ }
+ }
+ MapSnapshots cow(path, metadata_on_super);
if (!cow.ApplyUpdate()) {
return false;
}
@@ -607,7 +634,7 @@
}
if (argc < 3) {
- std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+ std::cerr << " map-snapshots <directory location where snapshot patches are present> {-w}"
" Map all snapshots based on patches present in the directory\n";
return false;
}
@@ -638,7 +665,14 @@
}
}
- MapSnapshots cow(path);
+ bool metadata_on_super = false;
+ if (argc == 4) {
+ if (std::string(argv[3]) == "-w") {
+ metadata_on_super = true;
+ }
+ }
+
+ MapSnapshots cow(path, metadata_on_super);
if (!cow.BeginUpdate()) {
LOG(ERROR) << "BeginUpdate failed";
return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index efbcb5a..734066b 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -42,6 +42,7 @@
static_libs: [
"libcutils_sockets",
"libfs_mgr_file_wait",
+ "libdm",
],
shared_libs: [
"libbase",
@@ -248,7 +249,7 @@
defaults: ["snapuserd_test_defaults"],
host_supported: true,
test_suites: [
- "device-tests",
+ "general-tests",
],
test_options: {
test_runner_options: [
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 789c980..ddefb9f 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -35,6 +35,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
#include <snapuserd/snapuserd_client.h>
namespace android {
@@ -333,7 +334,21 @@
}
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
- return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
+ std::string metadata_dir;
+ std::string temp_metadata_mnt = "/mnt/scratch_ota_metadata_super";
+
+ auto& dm = ::android::dm::DeviceMapper::Instance();
+ auto partition_name = android::base::Basename(temp_metadata_mnt);
+
+ bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);
+ std::string temp_device;
+ if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {
+ metadata_dir = temp_metadata_mnt + "/" + "ota/";
+ } else {
+ metadata_dir = "/metadata/ota/";
+ }
+
+ return metadata_dir + std::string(kDaemonAliveIndicator);
}
bool SnapuserdClient::IsTransitionedDaemonReady() {
diff --git a/init/Android.bp b/init/Android.bp
index ed1f148..18a79d6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -38,7 +38,6 @@
"capabilities.cpp",
"epoll.cpp",
"import_parser.cpp",
- "interface_utils.cpp",
"interprocess_fifo.cpp",
"keychords.cpp",
"parser.cpp",
@@ -85,10 +84,6 @@
"ueventd.cpp",
"ueventd_parser.cpp",
]
-init_host_sources = [
- "check_builtins.cpp",
- "host_import_parser.cpp",
-]
soong_config_module_type {
name: "libinit_cc_defaults",
@@ -190,7 +185,6 @@
"libext4_utils",
"libfs_mgr",
"libgsi",
- "libhidl-gen-utils",
"liblog",
"liblogwrap",
"liblp",
@@ -607,8 +601,6 @@
whole_static_libs: ["libcap"],
shared_libs: [
"libcutils",
- "libhidl-gen-utils",
- "libhidlmetadata",
"liblog",
"libprocessgroup",
"libprotobuf-cpp-lite",
@@ -616,9 +608,6 @@
proto: {
type: "lite",
},
- generated_headers: [
- "generated_stub_builtin_function_map",
- ],
target: {
android: {
enabled: false,
@@ -637,17 +626,43 @@
cc_binary {
name: "host_init_verifier",
defaults: ["init_host_defaults"],
- srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
+ srcs: [
+ "check_builtins.cpp",
+ "host_import_parser.cpp",
+ "host_init_verifier.cpp",
+ "interface_utils.cpp",
+ ] + init_common_sources,
generated_headers: [
"generated_android_ids",
+ "generated_stub_builtin_function_map",
],
+ shared_libs: [
+ "libhidl-gen-utils",
+ "libhidlmetadata",
+ ],
+}
+
+genrule {
+ name: "noop_builtin_function_map",
+ tool_files: ["host_builtin_map.py"],
+ out: ["noop_builtin_function_map.h"],
+ srcs: [
+ "builtins.cpp",
+ "noop_builtins.cpp",
+ ],
+ cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location noop_builtins.cpp) > $(out)",
}
cc_library_host_static {
name: "libinit_host",
defaults: ["init_host_defaults"],
- srcs: init_common_sources + init_host_sources,
+ srcs: [
+ "noop_builtins.cpp",
+ ] + init_common_sources,
export_include_dirs: ["."],
+ generated_headers: [
+ "noop_builtin_function_map",
+ ],
proto: {
export_proto_headers: true,
},
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index bfe636b..e06a645 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android/avf_cc_flags.h>
+#include <fs_mgr.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -303,6 +304,22 @@
return BootMode::NORMAL_MODE;
}
+static void MaybeResumeFromHibernation(const std::string& bootconfig) {
+ std::string hibernationResumeDevice;
+ android::fs_mgr::GetBootconfigFromString(bootconfig, "androidboot.hibernation_resume_device",
+ &hibernationResumeDevice);
+ if (!hibernationResumeDevice.empty()) {
+ android::base::unique_fd fd(open("/sys/power/resume", O_RDWR | O_CLOEXEC));
+ if (fd >= 0) {
+ if (!android::base::WriteStringToFd(hibernationResumeDevice, fd)) {
+ PLOG(ERROR) << "Failed to write to /sys/power/resume";
+ }
+ } else {
+ PLOG(ERROR) << "Failed to open /sys/power/resume";
+ }
+ }
+}
+
static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
auto ret = FirstStageMount::Create(cmdline);
if (ret.ok()) {
@@ -442,6 +459,8 @@
<< module_elapse_time.count() << " ms";
}
+ MaybeResumeFromHibernation(bootconfig);
+
std::unique_ptr<FirstStageMount> fsm;
bool created_devices = false;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 927b45f..ece430b 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -371,6 +371,14 @@
}
if (SnapshotManager::IsSnapshotManagerNeeded()) {
+ auto init_devices = [this](const std::string& device) -> bool {
+ if (android::base::StartsWith(device, "/dev/block/dm-")) {
+ return block_dev_init_.InitDmDevice(device);
+ }
+ return block_dev_init_.InitDevices({device});
+ };
+
+ SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);
auto sm = SnapshotManager::NewForFirstStageMount();
if (!sm) {
return false;
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
index 5823932..8cfd597 100644
--- a/init/fuzzer/Android.bp
+++ b/init/fuzzer/Android.bp
@@ -30,7 +30,6 @@
shared_libs: [
"libbase",
"libfs_mgr",
- "libhidl-gen-utils",
"liblog",
"libprocessgroup",
"libselinux",
@@ -49,7 +48,6 @@
srcs: [
"init_parser_fuzzer.cpp",
],
- shared_libs: ["libhidlmetadata",],
defaults: [
"libinit_fuzzer_defaults",
],
diff --git a/init/fuzzer/init_parser_fuzzer.cpp b/init/fuzzer/init_parser_fuzzer.cpp
index dc76465..21b04f4 100644
--- a/init/fuzzer/init_parser_fuzzer.cpp
+++ b/init/fuzzer/init_parser_fuzzer.cpp
@@ -15,9 +15,7 @@
*/
#include <fuzzer/FuzzedDataProvider.h>
-#include <hidl/metadata.h>
#include <import_parser.h>
-#include <interface_utils.h>
#include <rlimit_parser.h>
using namespace android;
@@ -34,7 +32,6 @@
};
const int32_t kMaxBytes = 256;
-const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
class InitParserFuzzer {
public:
@@ -44,9 +41,6 @@
private:
void InvokeParser();
void InvokeLimitParser();
- void InvokeInterfaceUtils();
- InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
- std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
FuzzedDataProvider fdp_;
};
@@ -64,60 +58,6 @@
}
}
-std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
- std::vector<HidlInterfaceMetadata> random_interface;
- for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
- HidlInterfaceMetadata metadata;
- metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
- for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
- metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
- }
- random_interface.push_back(metadata);
- }
- return random_interface;
-}
-
-InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
- InterfaceInheritanceHierarchyMap result;
- std::vector<HidlInterfaceMetadata> random_interface;
- if (fdp_.ConsumeBool()) {
- random_interface = GenerateInterfaceMetadata();
- } else {
- random_interface = HidlInterfaceMetadata::all();
- }
-
- for (const HidlInterfaceMetadata& iface : random_interface) {
- std::set<FQName> inherited_interfaces;
- for (const std::string& intf : iface.inherited) {
- FQName fqname;
- (void)fqname.setTo(intf);
- inherited_interfaces.insert(fqname);
- }
- FQName fqname;
- (void)fqname.setTo(iface.name);
- result[fqname] = inherited_interfaces;
- }
- return result;
-}
-
-void InitParserFuzzer::InvokeInterfaceUtils() {
- InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
- SetKnownInterfaces(hierarchy_map);
- IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
- std::set<std::string> interface_set;
- for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
- auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
- [&]() {
- interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
- },
- [&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
- [&]() { interface_set.insert(kValidInterfaces); },
- });
- set_interface_values();
- }
- CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
-}
-
void InitParserFuzzer::InvokeParser() {
Parser parser;
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
@@ -132,7 +72,6 @@
while (fdp_.remaining_bytes()) {
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
[&]() { InvokeParser(); },
- [&]() { InvokeInterfaceUtils(); },
[&]() { InvokeLimitParser(); },
});
invoke_parser_fuzzer();
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index f746ab9..287857a 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -297,9 +297,7 @@
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&sl, GetSubcontext(),
- *interface_inheritance_hierarchy_map));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, GetSubcontext()));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
@@ -317,11 +315,23 @@
return EXIT_FAILURE;
}
}
+
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) {
LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
return EXIT_FAILURE;
}
+
+ for (const auto& service : sl) {
+ if (const auto& result = CheckInterfaceInheritanceHierarchy(
+ service->interfaces(), *interface_inheritance_hierarchy_map);
+ !result.ok()) {
+ LOG(ERROR) << service->filename() << ": invalid interface in service '"
+ << service->name() << "': " << result.error();
+ return EXIT_FAILURE;
+ }
+ }
+
return EXIT_SUCCESS;
}
diff --git a/init/init.cpp b/init/init.cpp
index 4878660..6c80899 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -268,8 +268,8 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &service_list, GetSubcontext(), std::nullopt));
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&service_list, GetSubcontext()));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
@@ -324,9 +324,7 @@
}
}
#endif // RECOVERY
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, subcontext,
- std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontext));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
return parser;
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 5088273..f280de9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -62,8 +62,7 @@
Action::set_function_map(&test_function_map);
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
@@ -625,8 +624,7 @@
ServiceList service_list;
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
ASSERT_TRUE(parser.ParseConfig(tf.path));
@@ -657,8 +655,7 @@
ServiceList service_list;
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
ASSERT_TRUE(parser.ParseConfig(tf.path));
ASSERT_EQ(1u, parser.parse_error_count());
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
index 1b76bba..84407aa 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -39,27 +39,6 @@
return android::base::Join(fqname_strings, " ");
}
-} // namespace
-
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
- const InterfaceInheritanceHierarchyMap& hierarchy) {
- std::set<FQName> interface_fqnames;
- for (const std::string& instance : instances) {
- // There is insufficient build-time information on AIDL interfaces to check them here
- // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
- if (base::Split(instance, "/")[0] == "aidl") {
- continue;
- }
-
- FqInstance fqinstance;
- if (!fqinstance.setTo(instance)) {
- return Error() << "Unable to parse interface instance '" << instance << "'";
- }
- interface_fqnames.insert(fqinstance.getFqName());
- }
- return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
-}
-
Result<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,
const InterfaceInheritanceHierarchyMap& hierarchy) {
std::ostringstream error_stream;
@@ -90,6 +69,27 @@
return {};
}
+} // namespace
+
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
+ const InterfaceInheritanceHierarchyMap& hierarchy) {
+ std::set<FQName> interface_fqnames;
+ for (const std::string& instance : instances) {
+ // There is insufficient build-time information on AIDL interfaces to check them here
+ // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
+ if (base::Split(instance, "/")[0] == "aidl") {
+ continue;
+ }
+
+ FqInstance fqinstance;
+ if (!fqinstance.setTo(instance)) {
+ return Error() << "Unable to parse interface instance '" << instance << "'";
+ }
+ interface_fqnames.insert(fqinstance.getFqName());
+ }
+ return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
+}
+
std::optional<std::set<FQName>> known_interfaces;
void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {
diff --git a/init/interface_utils.h b/init/interface_utils.h
index 4ca377f..214feda 100644
--- a/init/interface_utils.h
+++ b/init/interface_utils.h
@@ -34,8 +34,6 @@
// interface set. Uses the provided hierarchy data.
Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
const InterfaceInheritanceHierarchyMap& hierarchy);
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<android::FQName>& interfaces,
- const InterfaceInheritanceHierarchyMap& hierarchy);
// Saves the set of known interfaces using the provided HIDL interface
// inheritance hierarchy.
diff --git a/init/noop_builtins.cpp b/init/noop_builtins.cpp
new file mode 100644
index 0000000..c4e140b
--- /dev/null
+++ b/init/noop_builtins.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+// Note that parser will perform arity checks only.
+
+#include <android-base/result.h>
+
+#include "builtin_arguments.h"
+#include "builtins.h"
+
+namespace android::init {
+
+static base::Result<void> check_stub(const BuiltinArguments&) {
+ return {};
+}
+
+#include "noop_builtin_function_map.h"
+
+} // namespace android::init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4d3742a..f2606e3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1251,6 +1251,16 @@
update_sys_usb_config();
}
+void PropertyLoadDerivedDefaults() {
+ const char* PAGE_PROP = "ro.boot.hardware.cpu.pagesize";
+ if (GetProperty(PAGE_PROP, "").empty()) {
+ std::string error;
+ if (PropertySetNoSocket(PAGE_PROP, std::to_string(getpagesize()), &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Could not set '" << PAGE_PROP << "' because: " << error;
+ }
+ }
+}
+
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
@@ -1421,6 +1431,7 @@
ExportKernelBootProps();
PropertyLoadBootDefaults();
+ PropertyLoadDerivedDefaults();
}
static void HandleInitSocket() {
diff --git a/init/reboot_test.cpp b/init/reboot_test.cpp
index b3d038d..b7a1cfd 100644
--- a/init/reboot_test.cpp
+++ b/init/reboot_test.cpp
@@ -103,8 +103,7 @@
"$selabel", GetSecurityContext(), false);
ServiceList& service_list = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 6781c70..e6f3af6 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -27,7 +27,6 @@
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
-#include <hidl-util/FQName.h>
#include <processgroup/processgroup.h>
#include <system/thread_defs.h>
@@ -201,24 +200,6 @@
Result<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) {
const std::string& interface_name = args[1];
const std::string& instance_name = args[2];
-
- // AIDL services don't use fully qualified names and instead just use "interface aidl <name>"
- if (interface_name != "aidl") {
- FQName fq_name;
- if (!FQName::parse(interface_name, &fq_name)) {
- return Error() << "Invalid fully-qualified name for interface '" << interface_name
- << "'";
- }
-
- if (!fq_name.isFullyQualified()) {
- return Error() << "Interface name not fully-qualified '" << interface_name << "'";
- }
-
- if (fq_name.isValidValueName()) {
- return Error() << "Interface name must not be a value name '" << interface_name << "'";
- }
- }
-
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
@@ -702,14 +683,6 @@
}
}
- if (interface_inheritance_hierarchy_) {
- if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
- service_->interfaces(), *interface_inheritance_hierarchy_);
- !check_hierarchy_result.ok()) {
- return Error() << check_hierarchy_result.error();
- }
- }
-
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
return Error() << "service '" << service_->name()
diff --git a/init/service_parser.h b/init/service_parser.h
index 670a5c6..f06cfc4 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -18,7 +18,6 @@
#include <vector>
-#include "interface_utils.h"
#include "parser.h"
#include "service.h"
#include "service_list.h"
@@ -29,13 +28,8 @@
class ServiceParser : public SectionParser {
public:
- ServiceParser(
- ServiceList* service_list, Subcontext* subcontext,
- const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
- : service_list_(service_list),
- subcontext_(subcontext),
- interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
- service_(nullptr) {}
+ ServiceParser(ServiceList* service_list, Subcontext* subcontext)
+ : service_list_(service_list), subcontext_(subcontext), service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -88,7 +82,6 @@
ServiceList* service_list_;
Subcontext* subcontext_;
- std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
};
diff --git a/init/service_test.cpp b/init/service_test.cpp
index a3590b5..53b53ed 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -253,8 +253,7 @@
"$selabel", GetSecurityContext(), false);
ServiceList& service_list = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
TemporaryFile tf;
ASSERT_GE(tf.fd, 0);
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
index 6426ed9..7002a67 100644
--- a/init/test_utils/service_utils.cpp
+++ b/init/test_utils/service_utils.cpp
@@ -30,8 +30,7 @@
android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap() {
ServiceList& service_list = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service",
- std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
for (const auto& location : {
"/init.rc",
"/system/etc/init",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7b2b96a..63e3d06 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -103,7 +103,7 @@
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
- copy /system/etc/prop.default /dev/urandom
+ copy /proc/bootconfig /dev/urandom
symlink /proc/self/fd/0 /dev/stdin
symlink /proc/self/fd/1 /dev/stdout