summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-06-08 17:33:00 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-06-08 17:33:00 +0000
commitb92fc9a6a12c27ae83c95ff94f0a32b5e9401d63 (patch)
tree770486c47fbedcf2f8a330268ad751fb39df6635
parent577ba9df7df91f1ebcfbac39dfb0783d041a925a (diff)
parent4f5062b78124bf51c38572ebd0576622ff4790c0 (diff)
downloadcore-b92fc9a6a12c27ae83c95ff94f0a32b5e9401d63.tar.gz
Change-Id: I02e320245207cf89a25d55f98c7a350405a87035
-rw-r--r--debuggerd/debuggerd_test.cpp60
-rw-r--r--debuggerd/libdebuggerd/tombstone_proto_to_text.cpp10
-rw-r--r--fastboot/device/usb.cpp10
-rw-r--r--fs_mgr/libsnapshot/Android.bp1
-rw-r--r--fs_mgr/libsnapshot/snapshot_test.cpp14
-rw-r--r--fs_mgr/libsnapshot/snapuserd/Android.bp7
-rw-r--r--fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h4
-rw-r--r--fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp10
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp242
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h45
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp26
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h3
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp222
-rwxr-xr-x[-rw-r--r--]fs_mgr/libsnapshot/vts_ota_config_test.cpp3
-rw-r--r--rootdir/init.rc61
-rw-r--r--rootdir/init.zygote32.rc3
-rw-r--r--rootdir/init.zygote64.rc3
17 files changed, 438 insertions, 286 deletions
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e11330819..9163ca0ab 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <string>
#include <thread>
+#include <android/dlext.h>
#include <android/fdsan.h>
#include <android/set_abort_message.h>
#include <bionic/malloc.h>
@@ -1842,7 +1843,7 @@ TEST_F(CrasherTest, stack_overflow) {
ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
}
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+static std::string GetTestLibraryPath() {
std::string test_lib(testing::internal::GetArgvs()[0]);
auto const value = test_lib.find_last_of('/');
if (value == std::string::npos) {
@@ -1850,7 +1851,62 @@ static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
} else {
test_lib = test_lib.substr(0, value + 1) + "./";
}
- test_lib += "libcrash_test.so";
+ return test_lib + "libcrash_test.so";
+}
+
+static void CreateEmbeddedLibrary(int out_fd) {
+ std::string test_lib(GetTestLibraryPath());
+ android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_NE(fd.get(), -1);
+ off_t file_size = lseek(fd, 0, SEEK_END);
+ ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+ std::vector<uint8_t> contents(file_size);
+ ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));
+
+ // Put the shared library data at a pagesize() offset.
+ ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());
+ ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());
+}
+
+TEST_F(CrasherTest, non_zero_offset_in_library) {
+ int intercept_result;
+ unique_fd output_fd;
+ TemporaryFile tf;
+ CreateEmbeddedLibrary(tf.fd);
+ StartProcess([&tf]() {
+ android_dlextinfo extinfo{};
+ extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
+ extinfo.library_fd = tf.fd;
+ extinfo.library_fd_offset = 4 * getpagesize();
+ void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);
+ if (handle == nullptr) {
+ _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);
+
+ // Verify the crash includes an offset value in the backtrace.
+ std::string match_str = android::base::StringPrintf("%s\\!libcrash_test.so \\(offset 0x%x\\)",
+ tf.path, 4 * getpagesize());
+ ASSERT_MATCH(result, match_str);
+}
+
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string test_lib(GetTestLibraryPath());
*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);
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 026564112..0696601bc 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -168,8 +168,14 @@ static void print_backtrace(CallbackType callback, const Tombstone& tombstone,
build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
}
- CB(should_log, " #%02d pc %0*" PRIx64 " %s%s%s", index++, pointer_width(tombstone) * 2,
- frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
+ std::string line =
+ StringPrintf(" #%02d pc %0*" PRIx64 " %s", index++, pointer_width(tombstone) * 2,
+ frame.rel_pc(), frame.file_name().c_str());
+ if (frame.file_map_offset() != 0) {
+ line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
+ }
+ line += function + build_id;
+ CB(should_log, "%s", line.c_str());
}
}
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4115a6d28..1257055de 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -171,6 +171,16 @@ static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
continue;
}
+ if (read && aiob->events[0].res == -EPIPE) {
+ // On initial connection, some clients will send a ClearFeature(HALT) to
+ // attempt to resynchronize host and device after the fastboot server is killed.
+ // On newer device kernels, the reads we've already dispatched will be cancelled.
+ // Instead of treating this as a failure, which will tear down the interface and
+ // lead to the client doing the same thing again, just resubmit if this happens
+ // before we've actually read anything.
+ PLOG(ERROR) << "aio: got -EPIPE on first read attempt. Re-submitting read... ";
+ continue;
+ }
int ret = 0;
for (int i = 0; i < num_bufs; i++) {
if (aiob->events[i].res < 0) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f139f..0d8828a47 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -257,6 +257,7 @@ cc_defaults {
},
auto_gen_config: true,
require_root: true,
+ compile_multilib: "first",
}
cc_test {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c145da718..e7ffb1600 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2609,7 +2609,6 @@ class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t
}
void TearDown() override {
RETURN_IF_NON_VIRTUAL_AB();
- return; // BUG(149738928)
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
image_manager_->DeleteBackingImage(kImageName));
@@ -2618,19 +2617,6 @@ class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t
std::unique_ptr<LowSpaceUserdata> userdata_;
};
-TEST_P(ImageManagerTest, CreateImageEnoughAvailSpace) {
- if (userdata_->available_space() == 0) {
- GTEST_SKIP() << "/data is full (" << userdata_->available_space()
- << " bytes available), skipping";
- }
- ASSERT_TRUE(image_manager_->CreateBackingImage(kImageName, userdata_->available_space(),
- IImageManager::CREATE_IMAGE_DEFAULT))
- << "Should be able to create image with size = " << userdata_->available_space()
- << " bytes";
- ASSERT_TRUE(image_manager_->DeleteBackingImage(kImageName))
- << "Should be able to delete created image";
-}
-
TEST_P(ImageManagerTest, CreateImageNoSpace) {
uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bcebbf..d751fb88f 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -47,6 +47,7 @@ cc_library_static {
"libbase",
"liblog",
],
+ export_include_dirs: ["include"],
ramdisk_available: true,
}
@@ -68,6 +69,7 @@ cc_defaults {
"user-space-merge/snapuserd_readahead.cpp",
"user-space-merge/snapuserd_transitions.cpp",
"user-space-merge/snapuserd_server.cpp",
+ "user-space-merge/snapuserd_verify.cpp",
],
cflags: [
@@ -88,6 +90,11 @@ cc_defaults {
"libext4_utils",
"liburing",
],
+
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+
include_dirs: ["bionic/libc/kernel"],
}
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index cebda1ccb..9a69d588c 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -89,6 +89,10 @@ class SnapuserdClient {
// Return the status of the snapshot
std::string QuerySnapshotStatus(const std::string& misc_name);
+
+ // Check the update verification status - invoked by update_verifier during
+ // boot
+ bool QueryUpdateVerification();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 7b1c7a3a6..e08cf9b59 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -269,5 +269,15 @@ std::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) {
return Receivemsg();
}
+bool SnapuserdClient::QueryUpdateVerification() {
+ std::string msg = "update-verify";
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
+ }
+ std::string response = Receivemsg();
+ return response == "success";
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 718c13ce0..8939b786c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -18,6 +18,7 @@
#include <sys/utsname.h>
+#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
@@ -70,6 +71,9 @@ bool SnapshotHandler::InitializeWorkers() {
read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
GetSharedPtr());
+
+ update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+
return true;
}
@@ -307,206 +311,6 @@ bool SnapshotHandler::InitCowDevice() {
return ReadMetadata();
}
-void SnapshotHandler::FinalizeIouring() {
- io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
- ring_ = std::make_unique<struct io_uring>();
-
- int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
- if (ret) {
- LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
- return false;
- }
-
- LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
- return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
- const std::string& partition_name, size_t size) {
- // 64k block size with io_depth of 64 is optimal
- // for a single thread. We just need a single thread
- // to read all the blocks from all dynamic partitions.
- size_t io_depth = 64;
- size_t bs = (64 * 1024);
-
- if (!InitializeIouring(io_depth)) {
- return false;
- }
-
- LOG(INFO) << "ReadBlockAsync start "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
-
- auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
- std::vector<std::unique_ptr<struct iovec>> vecs;
- using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
- std::vector<AlignedBuf> alignedBufVector;
-
- /*
- * TODO: We need aligned memory for DIRECT-IO. However, if we do
- * a DIRECT-IO and verify the blocks then we need to inform
- * update-verifier that block verification has been done and
- * there is no need to repeat the same. We are not there yet
- * as we need to see if there are any boot time improvements doing
- * a DIRECT-IO.
- *
- * Also, we could you the same function post merge for block verification;
- * again, we can do a DIRECT-IO instead of thrashing page-cache and
- * hurting other applications.
- *
- * For now, we will just create aligned buffers but rely on buffered
- * I/O until we have perf numbers to justify DIRECT-IO.
- */
- for (int i = 0; i < io_depth; i++) {
- auto iovec = std::make_unique<struct iovec>();
- vecs.push_back(std::move(iovec));
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
- LOG(ERROR) << "posix_memalign failed";
- return false;
- }
-
- iovec_ptr->iov_len = bs;
- alignedBufVector.push_back(
- std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
- }
-
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
- << " partition-name: " << partition_name;
- return false;
- }
-
- loff_t offset = 0;
- size_t remain = size;
- size_t read_sz = io_depth * bs;
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
- size_t queue_size = to_read / bs;
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
- if (!sqe) {
- SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
- return false;
- }
-
- struct iovec* iovec_ptr = vecs[i].get();
-
- io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
- sqe->flags |= IOSQE_ASYNC;
- offset += bs;
- }
-
- int ret = io_uring_submit(ring_.get());
- if (ret != queue_size) {
- SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
- return false;
- }
-
- for (int i = 0; i < queue_size; i++) {
- struct io_uring_cqe* cqe;
-
- int ret = io_uring_wait_cqe(ring_.get(), &cqe);
- if (ret) {
- SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
- return false;
- }
-
- if (cqe->res < 0) {
- SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
- return false;
- }
- io_uring_cqe_seen(ring_.get(), cqe);
- }
-
- remain -= to_read;
- }
-
- LOG(INFO) << "ReadBlockAsync complete: "
- << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
- << " Size: " << size;
- return true;
-}
-
-void SnapshotHandler::ReadBlocksToCache(const std::string& dm_block_device,
- const std::string& partition_name, off_t offset,
- size_t size) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
- << " partition-name: " << partition_name;
- return;
- }
-
- size_t remain = size;
- off_t file_offset = offset;
- // We pick 4M I/O size based on the fact that the current
- // update_verifier has a similar I/O size.
- size_t read_sz = 1024 * BLOCK_SZ;
- std::vector<uint8_t> buf(read_sz);
-
- while (remain > 0) {
- size_t to_read = std::min(remain, read_sz);
-
- if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
- SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
- << " at offset: " << file_offset
- << " partition-name: " << partition_name << " total-size: " << size
- << " remain_size: " << remain;
- return;
- }
-
- file_offset += to_read;
- remain -= to_read;
- }
-
- SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
- << " partition: " << partition_name << " size: " << size
- << " offset: " << offset;
-}
-
-void SnapshotHandler::ReadBlocks(const std::string partition_name,
- const std::string& dm_block_device) {
- SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
- << " Block-Device: " << dm_block_device;
-
- uint64_t dev_sz = 0;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- SNAP_LOG(ERROR) << "Cannot open block device";
- return;
- }
-
- dev_sz = get_block_device_size(fd.get());
- if (!dev_sz) {
- SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
- return;
- }
-
- int num_threads = 2;
- size_t num_blocks = dev_sz >> BLOCK_SHIFT;
- size_t num_blocks_per_thread = num_blocks / num_threads;
- size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
- off_t offset = 0;
-
- for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
- partition_name, offset, read_sz_per_thread);
-
- offset += read_sz_per_thread;
- }
-}
-
/*
* Entry point to launch threads
*/
@@ -527,42 +331,22 @@ bool SnapshotHandler::Start() {
std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
}
- bool second_stage_init = true;
+ bool partition_verification = true;
- // We don't want to read the blocks during first stage init.
+ // We don't want to read the blocks during first stage init or
+ // during post-install phase.
if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
- second_stage_init = false;
- }
-
- if (second_stage_init) {
- SNAP_LOG(INFO) << "Reading blocks to cache....";
- auto& dm = DeviceMapper::Instance();
- auto dm_block_devices = dm.FindDmPartitions();
- if (dm_block_devices.empty()) {
- SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
- } else {
- auto parts = android::base::Split(misc_name_, "-");
- std::string partition_name = parts[0];
-
- const char* suffix_b = "_b";
- const char* suffix_a = "_a";
-
- partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
- partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
-
- if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
- SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
- } else {
- ReadBlocks(partition_name, dm_block_devices.at(partition_name));
- }
- }
- } else {
- SNAP_LOG(INFO) << "Not reading block device into cache";
+ partition_verification = false;
}
std::future<bool> merge_thread =
std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+ // Now that the worker threads are up, scan the partitions.
+ if (partition_verification) {
+ update_verify_->VerifyUpdatePartition();
+ }
+
bool ret = true;
for (auto& t : threads) {
ret = t.get() && ret;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index c6805e5d9..42237ef60 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -45,12 +45,14 @@
#include <liburing.h>
#include <snapuserd/snapuserd_buffer.h>
#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
namespace android {
namespace snapshot {
using android::base::unique_fd;
using namespace std::chrono_literals;
+using namespace android::storage_literals;
static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
@@ -170,6 +172,36 @@ class ReadAhead {
std::unique_ptr<struct io_uring> ring_;
};
+class UpdateVerify {
+ public:
+ UpdateVerify(const std::string& misc_name);
+ void VerifyUpdatePartition();
+ bool CheckPartitionVerification();
+
+ private:
+ enum class UpdateVerifyState {
+ VERIFY_UNKNOWN,
+ VERIFY_FAILED,
+ VERIFY_SUCCESS,
+ };
+
+ std::string misc_name_;
+ UpdateVerifyState state_;
+ std::mutex m_lock_;
+ std::condition_variable m_cv_;
+
+ int kMinThreadsToVerify = 1;
+ int kMaxThreadsToVerify = 4;
+ uint64_t kThresholdSize = 512_MiB;
+ uint64_t kBlockSizeVerify = 1_MiB;
+
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ void UpdatePartitionVerificationState(UpdateVerifyState state);
+ bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+ bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+ off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
class Worker {
public:
Worker(const std::string& cow_device, const std::string& backing_device,
@@ -352,24 +384,16 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
bool IsIouringSupported();
+ bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
private:
bool ReadMetadata();
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
- bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+ bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
struct BufferState* GetBufferState();
void UpdateMergeCompletionPercentage();
- void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
- void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
- off_t offset, size_t size);
-
- bool InitializeIouring(int io_depth);
- void FinalizeIouring();
- bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
- size_t size);
-
// COW device
std::string cow_device_;
// Source device
@@ -422,6 +446,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
bool scratch_space_ = false;
std::unique_ptr<struct io_uring> ring_;
+ std::unique_ptr<UpdateVerify> update_verify_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 5d93f01a7..1e7daed39 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -55,6 +55,7 @@ DaemonOps UserSnapshotServer::Resolveop(std::string& input) {
if (input == "initiate_merge") return DaemonOps::INITIATE;
if (input == "merge_percent") return DaemonOps::PERCENTAGE;
if (input == "getstatus") return DaemonOps::GETSTATUS;
+ if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
return DaemonOps::INVALID;
}
@@ -290,6 +291,14 @@ bool UserSnapshotServer::Receivemsg(android::base::borrowed_fd fd, const std::st
return Sendmsg(fd, merge_status);
}
}
+ case DaemonOps::UPDATE_VERIFY: {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (!UpdateVerification(&lock)) {
+ return Sendmsg(fd, "fail");
+ }
+
+ return Sendmsg(fd, "success");
+ }
default: {
LOG(ERROR) << "Received unknown message type from client";
Sendmsg(fd, "fail");
@@ -750,5 +759,22 @@ bool UserSnapshotServer::RunForSocketHandoff() {
return true;
}
+bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
+ CHECK(proof_of_lock);
+
+ bool status = true;
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ auto& th = (*iter)->thread();
+ if (th.joinable() && status) {
+ status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+ } else {
+ // return immediately if there is a failure
+ return false;
+ }
+ }
+
+ return status;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index e0844aeb1..c2af61fbf 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -50,6 +50,7 @@ enum class DaemonOps {
INITIATE,
PERCENTAGE,
GETSTATUS,
+ UPDATE_VERIFY,
INVALID,
};
@@ -130,6 +131,8 @@ class UserSnapshotServer {
double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
+ bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
+
public:
UserSnapshotServer();
~UserSnapshotServer();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
new file mode 100644
index 000000000..18c1dfcd9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "snapuserd_core.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+UpdateVerify::UpdateVerify(const std::string& misc_name)
+ : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+
+bool UpdateVerify::CheckPartitionVerification() {
+ auto now = std::chrono::system_clock::now();
+ auto deadline = now + 10s;
+ {
+ std::unique_lock<std::mutex> cv_lock(m_lock_);
+ while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {
+ auto status = m_cv_.wait_until(cv_lock, deadline);
+ if (status == std::cv_status::timeout) {
+ return false;
+ }
+ }
+ }
+
+ return (state_ == UpdateVerifyState::VERIFY_SUCCESS);
+}
+
+void UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {
+ {
+ std::lock_guard<std::mutex> lock(m_lock_);
+ state_ = state;
+ }
+ m_cv_.notify_all();
+}
+
+void UpdateVerify::VerifyUpdatePartition() {
+ bool succeeded = false;
+
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ auto& dm = DeviceMapper::Instance();
+ auto dm_block_devices = dm.FindDmPartitions();
+ if (dm_block_devices.empty()) {
+ SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+ return;
+ }
+
+ const auto parts = android::base::Split(misc_name_, "-");
+ std::string partition_name = parts[0];
+
+ constexpr auto&& suffix_b = "_b";
+ constexpr auto&& suffix_a = "_a";
+
+ partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+ partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+ if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+ SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+ return;
+ }
+
+ if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {
+ SNAP_LOG(ERROR) << "Partition: " << partition_name
+ << " Block-device: " << dm_block_devices.at(partition_name)
+ << " verification failed";
+ }
+ succeeded = true;
+}
+
+bool UpdateVerify::VerifyBlocks(const std::string& partition_name,
+ const std::string& dm_block_device, off_t offset, int skip_blocks,
+ uint64_t dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ loff_t file_offset = offset;
+ const uint64_t read_sz = kBlockSizeVerify;
+
+ void* addr;
+ ssize_t page_size = getpagesize();
+ if (posix_memalign(&addr, page_size, read_sz) < 0) {
+ SNAP_PLOG(ERROR) << "posix_memalign failed "
+ << " page_size: " << page_size << " read_sz: " << read_sz;
+ return false;
+ }
+
+ std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+ uint64_t bytes_read = 0;
+
+ while (true) {
+ size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+ if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+ SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+ << " partition-name: " << partition_name
+ << " at offset: " << file_offset << " read-size: " << to_read
+ << " block-size: " << dev_sz;
+ return false;
+ }
+
+ bytes_read += to_read;
+ file_offset += (skip_blocks * kBlockSizeVerify);
+ if (file_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
+ << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+
+ return true;
+}
+
+bool UpdateVerify::VerifyPartition(const std::string& partition_name,
+ const std::string& dm_block_device) {
+ android::base::Timer timer;
+
+ SNAP_LOG(INFO) << "VerifyPartition: " << partition_name << " Block-device: " << dm_block_device;
+
+ bool succeeded = false;
+ auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+ if (!succeeded) {
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+ }
+ });
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+ if (fd < 0) {
+ SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+ return false;
+ }
+
+ uint64_t dev_sz = get_block_device_size(fd.get());
+ if (!dev_sz) {
+ SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+ return false;
+ }
+
+ if (!IsBlockAligned(dev_sz)) {
+ SNAP_LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+ return false;
+ }
+
+ /*
+ * Not all partitions are of same size. Some partitions are as small as
+ * 100Mb. We can just finish them in a single thread. For bigger partitions
+ * such as product, 4 threads are sufficient enough.
+ *
+ * TODO: With io_uring SQ_POLL support, we can completely cut this
+ * down to just single thread for all partitions and potentially verify all
+ * the partitions with zero syscalls. Additionally, since block layer
+ * supports polling, IO_POLL could be used which will further cut down
+ * latency.
+ */
+ int num_threads = kMinThreadsToVerify;
+ if (dev_sz > kThresholdSize) {
+ num_threads = kMaxThreadsToVerify;
+ }
+
+ std::vector<std::future<bool>> threads;
+ off_t start_offset = 0;
+ const int skip_blocks = num_threads;
+
+ while (num_threads) {
+ threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,
+ partition_name, dm_block_device, start_offset, skip_blocks,
+ dev_sz));
+ start_offset += kBlockSizeVerify;
+ num_threads -= 1;
+ if (start_offset >= dev_sz) {
+ break;
+ }
+ }
+
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+
+ if (ret) {
+ succeeded = true;
+ UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
+ SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
+ << " Size: " << dev_sz
+ << " verification success. Duration : " << timer.duration().count() << " ms";
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
index 02bcc3438..d387eb32d 100644..100755
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -22,6 +22,9 @@ static int GetVsrLevel() {
}
TEST(VAB, Enabled) {
+ if (!android::base::GetBoolProperty("ro.build.ab_update", false) && (GetVsrLevel() < __ANDROID_API_T__)) {
+ GTEST_SKIP();
+ }
ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
if (GetVsrLevel() < __ANDROID_API_T__) {
GTEST_SKIP();
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 02e51d2c4..2b53d883e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,32 +86,6 @@ on early-init
mkdir /dev/sys/fs 0755 system system
mkdir /dev/sys/block 0755 system system
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test32
-on early-init && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
- exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
- exec_start boringssl_self_test_apex64
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
- reboot_on_failure reboot,boringssl-self-check-failed
- stdio_to_kmsg
-
on init
sysclktz 0
@@ -502,6 +476,33 @@ on init
start hwservicemanager
start vndservicemanager
+# Run boringssl self test for each ABI. Any failures trigger reboot to firmware.
+on init && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+ exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+ exec_start boringssl_self_test_apex64
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+ reboot_on_failure reboot,boringssl-self-check-failed
+ stdio_to_kmsg
+
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -1269,11 +1270,13 @@ service console /system/bin/sh
setenv HOSTNAME console
on property:ro.debuggable=1
- # Give writes to anyone for the trace folder on debug builds.
+ # Give writes to the same group for the trace folder on debug builds,
+ # it's further protected by selinux policy.
# The folder is used to store method traces.
chmod 0773 /data/misc/trace
- # Give reads to anyone for the window trace folder on debug builds.
- chmod 0775 /data/misc/wmtrace
+ # Give writes and reads to anyone for the window trace folder on debug builds,
+ # it's further protected by selinux policy.
+ chmod 0777 /data/misc/wmtrace
# Give reads to anyone for the accessibility trace folder on debug builds.
chmod 0775 /data/misc/a11ytrace
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 63b09c0d6..2f0ec8a17 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -7,6 +7,9 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
+ # NOTE: If the wakelock name here is changed, then also
+ # update it in SystemSuspend.cpp
+ onrestart write /sys/power/wake_lock zygote_kwl
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5bde5f469..805267d1d 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -7,6 +7,9 @@ service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-s
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
+ # NOTE: If the wakelock name here is changed, then also
+ # update it in SystemSuspend.cpp
+ onrestart write /sys/power/wake_lock zygote_kwl
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media