Merge "tombstoned: fix file creation for ANRs."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7b6f6c0..198e4de 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -276,6 +276,12 @@
],
}
+cc_test_library {
+ name: "libcrash_test",
+ defaults: ["debuggerd_defaults"],
+ srcs: ["crash_test.cpp"],
+}
+
cc_test {
name: "debuggerd_test",
defaults: ["debuggerd_defaults"],
@@ -341,6 +347,10 @@
},
},
+ data: [
+ ":libcrash_test",
+ ],
+
test_suites: ["device-tests"],
}
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
new file mode 100644
index 0000000..c15145f
--- /dev/null
+++ b/debuggerd/crash_test.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 <stdint.h>
+
+extern "C" void crash() {
+ *reinterpret_cast<volatile char*>(0xdead) = '1';
+}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 93725b9..4634283 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -15,6 +15,7 @@
*/
#include <dirent.h>
+#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
#include <malloc.h>
@@ -30,6 +31,7 @@
#include <chrono>
#include <regex>
+#include <string>
#include <thread>
#include <android/fdsan.h>
@@ -473,7 +475,7 @@
struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
-INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(16, 131072));
+INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131072));
TEST_P(SizeParamCrasherTest, mte_uaf) {
#if defined(__aarch64__)
@@ -481,6 +483,11 @@
GTEST_SKIP() << "Requires MTE";
}
+ // Any UAF on a zero-sized allocation will be out-of-bounds so it won't be reported.
+ if (GetParam() == 0) {
+ return;
+ }
+
int intercept_result;
unique_fd output_fd;
StartProcess([&]() {
@@ -512,6 +519,38 @@
#endif
}
+TEST_P(SizeParamCrasherTest, mte_oob_uaf) {
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ volatile int* p = (volatile int*)malloc(GetParam());
+ free((void *)p);
+ p[-1] = 42;
+ });
+
+ 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"(signal 11 \(SIGSEGV\))");
+ ASSERT_NOT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 4 bytes left)");
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
TEST_P(SizeParamCrasherTest, mte_overflow) {
#if defined(__aarch64__)
if (!mte_supported()) {
@@ -1510,6 +1549,60 @@
ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
}
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string test_lib(testing::internal::GetArgvs()[0]);
+ auto const value = test_lib.find_last_of('/');
+ if (value == std::string::npos) {
+ test_lib = "./";
+ } else {
+ test_lib = test_lib.substr(0, value + 1) + "./";
+ }
+ test_lib += "libcrash_test.so";
+
+ *tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
+ std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ return system(cp_cmd.c_str()) == 0;
+}
+
+TEST_F(CrasherTest, unreadable_elf) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() {
+ TemporaryDir td;
+ std::string tmp_so_name;
+ if (!CopySharedLibrary(td.path, &tmp_so_name)) {
+ _exit(1);
+ }
+ void* handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
+ if (handle == nullptr) {
+ _exit(1);
+ }
+ // Delete the original shared library so that we get the warning
+ // about unreadable elf files.
+ if (unlink(tmp_so_name.c_str()) == -1) {
+ _exit(1);
+ }
+ void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
+ if (crash_func == nullptr) {
+ _exit(1);
+ }
+ crash_func();
+ });
+
+ 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"(NOTE: Function names and BuildId information is missing )");
+}
+
TEST(tombstoned, proto) {
const pid_t self = getpid();
unique_fd tombstoned_socket, text_fd, proto_fd;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e0cc662..ad903ce 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -106,9 +106,9 @@
unwindstack::MapInfo* map_info = maps->Find(sp);
if (map_info == nullptr) {
return "stack pointer is in a non-existent map; likely due to stack overflow.";
- } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+ } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
return "stack pointer is not in a rw map; likely due to stack overflow.";
- } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
+ } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {
return "stack pointer is close to top of stack; likely stack overflow.";
}
}
@@ -137,7 +137,7 @@
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
unwindstack::MapInfo* map_info = maps->Find(fault_addr);
- if (map_info != nullptr && map_info->flags == PROT_EXEC) {
+ if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else {
cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
@@ -244,7 +244,7 @@
"memory map (%zu entr%s):",
maps->Total(), maps->Total() == 1 ? "y" : "ies");
if (print_fault_address_marker) {
- if (maps->Total() != 0 && addr < maps->Get(0)->start) {
+ if (maps->Total() != 0 && addr < maps->Get(0)->start()) {
_LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
@@ -261,37 +261,37 @@
for (auto const& map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
- if (addr < map_info->start) {
+ if (addr < map_info->start()) {
_LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
- } else if (addr >= map_info->start && addr < map_info->end) {
+ } else if (addr >= map_info->start() && addr < map_info->end()) {
line = "--->";
print_fault_address_marker = false;
}
}
- line += get_addr_string(map_info->start) + '-' + get_addr_string(map_info->end - 1) + ' ';
- if (map_info->flags & PROT_READ) {
+ line += get_addr_string(map_info->start()) + '-' + get_addr_string(map_info->end() - 1) + ' ';
+ if (map_info->flags() & PROT_READ) {
line += 'r';
} else {
line += '-';
}
- if (map_info->flags & PROT_WRITE) {
+ if (map_info->flags() & PROT_WRITE) {
line += 'w';
} else {
line += '-';
}
- if (map_info->flags & PROT_EXEC) {
+ if (map_info->flags() & PROT_EXEC) {
line += 'x';
} else {
line += '-';
}
- line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset,
- map_info->end - map_info->start);
+ line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset(),
+ map_info->end() - map_info->start());
bool space_needed = true;
- if (!map_info->name.empty()) {
+ if (!map_info->name().empty()) {
space_needed = false;
- line += " " + map_info->name;
+ line += " " + map_info->name();
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
line += " (BuildId: " + build_id + ")";
@@ -369,8 +369,8 @@
std::string label{"memory near "s + reg_name};
if (maps) {
unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
- if (map_info != nullptr && !map_info->name.empty()) {
- label += " (" + map_info->name + ")";
+ if (map_info != nullptr && !map_info->name().empty()) {
+ label += " (" + map_info->name() + ")";
}
}
dump_memory(log, memory, reg_value, label);
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 7657001..abd1f12 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -102,9 +102,9 @@
unwindstack::MapInfo* map_info = maps->Find(sp);
if (map_info == nullptr) {
return "stack pointer is in a non-existent map; likely due to stack overflow.";
- } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+ } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
return "stack pointer is not in a rw map; likely due to stack overflow.";
- } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
+ } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {
return "stack pointer is close to top of stack; likely stack overflow.";
}
}
@@ -221,7 +221,7 @@
}
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
unwindstack::MapInfo* map_info = maps->Find(fault_addr);
- if (map_info != nullptr && map_info->flags == PROT_EXEC) {
+ if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
} else {
cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
@@ -359,7 +359,7 @@
dump.set_register_name(name);
unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
if (map_info) {
- dump.set_mapping_name(map_info->name);
+ dump.set_mapping_name(map_info->name());
}
char buf[256];
@@ -395,6 +395,15 @@
unwinder->LastErrorAddress());
}
} else {
+ if (unwinder->elf_from_memory_not_file()) {
+ auto backtrace_note = thread.mutable_backtrace_note();
+ *backtrace_note->Add() =
+ "Function names and BuildId information is missing for some frames due";
+ *backtrace_note->Add() =
+ "to unreadable libraries. For unwinds of apps, only shared libraries";
+ *backtrace_note->Add() = "found under the lib/ directory are readable.";
+ *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+ }
unwinder->SetDisplayBuildID(true);
for (const auto& frame : unwinder->frames()) {
BacktraceFrame* f = thread.add_current_backtrace();
@@ -417,21 +426,21 @@
for (const auto& map_info : *maps) {
auto* map = tombstone->add_memory_mappings();
- map->set_begin_address(map_info->start);
- map->set_end_address(map_info->end);
- map->set_offset(map_info->offset);
+ map->set_begin_address(map_info->start());
+ map->set_end_address(map_info->end());
+ map->set_offset(map_info->offset());
- if (map_info->flags & PROT_READ) {
+ if (map_info->flags() & PROT_READ) {
map->set_read(true);
}
- if (map_info->flags & PROT_WRITE) {
+ if (map_info->flags() & PROT_WRITE) {
map->set_write(true);
}
- if (map_info->flags & PROT_EXEC) {
+ if (map_info->flags() & PROT_EXEC) {
map->set_execute(true);
}
- map->set_mapping_name(map_info->name);
+ map->set_mapping_name(map_info->name());
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 020b0a5..b780b22 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -171,6 +171,10 @@
const Thread& thread, bool should_log) {
CBS("");
CB(should_log, "backtrace:");
+ if (!thread.backtrace_note().empty()) {
+ CB(should_log, " NOTE: %s",
+ android::base::Join(thread.backtrace_note(), "\n NOTE: ").c_str());
+ }
print_backtrace(callback, tombstone, thread.current_backtrace(), should_log);
}
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index b78224b..73cf573 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -31,6 +31,7 @@
stl: "libc++_static",
apex_available: [
+ "//apex_available:platform",
"com.android.runtime",
],
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 294e4e5..22fc30e 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -119,11 +119,12 @@
int32 id = 1;
string name = 2;
repeated Register registers = 3;
+ repeated string backtrace_note = 7;
repeated BacktraceFrame current_backtrace = 4;
repeated MemoryDump memory_dump = 5;
int64 tagged_addr_ctrl = 6;
- reserved 7 to 999;
+ reserved 8 to 999;
}
message BacktraceFrame {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index a349408..84709b6 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -17,6 +17,9 @@
},
{
"name": "libsnapshot_fuzzer_test"
+ },
+ {
+ "name": "cow_api_test"
}
]
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 42bf356..853b24d 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -298,6 +298,8 @@
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
}
+ } else if (StartsWith(flag, "lowerdir=")) {
+ entry->lowerdir = arg;
} else {
LWARNING << "Warning: unknown flag: " << flag;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index cb09383..9a94d79 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -92,6 +92,10 @@
return false;
}
+bool fs_mgr_overlayfs_mount_fstab_entry(const std::string&, const std::string&) {
+ return false;
+}
+
std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
return {};
}
@@ -1295,6 +1299,18 @@
}
}
+bool fs_mgr_overlayfs_mount_fstab_entry(const std::string& lowers,
+ const std::string& mount_point) {
+ if (fs_mgr_overlayfs_invalid()) return false;
+
+ std::string aux = "lowerdir=" + lowers + ",override_creds=off";
+ auto rc = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME, aux.c_str());
+
+ if (rc == 0) return true;
+
+ return false;
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
auto ret = false;
if (fs_mgr_overlayfs_invalid()) return ret;
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index d45e2de..ac95ef5 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -27,6 +27,7 @@
android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
+bool fs_mgr_overlayfs_mount_fstab_entry (const std::string& lowers, const std::string& mount_point);
std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr, bool force = true);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 2704e47..f33768b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -55,6 +55,7 @@
std::string vbmeta_partition;
uint64_t zram_backingdev_size = 0;
std::string avb_keys;
+ std::string lowerdir;
struct FsMgrFlags {
bool wait : 1;
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index c8516ab..31a57a8 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -224,8 +224,9 @@
return false;
}
-std::unique_ptr<IImageManager> IImageManager::Open(
- const std::string& dir, const std::chrono::milliseconds& /*timeout_ms*/) {
+std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,
+ const std::chrono::milliseconds& /*timeout_ms*/,
+ const DeviceInfo&) {
android::sp<IGsiService> service = android::gsi::GetGsiService();
android::sp<IImageService> manager;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 44f659b..dcbbc54 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -55,7 +55,8 @@
static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test";
static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test";
-std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix) {
+std::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix,
+ const DeviceInfo& device_info) {
auto metadata_dir = "/metadata/gsi/" + dir_prefix;
auto data_dir = "/data/gsi/" + dir_prefix;
auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));
@@ -63,17 +64,28 @@
if (ReadFileToString(install_dir_file, &path)) {
data_dir = path;
}
- return Open(metadata_dir, data_dir);
+ return Open(metadata_dir, data_dir, device_info);
}
std::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,
- const std::string& data_dir) {
- return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir));
+ const std::string& data_dir,
+ const DeviceInfo& device_info) {
+ return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir, device_info));
}
-ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir)
- : metadata_dir_(metadata_dir), data_dir_(data_dir) {
+ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir,
+ const DeviceInfo& device_info)
+ : metadata_dir_(metadata_dir), data_dir_(data_dir), device_info_(device_info) {
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
+
+ // Allow overriding whether ImageManager thinks it's in recovery, for testing.
+#ifdef __ANDROID_RECOVERY__
+ device_info_.is_recovery = {true};
+#else
+ if (!device_info_.is_recovery.has_value()) {
+ device_info_.is_recovery = {false};
+ }
+#endif
}
std::string ImageManager::GetImageHeaderPath(const std::string& name) {
@@ -261,10 +273,11 @@
return false;
}
-#if defined __ANDROID_RECOVERY__
- LOG(ERROR) << "Cannot remove images backed by /data in recovery";
- return false;
-#else
+ if (device_info_.is_recovery.value()) {
+ LOG(ERROR) << "Cannot remove images backed by /data in recovery";
+ return false;
+ }
+
std::string message;
auto header_file = GetImageHeaderPath(name);
if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {
@@ -278,7 +291,6 @@
LOG(ERROR) << "Error removing " << status_file << ": " << message;
}
return RemoveImageMetadata(metadata_dir_, name);
-#endif
}
// Create a block device for an image file, using its extents in its
@@ -521,6 +533,9 @@
// filesystem. This should only happen on devices with no encryption, or
// devices with FBE and no metadata encryption. For these cases it suffices
// to perform normal file writes to /data/gsi (which is unencrypted).
+ //
+ // Note: this is not gated on DeviceInfo, because the recovery-specific path
+ // must only be used in actual recovery.
std::string block_device;
bool can_use_devicemapper;
if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 7e30509..3c87000 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -21,6 +21,7 @@
#include <chrono>
#include <functional>
#include <memory>
+#include <optional>
#include <set>
#include <string>
@@ -37,11 +38,17 @@
virtual ~IImageManager() {}
+ // Helper for dependency injection.
+ struct DeviceInfo {
+ std::optional<bool> is_recovery;
+ };
+
// When linking to libfiemap_binder, the Open() call will use binder.
// Otherwise, the Open() call will use the ImageManager implementation
- // below.
+ // below. In binder mode, device_info is ignored.
static std::unique_ptr<IImageManager> Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms);
+ const std::chrono::milliseconds& timeout_ms,
+ const DeviceInfo& device_info = {});
// Flags for CreateBackingImage().
static constexpr int CREATE_IMAGE_DEFAULT = 0x0;
@@ -131,11 +138,13 @@
// Return an ImageManager for the given metadata and data directories. Both
// directories must already exist.
static std::unique_ptr<ImageManager> Open(const std::string& metadata_dir,
- const std::string& data_dir);
+ const std::string& data_dir,
+ const DeviceInfo& device_info = {});
// Helper function that derives the metadata and data dirs given a single
// prefix.
- static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix);
+ static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix,
+ const DeviceInfo& device_info = {});
// Methods that must be implemented from IImageManager.
FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,
@@ -166,7 +175,8 @@
FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
private:
- ImageManager(const std::string& metadata_dir, const std::string& data_dir);
+ ImageManager(const std::string& metadata_dir, const std::string& data_dir,
+ const DeviceInfo& device_info);
std::string GetImageHeaderPath(const std::string& name);
std::string GetStatusFilePath(const std::string& image_name);
bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
@@ -187,6 +197,7 @@
std::string metadata_dir_;
std::string data_dir_;
std::unique_ptr<IPartitionOpener> partition_opener_;
+ DeviceInfo device_info_;
};
// RAII helper class for mapping and opening devices with an ImageManager.
diff --git a/fs_mgr/libfiemap/passthrough.cpp b/fs_mgr/libfiemap/passthrough.cpp
index 1ccd9a0..d521804 100644
--- a/fs_mgr/libfiemap/passthrough.cpp
+++ b/fs_mgr/libfiemap/passthrough.cpp
@@ -20,9 +20,10 @@
namespace fiemap {
std::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir_prefix,
- const std::chrono::milliseconds& timeout_ms) {
+ const std::chrono::milliseconds& timeout_ms,
+ const DeviceInfo& device_info) {
(void)timeout_ms;
- return ImageManager::Open(dir_prefix);
+ return ImageManager::Open(dir_prefix, device_info);
}
} // namespace fiemap
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index c1c181a..6892025 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -78,6 +78,7 @@
shared_libs: [
"libbase",
"libchrome",
+ "libcrypto",
],
target: {
darwin: {
@@ -107,9 +108,6 @@
static_libs: [
"libfs_avb_test_util",
],
- shared_libs: [
- "libcrypto",
- ],
compile_multilib: "first",
data: [
":avbtool",
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
index 17f4c4e..1c95cf0 100644
--- a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -122,6 +122,7 @@
const size_t padding_size) {
VBMetaImage vbmeta_image;
vbmeta_image.path = test_dir_.Append(output_file_name);
+ GTEST_LOG_(INFO) << "ExtractVBMetaImage: " << image_path << " to " << output_file_name;
EXPECT_COMMAND(0,
"avbtool extract_vbmeta_image"
" --image %s"
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index c97dca0..6a764e4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -251,6 +251,7 @@
"snapshot_metadata_updater_test.cpp",
"snapshot_reader_test.cpp",
"snapshot_test.cpp",
+ "snapshot_writer_test.cpp",
],
shared_libs: [
"libbinder",
@@ -265,7 +266,7 @@
"android.hardware.boot@1.1",
"libbrotli",
"libc++fs",
- "libfs_mgr",
+ "libfs_mgr_binder",
"libgsi",
"libgmock",
"liblp",
@@ -420,8 +421,8 @@
"snapuserd_server.cpp",
"snapuserd.cpp",
"snapuserd_daemon.cpp",
- "snapuserd_worker.cpp",
- "snapuserd_readahead.cpp",
+ "snapuserd_worker.cpp",
+ "snapuserd_readahead.cpp",
],
cflags: [
@@ -478,6 +479,9 @@
"libgtest",
"libsnapshot_cow",
],
+ test_suites: [
+ "device-tests"
+ ],
test_min_api_level: 30,
auto_gen_config: true,
require_root: false,
@@ -560,7 +564,7 @@
srcs: [
"cow_snapuserd_test.cpp",
"snapuserd.cpp",
- "snapuserd_worker.cpp",
+ "snapuserd_worker.cpp",
],
cflags: [
"-Wall",
@@ -577,7 +581,7 @@
"libsnapshot_snapuserd",
"libcutils_sockets",
"libz",
- "libfs_mgr",
+ "libfs_mgr",
"libdm",
],
header_libs: [
diff --git a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt b/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
new file mode 100644
index 0000000..c474f4c
--- /dev/null
+++ b/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
@@ -0,0 +1,41 @@
+device_info_data {
+ allow_set_slot_as_unbootable: true
+ is_recovery: true
+}
+is_super_metadata_valid: true
+super_data {
+ partitions {
+ partition_name: "sys_a"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "vnnd_"
+ new_partition_info {
+ size: 3145728
+ }
+ }
+ partitions {
+ partition_name: "prd_a"
+ new_partition_info {
+ }
+ }
+ dynamic_partition_metadata {
+ groups {
+ name: "group_google_dp_a"
+ size: 34375467008
+ partition_names: "sys_a"
+ partition_names: "vnd_a"
+ partition_names: "prd_a"
+ }
+ }
+}
+has_metadata_snapshots_dir: true
+actions {
+ handle_imminent_data_wipe: true
+}
+actions {
+ begin_update {
+ }
+}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 35a02e6..2349e4a 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -377,7 +377,6 @@
});
if (header_.num_merge_ops > 0) {
- CHECK(ops_->size() >= header_.num_merge_ops);
ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
}
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 313eb64..3888eb1 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -96,7 +96,8 @@
class CowSnapuserdTest final {
public:
bool Setup();
- bool SetupCopyOverlap();
+ bool SetupCopyOverlap_1();
+ bool SetupCopyOverlap_2();
bool Merge();
void ValidateMerge();
void ReadSnapshotDeviceAndValidate();
@@ -115,7 +116,9 @@
void StartMerge();
void CreateCowDevice();
- void CreateCowDeviceWithCopyOverlap();
+ void CreateCowDeviceWithCopyOverlap_1();
+ void CreateCowDeviceWithCopyOverlap_2();
+ bool SetupDaemon();
void CreateBaseDevice();
void InitCowDevice();
void SetDeviceControlName();
@@ -193,10 +196,19 @@
return setup_ok_;
}
-bool CowSnapuserdTest::SetupCopyOverlap() {
+bool CowSnapuserdTest::SetupCopyOverlap_1() {
CreateBaseDevice();
- CreateCowDeviceWithCopyOverlap();
+ CreateCowDeviceWithCopyOverlap_1();
+ return SetupDaemon();
+}
+bool CowSnapuserdTest::SetupCopyOverlap_2() {
+ CreateBaseDevice();
+ CreateCowDeviceWithCopyOverlap_2();
+ return SetupDaemon();
+}
+
+bool CowSnapuserdTest::SetupDaemon() {
SetDeviceControlName();
StartSnapuserdDaemon();
@@ -275,7 +287,59 @@
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
}
-void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap() {
+void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
+ std::string path = android::base::GetExecutableDirectory();
+ cow_system_ = std::make_unique<TemporaryFile>(path);
+
+ 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 blk_src_copy = 0;
+
+ // Create overlapping copy operations
+ while (1) {
+ ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+ x -= 1;
+ if (x == 1) {
+ break;
+ }
+ 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 operations required for validation
+ int block_size = 4096;
+ x = num_blocks;
+ loff_t src_offset = block_size;
+ loff_t dest_offset = 0;
+
+ while (1) {
+ memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,
+ block_size);
+ x -= 1;
+ if (x == 1) {
+ break;
+ }
+ src_offset += block_size;
+ dest_offset += block_size;
+ }
+}
+
+void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
std::string path = android::base::GetExecutableDirectory();
cow_system_ = std::make_unique<TemporaryFile>(path);
@@ -770,19 +834,19 @@
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 21);
- ASSERT_EQ(de->new_chunk, 536);
- 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, 537);
offset += sizeof(struct disk_exception);
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
- ASSERT_EQ(de->old_chunk, 23);
+ ASSERT_EQ(de->old_chunk, 22);
ASSERT_EQ(de->new_chunk, 538);
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);
+ offset += sizeof(struct disk_exception);
+
// End of metadata
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, 0);
@@ -821,9 +885,17 @@
harness.Shutdown();
}
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST) {
+TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap());
+ ASSERT_TRUE(harness.SetupCopyOverlap_1());
+ ASSERT_TRUE(harness.Merge());
+ harness.ValidateMerge();
+ harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_2) {
+ CowSnapuserdTest harness;
+ ASSERT_TRUE(harness.SetupCopyOverlap_2());
ASSERT_TRUE(harness.Merge());
harness.ValidateMerge();
harness.Shutdown();
@@ -831,7 +903,7 @@
TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
CowSnapuserdTest harness;
- ASSERT_TRUE(harness.SetupCopyOverlap());
+ ASSERT_TRUE(harness.SetupCopyOverlap_1());
harness.MergeInterrupt();
harness.ValidateMerge();
harness.Shutdown();
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 0e90100..14ce0ee 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -17,6 +17,7 @@
#include <android-base/logging.h>
#include <fs_mgr.h>
#include <fs_mgr_overlayfs.h>
+#include <libfiemap/image_manager.h>
namespace android {
namespace snapshot {
@@ -26,6 +27,7 @@
using android::hardware::boot::V1_0::CommandResult;
#endif
+using namespace std::chrono_literals;
using namespace std::string_literals;
#ifdef __ANDROID_RECOVERY__
@@ -34,10 +36,6 @@
constexpr bool kIsRecovery = false;
#endif
-std::string DeviceInfo::GetGsidDir() const {
- return "ota"s;
-}
-
std::string DeviceInfo::GetMetadataDir() const {
return "/metadata/ota"s;
}
@@ -100,6 +98,10 @@
return kIsRecovery;
}
+bool DeviceInfo::IsFirstStageInit() const {
+ return first_stage_init_;
+}
+
bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {
#ifdef LIBSNAPSHOT_USE_HAL
if (!EnsureBootHal()) {
@@ -120,5 +122,22 @@
#endif
}
+std::unique_ptr<android::fiemap::IImageManager> DeviceInfo::OpenImageManager() const {
+ return IDeviceInfo::OpenImageManager("ota");
+}
+
+std::unique_ptr<android::fiemap::IImageManager> ISnapshotManager::IDeviceInfo::OpenImageManager(
+ const std::string& gsid_dir) const {
+ if (IsRecovery() || IsFirstStageInit()) {
+ android::fiemap::ImageManager::DeviceInfo device_info = {
+ .is_recovery = {IsRecovery()},
+ };
+ return android::fiemap::ImageManager::Open(gsid_dir, device_info);
+ } else {
+ // For now, use a preset timeout.
+ return android::fiemap::IImageManager::Open(gsid_dir, 15000ms);
+ }
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index d8d3d91..7999c99 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -29,7 +29,6 @@
using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
public:
- std::string GetGsidDir() const override;
std::string GetMetadataDir() const override;
std::string GetSlotSuffix() const override;
std::string GetOtherSlotSuffix() const override;
@@ -39,11 +38,16 @@
bool SetBootControlMergeStatus(MergeStatus status) override;
bool SetSlotAsUnbootable(unsigned int slot) override;
bool IsRecovery() const override;
+ std::unique_ptr<IImageManager> OpenImageManager() const override;
+ bool IsFirstStageInit() const override;
+
+ void set_first_stage_init(bool value) { first_stage_init_ = value; }
private:
bool EnsureBootHal();
android::fs_mgr::PartitionOpener opener_;
+ bool first_stage_init_ = false;
#ifdef LIBSNAPSHOT_USE_HAL
android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
#endif
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
index ef9d648..573a85b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -22,7 +22,6 @@
class MockDeviceInfo : public SnapshotManager::IDeviceInfo {
public:
- MOCK_METHOD(std::string, GetGsidDir, (), (const, override));
MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));
MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));
MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));
@@ -32,6 +31,9 @@
MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
MOCK_METHOD(bool, IsRecovery, (), (const, override));
+ MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));
+ MOCK_METHOD(std::unique_ptr<android::fiemap::IImageManager>, OpenImageManager, (),
+ (const, override));
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 195b6f2..603e896 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -94,8 +94,9 @@
// Dependency injection for testing.
class IDeviceInfo {
public:
+ using IImageManager = android::fiemap::IImageManager;
+
virtual ~IDeviceInfo() {}
- virtual std::string GetGsidDir() const = 0;
virtual std::string GetMetadataDir() const = 0;
virtual std::string GetSlotSuffix() const = 0;
virtual std::string GetOtherSlotSuffix() const = 0;
@@ -107,6 +108,11 @@
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
+ virtual bool IsFirstStageInit() const = 0;
+ virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
+
+ // Helper method for implementing OpenImageManager.
+ std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
};
virtual ~ISnapshotManager() = default;
@@ -392,6 +398,7 @@
FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
+ FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots);
FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
@@ -419,7 +426,6 @@
bool EnsureSnapuserdConnected();
// Helpers for first-stage init.
- bool ForceLocalImageManager();
const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
// Helper functions for tests.
@@ -764,7 +770,6 @@
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
std::unique_ptr<IImageManager> images_;
- bool has_local_image_manager_ = false;
bool use_first_stage_snapuserd_ = false;
bool in_factory_data_reset_ = false;
std::function<bool(const std::string&)> uevent_regen_callback_;
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index b038527..4e7ccf1 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -77,7 +77,6 @@
: TestDeviceInfo(fake_super) {
set_slot_suffix(slot_suffix);
}
- std::string GetGsidDir() const override { return "ota/test"s; }
std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
std::string GetSlotSuffix() const override { return slot_suffix_; }
std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
@@ -96,6 +95,10 @@
return true;
}
bool IsTestDevice() const override { return true; }
+ bool IsFirstStageInit() const override { return first_stage_init_; }
+ std::unique_ptr<IImageManager> OpenImageManager() const override {
+ return IDeviceInfo::OpenImageManager("ota/test");
+ }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
@@ -104,6 +107,7 @@
opener_ = std::make_unique<TestPartitionOpener>(path);
}
void set_recovery(bool value) { recovery_ = value; }
+ void set_first_stage_init(bool value) { first_stage_init_ = value; }
MergeStatus merge_status() const { return merge_status_; }
private:
@@ -111,6 +115,7 @@
std::unique_ptr<TestPartitionOpener> opener_;
MergeStatus merge_status_;
bool recovery_ = false;
+ bool first_stage_init_ = false;
std::unordered_set<uint32_t> unbootable_slots_;
};
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 8f3926a..e2c03ae 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -95,18 +95,16 @@
if (!info) {
info = new DeviceInfo();
}
- auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
- if (info->IsRecovery()) {
- sm->ForceLocalImageManager();
- }
- return sm;
+ return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
}
std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
- auto sm = New(info);
- if (!sm || !sm->ForceLocalImageManager()) {
- return nullptr;
+ if (!info) {
+ DeviceInfo* impl = new DeviceInfo();
+ impl->set_first_stage_init(true);
+ info = impl;
}
+ auto sm = New(info);
// The first-stage version of snapuserd is explicitly started by init. Do
// not attempt to using it during tests (which run in normal AOSP).
@@ -117,7 +115,6 @@
}
SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
- gsid_dir_ = device_->GetGsidDir();
metadata_dir_ = device_->GetMetadataDir();
}
@@ -538,9 +535,7 @@
bool ok;
std::string cow_dev;
- if (has_local_image_manager_) {
- // If we forced a local image manager, it means we don't have binder,
- // which means first-stage init. We must use device-mapper.
+ if (device_->IsRecovery() || device_->IsFirstStageInit()) {
const auto& opener = device_->GetPartitionOpener();
ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);
} else {
@@ -1836,6 +1831,10 @@
return false;
}
+ if (!EnsureImageManager()) {
+ return false;
+ }
+
for (const auto& partition : metadata->partitions) {
if (GetPartitionGroupName(metadata->groups[partition.group_index]) == kCowGroupName) {
LOG(INFO) << "Skip mapping partition " << GetPartitionName(partition) << " in group "
@@ -2556,8 +2555,7 @@
bool SnapshotManager::EnsureImageManager() {
if (images_) return true;
- // For now, use a preset timeout.
- images_ = android::fiemap::IImageManager::Open(gsid_dir_, 15000ms);
+ images_ = device_->OpenImageManager();
if (!images_) {
LOG(ERROR) << "Could not open ImageManager";
return false;
@@ -2582,16 +2580,6 @@
return true;
}
-bool SnapshotManager::ForceLocalImageManager() {
- images_ = android::fiemap::ImageManager::Open(gsid_dir_);
- if (!images_) {
- LOG(ERROR) << "Could not open ImageManager";
- return false;
- }
- has_local_image_manager_ = true;
- return true;
-}
-
void SnapshotManager::UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
std::vector<std::string> to_delete;
for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index 8926535..0096f85 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -381,8 +381,9 @@
CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
}
-std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
- const std::string& metadata_dir, const std::string& data_dir) {
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager() {
+ auto metadata_dir = fake_root_->tmp_path() + "/images_manager_metadata";
+ auto data_dir = fake_data_mount_point_ + "/image_manager_data";
PCHECK(Mkdir(metadata_dir));
PCHECK(Mkdir(data_dir));
return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
@@ -428,13 +429,9 @@
PCHECK(Mkdir(metadata_dir + "/snapshots"));
}
- ret.device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(),
+ ret.device_info = new SnapshotFuzzDeviceInfo(this, data.device_info_data(),
std::move(partition_opener), metadata_dir);
auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
- snapshot->images_ =
- CheckCreateFakeImageManager(fake_root_->tmp_path() + "/images_manager_metadata",
- fake_data_mount_point_ + "/image_manager_data");
- snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager();
ret.snapshot = std::move(snapshot);
return ret;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 5319e69..3ed27c8 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -65,6 +65,8 @@
// ISnapshotManager.
SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
+ std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager();
+
// Return path to super partition.
const std::string& super() const;
@@ -79,8 +81,6 @@
std::string fake_data_block_device_;
std::unique_ptr<AutoDevice> mounted_data_;
- static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
- const std::string& metadata_dir, const std::string& data_dir);
static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
uint64_t size,
android::dm::LoopControl* control,
@@ -95,15 +95,15 @@
class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
public:
// Client is responsible for maintaining the lifetime of |data|.
- SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& data,
+ SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data,
std::unique_ptr<TestPartitionOpener>&& partition_opener,
const std::string& metadata_dir)
- : data_(&data),
+ : env_(env),
+ data_(&data),
partition_opener_(std::move(partition_opener)),
metadata_dir_(metadata_dir) {}
// Following APIs are mocked.
- std::string GetGsidDir() const override { return "fuzz_ota"; }
std::string GetMetadataDir() const override { return metadata_dir_; }
std::string GetSuperDevice(uint32_t) const override {
// TestPartitionOpener can recognize this.
@@ -124,10 +124,15 @@
return data_->allow_set_slot_as_unbootable();
}
bool IsRecovery() const override { return data_->is_recovery(); }
+ bool IsFirstStageInit() const override { return false; }
+ std::unique_ptr<IImageManager> OpenImageManager() const {
+ return env_->CheckCreateFakeImageManager();
+ }
void SwitchSlot() { switched_slot_ = !switched_slot_; }
private:
+ SnapshotFuzzEnv* env_;
const FuzzDeviceInfoData* data_;
std::unique_ptr<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 45db7a4..6018643 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -403,6 +403,7 @@
}
std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {
+ info->set_first_stage_init(true);
auto init = SnapshotManager::NewForFirstStageMount(info);
if (!init) {
return nullptr;
@@ -1860,6 +1861,67 @@
}
}
+// Test update package that requests data wipe.
+TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
+ AddOperationForPartitions();
+
+ // Execute the update.
+ ASSERT_TRUE(sm->BeginUpdate());
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
+ }
+
+ // Create a stale snapshot that should not exist.
+ {
+ ASSERT_TRUE(AcquireLock());
+
+ PartitionCowCreator cow_creator = {
+ .compression_enabled = IsCompressionEnabled(),
+ .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
+ };
+ SnapshotStatus status;
+ status.set_name("sys_a");
+ status.set_device_size(1_MiB);
+ status.set_snapshot_size(2_MiB);
+ status.set_cow_partition_size(2_MiB);
+
+ ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));
+ lock_ = nullptr;
+
+ ASSERT_TRUE(sm->EnsureImageManager());
+ ASSERT_TRUE(sm->image_manager()->CreateBackingImage("sys_a", 1_MiB, 0));
+ }
+
+ ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
+
+ // Simulate shutting down the device.
+ ASSERT_TRUE(UnmapAll());
+
+ // Simulate a reboot into recovery.
+ auto test_device = new TestDeviceInfo(fake_super, "_b");
+ test_device->set_recovery(true);
+ auto new_sm = NewManagerForFirstStageMount(test_device);
+
+ ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ // Manually mount metadata so that we can call GetUpdateState() below.
+ MountMetadata();
+ EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
+ ASSERT_FALSE(test_device->IsSlotUnbootable(1));
+ ASSERT_FALSE(test_device->IsSlotUnbootable(0));
+
+ // Now reboot into new slot.
+ test_device = new TestDeviceInfo(fake_super, "_b");
+ auto init = NewManagerForFirstStageMount(test_device);
+ ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+ // Verify that we are on the downgraded build.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name)) << name;
+ }
+}
+
TEST_F(SnapshotUpdateTest, Hashtree) {
constexpr auto partition_size = 4_MiB;
constexpr auto data_size = 3_MiB;
@@ -2247,9 +2309,27 @@
void TearDown() override;
private:
+ bool CreateFakeSuper();
+
std::unique_ptr<IImageManager> super_images_;
};
+bool SnapshotTestEnvironment::CreateFakeSuper() {
+ // Create and map the fake super partition.
+ static constexpr int kImageFlags =
+ IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
+ if (!super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
+ LOG(ERROR) << "Could not create fake super partition";
+ return false;
+ }
+ if (!super_images_->MapImageDevice("fake-super", 10s, &fake_super)) {
+ LOG(ERROR) << "Could not map fake super partition";
+ return false;
+ }
+ test_device->set_fake_super(fake_super);
+ return true;
+}
+
void SnapshotTestEnvironment::SetUp() {
// b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
// that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
@@ -2275,27 +2355,36 @@
sm = SnapshotManager::New(test_device);
ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
+ // Use a separate image manager for our fake super partition.
+ super_images_ = IImageManager::Open("ota/test/super", 10s);
+ ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
+
+ // Map the old image if one exists so we can safely unmap everything that
+ // depends on it.
+ bool recreate_fake_super;
+ if (super_images_->BackingImageExists("fake-super")) {
+ if (super_images_->IsImageMapped("fake-super")) {
+ ASSERT_TRUE(super_images_->GetMappedImageDevice("fake-super", &fake_super));
+ } else {
+ ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super));
+ }
+ test_device->set_fake_super(fake_super);
+ recreate_fake_super = true;
+ } else {
+ ASSERT_TRUE(CreateFakeSuper());
+ recreate_fake_super = false;
+ }
+
// Clean up previous run.
MetadataMountedTest().TearDown();
SnapshotUpdateTest().Cleanup();
SnapshotTest().Cleanup();
- // Use a separate image manager for our fake super partition.
- super_images_ = IImageManager::Open("ota/test/super", 10s);
- ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
-
- // Clean up any old copy.
- DeleteBackingImage(super_images_.get(), "fake-super");
-
- // Create and map the fake super partition.
- static constexpr int kImageFlags =
- IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
- ASSERT_TRUE(super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags))
- << "Could not create fake super partition";
-
- ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super))
- << "Could not map fake super partition";
- test_device->set_fake_super(fake_super);
+ if (recreate_fake_super) {
+ // Clean up any old copy.
+ DeleteBackingImage(super_images_.get(), "fake-super");
+ ASSERT_TRUE(CreateFakeSuper());
+ }
}
void SnapshotTestEnvironment::TearDown() {
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 8e379e4..080f3b7 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -90,7 +90,9 @@
}
const auto& cow_options = options();
- reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
+ if (cow_options.max_blocks) {
+ reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
+ }
return reader;
}
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
new file mode 100644
index 0000000..da48eb9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <libsnapshot/snapshot.h>
+
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/snapshot_writer.h>
+#include <payload_consumer/file_descriptor.h>
+
+namespace android::snapshot {
+class CompressedSnapshotWriterTest : public ::testing::Test {
+ public:
+ static constexpr size_t BLOCK_SIZE = 4096;
+};
+
+TEST_F(CompressedSnapshotWriterTest, ReadAfterWrite) {
+ TemporaryFile cow_device_file{};
+ android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
+ android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
+ snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd});
+ snapshot_writer.Initialize();
+ std::vector<unsigned char> buffer;
+ buffer.resize(BLOCK_SIZE);
+ std::fill(buffer.begin(), buffer.end(), 123);
+
+ ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size()));
+ ASSERT_TRUE(snapshot_writer.Finalize());
+ auto cow_reader = snapshot_writer.OpenReader();
+ ASSERT_NE(cow_reader, nullptr);
+ ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size()));
+ ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size()));
+ ASSERT_TRUE(snapshot_writer.Finalize());
+ // After wrigin some data, if we call OpenReader() again, writes should
+ // be visible to the newly opened reader. update_engine relies on this
+ // behavior for verity writes.
+ cow_reader = snapshot_writer.OpenReader();
+ ASSERT_NE(cow_reader, nullptr);
+ std::vector<unsigned char> read_back;
+ read_back.resize(buffer.size());
+ cow_reader->Seek(BLOCK_SIZE, SEEK_SET);
+ const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size());
+ ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE);
+ ASSERT_EQ(read_back, buffer);
+}
+
+} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 2ccc750..03c2ef6 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -90,7 +90,10 @@
}
bool Snapuserd::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer) {
- CHECK(lock->owns_lock());
+ if (!lock->owns_lock()) {
+ SNAP_LOG(ERROR) << "GetRABuffer - Lock not held";
+ return false;
+ }
std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
// This will be true only for IO's generated as part of reading a root
@@ -344,7 +347,10 @@
return false;
}
- CHECK(header.block_size == BLOCK_SZ);
+ if (!(header.block_size == BLOCK_SZ)) {
+ SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
+ return false;
+ }
reader_->InitializeMerge();
SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
@@ -437,6 +443,7 @@
int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
std::optional<chunk_t> prev_id = {};
std::map<uint64_t, const CowOperation*> map;
+ std::set<uint64_t> dest_blocks;
size_t pending_copy_ops = exceptions_per_area_ - num_ops;
uint64_t total_copy_ops = reader_->total_copy_ops();
@@ -555,10 +562,15 @@
if (diff != 1) {
break;
}
+
+ if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
+ break;
+ }
}
metadata_found = true;
pending_copy_ops -= 1;
map[cow_op->new_block] = cow_op;
+ dest_blocks.insert(cow_op->source);
prev_id = cow_op->new_block;
cowop_riter_->Next();
} while (!cowop_riter_->Done() && pending_copy_ops);
@@ -604,7 +616,11 @@
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
}
- CHECK(pending_copy_ops == 0);
+ if (!(pending_copy_ops == 0)) {
+ SNAP_LOG(ERROR)
+ << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
+ return false;
+ }
pending_copy_ops = exceptions_per_area_;
}
@@ -620,6 +636,7 @@
}
}
map.clear();
+ dest_blocks.clear();
prev_id.reset();
}
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd_readahead.cpp
index 09ee2f2..16d5919 100644
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_readahead.cpp
@@ -257,7 +257,12 @@
// Verify that we have covered all the ops which were re-constructed
// from COW device - These are the ops which are being
// re-constructed after crash.
- CHECK(num_ops == 0);
+ if (!(num_ops == 0)) {
+ SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
+ << " Pending ops: " << num_ops;
+ snapuserd_->ReadAheadIOFailed();
+ return false;
+ }
break;
}
}
@@ -370,8 +375,6 @@
bm->file_offset = 0;
buffer_offset += io_size;
- CHECK(offset == buffer_offset);
- CHECK((file_offset - snapuserd_->GetBufferDataOffset()) == offset);
}
snapuserd_->SetTotalRaBlocksMerged(total_blocks_merged);
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index ff8a259..8339690 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -191,7 +191,7 @@
}
case DaemonOperations::DETACH: {
terminating_ = true;
- return Sendmsg(fd, "success");
+ return true;
}
default: {
LOG(ERROR) << "Received unknown message type from client";
@@ -378,7 +378,10 @@
}
bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
- CHECK(!handler->snapuserd()->IsAttached());
+ if (handler->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Handler already attached";
+ return false;
+ }
handler->snapuserd()->AttachControlDevice();
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 9f42ab8..7e0f493 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -57,7 +57,9 @@
}
struct dm_user_header* BufferSink::GetHeaderPtr() {
- CHECK(sizeof(struct dm_user_header) <= buffer_size_);
+ if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
+ return nullptr;
+ }
char* buf = reinterpret_cast<char*>(GetBufPtr());
struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
return header;
@@ -111,7 +113,6 @@
// the header, zero out the remaining block.
void WorkerThread::ConstructKernelCowHeader() {
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- CHECK(buffer != nullptr);
memset(buffer, 0, BLOCK_SZ);
@@ -137,7 +138,10 @@
bool WorkerThread::ReadFromBaseDevice(const CowOperation* cow_op) {
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- CHECK(buffer != nullptr);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
+ return false;
+ }
SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
<< " Source: " << cow_op->source;
if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
@@ -152,7 +156,10 @@
bool WorkerThread::GetReadAheadPopulatedBuffer(const CowOperation* cow_op) {
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- CHECK(buffer != nullptr);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "GetReadAheadPopulatedBuffer: Failed to get payload buffer";
+ return false;
+ }
if (!snapuserd_->GetReadAheadPopulatedBuffer(cow_op->new_block, buffer)) {
return false;
@@ -178,14 +185,20 @@
bool WorkerThread::ProcessZeroOp() {
// Zero out the entire block
void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- CHECK(buffer != nullptr);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
+ return false;
+ }
memset(buffer, 0, BLOCK_SZ);
return true;
}
bool WorkerThread::ProcessCowOp(const CowOperation* cow_op) {
- CHECK(cow_op != nullptr);
+ if (cow_op == nullptr) {
+ SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
+ return false;
+ }
switch (cow_op->type) {
case kCowReplaceOp: {
@@ -216,7 +229,8 @@
<< " Aligned sector: " << it->first;
if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
+ SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
+ << " Aligned sector: " << it->first;
return -1;
}
@@ -261,7 +275,10 @@
it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
Snapuserd::compare);
- CHECK(it != chunk_vec.end());
+ if (!(it != chunk_vec.end())) {
+ SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
+ return -1;
+ }
// We didn't find the required sector; hence find the previous sector
// as lower_bound will gives us the value greater than
@@ -334,7 +351,10 @@
}
void* buffer = bufsink_.GetPayloadBuffer(size);
- CHECK(buffer != nullptr);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "ZerofillDiskExceptions: Failed to get payload buffer";
+ return false;
+ }
memset(buffer, 0, size);
return true;
@@ -364,10 +384,17 @@
if (divresult.quot < vec.size()) {
size = exceptions_per_area_ * sizeof(struct disk_exception);
- CHECK(read_size == size);
+ if (read_size != size) {
+ SNAP_LOG(ERROR) << "ReadDiskExceptions: read_size: " << read_size
+ << " does not match with size: " << size;
+ return false;
+ }
void* buffer = bufsink_.GetPayloadBuffer(size);
- CHECK(buffer != nullptr);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "ReadDiskExceptions: Failed to get payload buffer of size: " << size;
+ return false;
+ }
memcpy(buffer, vec[divresult.quot].get(), size);
} else {
@@ -390,8 +417,19 @@
// Unmerged op by the kernel
if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
- CHECK(merged_de->old_chunk == cow_de->old_chunk);
- CHECK(merged_de->new_chunk == cow_de->new_chunk);
+ if (!(merged_de->old_chunk == cow_de->old_chunk)) {
+ SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->old_chunk: "
+ << merged_de->old_chunk
+ << "cow_de->old_chunk: " << cow_de->old_chunk;
+ return -1;
+ }
+
+ if (!(merged_de->new_chunk == cow_de->new_chunk)) {
+ SNAP_LOG(ERROR) << "GetMergeStartOffset: merged_de->new_chunk: "
+ << merged_de->new_chunk
+ << "cow_de->new_chunk: " << cow_de->new_chunk;
+ return -1;
+ }
offset += sizeof(struct disk_exception);
*unmerged_exceptions += 1;
@@ -401,8 +439,6 @@
break;
}
- CHECK(!(*unmerged_exceptions == exceptions_per_area_));
-
SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
return offset;
}
@@ -421,8 +457,15 @@
struct disk_exception* cow_de =
reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
- CHECK(merged_de->new_chunk == 0);
- CHECK(merged_de->old_chunk == 0);
+ if (!(merged_de->new_chunk == 0)) {
+ SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid new-chunk: " << merged_de->new_chunk;
+ return -1;
+ }
+
+ if (!(merged_de->old_chunk == 0)) {
+ SNAP_LOG(ERROR) << "GetNumberOfMergedOps: Invalid old-chunk: " << merged_de->old_chunk;
+ return -1;
+ }
if (cow_de->new_chunk != 0) {
merged_ops_cur_iter += 1;
@@ -430,11 +473,18 @@
auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
std::make_pair(ChunkToSector(cow_de->new_chunk), nullptr),
Snapuserd::compare);
- CHECK(it != chunk_vec.end());
- CHECK(it->first == ChunkToSector(cow_de->new_chunk));
+
+ if (!(it != chunk_vec.end())) {
+ SNAP_LOG(ERROR) << "Sector not found: " << ChunkToSector(cow_de->new_chunk);
+ return -1;
+ }
+
+ if (!(it->first == ChunkToSector(cow_de->new_chunk))) {
+ SNAP_LOG(ERROR) << "Invalid sector: " << ChunkToSector(cow_de->new_chunk);
+ return -1;
+ }
const CowOperation* cow_op = it->second;
- CHECK(cow_op != nullptr);
if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
*copy_op = true;
// Every single copy operation has to come from read-ahead
@@ -453,7 +503,6 @@
}
}
- CHECK(cow_op->new_block == cow_de->old_chunk);
// zero out to indicate that operation is merged.
cow_de->old_chunk = 0;
cow_de->new_chunk = 0;
@@ -463,7 +512,6 @@
//
// If the op was merged in previous cycle, we don't have
// to count them.
- CHECK(cow_de->new_chunk == 0);
break;
} else {
SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata: "
@@ -488,18 +536,33 @@
// ChunkID to vector index
lldiv_t divresult = lldiv(chunk, stride);
- CHECK(divresult.quot < vec.size());
+
+ if (!(divresult.quot < vec.size())) {
+ SNAP_LOG(ERROR) << "ProcessMergeComplete: Invalid chunk: " << chunk
+ << " Metadata-Index: " << divresult.quot << " Area-size: " << vec.size();
+ return false;
+ }
+
SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
<< " Metadata-Index: " << divresult.quot;
int unmerged_exceptions = 0;
loff_t offset = GetMergeStartOffset(buffer, vec[divresult.quot].get(), &unmerged_exceptions);
+ if (offset < 0) {
+ SNAP_LOG(ERROR) << "GetMergeStartOffset failed: unmerged_exceptions: "
+ << unmerged_exceptions;
+ return false;
+ }
+
int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
unmerged_exceptions, ©_op, &commit);
// There should be at least one operation merged in this cycle
- CHECK(merged_ops_cur_iter > 0);
+ if (!(merged_ops_cur_iter > 0)) {
+ SNAP_LOG(ERROR) << "Merge operation failed: " << merged_ops_cur_iter;
+ return false;
+ }
if (copy_op) {
if (commit) {
@@ -570,8 +633,12 @@
// REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
// to flush per se; hence, just respond back with a success message.
if (header->sector == 0) {
- CHECK(header->len == 0);
- header->type = DM_USER_RESP_SUCCESS;
+ if (!(header->len == 0)) {
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ header->type = DM_USER_RESP_SUCCESS;
+ }
+
if (!WriteDmUserPayload(0)) {
return false;
}
@@ -581,33 +648,37 @@
std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
size_t remaining_size = header->len;
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
- CHECK(read_size == BLOCK_SZ) << "DmuserWriteRequest: read_size: " << read_size;
- CHECK(header->sector > 0);
chunk_t chunk = SectorToChunk(header->sector);
auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
std::make_pair(header->sector, nullptr), Snapuserd::compare);
bool not_found = (it == chunk_vec.end() || it->first != header->sector);
- CHECK(not_found);
- void* buffer = bufsink_.GetPayloadBuffer(read_size);
- CHECK(buffer != nullptr);
- header->type = DM_USER_RESP_SUCCESS;
+ if (not_found) {
+ void* buffer = bufsink_.GetPayloadBuffer(read_size);
+ if (buffer == nullptr) {
+ SNAP_LOG(ERROR) << "DmuserWriteRequest: Failed to get payload buffer of size: "
+ << read_size;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ header->type = DM_USER_RESP_SUCCESS;
- if (!ReadDmUserPayload(buffer, read_size)) {
- SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
+ if (!ReadDmUserPayload(buffer, read_size)) {
+ SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ }
- if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
+ if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
+ SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ }
+ }
} else {
- SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
- << "Sector: " << header->sector;
+ SNAP_LOG(ERROR) << "DmuserWriteRequest: Invalid sector received: header->sector";
+ header->type = DM_USER_RESP_ERROR;
}
if (!WriteDmUserPayload(0)) {
@@ -636,7 +707,6 @@
// never see multiple IO requests. Additionally this IO
// will always be a single 4k.
if (header->sector == 0) {
- CHECK(read_size == BLOCK_SZ) << " Sector 0 read request of size: " << read_size;
ConstructKernelCowHeader();
SNAP_LOG(DEBUG) << "Kernel header constructed";
} else {
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 83174ef..eb2919b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -325,14 +325,14 @@
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
-source / ext4 ro,barrier=1 wait,slotselect,avb
+source / ext4 ro,barrier=1 wait,avb
source /metadata ext4 noatime,nosuid,nodev,discard wait,formattable
source /data f2fs noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M
source /misc emmc defaults defaults
-source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect
+source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait
source auto vfat defaults voldmanaged=usb:auto
source none swap defaults zramsize=1073741824,max_comp_streams=8
@@ -412,8 +412,8 @@
ASSERT_TRUE(tf.fd != -1);
std::string fstab_contents = R"fs(
source none0 swap defaults wait,check,nonremovable,recoveryonly,verifyatboot,verify
-source none1 swap defaults avb,noemulatedsd,notrim,formattable,slotselect,nofail
-source none2 swap defaults first_stage_mount,latemount,quota,logical,slotselect_other
+source none1 swap defaults avb,noemulatedsd,notrim,formattable,nofail
+source none2 swap defaults first_stage_mount,latemount,quota,logical
source none3 swap defaults checkpoint=block
source none4 swap defaults checkpoint=fs
source none5 swap defaults defaults
@@ -445,7 +445,6 @@
flags.no_emulated_sd = true;
flags.no_trim = true;
flags.formattable = true;
- flags.slot_select = true;
flags.no_fail = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
@@ -458,7 +457,6 @@
flags.late_mount = true;
flags.quota = true;
flags.logical = true;
- flags.slot_select_other = true;
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
}
diff --git a/init/README.md b/init/README.md
index 4a262c9..75dc328 100644
--- a/init/README.md
+++ b/init/README.md
@@ -277,6 +277,8 @@
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
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 3ffca88..76f5193 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -123,13 +123,20 @@
The exact firmware file to be served can be customized by running an external program by a
`external_firmware_handler` line in a ueventd.rc file. This line takes the format of
- external_firmware_handler <devpath> <user name to run as> <path to external program>
+ external_firmware_handler <devpath> <user [group]> <path to external program>
+
+The handler will be run as the given user, or if a group is provided, as the given user and group.
+
For example
external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
Will launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware
for `/devices/leds/red/firmware/coeffs.bin`.
+The `devpath` argument may include asterisks (`*`) to match multiple paths. For example, the string
+`/dev/*/red` will match `/dev/leds/red` as well as `/dev/lights/red`. The pattern matching follows
+the rules of the fnmatch() function.
+
Ueventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment
via environment variables with the same names. Ueventd will use the string written to stdout as the
new name of the firmware to load. It will still look for the new firmware in the list of firmware
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index bdc2922..30e808d 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <fnmatch.h>
#include <glob.h>
+#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@@ -81,9 +82,9 @@
return access("/dev/.booting", F_OK) == 0;
}
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
std::string handler_path)
- : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
+ : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
auto wildcard_position = this->devpath.find('*');
if (wildcard_position != std::string::npos) {
if (wildcard_position == this->devpath.length() - 1) {
@@ -97,13 +98,17 @@
}
}
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+ std::string handler_path)
+ : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
+
FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
std::vector<ExternalFirmwareHandler> external_firmware_handlers)
: firmware_directories_(std::move(firmware_directories)),
external_firmware_handlers_(std::move(external_firmware_handlers)) {}
Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
- const Uevent& uevent) const {
+ gid_t gid, const Uevent& uevent) const {
unique_fd child_stdout;
unique_fd parent_stdout;
if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
@@ -140,6 +145,13 @@
}
c_args.emplace_back(nullptr);
+ if (gid != 0) {
+ if (setgid(gid) != 0) {
+ fprintf(stderr, "setgid() failed: %s", strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+ }
+
if (setuid(uid) != 0) {
fprintf(stderr, "setuid() failed: %s", strerror(errno));
_exit(EXIT_FAILURE);
@@ -196,8 +208,8 @@
<< "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
<< "'";
- auto result =
- RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
+ auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
+ external_handler.gid, uevent);
if (!result.ok()) {
LOG(ERROR) << "Using default firmware; External firmware handler failed: "
<< result.error();
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3c35b1f..d2f7347 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -16,6 +16,7 @@
#pragma once
+#include <grp.h>
#include <pwd.h>
#include <functional>
@@ -31,9 +32,11 @@
struct ExternalFirmwareHandler {
ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
+ ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);
std::string devpath;
uid_t uid;
+ gid_t gid;
std::string handler_path;
std::function<bool(const std::string&)> match;
@@ -51,7 +54,7 @@
friend void FirmwareTestWithExternalHandler(const std::string& test_name,
bool expect_new_firmware);
- Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
+ Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
index 5124a6f..f6e75b0 100644
--- a/init/firmware_handler_test.cpp
+++ b/init/firmware_handler_test.cpp
@@ -103,6 +103,23 @@
return 0;
}
+TEST(firmware_handler, Matching) {
+ ExternalFirmwareHandler h("/dev/path/a.bin", getuid(), "/test");
+ ASSERT_TRUE(h.match("/dev/path/a.bin"));
+ ASSERT_FALSE(h.match("/dev/path/a.bi"));
+
+ h = ExternalFirmwareHandler("/dev/path/a.*", getuid(), "/test");
+ ASSERT_TRUE(h.match("/dev/path/a.bin"));
+ ASSERT_TRUE(h.match("/dev/path/a.bix"));
+ ASSERT_FALSE(h.match("/dev/path/b.bin"));
+
+ h = ExternalFirmwareHandler("/dev/*/a.bin", getuid(), "/test");
+ ASSERT_TRUE(h.match("/dev/path/a.bin"));
+ ASSERT_TRUE(h.match("/dev/other/a.bin"));
+ ASSERT_FALSE(h.match("/dev/other/c.bin"));
+ ASSERT_FALSE(h.match("/dev/path/b.bin"));
+}
+
} // namespace init
} // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 84cda98..78e5b60 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -329,23 +329,10 @@
// If "/force_debuggable" is present, the second-stage init will use a userdebug
// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
- bool found_debuggable = false;
- std::string adb_debug_prop("/adb_debug.prop");
- std::string userdebug_sepolicy("/userdebug_plat_sepolicy.cil");
if (access("/force_debuggable", F_OK) == 0) {
- found_debuggable = true;
- } else if (access("/first_stage_ramdisk/force_debuggable", F_OK) == 0) {
- // Fallback to legacy debug resource paths.
- // TODO(b/186485355): removes the fallback path once it is not needed.
- found_debuggable = true;
- adb_debug_prop.insert(0, "/first_stage_ramdisk");
- userdebug_sepolicy.insert(0, "/first_stage_ramdisk");
- }
-
- if (found_debuggable) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
- if (!fs::copy_file(adb_debug_prop, kDebugRamdiskProp, ec) ||
- !fs::copy_file(userdebug_sepolicy, kDebugRamdiskSEPolicy, ec)) {
+ if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
+ !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
LOG(ERROR) << "Failed to setup debug ramdisk";
} else {
// setenv for second-stage init to read above kDebugRamdisk* files.
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3faf430..a733839 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -331,6 +331,12 @@
if (devices.empty()) {
return true;
}
+ // excluding overlays
+ for (auto iter = devices.begin(); iter != devices.end(); ) {
+ if (*iter=="overlay") iter = devices.erase(iter);
+ else iter++;
+ }
+
return block_dev_init_.InitDevices(std::move(devices));
}
@@ -542,6 +548,11 @@
continue;
}
+ if (current->fs_type == "overlay") {
+ ++current;
+ continue;
+ }
+
// Skip raw partition entries such as boot, dtbo, etc.
// Having emmc fstab entries allows us to probe current->vbmeta_partition
// in InitDevices() when they are AVB chained partitions.
@@ -591,6 +602,13 @@
};
MapScratchPartitionIfNeeded(&fstab_, init_devices);
+ for (auto current = fstab_.begin(); current != fstab_.end(); ) {
+ if (current->fs_type == "overlay") {
+ fs_mgr_overlayfs_mount_fstab_entry(current->lowerdir, current->mount_point);
+ }
+ ++current;
+ }
+
fs_mgr_overlayfs_mount_all(&fstab_);
return true;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index fe3490d..ff9da42 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -94,6 +94,12 @@
namespace android {
namespace init {
+constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint";
+constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint";
+constexpr auto ID_PROP = "ro.build.id";
+constexpr auto LEGACY_ID_PROP = "ro.build.legacy.id";
+constexpr auto VBMETA_DIGEST_PROP = "ro.boot.vbmeta.digest";
+constexpr auto DIGEST_SIZE_USED = 8;
static bool persistent_properties_loaded = false;
@@ -857,15 +863,33 @@
}
}
-// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
-static void property_derive_build_fingerprint() {
- std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
- if (!build_fingerprint.empty()) {
+static void property_initialize_build_id() {
+ std::string build_id = GetProperty(ID_PROP, "");
+ if (!build_id.empty()) {
return;
}
+ std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, "");
+ std::string vbmeta_digest = GetProperty(VBMETA_DIGEST_PROP, "");
+ if (vbmeta_digest.size() < DIGEST_SIZE_USED) {
+ LOG(ERROR) << "vbmeta digest size too small " << vbmeta_digest;
+ // Still try to set the id field in the unexpected case.
+ build_id = legacy_build_id;
+ } else {
+ // Derive the ro.build.id by appending the vbmeta digest to the base value.
+ build_id = legacy_build_id + "." + vbmeta_digest.substr(0, DIGEST_SIZE_USED);
+ }
+
+ std::string error;
+ auto res = PropertySet(ID_PROP, build_id, &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id;
+ }
+}
+
+static std::string ConstructBuildFingerprint(bool legacy) {
const std::string UNKNOWN = "unknown";
- build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
+ std::string build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
build_fingerprint += '/';
@@ -873,7 +897,10 @@
build_fingerprint += ':';
build_fingerprint += GetProperty("ro.build.version.release_or_codename", UNKNOWN);
build_fingerprint += '/';
- build_fingerprint += GetProperty("ro.build.id", UNKNOWN);
+
+ std::string build_id =
+ legacy ? GetProperty(LEGACY_ID_PROP, UNKNOWN) : GetProperty(ID_PROP, UNKNOWN);
+ build_fingerprint += build_id;
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.version.incremental", UNKNOWN);
build_fingerprint += ':';
@@ -881,13 +908,49 @@
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.tags", UNKNOWN);
- LOG(INFO) << "Setting property 'ro.build.fingerprint' to '" << build_fingerprint << "'";
+ return build_fingerprint;
+}
+
+// Derive the legacy build fingerprint if we overwrite the build id at runtime.
+static void property_derive_legacy_build_fingerprint() {
+ std::string legacy_build_fingerprint = GetProperty(LEGACY_FINGERPRINT_PROP, "");
+ if (!legacy_build_fingerprint.empty()) {
+ return;
+ }
+
+ // The device doesn't have a legacy build id, skipping the legacy fingerprint.
+ std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, "");
+ if (legacy_build_id.empty()) {
+ return;
+ }
+
+ legacy_build_fingerprint = ConstructBuildFingerprint(true /* legacy fingerprint */);
+ LOG(INFO) << "Setting property '" << LEGACY_FINGERPRINT_PROP << "' to '"
+ << legacy_build_fingerprint << "'";
std::string error;
- uint32_t res = PropertySet("ro.build.fingerprint", build_fingerprint, &error);
+ uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);
if (res != PROP_SUCCESS) {
- LOG(ERROR) << "Error setting property 'ro.build.fingerprint': err=" << res << " (" << error
- << ")";
+ LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res
+ << " (" << error << ")";
+ }
+}
+
+// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
+static void property_derive_build_fingerprint() {
+ std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
+ if (!build_fingerprint.empty()) {
+ return;
+ }
+
+ build_fingerprint = ConstructBuildFingerprint(false /* legacy fingerprint */);
+ LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'";
+
+ std::string error;
+ uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " ("
+ << error << ")";
}
}
@@ -1035,7 +1098,9 @@
}
property_initialize_ro_product_props();
+ property_initialize_build_id();
property_derive_build_fingerprint();
+ property_derive_legacy_build_fingerprint();
property_initialize_ro_cpu_abilist();
update_sys_usb_config();
@@ -1165,55 +1230,10 @@
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
-// emulator specific, should be removed once emulator is migrated to
-// bootconfig, see b/182291166.
-static std::string RemapEmulatorPropertyName(const std::string_view qemu_key) {
- if (StartsWith(qemu_key, "dalvik."sv) || StartsWith(qemu_key, "opengles."sv) ||
- StartsWith(qemu_key, "config."sv)) {
- return std::string(qemu_key);
- } else if (qemu_key == "uirenderer"sv) {
- return "debug.hwui.renderer"s;
- } else if (qemu_key == "media.ccodec"sv) {
- return "debug.stagefright.ccodec"s;
- } else {
- return "qemu."s + std::string(qemu_key);
- }
-}
-
static void ProcessKernelCmdline() {
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
- constexpr auto qemu_prefix = "qemu."sv;
-
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
- } else if (StartsWith(key, qemu_prefix)) {
- // emulator specific, should be retired once emulator migrates to
- // androidboot.
- const auto new_name =
- RemapEmulatorPropertyName(std::string_view(key).substr(qemu_prefix.size()));
- if (!new_name.empty()) {
- InitPropertySet("ro.boot." + new_name, value);
- }
- } else if (key == "qemu") {
- // emulator specific, should be retired once emulator migrates to
- // androidboot.
- InitPropertySet("ro.boot." + key, value);
- } else if (key == "android.bootanim" && value == "0") {
- // emulator specific, should be retired once emulator migrates to
- // androidboot.
- InitPropertySet("ro.boot.debug.sf.nobootanimation", "1");
- } else if (key == "android.checkjni") {
- // emulator specific, should be retired once emulator migrates to
- // androidboot.
- std::string value_bool;
- if (value == "0") {
- value_bool = "false";
- } else if (value == "1") {
- value_bool = "true";
- } else {
- value_bool = value;
- }
- InitPropertySet("ro.boot.dalvik.vm.checkjni", value_bool);
}
});
}
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index c6dcfa2..ac6b7b2 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -23,6 +23,7 @@
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
using android::base::GetProperty;
@@ -90,5 +91,39 @@
EXPECT_FALSE(SetProperty("sys.powerctl", "reboot,userspace"));
}
+TEST(property_service, check_fingerprint_with_legacy_build_id) {
+ std::string legacy_build_id = GetProperty("ro.build.legacy.id", "");
+ if (legacy_build_id.empty()) {
+ GTEST_SKIP() << "Skipping test, legacy build id isn't set.";
+ }
+
+ std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
+ ASSERT_GE(vbmeta_digest.size(), 8u);
+ std::string build_id = GetProperty("ro.boot.build.id", "");
+ // Check that the build id is constructed with the prefix of vbmeta digest
+ std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
+ ASSERT_EQ(expected_build_id, build_id);
+ // Check that the fingerprint is constructed with the expected format.
+ std::string fingerprint = GetProperty("ro.build.fingerprint", "");
+ std::vector<std::string> fingerprint_fields = {
+ GetProperty("ro.product.brand", ""),
+ "/",
+ GetProperty("ro.product.name", ""),
+ "/",
+ GetProperty("ro.product.device", ""),
+ ":",
+ GetProperty("ro.build.version.release_or_codename", ""),
+ "/",
+ expected_build_id,
+ "/",
+ GetProperty("ro.build.version.incremental", ""),
+ ":",
+ GetProperty("ro.build.type", ""),
+ "/",
+ GetProperty("ro.build.tags", "")};
+
+ ASSERT_EQ(android::base::Join(fingerprint_fields, ""), fingerprint);
+}
+
} // namespace init
} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index c3069f5..5af81bf 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -194,6 +194,8 @@
<< ") process group...";
int max_processes = 0;
int r;
+
+ flags_ |= SVC_STOPPING;
if (signal == SIGTERM) {
r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
} else {
@@ -277,7 +279,8 @@
f(siginfo);
}
- if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
+ if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
+ !(flags_ & SVC_STOPPING)) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
trigger_shutdown(*on_failure_reboot_target_);
}
@@ -287,7 +290,7 @@
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
- flags_ &= (~SVC_RUNNING);
+ flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
@@ -411,7 +414,8 @@
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));
+ flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
+ SVC_STOPPING));
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
diff --git a/init/service.h b/init/service.h
index 043555f..89b1f09 100644
--- a/init/service.h
+++ b/init/service.h
@@ -54,6 +54,7 @@
// 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
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 2221228..9a14406 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -101,8 +101,8 @@
Result<void> ParseExternalFirmwareHandlerLine(
std::vector<std::string>&& args,
std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
- if (args.size() != 4) {
- return Error() << "external_firmware_handler lines must have exactly 3 parameters";
+ if (args.size() != 4 && args.size() != 5) {
+ return Error() << "external_firmware_handler lines must have 3 or 4 parameters";
}
if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
@@ -117,7 +117,19 @@
return ErrnoError() << "invalid handler uid'" << args[2] << "'";
}
- ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
+ gid_t gid = 0;
+ int handler_index = 3;
+ if (args.size() == 5) {
+ struct group* grp = getgrnam(args[3].c_str());
+ if (!grp) {
+ return ErrnoError() << "invalid handler gid '" << args[3] << "'";
+ }
+ gid = grp->gr_gid;
+ handler_index = 4;
+ }
+
+ ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,
+ std::move(args[handler_index]));
external_firmware_handlers->emplace_back(std::move(handler));
return {};
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index c5aa9e3..d77cb03 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -49,6 +49,7 @@
const ExternalFirmwareHandler& test) {
EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
EXPECT_EQ(expected.uid, test.uid) << expected.uid;
+ EXPECT_EQ(expected.gid, test.gid) << expected.gid;
EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
}
@@ -157,39 +158,59 @@
external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
+external_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh
+external_firmware_handler /devices/path/firmware/something004.bin radio radio "/vendor/bin/firmware_handler.sh --has --arguments"
)";
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
{
"devpath",
AID_ROOT,
+ AID_ROOT,
"handler_path",
},
{
"/devices/path/firmware/something001.bin",
AID_SYSTEM,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something002.bin",
AID_RADIO,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh --has --arguments",
},
{
"/devices/path/firmware/",
AID_ROOT,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something",
AID_SYSTEM,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/*/firmware/something*.bin",
AID_RADIO,
+ AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
+ {
+ "/devices/path/firmware/something003.bin",
+ AID_SYSTEM,
+ AID_SYSTEM,
+ "/vendor/bin/firmware_handler.sh",
+ },
+ {
+ "/devices/path/firmware/something004.bin",
+ AID_RADIO,
+ AID_RADIO,
+ "/vendor/bin/firmware_handler.sh --has --arguments",
+ },
};
TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
@@ -205,6 +226,7 @@
{
"devpath",
AID_ROOT,
+ AID_ROOT,
"handler_path",
},
};
@@ -305,7 +327,7 @@
};
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
- {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
+ {"/devices/path/firmware/firmware001.bin", AID_ROOT, AID_ROOT, "/vendor/bin/touch.sh"},
};
size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index db00a49..cf74e65 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -308,9 +308,7 @@
bool ApplyProfileAction::ExecuteForTask(int tid) const {
for (const auto& profile : profiles_) {
- if (!profile->ExecuteForTask(tid)) {
- PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
- }
+ profile->ExecuteForTask(tid);
}
return true;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 6201569..13e4c02 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -141,6 +141,7 @@
"Errors.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
+ "LightRefBase.cpp",
"NativeHandle.cpp",
"Printer.cpp",
"RefBase.cpp",
@@ -274,12 +275,6 @@
}
cc_fuzz {
- name: "libutils_fuzz_stopwatch",
- defaults: ["libutils_fuzz_defaults"],
- srcs: ["StopWatch_fuzz.cpp"],
-}
-
-cc_fuzz {
name: "libutils_fuzz_refbase",
defaults: ["libutils_fuzz_defaults"],
srcs: ["RefBase_fuzz.cpp"],
diff --git a/libutils/LightRefBase.cpp b/libutils/LightRefBase.cpp
new file mode 100644
index 0000000..e08ffec
--- /dev/null
+++ b/libutils/LightRefBase.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LightRefBase"
+
+#include <utils/LightRefBase.h>
+
+#include <log/log.h>
+
+namespace android {
+
+void LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz) {
+ LOG_ALWAYS_FATAL("incStrongRequireStrong() called on %p which isn't already owned", thiz);
+}
+
+} // namespace android
diff --git a/libutils/README b/libutils/README
deleted file mode 100644
index 01741e0..0000000
--- a/libutils/README
+++ /dev/null
@@ -1,289 +0,0 @@
-Android Utility Function Library
-================================
-
-
-If you need a feature that is native to Linux but not present on other
-platforms, construct a platform-dependent implementation that shares
-the Linux interface. That way the actual device runs as "light" as
-possible.
-
-If that isn't feasible, create a system-independent interface and hide
-the details.
-
-The ultimate goal is *not* to create a super-duper platform abstraction
-layer. The goal is to provide an optimized solution for Linux with
-reasonable implementations for other platforms.
-
-
-
-Resource overlay
-================
-
-
-Introduction
-------------
-
-Overlay packages are special .apk files which provide no code but
-additional resource values (and possibly new configurations) for
-resources in other packages. When an application requests resources,
-the system will return values from either the application's original
-package or any associated overlay package. Any redirection is completely
-transparent to the calling application.
-
-Resource values have the following precedence table, listed in
-descending precedence.
-
- * overlay package, matching config (eg res/values-en-land)
-
- * original package, matching config
-
- * overlay package, no config (eg res/values)
-
- * original package, no config
-
-During compilation, overlay packages are differentiated from regular
-packages by passing the -o flag to aapt.
-
-
-Background
-----------
-
-This section provides generic background material on resources in
-Android.
-
-
-How resources are bundled in .apk files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Android .apk files are .zip files, usually housing .dex code,
-certificates and resources, though packages containing resources but
-no code are possible. Resources can be divided into the following
-categories; a `configuration' indicates a set of phone language, display
-density, network operator, etc.
-
- * assets: uncompressed, raw files packaged as part of an .apk and
- explicitly referenced by filename. These files are
- independent of configuration.
-
- * res/drawable: bitmap or xml graphics. Each file may have different
- values depending on configuration.
-
- * res/values: integers, strings, etc. Each resource may have different
- values depending on configuration.
-
-Resource meta information and information proper is stored in a binary
-format in a named file resources.arsc, bundled as part of the .apk.
-
-Resource IDs and lookup
-~~~~~~~~~~~~~~~~~~~~~~~
-During compilation, the aapt tool gathers application resources and
-generates a resources.arsc file. Each resource name is assigned an
-integer ID 0xppttiii (translated to a symbolic name via R.java), where
-
- * pp: corresponds to the package namespace (details below).
-
- * tt: corresponds to the resource type (string, int, etc). Every
- resource of the same type within the same package has the same
- tt value, but depending on available types, the actual numerical
- value may be different between packages.
-
- * iiii: sequential number, assigned in the order resources are found.
-
-Resource values are specified paired with a set of configuration
-constraints (the default being the empty set), eg res/values-sv-port
-which imposes restrictions on language (Swedish) and display orientation
-(portrait). During lookup, every constraint set is matched against the
-current configuration, and the value corresponding to the best matching
-constraint set is returned (ResourceTypes.{h,cpp}).
-
-Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility
-is governed by AssetManager.cpp, which tracks loaded resources per
-process.
-
-Assets are looked up by path and filename in AssetManager.cpp. The path
-to resources in res/drawable are located by ResourceTypes.cpp and then
-handled like assets by AssetManager.cpp. Other resources are handled
-solely by ResourceTypes.cpp.
-
-Package ID as namespace
-~~~~~~~~~~~~~~~~~~~~~~~
-The pp part of a resource ID defines a namespace. Android currently
-defines two namespaces:
-
- * 0x01: system resources (pre-installed in framework-res.apk)
-
- * 0x7f: application resources (bundled in the application .apk)
-
-ResourceTypes.cpp supports package IDs between 0x01 and 0x7f
-(inclusive); values outside this range are invalid.
-
-Each running (Dalvik) process is assigned a unique instance of
-AssetManager, which in turn keeps a forest structure of loaded
-resource.arsc files. Normally, this forest is structured as follows,
-where mPackageMap is the internal vector employed in ResourceTypes.cpp.
-
-mPackageMap[0x00] -> system package
-mPackageMap[0x01] -> NULL
-mPackageMap[0x02] -> NULL
-...
-mPackageMap[0x7f - 2] -> NULL
-mPackageMap[0x7f - 1] -> application package
-
-
-
-The resource overlay extension
-------------------------------
-
-The resource overlay mechanism aims to (partly) shadow and extend
-existing resources with new values for defined and new configurations.
-Technically, this is achieved by adding resource-only packages (called
-overlay packages) to existing resource namespaces, like so:
-
-mPackageMap[0x00] -> system package -> system overlay package
-mPackageMap[0x01] -> NULL
-mPackageMap[0x02] -> NULL
-...
-mPackageMap[0x7f - 2] -> NULL
-mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2
-
-The use of overlay resources is completely transparent to
-applications; no additional resource identifiers are introduced, only
-configuration/value pairs. Any number of overlay packages may be loaded
-at a time; overlay packages are agnostic to what they target -- both
-system and application resources are fair game.
-
-The package targeted by an overlay package is called the target or
-original package.
-
-Resource overlay operates on symbolic resources names. Hence, to
-override the string/str1 resources in a package, the overlay package
-would include a resource also named string/str1. The end user does not
-have to worry about the numeric resources IDs assigned by aapt, as this
-is resolved automatically by the system.
-
-As of this writing, the use of resource overlay has not been fully
-explored. Until it has, only OEMs are trusted to use resource overlay.
-For this reason, overlay packages must reside in /system/overlay.
-
-
-Resource ID mapping
-~~~~~~~~~~~~~~~~~~~
-Resource identifiers must be coherent within the same namespace (ie
-PackageGroup in ResourceTypes.cpp). Calling applications will refer to
-resources using the IDs defined in the original package, but there is no
-guarantee aapt has assigned the same ID to the corresponding resource in
-an overlay package. To translate between the two, a resource ID mapping
-{original ID -> overlay ID} is created during package installation
-(PackageManagerService.java) and used during resource lookup. The
-mapping is stored in /data/resource-cache, with a @idmap file name
-suffix.
-
-The idmap file format is documented in a separate section, below.
-
-
-Package management
-~~~~~~~~~~~~~~~~~~
-Packages are managed by the PackageManagerService. Addition and removal
-of packages are monitored via the inotify framework, exposed via
-android.os.FileObserver.
-
-During initialization of a Dalvik process, ActivityThread.java requests
-the process' AssetManager (by proxy, via AssetManager.java and JNI)
-to load a list of packages. This list includes overlay packages, if
-present.
-
-When a target package or a corresponding overlay package is installed,
-the target package's process is stopped and a new idmap is generated.
-This is similar to how applications are stopped when their packages are
-upgraded.
-
-
-Creating overlay packages
--------------------------
-
-Overlay packages should contain no code, define (some) resources with
-the same type and name as in the original package, and be compiled with
-the -o flag passed to aapt.
-
-The aapt -o flag instructs aapt to create an overlay package.
-Technically, this means the package will be assigned package id 0x00.
-
-There are no restrictions on overlay packages names, though the naming
-convention <original.package.name>.overlay.<name> is recommended.
-
-
-Example overlay package
-~~~~~~~~~~~~~~~~~~~~~~~
-
-To overlay the resource bool/b in package com.foo.bar, to be applied
-when the display is in landscape mode, create a new package with
-no source code and a single .xml file under res/values-land, with
-an entry for bool/b. Compile with aapt -o and place the results in
-/system/overlay by adding the following to Android.mk:
-
-LOCAL_AAPT_FLAGS := -o com.foo.bar
-LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay
-
-
-The ID map (idmap) file format
-------------------------------
-
-The idmap format is designed for lookup performance. However, leading
-and trailing undefined overlay values are discarded to reduce the memory
-footprint.
-
-
-idmap grammar
-~~~~~~~~~~~~~
-All atoms (names in square brackets) are uint32_t integers. The
-idmap-magic constant spells "idmp" in ASCII. Offsets are given relative
-to the data_header, not to the beginning of the file.
-
-map := header data
-header := idmap-magic <crc32-original-pkg> <crc32-overlay-pkg>
-idmap-magic := <0x706d6469>
-data := data_header type_block+
-data_header := <m> header_block{m}
-header_block := <0> | <type_block_offset>
-type_block := <n> <id_offset> entry{n}
-entry := <resource_id_in_target_package>
-
-
-idmap example
-~~~~~~~~~~~~~
-Given a pair of target and overlay packages with CRC sums 0x216a8fe2
-and 0x6b9beaec, each defining the following resources
-
-Name Target package Overlay package
-string/str0 0x7f010000 -
-string/str1 0x7f010001 0x7f010000
-string/str2 0x7f010002 -
-string/str3 0x7f010003 0x7f010001
-string/str4 0x7f010004 -
-bool/bool0 0x7f020000 -
-integer/int0 0x7f030000 0x7f020000
-integer/int1 0x7f030001 -
-
-the corresponding resource map is
-
-0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \
-0x00000004 0x00000000 0x00000009 0x00000003 \
-0x00000001 0x7f010000 0x00000000 0x7f010001 \
-0x00000001 0x00000000 0x7f020000
-
-or, formatted differently
-
-0x706d6469 # magic: all idmap files begin with this constant
-0x216a8fe2 # CRC32 of the resources.arsc file in the original package
-0x6b9beaec # CRC32 of the resources.arsc file in the overlay package
-0x00000003 # header; three types (string, bool, integer) in the target package
-0x00000004 # header_block for type 0 (string) is located at offset 4
-0x00000000 # no bool type exists in overlay package -> no header_block
-0x00000009 # header_block for type 2 (integer) is located at offset 9
-0x00000003 # header_block for string; overlay IDs span 3 elements
-0x00000001 # the first string in target package is entry 1 == offset
-0x7f010000 # target 0x7f01001 -> overlay 0x7f010000
-0x00000000 # str2 not defined in overlay package
-0x7f010001 # target 0x7f010003 -> overlay 0x7f010001
-0x00000001 # header_block for integer; overlay IDs span 1 element
-0x00000000 # offset == 0
-0x7f020000 # target 0x7f030000 -> overlay 0x7f020000
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index d01865e..28e2d76 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -26,58 +26,26 @@
#include <utils/Log.h>
-/*****************************************************************************/
-
namespace android {
StopWatch::StopWatch(const char* name, int clock) : mName(name), mClock(clock) {
reset();
}
-StopWatch::~StopWatch()
-{
- nsecs_t elapsed = elapsedTime();
- const int n = mNumLaps;
- ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed));
- for (int i=0 ; i<n ; i++) {
- const nsecs_t soFar = mLaps[i].soFar;
- const nsecs_t thisLap = mLaps[i].thisLap;
- ALOGD(" [%d: %" PRId64 ", %" PRId64, i, ns2us(soFar), ns2us(thisLap));
- }
+StopWatch::~StopWatch() {
+ ALOGD("StopWatch %s (us): %" PRId64 " ", name(), ns2us(elapsedTime()));
}
-const char* StopWatch::name() const
-{
+const char* StopWatch::name() const {
return mName;
}
-nsecs_t StopWatch::lap()
-{
- nsecs_t elapsed = elapsedTime();
- if (mNumLaps >= 8) {
- elapsed = 0;
- } else {
- const int n = mNumLaps;
- mLaps[n].soFar = elapsed;
- mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed;
- mNumLaps = n+1;
- }
- return elapsed;
-}
-
-nsecs_t StopWatch::elapsedTime() const
-{
+nsecs_t StopWatch::elapsedTime() const {
return systemTime(mClock) - mStartTime;
}
-void StopWatch::reset()
-{
- mNumLaps = 0;
+void StopWatch::reset() {
mStartTime = systemTime(mClock);
}
-
-/*****************************************************************************/
-
-}; // namespace android
-
+} // namespace android
diff --git a/libutils/StopWatch_fuzz.cpp b/libutils/StopWatch_fuzz.cpp
deleted file mode 100644
index 63d8a28..0000000
--- a/libutils/StopWatch_fuzz.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2020 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 "fuzzer/FuzzedDataProvider.h"
-#include "utils/StopWatch.h"
-
-static constexpr int MAX_OPERATIONS = 100;
-static constexpr int MAX_NAME_LEN = 2048;
-
-static const std::vector<std::function<void(android::StopWatch)>> operations = {
- [](android::StopWatch stopWatch) -> void { stopWatch.reset(); },
- [](android::StopWatch stopWatch) -> void { stopWatch.lap(); },
- [](android::StopWatch stopWatch) -> void { stopWatch.elapsedTime(); },
- [](android::StopWatch stopWatch) -> void { stopWatch.name(); },
-};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FuzzedDataProvider dataProvider(data, size);
- std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
- int clockVal = dataProvider.ConsumeIntegral<int>();
- android::StopWatch stopWatch = android::StopWatch(nameStr.c_str(), clockVal);
- std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
- int opsRun = 0;
- for (auto it : opsToRun) {
- if (opsRun++ >= MAX_OPERATIONS) {
- break;
- }
- it = it % operations.size();
- operations[it](stopWatch);
- }
- return 0;
-}
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index e3e5f11..faf90c2 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -411,36 +411,4 @@
return OK;
}
-status_t String16::remove(size_t len, size_t begin)
-{
- const size_t N = size();
- if (begin >= N) {
- release();
- mString = getEmptyString();
- return OK;
- }
- if (len > N || len > N - begin) len = N - begin;
- if (begin == 0 && len == N) {
- return OK;
- }
-
- if (begin > 0) {
- SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
- if (!buf) {
- return NO_MEMORY;
- }
- char16_t* str = (char16_t*)buf->data();
- memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
- mString = str;
- }
- SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- str[len] = 0;
- mString = str;
- return OK;
- }
- return NO_MEMORY;
-}
-
}; // namespace android
diff --git a/libutils/String16_fuzz.cpp b/libutils/String16_fuzz.cpp
index defa0f5..d7e5ec7 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/String16_fuzz.cpp
@@ -72,12 +72,6 @@
char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();
str1.replaceAll(findChar, replaceChar);
}),
- ([](FuzzedDataProvider& dataProvider, android::String16 str1,
- android::String16) -> void {
- size_t len = dataProvider.ConsumeIntegral<size_t>();
- size_t begin = dataProvider.ConsumeIntegral<size_t>();
- str1.remove(len, begin);
- }),
};
void callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,
@@ -111,7 +105,5 @@
callFunc(op, dataProvider, str_one_utf16, str_two_utf16);
}
- str_one_utf16.remove(0, str_one_utf16.size());
- str_two_utf16.remove(0, str_two_utf16.size());
return 0;
}
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index c2e9b02..54662ac 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -90,13 +90,6 @@
EXPECT_STR16EQ(u"VerifyInsert me", tmp);
}
-TEST(String16Test, Remove) {
- String16 tmp("Verify me");
- tmp.remove(2, 6);
- EXPECT_EQ(2U, tmp.size());
- EXPECT_STR16EQ(u" m", tmp);
-}
-
TEST(String16Test, ReplaceAll) {
String16 tmp("Verify verify Verify");
tmp.replaceAll(u'r', u'!');
@@ -161,14 +154,6 @@
EXPECT_FALSE(tmp.isStaticString());
}
-TEST(String16Test, StaticStringRemove) {
- StaticString16 tmp(u"Verify me");
- tmp.remove(2, 6);
- EXPECT_EQ(2U, tmp.size());
- EXPECT_STR16EQ(u" m", tmp);
- EXPECT_FALSE(tmp.isStaticString());
-}
-
TEST(String16Test, StaticStringReplaceAll) {
StaticString16 tmp(u"Verify verify Verify");
tmp.replaceAll(u'r', u'!');
diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
index 29f6bd4..f27c1f1 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/StrongPointer_test.cpp
@@ -30,17 +30,34 @@
~SPFoo() {
*mDeleted = true;
}
-private:
+
+ private:
bool* mDeleted;
};
-TEST(StrongPointer, move) {
+class SPLightFoo : virtual public VirtualLightRefBase {
+ public:
+ explicit SPLightFoo(bool* deleted_check) : mDeleted(deleted_check) { *mDeleted = false; }
+
+ ~SPLightFoo() { *mDeleted = true; }
+
+ private:
+ bool* mDeleted;
+};
+
+template <typename T>
+class StrongPointer : public ::testing::Test {};
+
+using RefBaseTypes = ::testing::Types<SPFoo, SPLightFoo>;
+TYPED_TEST_CASE(StrongPointer, RefBaseTypes);
+
+TYPED_TEST(StrongPointer, move) {
bool isDeleted;
- sp<SPFoo> sp1 = sp<SPFoo>::make(&isDeleted);
- SPFoo* foo = sp1.get();
+ sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);
+ TypeParam* foo = sp1.get();
ASSERT_EQ(1, foo->getStrongCount());
{
- sp<SPFoo> sp2 = std::move(sp1);
+ sp<TypeParam> sp2 = std::move(sp1);
ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
// The strong count isn't increasing, let's double check the old object
@@ -50,33 +67,42 @@
ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
{
// Now let's double check it deletes on time
- sp<SPFoo> sp2 = std::move(sp1);
+ sp<TypeParam> sp2 = std::move(sp1);
}
ASSERT_TRUE(isDeleted) << "foo was leaked!";
}
-TEST(StrongPointer, NullptrComparison) {
- sp<SPFoo> foo;
+TYPED_TEST(StrongPointer, NullptrComparison) {
+ sp<TypeParam> foo;
ASSERT_EQ(foo, nullptr);
ASSERT_EQ(nullptr, foo);
}
-TEST(StrongPointer, PointerComparison) {
+TYPED_TEST(StrongPointer, PointerComparison) {
bool isDeleted;
- sp<SPFoo> foo = sp<SPFoo>::make(&isDeleted);
+ sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);
ASSERT_EQ(foo.get(), foo);
ASSERT_EQ(foo, foo.get());
ASSERT_NE(nullptr, foo);
ASSERT_NE(foo, nullptr);
}
-TEST(StrongPointer, AssertStrongRefExists) {
- // uses some other refcounting method, or non at all
+TYPED_TEST(StrongPointer, Deleted) {
bool isDeleted;
- SPFoo* foo = new SPFoo(&isDeleted);
+ sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);
- // can only get a valid sp<> object when you construct it as an sp<> object
- EXPECT_DEATH(sp<SPFoo>::fromExisting(foo), "");
+ auto foo2 = sp<TypeParam>::fromExisting(foo.get());
+ EXPECT_FALSE(isDeleted);
+ foo = nullptr;
+ EXPECT_FALSE(isDeleted);
+ foo2 = nullptr;
+ EXPECT_TRUE(isDeleted);
+}
+
+TYPED_TEST(StrongPointer, AssertStrongRefExists) {
+ bool isDeleted;
+ TypeParam* foo = new TypeParam(&isDeleted);
+ EXPECT_DEATH(sp<TypeParam>::fromExisting(foo), "");
delete foo;
}
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index b04e5c1..40edf67 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -28,6 +28,8 @@
class ReferenceRenamer;
+void LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz);
+
template <class T>
class LightRefBase
{
@@ -36,6 +38,11 @@
inline void incStrong(__attribute__((unused)) const void* id) const {
mCount.fetch_add(1, std::memory_order_relaxed);
}
+ inline void incStrongRequireStrong(__attribute__((unused)) const void* id) const {
+ if (0 == mCount.fetch_add(1, std::memory_order_relaxed)) {
+ LightRefBase_reportIncStrongRequireStrongFailed(this);
+ }
+ }
inline void decStrong(__attribute__((unused)) const void* id) const {
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
@@ -59,7 +66,6 @@
mutable std::atomic<int32_t> mCount;
};
-
// This is a wrapper around LightRefBase that simply enforces a virtual
// destructor to eliminate the template requirement of LightRefBase
class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
diff --git a/libutils/include/utils/StopWatch.h b/libutils/include/utils/StopWatch.h
index 9b14ac8..4e53eda 100644
--- a/libutils/include/utils/StopWatch.h
+++ b/libutils/include/utils/StopWatch.h
@@ -14,46 +14,30 @@
* limitations under the License.
*/
-#ifndef ANDROID_STOPWATCH_H
-#define ANDROID_STOPWATCH_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <utils/Timers.h>
-// ---------------------------------------------------------------------------
-
namespace android {
-class StopWatch
-{
-public:
- StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
- ~StopWatch();
+class StopWatch {
+ public:
+ StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);
+ ~StopWatch();
- const char* name() const;
- nsecs_t lap();
- nsecs_t elapsedTime() const;
+ const char* name() const;
+ nsecs_t elapsedTime() const;
- void reset();
+ void reset();
-private:
- const char* mName;
- int mClock;
-
- struct lap_t {
- nsecs_t soFar;
- nsecs_t thisLap;
- };
-
- nsecs_t mStartTime;
- lap_t mLaps[8];
- int mNumLaps;
+ private:
+ const char* mName;
+ int mClock;
+
+ nsecs_t mStartTime;
};
} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STOPWATCH_H
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 5ce48c6..60d523a 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -88,8 +88,6 @@
status_t replaceAll(char16_t replaceThis,
char16_t withThis);
- status_t remove(size_t len, size_t begin=0);
-
inline int compare(const String16& other) const;
inline bool operator<(const String16& other) const;
diff --git a/cpio/Android.bp b/mkbootfs/Android.bp
similarity index 100%
rename from cpio/Android.bp
rename to mkbootfs/Android.bp
diff --git a/cpio/mkbootfs.c b/mkbootfs/mkbootfs.c
similarity index 100%
rename from cpio/mkbootfs.c
rename to mkbootfs/mkbootfs.c
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index a22ef6f..6b03a1d 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -1,13 +1,14 @@
{
"requireLibs": [
+ "libandroidicu.so",
"libdexfile.so",
"libdexfiled.so",
+ "libicu.so",
+ "libjdwp.so",
"libnativebridge.so",
"libnativehelper.so",
"libnativeloader.so",
"libsigchain.so",
- "libandroidicu.so",
- "libicu.so",
// TODO(b/122876336): Remove libpac.so once it's migrated to Webview
"libpac.so",
// TODO(b/120786417 or b/134659294): libicuuc.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4ec5d33..6e85da5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -692,7 +692,7 @@
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
- mkdir /data/apex/decompressed 0700 root system encryption=Require
+ mkdir /data/apex/decompressed 0755 root system encryption=Require
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary
@@ -762,7 +762,7 @@
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
- mkdir /data/misc/profiles/ref 0770 system system
+ mkdir /data/misc/profiles/ref 0771 system system
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
mkdir /data/misc/installd 0700 root root