Merge ab/7633965
Bug: 169893837 Merged-In: I7afea72a15b1a2a7aa676bddd12ea4a2dd896f81 Change-Id: Id30ca5943b34b6fffc1f203667b5a59a79950f67
This commit is contained in:
commit
65335e200f
|
@ -58,6 +58,7 @@
|
|||
#include <scoped_minijail.h>
|
||||
|
||||
#include "debuggerd/handler.h"
|
||||
#include "libdebuggerd/utility.h"
|
||||
#include "protocol.h"
|
||||
#include "tombstoned/tombstoned.h"
|
||||
#include "util.h"
|
||||
|
@ -526,6 +527,8 @@ TEST_P(SizeParamCrasherTest, mte_uaf) {
|
|||
std::vector<std::string> log_sources(2);
|
||||
ConsumeFd(std::move(output_fd), &log_sources[0]);
|
||||
logcat_collector.Collect(&log_sources[1]);
|
||||
// Tag dump only available in the tombstone, not logcat.
|
||||
ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
|
||||
|
||||
for (const auto& result : log_sources) {
|
||||
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
|
||||
|
@ -597,6 +600,12 @@ TEST_P(SizeParamCrasherTest, mte_overflow) {
|
|||
ConsumeFd(std::move(output_fd), &log_sources[0]);
|
||||
logcat_collector.Collect(&log_sources[1]);
|
||||
|
||||
// Tag dump only in tombstone, not logcat, and tagging is not used for
|
||||
// overflow protection in the scudo secondary (guard pages are used instead).
|
||||
if (GetParam() < 0x10000) {
|
||||
ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
|
||||
}
|
||||
|
||||
for (const auto& result : log_sources) {
|
||||
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
|
||||
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
|
||||
|
@ -637,6 +646,7 @@ TEST_P(SizeParamCrasherTest, mte_underflow) {
|
|||
std::to_string(GetParam()) + R"(-byte allocation)");
|
||||
ASSERT_MATCH(result, R"((^|\s)allocated by thread .*
|
||||
#00 pc)");
|
||||
ASSERT_MATCH(result, "Memory tags around the fault address");
|
||||
#else
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#endif
|
||||
|
@ -686,6 +696,9 @@ TEST_F(CrasherTest, mte_multiple_causes) {
|
|||
ConsumeFd(std::move(output_fd), &log_sources[0]);
|
||||
logcat_collector.Collect(&log_sources[1]);
|
||||
|
||||
// Tag dump only in the tombstone, not logcat.
|
||||
ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
|
||||
|
||||
for (const auto& result : log_sources) {
|
||||
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
|
||||
ASSERT_THAT(result, HasSubstr("Note: multiple potential causes for this crash were detected, "
|
||||
|
@ -706,21 +719,26 @@ TEST_F(CrasherTest, mte_multiple_causes) {
|
|||
|
||||
#if defined(__aarch64__)
|
||||
static uintptr_t CreateTagMapping() {
|
||||
uintptr_t mapping =
|
||||
reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
|
||||
// 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.
|
||||
size_t page_size = getpagesize();
|
||||
void* mapping = mmap(nullptr, page_size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
uintptr_t mapping_uptr = reinterpret_cast<uintptr_t>(mapping);
|
||||
if (mapping == MAP_FAILED) {
|
||||
return 0;
|
||||
}
|
||||
__asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
|
||||
:
|
||||
: "r"(mapping + (1ULL << 56))
|
||||
: "memory");
|
||||
return mapping;
|
||||
mprotect(reinterpret_cast<void*>(mapping_uptr + page_size), page_size,
|
||||
PROT_READ | PROT_WRITE | PROT_MTE);
|
||||
// Stripe the mapping, where even granules get tag '1', and odd granules get tag '0'.
|
||||
for (uintptr_t offset = 0; offset < page_size; offset += 2 * kTagGranuleSize) {
|
||||
uintptr_t tagged_addr = mapping_uptr + page_size + offset + (1ULL << 56);
|
||||
__asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : : "r"(tagged_addr) : "memory");
|
||||
}
|
||||
return mapping_uptr + page_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(CrasherTest, mte_tag_dump) {
|
||||
TEST_F(CrasherTest, mte_register_tag_dump) {
|
||||
#if defined(__aarch64__)
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
|
@ -753,6 +771,107 @@ TEST_F(CrasherTest, mte_tag_dump) {
|
|||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
|
||||
#if defined(__aarch64__)
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
}
|
||||
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartProcess([&]() {
|
||||
SetTagCheckingLevelSync();
|
||||
volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
|
||||
p[0] = 0; // Untagged pointer, tagged memory.
|
||||
});
|
||||
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGSEGV);
|
||||
FinishIntercept(&intercept_result);
|
||||
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
|
||||
ASSERT_MATCH(result, R"(Memory tags around the fault address.*
|
||||
\s*=>0x[0-9a-f]+000:\[1\] 0 1 0)");
|
||||
#else
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, mte_fault_tag_dump) {
|
||||
#if defined(__aarch64__)
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
}
|
||||
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartProcess([&]() {
|
||||
SetTagCheckingLevelSync();
|
||||
volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
|
||||
p[320] = 0; // Untagged pointer, tagged memory.
|
||||
});
|
||||
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGSEGV);
|
||||
FinishIntercept(&intercept_result);
|
||||
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
|
||||
ASSERT_MATCH(result, R"(Memory tags around the fault address.*
|
||||
\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
|
||||
\s*=>0x[0-9a-f]+: 1 0 1 0 \[1\] 0 1 0 1 0 1 0 1 0 1 0
|
||||
\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
|
||||
)");
|
||||
#else
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
|
||||
#if defined(__aarch64__)
|
||||
if (!mte_supported()) {
|
||||
GTEST_SKIP() << "Requires MTE";
|
||||
}
|
||||
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
StartProcess([&]() {
|
||||
SetTagCheckingLevelSync();
|
||||
size_t page_size = getpagesize();
|
||||
volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
|
||||
p[page_size - kTagGranuleSize * 2] = 0; // Untagged pointer, tagged memory.
|
||||
});
|
||||
|
||||
StartIntercept(&output_fd);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGSEGV);
|
||||
FinishIntercept(&intercept_result);
|
||||
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
|
||||
ASSERT_MATCH(result, R"(Memory tags around the fault address)");
|
||||
ASSERT_MATCH(result,
|
||||
R"(\s*0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
|
||||
\s*=>0x[0-9a-f]+: 1 0 1 0 1 0 1 0 1 0 1 0 1 0 \[1\] 0
|
||||
|
||||
)"); // Ensure truncation happened and there's a newline after the tag fault.
|
||||
#else
|
||||
GTEST_SKIP() << "Requires aarch64";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, LD_PRELOAD) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
|
|
@ -96,4 +96,8 @@ const char* get_sigcode(const siginfo_t*);
|
|||
// Number of bytes per MTE granule.
|
||||
constexpr size_t kTagGranuleSize = 16;
|
||||
|
||||
// Number of rows and columns to display in an MTE tag dump.
|
||||
constexpr size_t kNumTagColumns = 16;
|
||||
constexpr size_t kNumTagRows = 16;
|
||||
|
||||
#endif // _DEBUGGERD_UTILITY_H
|
||||
|
|
|
@ -593,6 +593,9 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
|
|||
};
|
||||
|
||||
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
|
||||
auto process_memory =
|
||||
unwindstack::Memory::CreateProcessMemoryCached(getpid());
|
||||
unwinder.SetProcessMemory(process_memory);
|
||||
if (!unwinder.Init()) {
|
||||
async_safe_fatal("failed to init unwinder object");
|
||||
}
|
||||
|
|
|
@ -362,8 +362,10 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
|
|||
dump.set_mapping_name(map_info->name());
|
||||
}
|
||||
|
||||
char buf[256];
|
||||
uint8_t tags[256 / kTagGranuleSize];
|
||||
constexpr size_t kNumBytesAroundRegister = 256;
|
||||
constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
|
||||
char buf[kNumBytesAroundRegister];
|
||||
uint8_t tags[kNumTagsAroundRegister];
|
||||
size_t start_offset = 0;
|
||||
ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
|
||||
if (bytes == -1) {
|
||||
|
@ -377,7 +379,19 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
|
|||
}
|
||||
|
||||
dump.set_memory(buf, bytes);
|
||||
dump.set_tags(tags, bytes / kTagGranuleSize);
|
||||
|
||||
bool has_tags = false;
|
||||
#if defined(__aarch64__)
|
||||
for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
|
||||
if (tags[i] != 0) {
|
||||
has_tags = true;
|
||||
}
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
|
||||
if (has_tags) {
|
||||
dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
|
||||
}
|
||||
|
||||
*thread.add_memory_dump() = std::move(dump);
|
||||
}
|
||||
|
@ -531,6 +545,50 @@ static void dump_logcat(Tombstone* tombstone, pid_t pid) {
|
|||
dump_log_file(tombstone, "main", pid);
|
||||
}
|
||||
|
||||
static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
|
||||
unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
|
||||
if (tombstone.arch() != Architecture::ARM64) return;
|
||||
|
||||
fault_addr = untag_address(fault_addr);
|
||||
constexpr size_t kNumGranules = kNumTagRows * kNumTagColumns;
|
||||
constexpr size_t kBytesToRead = kNumGranules * kTagGranuleSize;
|
||||
|
||||
// If the low part of the tag dump would underflow to the high address space, it's probably not
|
||||
// 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;
|
||||
size_t granules_to_read = kNumGranules;
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
start_address += bytes_to_next_page;
|
||||
granules_to_read -= bytes_to_next_page / kTagGranuleSize;
|
||||
}
|
||||
tag_dump.set_begin_address(start_address);
|
||||
|
||||
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);
|
||||
if (tag < 0) break;
|
||||
mte_tags->push_back(static_cast<uint8_t>(tag));
|
||||
}
|
||||
|
||||
if (!mte_tags->empty()) {
|
||||
*signal->mutable_fault_adjacent_metadata() = tag_dump;
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<uint64_t> read_uptime_secs() {
|
||||
std::string uptime;
|
||||
if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
|
||||
|
@ -594,7 +652,9 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind
|
|||
|
||||
if (process_info.has_fault_address) {
|
||||
sig.set_has_fault_address(true);
|
||||
sig.set_fault_address(process_info.maybe_tagged_fault_address);
|
||||
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);
|
||||
}
|
||||
|
||||
*result.mutable_signal_info() = sig;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <async_safe/log.h>
|
||||
#include <bionic/macros.h>
|
||||
|
||||
#include "tombstone.pb.h"
|
||||
|
||||
|
@ -193,8 +194,11 @@ static void print_thread_memory_dump(CallbackType callback, const Tombstone& tom
|
|||
uint64_t addr = mem.begin_address();
|
||||
for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
|
||||
uint64_t tagged_addr = addr;
|
||||
if (mem.tags().size() > offset / kTagGranuleSize) {
|
||||
tagged_addr |= static_cast<uint64_t>(mem.tags()[offset / kTagGranuleSize]) << 56;
|
||||
if (mem.has_arm_mte_metadata() &&
|
||||
mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
|
||||
tagged_addr |=
|
||||
static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
|
||||
<< 56;
|
||||
}
|
||||
std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, tagged_addr + offset);
|
||||
|
||||
|
@ -232,6 +236,60 @@ static void print_thread(CallbackType callback, const Tombstone& tombstone, cons
|
|||
print_thread_memory_dump(callback, tombstone, thread);
|
||||
}
|
||||
|
||||
static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
|
||||
if (!tombstone.has_signal_info()) return;
|
||||
|
||||
const Signal& signal = tombstone.signal_info();
|
||||
|
||||
if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
|
||||
|
||||
if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
|
||||
|
||||
CBS("");
|
||||
CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
|
||||
signal.fault_address(), kTagGranuleSize);
|
||||
constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
|
||||
|
||||
size_t tag_index = 0;
|
||||
size_t num_tags = tags.length();
|
||||
uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
|
||||
for (size_t row = 0; tag_index < num_tags; ++row) {
|
||||
uintptr_t row_addr =
|
||||
(memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
|
||||
std::string row_contents;
|
||||
bool row_has_fault = false;
|
||||
|
||||
for (size_t column = 0; column < kNumTagColumns; ++column) {
|
||||
uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
|
||||
if (granule_addr < memory_dump.begin_address() ||
|
||||
granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
|
||||
row_contents += " . ";
|
||||
} else if (granule_addr == fault_granule) {
|
||||
row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
|
||||
row_has_fault = true;
|
||||
} else {
|
||||
row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (row_contents.back() == ' ') row_contents.pop_back();
|
||||
|
||||
if (row_has_fault) {
|
||||
CBS(" =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
|
||||
} else {
|
||||
CBS(" 0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
|
||||
const Thread& thread) {
|
||||
print_thread_header(callback, tombstone, thread, true);
|
||||
|
@ -299,6 +357,8 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
|
|||
}
|
||||
}
|
||||
|
||||
print_tag_dump(callback, tombstone);
|
||||
|
||||
print_thread_memory_dump(callback, tombstone, thread);
|
||||
|
||||
CBS("");
|
||||
|
|
|
@ -56,8 +56,11 @@ message Signal {
|
|||
|
||||
bool has_fault_address = 8;
|
||||
uint64 fault_address = 9;
|
||||
// Note, may or may not contain the dump of the actual memory contents. Currently, on arm64, we
|
||||
// only include metadata, and not the contents.
|
||||
MemoryDump fault_adjacent_metadata = 10;
|
||||
|
||||
reserved 10 to 999;
|
||||
reserved 11 to 999;
|
||||
}
|
||||
|
||||
message HeapObject {
|
||||
|
@ -142,14 +145,22 @@ message BacktraceFrame {
|
|||
reserved 9 to 999;
|
||||
}
|
||||
|
||||
message ArmMTEMetadata {
|
||||
// One memory tag per granule (e.g. every 16 bytes) of regular memory.
|
||||
bytes memory_tags = 1;
|
||||
reserved 2 to 999;
|
||||
}
|
||||
|
||||
message MemoryDump {
|
||||
string register_name = 1;
|
||||
string mapping_name = 2;
|
||||
uint64 begin_address = 3;
|
||||
bytes memory = 4;
|
||||
bytes tags = 5;
|
||||
oneof metadata {
|
||||
ArmMTEMetadata arm_mte_metadata = 6;
|
||||
}
|
||||
|
||||
reserved 6 to 999;
|
||||
reserved 5, 7 to 999;
|
||||
}
|
||||
|
||||
message MemoryMapping {
|
||||
|
|
|
@ -91,12 +91,6 @@ bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
|
|||
if (key == bootconfig_key) {
|
||||
*out_val = value;
|
||||
return true;
|
||||
} else if (android_key == "hardware" && android_key == key) {
|
||||
// bootconfig doesn't allow subkeys and values to coexist, so
|
||||
// "androidboot.hardware" cannot be used. It is replaced in
|
||||
// bootconfig with "hardware"
|
||||
*out_val = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -158,6 +158,13 @@ enum MergeFailureCode {
|
|||
ExpectedMergeTarget = 11;
|
||||
UnmergedSectorsAfterCompletion = 12;
|
||||
UnexpectedMergeState = 13;
|
||||
GetCowPathConsistencyCheck = 14;
|
||||
OpenCowConsistencyCheck = 15;
|
||||
ParseCowConsistencyCheck = 16;
|
||||
OpenCowDirectConsistencyCheck = 17;
|
||||
MemAlignConsistencyCheck = 18;
|
||||
DirectReadConsistencyCheck = 19;
|
||||
WrongMergeCountConsistencyCheck = 20;
|
||||
};
|
||||
|
||||
// Next: 8
|
||||
|
@ -184,6 +191,9 @@ message SnapshotUpdateStatus {
|
|||
|
||||
// Merge failure code, filled if state == MergeFailed.
|
||||
MergeFailureCode merge_failure_code = 7;
|
||||
|
||||
// Source build fingerprint.
|
||||
string source_build_fingerprint = 8;
|
||||
}
|
||||
|
||||
// Next: 10
|
||||
|
@ -215,4 +225,7 @@ message SnapshotMergeReport {
|
|||
|
||||
// Merge failure code, filled if state == MergeFailed.
|
||||
MergeFailureCode merge_failure_code = 9;
|
||||
|
||||
// The source fingerprint at the time the OTA was downloaded.
|
||||
string source_build_fingerprint = 10;
|
||||
}
|
||||
|
|
|
@ -96,6 +96,8 @@ class TempDevice {
|
|||
class CowSnapuserdTest final {
|
||||
public:
|
||||
bool Setup();
|
||||
bool SetupOrderedOps();
|
||||
bool SetupOrderedOpsInverted();
|
||||
bool SetupCopyOverlap_1();
|
||||
bool SetupCopyOverlap_2();
|
||||
bool Merge();
|
||||
|
@ -103,6 +105,9 @@ class CowSnapuserdTest final {
|
|||
void ReadSnapshotDeviceAndValidate();
|
||||
void Shutdown();
|
||||
void MergeInterrupt();
|
||||
void MergeInterruptFixed(int duration);
|
||||
void MergeInterruptRandomly(int max_duration);
|
||||
void ReadDmUserBlockWithoutDaemon();
|
||||
|
||||
std::string snapshot_dev() const { return snapshot_dev_->path(); }
|
||||
|
||||
|
@ -116,6 +121,8 @@ class CowSnapuserdTest final {
|
|||
void StartMerge();
|
||||
|
||||
void CreateCowDevice();
|
||||
void CreateCowDeviceOrderedOps();
|
||||
void CreateCowDeviceOrderedOpsInverted();
|
||||
void CreateCowDeviceWithCopyOverlap_1();
|
||||
void CreateCowDeviceWithCopyOverlap_2();
|
||||
bool SetupDaemon();
|
||||
|
@ -196,6 +203,18 @@ bool CowSnapuserdTest::Setup() {
|
|||
return setup_ok_;
|
||||
}
|
||||
|
||||
bool CowSnapuserdTest::SetupOrderedOps() {
|
||||
CreateBaseDevice();
|
||||
CreateCowDeviceOrderedOps();
|
||||
return SetupDaemon();
|
||||
}
|
||||
|
||||
bool CowSnapuserdTest::SetupOrderedOpsInverted() {
|
||||
CreateBaseDevice();
|
||||
CreateCowDeviceOrderedOpsInverted();
|
||||
return SetupDaemon();
|
||||
}
|
||||
|
||||
bool CowSnapuserdTest::SetupCopyOverlap_1() {
|
||||
CreateBaseDevice();
|
||||
CreateCowDeviceWithCopyOverlap_1();
|
||||
|
@ -382,6 +401,112 @@ void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
|
|||
true);
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
|
||||
unique_fd rnd_fd;
|
||||
loff_t offset = 0;
|
||||
|
||||
std::string path = android::base::GetExecutableDirectory();
|
||||
cow_system_ = std::make_unique<TemporaryFile>(path);
|
||||
|
||||
rnd_fd.reset(open("/dev/random", O_RDONLY));
|
||||
ASSERT_TRUE(rnd_fd > 0);
|
||||
|
||||
std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
|
||||
|
||||
// Fill random data
|
||||
for (size_t j = 0; j < (size_ / 1_MiB); j++) {
|
||||
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
|
||||
true);
|
||||
|
||||
offset += 1_MiB;
|
||||
}
|
||||
|
||||
CowOptions options;
|
||||
options.compression = "gz";
|
||||
CowWriter writer(options);
|
||||
|
||||
ASSERT_TRUE(writer.Initialize(cow_system_->fd));
|
||||
|
||||
size_t num_blocks = size_ / options.block_size;
|
||||
size_t blk_end_copy = num_blocks * 2;
|
||||
size_t source_blk = num_blocks - 1;
|
||||
size_t blk_src_copy = blk_end_copy - 1;
|
||||
|
||||
size_t x = num_blocks;
|
||||
while (1) {
|
||||
ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
|
||||
x -= 1;
|
||||
if (x == 0) {
|
||||
break;
|
||||
}
|
||||
source_blk -= 1;
|
||||
blk_src_copy -= 1;
|
||||
}
|
||||
|
||||
// Flush operations
|
||||
ASSERT_TRUE(writer.Finalize());
|
||||
// Construct the buffer required for validation
|
||||
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
|
||||
// Read the entire base device
|
||||
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
|
||||
true);
|
||||
// Merged Buffer
|
||||
memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
|
||||
unique_fd rnd_fd;
|
||||
loff_t offset = 0;
|
||||
|
||||
std::string path = android::base::GetExecutableDirectory();
|
||||
cow_system_ = std::make_unique<TemporaryFile>(path);
|
||||
|
||||
rnd_fd.reset(open("/dev/random", O_RDONLY));
|
||||
ASSERT_TRUE(rnd_fd > 0);
|
||||
|
||||
std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
|
||||
|
||||
// Fill random data
|
||||
for (size_t j = 0; j < (size_ / 1_MiB); j++) {
|
||||
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
|
||||
true);
|
||||
|
||||
offset += 1_MiB;
|
||||
}
|
||||
|
||||
CowOptions options;
|
||||
options.compression = "gz";
|
||||
CowWriter writer(options);
|
||||
|
||||
ASSERT_TRUE(writer.Initialize(cow_system_->fd));
|
||||
|
||||
size_t num_blocks = size_ / options.block_size;
|
||||
size_t x = num_blocks;
|
||||
size_t source_blk = 0;
|
||||
size_t blk_src_copy = num_blocks;
|
||||
|
||||
while (1) {
|
||||
ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
|
||||
|
||||
x -= 1;
|
||||
if (x == 0) {
|
||||
break;
|
||||
}
|
||||
source_blk += 1;
|
||||
blk_src_copy += 1;
|
||||
}
|
||||
|
||||
// Flush operations
|
||||
ASSERT_TRUE(writer.Finalize());
|
||||
// Construct the buffer required for validation
|
||||
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
|
||||
// Read the entire base device
|
||||
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
|
||||
true);
|
||||
// Merged Buffer
|
||||
memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::CreateCowDevice() {
|
||||
unique_fd rnd_fd;
|
||||
loff_t offset = 0;
|
||||
|
@ -481,6 +606,36 @@ void CowSnapuserdTest::CreateDmUserDevice() {
|
|||
ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::ReadDmUserBlockWithoutDaemon() {
|
||||
DmTable dmuser_table;
|
||||
std::string dm_user_name = "dm-test-device";
|
||||
unique_fd fd;
|
||||
|
||||
// Create a dm-user block device
|
||||
ASSERT_TRUE(dmuser_table.AddTarget(std::make_unique<DmTargetUser>(0, 123456, dm_user_name)));
|
||||
ASSERT_TRUE(dmuser_table.valid());
|
||||
|
||||
dmuser_dev_ = std::make_unique<TempDevice>(dm_user_name, dmuser_table);
|
||||
ASSERT_TRUE(dmuser_dev_->valid());
|
||||
ASSERT_FALSE(dmuser_dev_->path().empty());
|
||||
|
||||
fd.reset(open(dmuser_dev_->path().c_str(), O_RDONLY));
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(1_MiB);
|
||||
|
||||
loff_t offset = 0;
|
||||
// Every IO should fail as there is no daemon to process the IO
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
ASSERT_EQ(ReadFullyAtOffset(fd, (char*)buffer.get() + offset, BLOCK_SZ, offset), false);
|
||||
|
||||
offset += BLOCK_SZ;
|
||||
}
|
||||
|
||||
fd = {};
|
||||
ASSERT_TRUE(dmuser_dev_->Destroy());
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::InitDaemon() {
|
||||
bool ok = client_->AttachDmUser(system_device_ctrl_name_);
|
||||
ASSERT_TRUE(ok);
|
||||
|
@ -566,6 +721,7 @@ void CowSnapuserdTest::ValidateMerge() {
|
|||
|
||||
void CowSnapuserdTest::SimulateDaemonRestart() {
|
||||
Shutdown();
|
||||
std::this_thread::sleep_for(500ms);
|
||||
SetDeviceControlName();
|
||||
StartSnapuserdDaemon();
|
||||
InitCowDevice();
|
||||
|
@ -574,6 +730,34 @@ void CowSnapuserdTest::SimulateDaemonRestart() {
|
|||
CreateSnapshotDevice();
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
|
||||
std::srand(std::time(nullptr));
|
||||
StartMerge();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
int duration = std::rand() % max_duration;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
|
||||
SimulateDaemonRestart();
|
||||
StartMerge();
|
||||
}
|
||||
|
||||
SimulateDaemonRestart();
|
||||
ASSERT_TRUE(Merge());
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::MergeInterruptFixed(int duration) {
|
||||
StartMerge();
|
||||
|
||||
for (int i = 0; i < 25; i++) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
|
||||
SimulateDaemonRestart();
|
||||
StartMerge();
|
||||
}
|
||||
|
||||
SimulateDaemonRestart();
|
||||
ASSERT_TRUE(Merge());
|
||||
}
|
||||
|
||||
void CowSnapuserdTest::MergeInterrupt() {
|
||||
// Interrupt merge at various intervals
|
||||
StartMerge();
|
||||
|
@ -638,10 +822,9 @@ void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
|
|||
void* buffer = snapuserd_->GetExceptionBuffer(1);
|
||||
loff_t offset = 0;
|
||||
struct disk_exception* de;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (int i = 11; i >= 0; i--) {
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, i);
|
||||
ASSERT_EQ(de->new_chunk, new_chunk);
|
||||
offset += sizeof(struct disk_exception);
|
||||
new_chunk += 1;
|
||||
}
|
||||
|
@ -780,71 +963,71 @@ void CowSnapuserdMetadataTest::ValidateMetadata() {
|
|||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 100);
|
||||
ASSERT_EQ(de->new_chunk, 522);
|
||||
ASSERT_EQ(de->new_chunk, 521);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 105);
|
||||
ASSERT_EQ(de->new_chunk, 524);
|
||||
ASSERT_EQ(de->new_chunk, 522);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 110);
|
||||
ASSERT_EQ(de->new_chunk, 526);
|
||||
ASSERT_EQ(de->new_chunk, 523);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
// The next 4 operations are batch merged as
|
||||
// both old and new chunk are contiguous
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 50);
|
||||
ASSERT_EQ(de->new_chunk, 528);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 51);
|
||||
ASSERT_EQ(de->new_chunk, 529);
|
||||
ASSERT_EQ(de->old_chunk, 53);
|
||||
ASSERT_EQ(de->new_chunk, 524);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 52);
|
||||
ASSERT_EQ(de->new_chunk, 530);
|
||||
ASSERT_EQ(de->new_chunk, 525);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 53);
|
||||
ASSERT_EQ(de->new_chunk, 531);
|
||||
ASSERT_EQ(de->old_chunk, 51);
|
||||
ASSERT_EQ(de->new_chunk, 526);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 50);
|
||||
ASSERT_EQ(de->new_chunk, 527);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
// This is handling overlap operation with
|
||||
// two batch merge operations.
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 18);
|
||||
ASSERT_EQ(de->new_chunk, 533);
|
||||
ASSERT_EQ(de->new_chunk, 528);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 19);
|
||||
ASSERT_EQ(de->new_chunk, 534);
|
||||
ASSERT_EQ(de->new_chunk, 529);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 20);
|
||||
ASSERT_EQ(de->new_chunk, 535);
|
||||
ASSERT_EQ(de->new_chunk, 530);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 21);
|
||||
ASSERT_EQ(de->new_chunk, 537);
|
||||
ASSERT_EQ(de->new_chunk, 532);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 22);
|
||||
ASSERT_EQ(de->new_chunk, 538);
|
||||
ASSERT_EQ(de->new_chunk, 533);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
|
||||
ASSERT_EQ(de->old_chunk, 23);
|
||||
ASSERT_EQ(de->new_chunk, 539);
|
||||
ASSERT_EQ(de->new_chunk, 534);
|
||||
offset += sizeof(struct disk_exception);
|
||||
|
||||
// End of metadata
|
||||
|
@ -909,6 +1092,43 @@ TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
|
|||
harness.Shutdown();
|
||||
}
|
||||
|
||||
TEST(Snapuserd_Test, ReadDmUserBlockWithoutDaemon) {
|
||||
CowSnapuserdTest harness;
|
||||
harness.ReadDmUserBlockWithoutDaemon();
|
||||
}
|
||||
|
||||
TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
|
||||
CowSnapuserdTest harness;
|
||||
ASSERT_TRUE(harness.SetupOrderedOps());
|
||||
harness.MergeInterruptFixed(300);
|
||||
harness.ValidateMerge();
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
|
||||
CowSnapuserdTest harness;
|
||||
ASSERT_TRUE(harness.SetupOrderedOps());
|
||||
harness.MergeInterruptRandomly(500);
|
||||
harness.ValidateMerge();
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
|
||||
CowSnapuserdTest harness;
|
||||
ASSERT_TRUE(harness.SetupOrderedOpsInverted());
|
||||
harness.MergeInterruptFixed(50);
|
||||
harness.ValidateMerge();
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
|
||||
CowSnapuserdTest harness;
|
||||
ASSERT_TRUE(harness.SetupOrderedOpsInverted());
|
||||
harness.MergeInterruptRandomly(50);
|
||||
harness.ValidateMerge();
|
||||
harness.Shutdown();
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -143,12 +143,11 @@ class CowReader : public ICowReader {
|
|||
|
||||
void InitializeMerge();
|
||||
|
||||
// Number of copy, replace, and zero ops. Set if InitializeMerge is called.
|
||||
void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
|
||||
|
||||
uint64_t total_data_ops() { return total_data_ops_; }
|
||||
|
||||
// Number of copy ops. Set if InitializeMerge is called.
|
||||
void set_copy_ops(uint64_t size) { copy_ops_ = size; }
|
||||
|
||||
uint64_t total_copy_ops() { return copy_ops_; }
|
||||
|
||||
void CloseCowFd() { owned_fd_ = {}; }
|
||||
|
|
|
@ -60,6 +60,7 @@ class MockSnapshotManager : public ISnapshotManager {
|
|||
MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
|
||||
MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
|
||||
MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
|
||||
MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
|
||||
};
|
||||
|
||||
} // namespace android::snapshot
|
||||
|
|
|
@ -35,13 +35,16 @@ class MockSnapshotMergeStats final : public ISnapshotMergeStats {
|
|||
MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
|
||||
MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
|
||||
MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
|
||||
MOCK_METHOD(void, set_source_build_fingerprint, (const std::string&), (override));
|
||||
MOCK_METHOD(uint64_t, cow_file_size, (), (override));
|
||||
MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
|
||||
MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
|
||||
MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
|
||||
MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
|
||||
MOCK_METHOD(std::string, source_build_fingerprint, (), (override));
|
||||
MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
|
||||
MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
|
||||
MOCK_METHOD(bool, WriteState, (), (override));
|
||||
|
||||
using ISnapshotMergeStats::Result;
|
||||
// Return nullptr if any failure.
|
||||
|
|
|
@ -177,6 +177,9 @@ class ISnapshotManager {
|
|||
// code. Otherwise, MergeFailureCode::Ok is returned.
|
||||
virtual MergeFailureCode ReadMergeFailureCode() = 0;
|
||||
|
||||
// If an update is in progress, return the source build fingerprint.
|
||||
virtual std::string ReadSourceBuildFingerprint() = 0;
|
||||
|
||||
// Find the status of the current update, if any.
|
||||
//
|
||||
// |progress| depends on the returned status:
|
||||
|
@ -369,6 +372,7 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
|
||||
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
|
||||
bool UnmapAllSnapshots() override;
|
||||
std::string ReadSourceBuildFingerprint() override;
|
||||
|
||||
// We can't use WaitForFile during first-stage init, because ueventd is not
|
||||
// running and therefore will not automatically create symlinks. Instead,
|
||||
|
@ -603,6 +607,8 @@ class SnapshotManager final : public ISnapshotManager {
|
|||
MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
|
||||
MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,
|
||||
const SnapshotUpdateStatus& update_status);
|
||||
MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
|
||||
const SnapshotStatus& update_status);
|
||||
|
||||
// Interact with status files under /metadata/ota/snapshots.
|
||||
bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
|
||||
|
|
|
@ -35,12 +35,14 @@ class ISnapshotMergeStats {
|
|||
virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
|
||||
virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
|
||||
virtual void set_merge_failure_code(MergeFailureCode code) = 0;
|
||||
virtual void set_source_build_fingerprint(const std::string& fingerprint) = 0;
|
||||
virtual uint64_t cow_file_size() = 0;
|
||||
virtual uint64_t total_cow_size_bytes() = 0;
|
||||
virtual uint64_t estimated_cow_size_bytes() = 0;
|
||||
virtual uint32_t boot_complete_time_ms() = 0;
|
||||
virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;
|
||||
virtual MergeFailureCode merge_failure_code() = 0;
|
||||
virtual std::string source_build_fingerprint() = 0;
|
||||
|
||||
// Called when merge ends. Properly clean up permanent storage.
|
||||
class Result {
|
||||
|
@ -52,6 +54,10 @@ class ISnapshotMergeStats {
|
|||
};
|
||||
// Return nullptr if any failure.
|
||||
virtual std::unique_ptr<Result> Finish() = 0;
|
||||
|
||||
// Write out the current state. This should be called when data might be lost that
|
||||
// cannot be recovered (eg the COW sizes).
|
||||
virtual bool WriteState() = 0;
|
||||
};
|
||||
|
||||
class SnapshotMergeStats : public ISnapshotMergeStats {
|
||||
|
@ -74,11 +80,13 @@ class SnapshotMergeStats : public ISnapshotMergeStats {
|
|||
uint32_t boot_complete_to_merge_start_time_ms() override;
|
||||
void set_merge_failure_code(MergeFailureCode code) override;
|
||||
MergeFailureCode merge_failure_code() override;
|
||||
void set_source_build_fingerprint(const std::string& fingerprint) override;
|
||||
std::string source_build_fingerprint() override;
|
||||
std::unique_ptr<Result> Finish() override;
|
||||
bool WriteState() override;
|
||||
|
||||
private:
|
||||
bool ReadState();
|
||||
bool WriteState();
|
||||
bool DeleteState();
|
||||
SnapshotMergeStats(const std::string& path);
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ class SnapshotManagerStub : public ISnapshotManager {
|
|||
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
|
||||
bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
|
||||
bool UnmapAllSnapshots() override;
|
||||
std::string ReadSourceBuildFingerprint() override;
|
||||
};
|
||||
|
||||
} // namespace android::snapshot
|
||||
|
|
|
@ -716,7 +716,7 @@ bool SnapshotManager::InitiateMerge() {
|
|||
}
|
||||
}
|
||||
|
||||
SnapshotUpdateStatus initial_status;
|
||||
SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
|
||||
initial_status.set_state(UpdateState::Merging);
|
||||
initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
|
||||
initial_status.set_total_sectors(initial_target_values.total_sectors);
|
||||
|
@ -1126,6 +1126,11 @@ auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string&
|
|||
return MergeResult(UpdateState::Merging);
|
||||
}
|
||||
|
||||
auto code = CheckMergeConsistency(lock, name, snapshot_status);
|
||||
if (code != MergeFailureCode::Ok) {
|
||||
return MergeResult(UpdateState::MergeFailed, code);
|
||||
}
|
||||
|
||||
// Merging is done. First, update the status file to indicate the merge
|
||||
// is complete. We do this before calling OnSnapshotMergeComplete, even
|
||||
// though this means the write is potentially wasted work (since in the
|
||||
|
@ -1144,6 +1149,91 @@ auto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string&
|
|||
return MergeResult(UpdateState::MergeCompleted, MergeFailureCode::Ok);
|
||||
}
|
||||
|
||||
// This returns the backing device, not the dm-user layer.
|
||||
static std::string GetMappedCowDeviceName(const std::string& snapshot,
|
||||
const SnapshotStatus& status) {
|
||||
// If no partition was created (the COW exists entirely on /data), the
|
||||
// device-mapper layering is different than if we had a partition.
|
||||
if (status.cow_partition_size() == 0) {
|
||||
return GetCowImageDeviceName(snapshot);
|
||||
}
|
||||
return GetCowName(snapshot);
|
||||
}
|
||||
|
||||
MergeFailureCode SnapshotManager::CheckMergeConsistency(LockedFile* lock, const std::string& name,
|
||||
const SnapshotStatus& status) {
|
||||
CHECK(lock);
|
||||
|
||||
if (!status.compression_enabled()) {
|
||||
// Do not try to verify old-style COWs yet.
|
||||
return MergeFailureCode::Ok;
|
||||
}
|
||||
|
||||
auto& dm = DeviceMapper::Instance();
|
||||
|
||||
std::string cow_image_name = GetMappedCowDeviceName(name, status);
|
||||
std::string cow_image_path;
|
||||
if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
|
||||
LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
|
||||
return MergeFailureCode::GetCowPathConsistencyCheck;
|
||||
}
|
||||
|
||||
// First pass, count # of ops.
|
||||
size_t num_ops = 0;
|
||||
{
|
||||
unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "Failed to open " << cow_image_name;
|
||||
return MergeFailureCode::OpenCowConsistencyCheck;
|
||||
}
|
||||
|
||||
CowReader reader;
|
||||
if (!reader.Parse(std::move(fd))) {
|
||||
LOG(ERROR) << "Failed to parse cow " << cow_image_path;
|
||||
return MergeFailureCode::ParseCowConsistencyCheck;
|
||||
}
|
||||
|
||||
for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
|
||||
if (!IsMetadataOp(iter->Get())) {
|
||||
num_ops++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass, try as hard as we can to get the actual number of blocks
|
||||
// the system thinks is merged.
|
||||
unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_DIRECT | O_SYNC | O_CLOEXEC));
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "Failed to open direct " << cow_image_name;
|
||||
return MergeFailureCode::OpenCowDirectConsistencyCheck;
|
||||
}
|
||||
|
||||
void* addr;
|
||||
size_t page_size = getpagesize();
|
||||
if (posix_memalign(&addr, page_size, page_size) < 0) {
|
||||
PLOG(ERROR) << "posix_memalign with page size " << page_size;
|
||||
return MergeFailureCode::MemAlignConsistencyCheck;
|
||||
}
|
||||
|
||||
// COWs are always at least 2MB, this is guaranteed in snapshot creation.
|
||||
std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
|
||||
if (!android::base::ReadFully(fd, buffer.get(), page_size)) {
|
||||
PLOG(ERROR) << "Direct read failed " << cow_image_name;
|
||||
return MergeFailureCode::DirectReadConsistencyCheck;
|
||||
}
|
||||
|
||||
auto header = reinterpret_cast<CowHeader*>(buffer.get());
|
||||
if (header->num_merge_ops != num_ops) {
|
||||
LOG(ERROR) << "COW consistency check failed, expected " << num_ops << " to be merged, "
|
||||
<< "but " << header->num_merge_ops << " were actually recorded.";
|
||||
LOG(ERROR) << "Aborting merge progress for snapshot " << name
|
||||
<< ", will try again next boot";
|
||||
return MergeFailureCode::WrongMergeCountConsistencyCheck;
|
||||
}
|
||||
|
||||
return MergeFailureCode::Ok;
|
||||
}
|
||||
|
||||
MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
|
||||
std::vector<std::string> snapshots;
|
||||
if (!ListSnapshots(lock, &snapshots)) {
|
||||
|
@ -1429,14 +1519,7 @@ bool SnapshotManager::PerformInitTransition(InitTransition transition,
|
|||
continue;
|
||||
}
|
||||
|
||||
// If no partition was created (the COW exists entirely on /data), the
|
||||
// device-mapper layering is different than if we had a partition.
|
||||
std::string cow_image_name;
|
||||
if (snapshot_status.cow_partition_size() == 0) {
|
||||
cow_image_name = GetCowImageDeviceName(snapshot);
|
||||
} else {
|
||||
cow_image_name = GetCowName(snapshot);
|
||||
}
|
||||
std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);
|
||||
|
||||
std::string cow_image_device;
|
||||
if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
|
||||
|
@ -2432,15 +2515,25 @@ bool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state,
|
|||
SnapshotUpdateStatus status;
|
||||
status.set_state(state);
|
||||
|
||||
if (state == UpdateState::MergeFailed) {
|
||||
status.set_merge_failure_code(failure_code);
|
||||
switch (state) {
|
||||
case UpdateState::MergeFailed:
|
||||
status.set_merge_failure_code(failure_code);
|
||||
break;
|
||||
case UpdateState::Initiated:
|
||||
status.set_source_build_fingerprint(
|
||||
android::base::GetProperty("ro.build.fingerprint", ""));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're transitioning between two valid states (eg, we're not beginning
|
||||
// or ending an OTA), then make sure to propagate the compression bit.
|
||||
// or ending an OTA), then make sure to propagate the compression bit and
|
||||
// build fingerprint.
|
||||
if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
|
||||
SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
|
||||
status.set_compression_enabled(old_status.compression_enabled());
|
||||
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
|
||||
}
|
||||
return WriteSnapshotUpdateStatus(lock, status);
|
||||
}
|
||||
|
@ -2755,7 +2848,7 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
|
|||
}
|
||||
}
|
||||
|
||||
SnapshotUpdateStatus status = {};
|
||||
SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
|
||||
status.set_state(update_state);
|
||||
status.set_compression_enabled(cow_creator.compression_enabled);
|
||||
if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
|
||||
|
@ -3181,9 +3274,10 @@ bool SnapshotManager::Dump(std::ostream& os) {
|
|||
|
||||
std::stringstream ss;
|
||||
|
||||
auto update_status = ReadSnapshotUpdateStatus(file.get());
|
||||
|
||||
ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
|
||||
ss << "Compression: " << ReadSnapshotUpdateStatus(file.get()).compression_enabled()
|
||||
<< std::endl;
|
||||
ss << "Compression: " << update_status.compression_enabled() << std::endl;
|
||||
ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
|
||||
ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
|
||||
ss << "Rollback indicator: "
|
||||
|
@ -3192,6 +3286,7 @@ bool SnapshotManager::Dump(std::ostream& os) {
|
|||
ss << "Forward merge indicator: "
|
||||
<< (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
|
||||
<< std::endl;
|
||||
ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
|
||||
|
||||
bool ok = true;
|
||||
std::vector<std::string> snapshots;
|
||||
|
@ -3709,5 +3804,13 @@ MergeFailureCode SnapshotManager::ReadMergeFailureCode() {
|
|||
return status.merge_failure_code();
|
||||
}
|
||||
|
||||
std::string SnapshotManager::ReadSourceBuildFingerprint() {
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) return {};
|
||||
|
||||
SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
|
||||
return status.source_build_fingerprint();
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
@ -91,7 +91,6 @@ void SnapshotMergeStats::set_state(android::snapshot::UpdateState state, bool us
|
|||
|
||||
void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
|
||||
report_.set_cow_file_size(cow_file_size);
|
||||
WriteState();
|
||||
}
|
||||
|
||||
uint64_t SnapshotMergeStats::cow_file_size() {
|
||||
|
@ -138,6 +137,14 @@ MergeFailureCode SnapshotMergeStats::merge_failure_code() {
|
|||
return report_.merge_failure_code();
|
||||
}
|
||||
|
||||
void SnapshotMergeStats::set_source_build_fingerprint(const std::string& fingerprint) {
|
||||
report_.set_source_build_fingerprint(fingerprint);
|
||||
}
|
||||
|
||||
std::string SnapshotMergeStats::source_build_fingerprint() {
|
||||
return report_.source_build_fingerprint();
|
||||
}
|
||||
|
||||
class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
|
||||
public:
|
||||
SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
|
||||
|
|
|
@ -136,7 +136,10 @@ class SnapshotMergeStatsStub : public ISnapshotMergeStats {
|
|||
void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}
|
||||
uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }
|
||||
void set_merge_failure_code(MergeFailureCode) override {}
|
||||
MergeFailureCode merge_failure_code() { return MergeFailureCode::Ok; }
|
||||
MergeFailureCode merge_failure_code() override { return MergeFailureCode::Ok; }
|
||||
void set_source_build_fingerprint(const std::string&) override {}
|
||||
std::string source_build_fingerprint() override { return {}; }
|
||||
bool WriteState() override { return false; }
|
||||
};
|
||||
|
||||
ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
|
||||
|
@ -170,4 +173,9 @@ auto SnapshotManagerStub::ReadMergeFailureCode() -> MergeFailureCode {
|
|||
return MergeFailureCode::Ok;
|
||||
}
|
||||
|
||||
std::string SnapshotManagerStub::ReadSourceBuildFingerprint() {
|
||||
LOG(ERROR) << __FUNCTION__ << " should never be called.";
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace android::snapshot
|
||||
|
|
|
@ -442,8 +442,9 @@ bool Snapuserd::ReadMetadata() {
|
|||
|
||||
int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
|
||||
std::optional<chunk_t> prev_id = {};
|
||||
std::map<uint64_t, const CowOperation*> map;
|
||||
std::vector<const CowOperation*> vec;
|
||||
std::set<uint64_t> dest_blocks;
|
||||
std::set<uint64_t> source_blocks;
|
||||
size_t pending_copy_ops = exceptions_per_area_ - num_ops;
|
||||
uint64_t total_copy_ops = reader_->total_copy_ops();
|
||||
|
||||
|
@ -500,99 +501,45 @@ bool Snapuserd::ReadMetadata() {
|
|||
// scratch space and re-construct it thereby there
|
||||
// is no loss of data.
|
||||
//
|
||||
// Note that we will follow the same order of COW operations
|
||||
// as present in the COW file. This will make sure that\
|
||||
// the merge of operations are done based on the ops present
|
||||
// in the file.
|
||||
//===========================================================
|
||||
//
|
||||
// Case 2:
|
||||
//
|
||||
// Let's say we have three copy operations written to COW file
|
||||
// in the following order:
|
||||
//
|
||||
// op-1: 15 -> 18
|
||||
// op-2: 16 -> 19
|
||||
// op-3: 17 -> 20
|
||||
//
|
||||
// As aforementioned, kernel will initiate merge in reverse order.
|
||||
// Hence, we will read these ops in reverse order so that all these
|
||||
// ops are exectued in the same order as requested. Thus, we will
|
||||
// read the metadata in reverse order and for the kernel it will
|
||||
// look like:
|
||||
//
|
||||
// op-3: 17 -> 20
|
||||
// op-2: 16 -> 19
|
||||
// op-1: 15 -> 18 <-- Merge starts here in the kernel
|
||||
//
|
||||
// Now, this is problematic as kernel cannot batch merge them.
|
||||
//
|
||||
// Merge sequence will look like:
|
||||
//
|
||||
// Merge-1: op-1: 15 -> 18
|
||||
// Merge-2: op-2: 16 -> 19
|
||||
// Merge-3: op-3: 17 -> 20
|
||||
//
|
||||
// We have three merge operations.
|
||||
//
|
||||
// Even though the blocks are contiguous, kernel can batch merge
|
||||
// them if the blocks are in descending order. Update engine
|
||||
// addresses this issue partially for overlapping operations as
|
||||
// we see that op-1 to op-3 and op-4 to op-6 operatiosn are in
|
||||
// descending order. However, if the copy operations are not
|
||||
// overlapping, update engine cannot write these blocks
|
||||
// in descending order. Hence, we will try to address it.
|
||||
// Thus, we will send these blocks to the kernel and it will
|
||||
// look like:
|
||||
//
|
||||
// op-3: 15 -> 18
|
||||
// op-2: 16 -> 19
|
||||
// op-1: 17 -> 20 <-- Merge starts here in the kernel
|
||||
//
|
||||
// Now with this change, we can batch merge all these three
|
||||
// operations. Merge sequence will look like:
|
||||
//
|
||||
// Merge-1: {op-1: 17 -> 20, op-2: 16 -> 19, op-3: 15 -> 18}
|
||||
//
|
||||
// Note that we have changed the ordering of merge; However, this
|
||||
// is ok as each of these copy operations are independent and there
|
||||
// is no overlap.
|
||||
//
|
||||
//===================================================================
|
||||
if (prev_id.has_value()) {
|
||||
chunk_t diff = (cow_op->new_block > prev_id.value())
|
||||
? (cow_op->new_block - prev_id.value())
|
||||
: (prev_id.value() - cow_op->new_block);
|
||||
if (diff != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
|
||||
if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
metadata_found = true;
|
||||
pending_copy_ops -= 1;
|
||||
map[cow_op->new_block] = cow_op;
|
||||
vec.push_back(cow_op);
|
||||
dest_blocks.insert(cow_op->source);
|
||||
source_blocks.insert(cow_op->new_block);
|
||||
prev_id = cow_op->new_block;
|
||||
cowop_riter_->Next();
|
||||
} while (!cowop_riter_->Done() && pending_copy_ops);
|
||||
|
||||
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
|
||||
SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
|
||||
SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
|
||||
<< " Area: " << vec_.size() << " Area offset: " << offset
|
||||
<< " Pending-copy-ops in this area: " << pending_copy_ops;
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); it++) {
|
||||
for (size_t i = 0; i < vec.size(); i++) {
|
||||
struct disk_exception* de =
|
||||
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
|
||||
de->old_chunk = it->first;
|
||||
const CowOperation* cow_op = vec[i];
|
||||
|
||||
de->old_chunk = cow_op->new_block;
|
||||
de->new_chunk = data_chunk_id;
|
||||
|
||||
// Store operation pointer.
|
||||
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
|
||||
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
|
||||
offset += sizeof(struct disk_exception);
|
||||
num_ops += 1;
|
||||
copy_ops++;
|
||||
if (read_ahead_feature_) {
|
||||
read_ahead_ops_.push_back(it->second);
|
||||
read_ahead_ops_.push_back(cow_op);
|
||||
}
|
||||
|
||||
SNAP_LOG(DEBUG) << num_ops << ":"
|
||||
|
@ -635,8 +582,9 @@ bool Snapuserd::ReadMetadata() {
|
|||
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
|
||||
}
|
||||
}
|
||||
map.clear();
|
||||
vec.clear();
|
||||
dest_blocks.clear();
|
||||
source_blocks.clear();
|
||||
prev_id.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ const std::string bootconfig =
|
|||
"androidboot.serialno = \"BLAHBLAHBLAH\"\n"
|
||||
"androidboot.slot_suffix = \"_a\"\n"
|
||||
"androidboot.hardware.platform = \"sdw813\"\n"
|
||||
"hardware = \"foo\"\n"
|
||||
"androidboot.hardware = \"foo\"\n"
|
||||
"androidboot.revision = \"EVT1.0\"\n"
|
||||
"androidboot.bootloader = \"burp-0.1-7521\"\n"
|
||||
"androidboot.hardware.sku = \"mary\"\n"
|
||||
|
@ -159,7 +159,7 @@ const std::vector<std::pair<std::string, std::string>> bootconfig_result_space =
|
|||
{"androidboot.serialno", "BLAHBLAHBLAH"},
|
||||
{"androidboot.slot_suffix", "_a"},
|
||||
{"androidboot.hardware.platform", "sdw813"},
|
||||
{"hardware", "foo"},
|
||||
{"androidboot.hardware", "foo"},
|
||||
{"androidboot.revision", "EVT1.0"},
|
||||
{"androidboot.bootloader", "burp-0.1-7521"},
|
||||
{"androidboot.hardware.sku", "mary"},
|
||||
|
|
|
@ -277,8 +277,6 @@ runs the service.
|
|||
CLD_EXITED or an status other than '0', reboot the system with the target specified in
|
||||
_target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
|
||||
intended to be used with the `exec_start` builtin for any must-have checks during boot.
|
||||
A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
|
||||
considered a failure for the purpose of this setting.
|
||||
|
||||
`restart_period <seconds>`
|
||||
> If a non-oneshot service exits, it will be restarted at its start time plus
|
||||
|
|
|
@ -158,7 +158,8 @@ static bool ActivateFlattenedApexesIfPossible() {
|
|||
auto on_activate = [&](const std::string& apex_path,
|
||||
const apex::proto::ApexManifest& apex_manifest) {
|
||||
apex_infos.emplace_back(apex_manifest.name(), apex_path, apex_path, apex_manifest.version(),
|
||||
apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true);
|
||||
apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
|
||||
/* lastUpdateMillis= */ 0);
|
||||
};
|
||||
|
||||
for (const auto& dir : kBuiltinDirsForApexes) {
|
||||
|
|
|
@ -1238,21 +1238,11 @@ static void ProcessKernelCmdline() {
|
|||
});
|
||||
}
|
||||
|
||||
// bootconfig does not allow to populate `key=value` simultaneously with
|
||||
// `key.subkey=value` which does not work with the existing code for
|
||||
// `hardware` (e.g. we want both `ro.boot.hardware=value` and
|
||||
// `ro.boot.hardware.sku=value`) and for `qemu` (Android Stidio Emulator
|
||||
// specific).
|
||||
static bool IsAllowedBootconfigKey(const std::string_view key) {
|
||||
return (key == "hardware"sv) || (key == "qemu"sv);
|
||||
}
|
||||
|
||||
static void ProcessBootconfig() {
|
||||
ImportBootconfig([&](const std::string& key, const std::string& value) {
|
||||
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
|
||||
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
|
||||
} else if (IsAllowedBootconfigKey(key)) {
|
||||
InitPropertySet("ro.boot." + key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -113,6 +113,11 @@ service $name /system/bin/yes
|
|||
}
|
||||
|
||||
TEST_F(RebootTest, StopServicesSIGTERM) {
|
||||
if (getuid() != 0) {
|
||||
GTEST_SKIP() << "Skipping test, must be run as root.";
|
||||
return;
|
||||
}
|
||||
|
||||
AddTestService("A");
|
||||
AddTestService("B");
|
||||
|
||||
|
@ -148,6 +153,11 @@ TEST_F(RebootTest, StopServicesSIGTERM) {
|
|||
}
|
||||
|
||||
TEST_F(RebootTest, StopServicesSIGKILL) {
|
||||
if (getuid() != 0) {
|
||||
GTEST_SKIP() << "Skipping test, must be run as root.";
|
||||
return;
|
||||
}
|
||||
|
||||
AddTestService("A");
|
||||
AddTestService("B");
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "capabilities.h"
|
||||
#include "reboot_utils.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
@ -38,31 +39,51 @@ namespace init {
|
|||
static std::string init_fatal_reboot_target = "bootloader";
|
||||
static bool init_fatal_panic = false;
|
||||
|
||||
// this needs to read the /proc/* files directly because it is called before
|
||||
// ro.boot.* properties are initialized
|
||||
void SetFatalRebootTarget(const std::optional<std::string>& reboot_target) {
|
||||
std::string cmdline;
|
||||
android::base::ReadFileToString("/proc/cmdline", &cmdline);
|
||||
cmdline = android::base::Trim(cmdline);
|
||||
|
||||
const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
|
||||
init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
|
||||
const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
|
||||
if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
|
||||
init_fatal_panic = false;
|
||||
ImportBootconfig(
|
||||
[kInitFatalPanicParamString](const std::string& key, const std::string& value) {
|
||||
if (key == kInitFatalPanicParamString && value == "true") {
|
||||
init_fatal_panic = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
|
||||
init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
|
||||
}
|
||||
|
||||
if (reboot_target) {
|
||||
init_fatal_reboot_target = *reboot_target;
|
||||
return;
|
||||
}
|
||||
|
||||
const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
|
||||
const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
|
||||
auto start_pos = cmdline.find(kRebootTargetString);
|
||||
if (start_pos == std::string::npos) {
|
||||
return; // We already default to bootloader if no setting is provided.
|
||||
}
|
||||
start_pos += sizeof(kRebootTargetString) - 1;
|
||||
ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
|
||||
if (key == kRebootTargetString) {
|
||||
init_fatal_reboot_target = value;
|
||||
}
|
||||
});
|
||||
// We already default to bootloader if no setting is provided.
|
||||
} else {
|
||||
const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
|
||||
start_pos += sizeof(kRebootTargetStringPattern) - 1;
|
||||
|
||||
auto end_pos = cmdline.find(' ', start_pos);
|
||||
// if end_pos isn't found, then we've run off the end, but this is okay as this is the last
|
||||
// entry, and -1 is a valid size for string::substr();
|
||||
auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
|
||||
init_fatal_reboot_target = cmdline.substr(start_pos, size);
|
||||
auto end_pos = cmdline.find(' ', start_pos);
|
||||
// if end_pos isn't found, then we've run off the end, but this is okay as this is the last
|
||||
// entry, and -1 is a valid size for string::substr();
|
||||
auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
|
||||
init_fatal_reboot_target = cmdline.substr(start_pos, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRebootCapable() {
|
||||
|
|
|
@ -194,8 +194,6 @@ void Service::KillProcessGroup(int signal, bool report_oneshot) {
|
|||
<< ") process group...";
|
||||
int max_processes = 0;
|
||||
int r;
|
||||
|
||||
flags_ |= SVC_STOPPING;
|
||||
if (signal == SIGTERM) {
|
||||
r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
|
||||
} else {
|
||||
|
@ -279,8 +277,7 @@ void Service::Reap(const siginfo_t& siginfo) {
|
|||
f(siginfo);
|
||||
}
|
||||
|
||||
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
|
||||
!(flags_ & SVC_STOPPING)) {
|
||||
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
|
||||
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
|
||||
trigger_shutdown(*on_failure_reboot_target_);
|
||||
}
|
||||
|
@ -290,7 +287,7 @@ void Service::Reap(const siginfo_t& siginfo) {
|
|||
if (flags_ & SVC_TEMPORARY) return;
|
||||
|
||||
pid_ = 0;
|
||||
flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
|
||||
flags_ &= (~SVC_RUNNING);
|
||||
start_order_ = 0;
|
||||
|
||||
// Oneshot processes go into the disabled state on exit,
|
||||
|
@ -414,8 +411,7 @@ Result<void> Service::Start() {
|
|||
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
|
||||
// Starting a service removes it from the disabled or reset state and
|
||||
// immediately takes it out of the restarting state if it was in there.
|
||||
flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
|
||||
SVC_STOPPING));
|
||||
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
|
||||
|
||||
// Running processes require no additional work --- if they're in the
|
||||
// process of exiting, we've ensured that they will immediately restart
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
// should not be killed during shutdown
|
||||
#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
|
||||
// service list once it is reaped.
|
||||
#define SVC_STOPPING 0x2000 // service is being stopped by init
|
||||
|
||||
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
|
||||
|
||||
|
|
|
@ -253,8 +253,10 @@ void ImportBootconfig(const std::function<void(const std::string&, const std::st
|
|||
for (const auto& entry : android::base::Split(bootconfig, "\n")) {
|
||||
std::vector<std::string> pieces = android::base::Split(entry, "=");
|
||||
if (pieces.size() == 2) {
|
||||
pieces[1].erase(std::remove(pieces[1].begin(), pieces[1].end(), '"'), pieces[1].end());
|
||||
fn(android::base::Trim(pieces[0]), android::base::Trim(pieces[1]));
|
||||
// get rid of the extra space between a list of values and remove the quotes.
|
||||
std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
|
||||
value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
|
||||
fn(android::base::Trim(pieces[0]), android::base::Trim(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,8 +75,7 @@ __BEGIN_DECLS
|
|||
#define ATRACE_TAG_AIDL (1<<24)
|
||||
#define ATRACE_TAG_NNAPI (1<<25)
|
||||
#define ATRACE_TAG_RRO (1<<26)
|
||||
#define ATRACE_TAG_SYSPROP (1<<27)
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
|
||||
#define ATRACE_TAG_LAST ATRACE_TAG_RRO
|
||||
|
||||
// Reserved for initialization.
|
||||
#define ATRACE_TAG_NOT_READY (1ULL<<63)
|
||||
|
|
|
@ -146,12 +146,6 @@ static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned i
|
|||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
|
||||
// With the exception of boot or shutdown, system uid_ folders are always populated. Spinning
|
||||
// here would needlessly delay most pid removals. Additionally, once empty a uid_ cgroup won't
|
||||
// have processes hanging on it (we've already spun for all its pid_), so there's no need to
|
||||
// spin anyway.
|
||||
rmdir(uid_path.c_str());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -230,7 +224,11 @@ void removeAllProcessGroups() {
|
|||
* transferred for the user/group passed as uid/gid before system_server can properly access them.
|
||||
*/
|
||||
static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
|
||||
if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
|
||||
if (mkdir(path.c_str(), mode) == -1) {
|
||||
if (errno == EEXIST) {
|
||||
// Directory already exists and permissions have been set at the time it was created
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -584,7 +584,7 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
],
|
||||
|
||||
"AggregateProfiles": [
|
||||
|
@ -635,6 +635,10 @@
|
|||
{
|
||||
"Name": "CPUSET_SP_RESTRICTED",
|
||||
"Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
|
||||
},
|
||||
{
|
||||
"Name": "Dex2OatBootComplete",
|
||||
"Profiles": [ "SCHED_SP_BACKGROUND" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -191,33 +191,6 @@ on init
|
|||
chown system system /dev/cpuctl/camera-daemon/tasks
|
||||
chmod 0664 /dev/cpuctl/camera-daemon/tasks
|
||||
|
||||
# Android only use global RT throttling and doesn't use CONFIG_RT_GROUP_SCHED
|
||||
# for RT group throttling. These values here are just to make sure RT threads
|
||||
# can be migrated to those groups. These settings can be removed once we migrate
|
||||
# to GKI kernel.
|
||||
write /dev/cpuctl/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/cpu.rt_runtime_us 950000
|
||||
# Surfaceflinger is in FG group so giving it a bit more
|
||||
write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
|
||||
write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/background/cpu.rt_runtime_us 50000
|
||||
write /dev/cpuctl/background/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
|
||||
write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/rt/cpu.rt_runtime_us 100000
|
||||
write /dev/cpuctl/rt/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/system/cpu.rt_runtime_us 100000
|
||||
write /dev/cpuctl/system/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/system-background/cpu.rt_runtime_us 50000
|
||||
write /dev/cpuctl/system-background/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 50000
|
||||
write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
|
||||
write /dev/cpuctl/camera-daemon/cpu.rt_runtime_us 50000
|
||||
write /dev/cpuctl/camera-daemon/cpu.rt_period_us 1000000
|
||||
|
||||
# Migrate root group to system subgroup
|
||||
copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
|
||||
|
||||
# Create an stune group for camera-specific processes
|
||||
mkdir /dev/stune/camera-daemon
|
||||
chown system system /dev/stune/camera-daemon
|
||||
|
@ -1278,7 +1251,3 @@ on userspace-reboot-resume
|
|||
|
||||
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
|
||||
setprop sys.init.userspace_reboot.in_progress ""
|
||||
|
||||
# Migrate tasks again in case kernel threads are created during boot
|
||||
on property:sys.boot_completed=1
|
||||
copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
|
||||
|
|
|
@ -100,6 +100,7 @@ cc_binary {
|
|||
"ipc/trusty_keymaster_ipc.cpp",
|
||||
"keymint/TrustyKeyMintDevice.cpp",
|
||||
"keymint/TrustyKeyMintOperation.cpp",
|
||||
"keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
|
||||
"keymint/TrustySecureClock.cpp",
|
||||
"keymint/TrustySharedSecret.cpp",
|
||||
"keymint/service.cpp",
|
||||
|
@ -118,7 +119,6 @@ cc_binary {
|
|||
"libtrusty",
|
||||
],
|
||||
required: [
|
||||
"RemoteProvisioner",
|
||||
"android.hardware.hardware_keystore.xml",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -79,6 +79,16 @@ int TrustyKeymaster::Initialize(KmVersion version) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Set the vendor patchlevel to value retrieved from system property (which
|
||||
// requires SELinux permission).
|
||||
ConfigureVendorPatchlevelRequest vendor_req(message_version());
|
||||
vendor_req.vendor_patchlevel = GetVendorPatchlevel();
|
||||
ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);
|
||||
if (vendor_rsp.error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: " << vendor_rsp.error;
|
||||
// Don't fail if this message isn't understood.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -158,6 +168,16 @@ void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
|
|||
}
|
||||
}
|
||||
|
||||
void TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
|
||||
GenerateRkpKeyResponse* response) {
|
||||
ForwardCommand(KM_GENERATE_RKP_KEY, request, response);
|
||||
}
|
||||
|
||||
void TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,
|
||||
GenerateCsrResponse* response) {
|
||||
ForwardCommand(KM_GENERATE_CSR, request, response);
|
||||
}
|
||||
|
||||
void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
|
||||
GetKeyCharacteristicsResponse* response) {
|
||||
ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
|
||||
|
@ -252,4 +272,11 @@ DeviceLockedResponse TrustyKeymaster::DeviceLocked(const DeviceLockedRequest& re
|
|||
return response;
|
||||
}
|
||||
|
||||
ConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(
|
||||
const ConfigureVendorPatchlevelRequest& request) {
|
||||
ConfigureVendorPatchlevelResponse response(message_version());
|
||||
ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace keymaster
|
||||
|
|
|
@ -42,6 +42,8 @@ class TrustyKeymaster {
|
|||
void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
|
||||
void Configure(const ConfigureRequest& request, ConfigureResponse* response);
|
||||
void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
|
||||
void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
|
||||
void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
|
||||
void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
|
||||
GetKeyCharacteristicsResponse* response);
|
||||
void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
|
||||
|
@ -62,6 +64,8 @@ class TrustyKeymaster {
|
|||
GetVersion2Response GetVersion2(const GetVersion2Request& request);
|
||||
EarlyBootEndedResponse EarlyBootEnded();
|
||||
DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
|
||||
ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
|
||||
const ConfigureVendorPatchlevelRequest& request);
|
||||
|
||||
uint32_t message_version() const { return message_version_; }
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
|
||||
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
|
||||
#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
|
||||
|
||||
#include <trusty_keymaster/TrustyKeymaster.h>
|
||||
|
||||
namespace aidl::android::hardware::security::keymint::trusty {
|
||||
|
||||
using ::keymaster::TrustyKeymaster;
|
||||
using ::ndk::ScopedAStatus;
|
||||
using ::std::shared_ptr;
|
||||
|
||||
class TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
|
||||
public:
|
||||
explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)
|
||||
: impl_(std::move(impl)) {}
|
||||
virtual ~TrustyRemotelyProvisionedComponentDevice() = default;
|
||||
|
||||
ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
|
||||
|
||||
ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
|
||||
std::vector<uint8_t>* privateKeyHandle) override;
|
||||
|
||||
ScopedAStatus generateCertificateRequest(bool testMode,
|
||||
const std::vector<MacedPublicKey>& keysToSign,
|
||||
const std::vector<uint8_t>& endpointEncCertChain,
|
||||
const std::vector<uint8_t>& challenge,
|
||||
DeviceInfo* deviceInfo, ProtectedData* protectedData,
|
||||
std::vector<uint8_t>* keysToSignMac) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::trusty
|
|
@ -56,6 +56,9 @@ enum keymaster_command : uint32_t {
|
|||
KM_GET_VERSION_2 = (28 << KEYMASTER_REQ_SHIFT),
|
||||
KM_EARLY_BOOT_ENDED = (29 << KEYMASTER_REQ_SHIFT),
|
||||
KM_DEVICE_LOCKED = (30 << KEYMASTER_REQ_SHIFT),
|
||||
KM_GENERATE_RKP_KEY = (31 << KEYMASTER_REQ_SHIFT),
|
||||
KM_GENERATE_CSR = (32 << KEYMASTER_REQ_SHIFT),
|
||||
KM_CONFIGURE_VENDOR_PATCHLEVEL = (33 << KEYMASTER_REQ_SHIFT),
|
||||
|
||||
// Bootloader/provisioning calls.
|
||||
KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
|
||||
|
@ -69,7 +72,8 @@ enum keymaster_command : uint32_t {
|
|||
KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
|
||||
KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
|
||||
KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
|
||||
KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT)
|
||||
KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
|
||||
KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
|
||||
};
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
|
|
@ -34,6 +34,7 @@ using ::keymaster::FinishOperationRequest;
|
|||
using ::keymaster::FinishOperationResponse;
|
||||
using ::keymaster::TAG_ASSOCIATED_DATA;
|
||||
using ::keymaster::TAG_AUTH_TOKEN;
|
||||
using ::keymaster::TAG_CONFIRMATION_TOKEN;
|
||||
using ::keymaster::UpdateOperationRequest;
|
||||
using ::keymaster::UpdateOperationResponse;
|
||||
using km_utils::authToken2AidlVec;
|
||||
|
@ -106,12 +107,12 @@ ScopedAStatus TrustyKeyMintOperation::update(const vector<uint8_t>& input,
|
|||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus TrustyKeyMintOperation::finish(
|
||||
const optional<vector<uint8_t>>& input, //
|
||||
const optional<vector<uint8_t>>& signature, //
|
||||
const optional<HardwareAuthToken>& authToken,
|
||||
const optional<TimeStampToken>& /* timestampToken */,
|
||||
const optional<vector<uint8_t>>& /* confirmationToken */, vector<uint8_t>* output) {
|
||||
ScopedAStatus TrustyKeyMintOperation::finish(const optional<vector<uint8_t>>& input, //
|
||||
const optional<vector<uint8_t>>& signature, //
|
||||
const optional<HardwareAuthToken>& authToken,
|
||||
const optional<TimeStampToken>& /* timestampToken */,
|
||||
const optional<vector<uint8_t>>& confirmationToken,
|
||||
vector<uint8_t>* output) {
|
||||
if (!output) {
|
||||
return ScopedAStatus(AStatus_fromServiceSpecificError(
|
||||
static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));
|
||||
|
@ -119,6 +120,16 @@ ScopedAStatus TrustyKeyMintOperation::finish(
|
|||
output->clear();
|
||||
|
||||
FinishOperationRequest request(impl_->message_version());
|
||||
|
||||
if (authToken) {
|
||||
auto tokenAsVec(authToken2AidlVec(*authToken));
|
||||
request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
|
||||
}
|
||||
if (confirmationToken) {
|
||||
request.additional_params.push_back(TAG_CONFIRMATION_TOKEN, confirmationToken->data(),
|
||||
confirmationToken->size());
|
||||
}
|
||||
|
||||
request.op_handle = opHandle_;
|
||||
if (signature) request.signature.Reinitialize(signature->data(), signature->size());
|
||||
size_t serialized_size = request.SerializedSize();
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2021, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <variant>
|
||||
|
||||
#include <KeyMintUtils.h>
|
||||
#include <keymaster/keymaster_configuration.h>
|
||||
|
||||
#include <trusty_keymaster/TrustyKeyMintDevice.h>
|
||||
|
||||
namespace aidl::android::hardware::security::keymint::trusty {
|
||||
|
||||
using keymaster::GenerateCsrRequest;
|
||||
using keymaster::GenerateCsrResponse;
|
||||
using keymaster::GenerateRkpKeyRequest;
|
||||
using keymaster::GenerateRkpKeyResponse;
|
||||
using keymaster::KeymasterBlob;
|
||||
using ::std::string;
|
||||
using ::std::unique_ptr;
|
||||
using ::std::vector;
|
||||
using bytevec = ::std::vector<uint8_t>;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;
|
||||
|
||||
struct AStatusDeleter {
|
||||
void operator()(AStatus* p) { AStatus_delete(p); }
|
||||
};
|
||||
|
||||
class Status {
|
||||
public:
|
||||
Status() : status_(AStatus_newOk()) {}
|
||||
Status(int32_t errCode, const std::string& errMsg)
|
||||
: status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
|
||||
explicit Status(const std::string& errMsg)
|
||||
: status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
|
||||
explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}
|
||||
|
||||
Status(Status&&) = default;
|
||||
Status(const Status&) = delete;
|
||||
|
||||
operator ::ndk::ScopedAStatus() && { // NOLINT(google-explicit-constructor)
|
||||
return ndk::ScopedAStatus(status_.release());
|
||||
}
|
||||
|
||||
bool isOk() const { return AStatus_isOk(status_.get()); }
|
||||
|
||||
const char* getMessage() const { return AStatus_getMessage(status_.get()); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<AStatus, AStatusDeleter> status_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
|
||||
info->versionNumber = 1;
|
||||
info->rpcAuthorName = "Google";
|
||||
info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
|
||||
bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {
|
||||
GenerateRkpKeyRequest request(impl_->message_version());
|
||||
request.test_mode = testMode;
|
||||
GenerateRkpKeyResponse response(impl_->message_version());
|
||||
impl_->GenerateRkpKey(request, &response);
|
||||
if (response.error != KM_ERROR_OK) {
|
||||
return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
|
||||
}
|
||||
|
||||
macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
|
||||
*privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(
|
||||
bool testMode, const vector<MacedPublicKey>& keysToSign,
|
||||
const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
|
||||
ProtectedData* protectedData, bytevec* keysToSignMac) {
|
||||
GenerateCsrRequest request(impl_->message_version());
|
||||
request.test_mode = testMode;
|
||||
request.num_keys = keysToSign.size();
|
||||
request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
|
||||
for (size_t i = 0; i < keysToSign.size(); i++) {
|
||||
request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
|
||||
}
|
||||
request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
|
||||
request.SetChallenge(challenge.data(), challenge.size());
|
||||
GenerateCsrResponse response(impl_->message_version());
|
||||
impl_->GenerateCsr(request, &response);
|
||||
|
||||
if (response.error != KM_ERROR_OK) {
|
||||
return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
|
||||
}
|
||||
deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
|
||||
protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
|
||||
*keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::trusty
|
|
@ -11,4 +11,8 @@
|
|||
<name>android.hardware.security.sharedsecret</name>
|
||||
<fqname>ISharedSecret/default</fqname>
|
||||
</hal>
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.security.keymint</name>
|
||||
<fqname>IRemotelyProvisionedComponent/default</fqname>
|
||||
</hal>
|
||||
</manifest>
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
#include <android/binder_process.h>
|
||||
|
||||
#include <trusty_keymaster/TrustyKeyMintDevice.h>
|
||||
#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
|
||||
#include <trusty_keymaster/TrustySecureClock.h>
|
||||
#include <trusty_keymaster/TrustySharedSecret.h>
|
||||
|
||||
using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
|
||||
using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
|
||||
using aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;
|
||||
using aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;
|
||||
|
||||
|
@ -52,7 +54,8 @@ int main() {
|
|||
auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);
|
||||
auto secureClock = addService<TrustySecureClock>(trustyKeymaster);
|
||||
auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);
|
||||
|
||||
auto remotelyProvisionedComponent =
|
||||
addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);
|
||||
ABinderProcess_joinThreadPool();
|
||||
return EXIT_FAILURE; // should not reach
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ cc_binary {
|
|||
"proxy.c",
|
||||
],
|
||||
|
||||
shared_libs: ["liblog"],
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libhardware_legacy",
|
||||
],
|
||||
header_libs: ["libcutils_headers"],
|
||||
|
||||
static_libs: [
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <linux/major.h>
|
||||
#include <linux/mmc/ioctl.h>
|
||||
|
||||
#include <hardware_legacy/power.h>
|
||||
|
||||
#include "ipc.h"
|
||||
#include "log.h"
|
||||
#include "rpmb.h"
|
||||
|
@ -100,6 +102,8 @@ static int rpmb_fd = -1;
|
|||
static uint8_t read_buf[4096];
|
||||
static enum dev_type dev_type = UNKNOWN_RPMB;
|
||||
|
||||
static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
|
||||
|
||||
#ifdef RPMB_DEBUG
|
||||
|
||||
static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
|
||||
|
@ -194,6 +198,7 @@ static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req
|
|||
|
||||
static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
|
||||
int rc;
|
||||
int wl_rc;
|
||||
const uint8_t* write_buf = req->payload;
|
||||
/*
|
||||
* Meaning of member values are stated on the definition of struct sec_proto_cdb.
|
||||
|
@ -202,6 +207,12 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
|
|||
struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
|
||||
unsigned char sense_buffer[32];
|
||||
|
||||
wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
|
||||
if (wl_rc < 0) {
|
||||
ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
|
||||
return wl_rc;
|
||||
}
|
||||
|
||||
if (req->reliable_write_size) {
|
||||
/* Prepare SECURITY PROTOCOL OUT command. */
|
||||
out_cdb.length = __builtin_bswap32(req->reliable_write_size);
|
||||
|
@ -212,6 +223,7 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
|
|||
rc = ioctl(sg_fd, SG_IO, &io_hdr);
|
||||
if (rc < 0) {
|
||||
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
|
||||
goto err_op;
|
||||
}
|
||||
write_buf += req->reliable_write_size;
|
||||
}
|
||||
|
@ -225,6 +237,7 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
|
|||
rc = ioctl(sg_fd, SG_IO, &io_hdr);
|
||||
if (rc < 0) {
|
||||
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
|
||||
goto err_op;
|
||||
}
|
||||
write_buf += req->write_size;
|
||||
}
|
||||
|
@ -240,6 +253,13 @@ static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req)
|
|||
ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
err_op:
|
||||
wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);
|
||||
if (wl_rc < 0) {
|
||||
ALOGE("%s: failed to release wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -477,7 +477,6 @@ int storage_init(const char *dirname)
|
|||
if (ssdir_fd < 0) {
|
||||
ALOGE("failed to open ss root dir \"%s\": %s\n",
|
||||
dirname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
ssdir_name = dirname;
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue