diff options
Diffstat (limited to '')
-rw-r--r-- | update_verifier/update_verifier.cpp | 58 | ||||
-rw-r--r-- | updater/blockimg.cpp | 317 |
2 files changed, 190 insertions, 185 deletions
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 83b1c46c4..59f136c90 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -19,9 +19,14 @@ * update. It gets invoked by init, and will only perform the verification if * it's the first boot post an A/B OTA update. * - * It relies on dm-verity to capture any corruption on the partitions being - * verified. dm-verity must be in enforcing mode, so that it will reboot the - * device on dm-verity failures. When that happens, the bootloader should + * Update_verifier relies on dm-verity to capture any corruption on the partitions + * being verified. And its behavior varies depending on the dm-verity mode. + * Upon detection of failures: + * enforcing mode: dm-verity reboots the device + * eio mode: dm-verity fails the read and update_verifier reboots the device + * other mode: not supported and update_verifier reboots the device + * + * After a predefined number of failing boot attempts, the bootloader should * mark the slot as unbootable and stops trying. Other dm-verity modes ( * for example, veritymode=EIO) are not accepted and simply lead to a * verification failure. @@ -35,6 +40,7 @@ #include <fcntl.h> #include <stdio.h> #include <string.h> +#include <unistd.h> #include <string> #include <vector> @@ -46,6 +52,7 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <android/hardware/boot/1.0/IBootControl.h> +#include <cutils/android_reboot.h> using android::sp; using android::hardware::boot::V1_0::IBootControl; @@ -66,19 +73,11 @@ static int dm_name_filter(const dirent* de) { return 0; } -static bool read_blocks(const std::string& blk_device, const std::string& range_str) { - // Parse the partition in the end of the block_device string. - // Here is one example: "/dev/block/bootdevice/by-name/system" - std::string partition; - if (android::base::EndsWith(blk_device, "system")) { - partition = "system"; - } else if (android::base::EndsWith(blk_device, "vendor")) { - partition = "vendor"; - } else { - LOG(ERROR) << "Failed to parse partition string in " << blk_device; +static bool read_blocks(const std::string& partition, const std::string& range_str) { + if (partition != "system" && partition != "vendor") { + LOG(ERROR) << "partition name must be system or vendor: " << partition; return false; } - // Iterate the content of "/sys/block/dm-X/dm/name". If it matches "system" // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition. // Afterwards, update_verifier will read every block on the care_map_file of @@ -172,7 +171,7 @@ static bool verify_image(const std::string& care_map_name) { return true; } // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block device name, e.g./dev/block/.../by-name/system. + // First line has the block partition name (system/vendor). // Second line holds all ranges of blocks to verify. // The next two lines have the same format but for vendor partition. std::string file_content; @@ -198,6 +197,14 @@ static bool verify_image(const std::string& care_map_name) { return true; } +static int reboot_device() { + if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) == -1) { + LOG(ERROR) << "Failed to reboot."; + return -1; + } + while (true) pause(); +} + int main(int argc, char** argv) { for (int i = 1; i < argc; i++) { LOG(INFO) << "Started with arg " << i << ": " << argv[i]; @@ -206,7 +213,7 @@ int main(int argc, char** argv) { sp<IBootControl> module = IBootControl::getService(); if (module == nullptr) { LOG(ERROR) << "Error getting bootctrl module."; - return -1; + return reboot_device(); } uint32_t current_slot = module->getCurrentSlot(); @@ -221,18 +228,19 @@ int main(int argc, char** argv) { std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { LOG(ERROR) << "Failed to get dm-verity mode."; - return -1; + return reboot_device(); } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) { - // We shouldn't see verity in EIO mode if the current slot hasn't booted - // successfully before. Therefore, fail the verification when veritymode=eio. - LOG(ERROR) << "Found dm-verity in EIO mode, skip verification."; - return -1; + // We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before. + // Continue the verification until we fail to read some blocks. + LOG(WARNING) << "Found dm-verity in EIO mode."; } else if (verity_mode != "enforcing") { LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing."; - return -1; - } else if (!verify_image(CARE_MAP_FILE)) { + return reboot_device(); + } + + if (!verify_image(CARE_MAP_FILE)) { LOG(ERROR) << "Failed to verify all blocks in care map file."; - return -1; + return reboot_device(); } #else LOG(WARNING) << "dm-verity not enabled; marking without verification."; @@ -242,7 +250,7 @@ int main(int argc, char** argv) { module->markBootSuccessful([&cr](CommandResult result) { cr = result; }); if (!cr.success) { LOG(ERROR) << "Error marking booted successfully: " << cr.errMsg; - return -1; + return reboot_device(); } LOG(INFO) << "Marked slot " << current_slot << " as booted successfully."; } diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index fc7a561eb..8199447a9 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1317,10 +1317,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return nullptr; } - const Value* blockdev_filename = args[0].get(); - const Value* transfer_list_value = args[1].get(); - const Value* new_data_fn = args[2].get(); - const Value* patch_data_fn = args[3].get(); + const std::unique_ptr<Value>& blockdev_filename = args[0]; + const std::unique_ptr<Value>& transfer_list_value = args[1]; + const std::unique_ptr<Value>& new_data_fn = args[2]; + const std::unique_ptr<Value>& patch_data_fn = args[3]; if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); @@ -1610,64 +1610,62 @@ Value* BlockImageUpdateFn(const char* name, State* state, } Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* blockdev_filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr<Value>& blockdev_filename = args[0]; + const std::unique_ptr<Value>& ranges = args[1]; - if (blockdev_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", - name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet rs = RangeSet::Parse(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - SHA_CTX ctx; - SHA1_Init(&ctx); + SHA_CTX ctx; + SHA1_Init(&ctx); - std::vector<uint8_t> buffer(BLOCKSIZE); - for (size_t i = 0; i < rs.count; ++i) { - if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + std::vector<uint8_t> buffer(BLOCKSIZE); + for (size_t i = 0; i < rs.count; ++i) { + if (!check_lseek(fd, (off64_t)rs.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) { - if (read_all(fd, buffer, BLOCKSIZE) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + if (read_all(fd, buffer, BLOCKSIZE) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); - } + SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); } - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1_Final(digest, &ctx); + } + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1_Final(digest, &ctx); - return StringValue(print_sha1(digest)); + return StringValue(print_sha1(digest)); } // This function checks if a device has been remounted R/W prior to an incremental @@ -1677,145 +1675,144 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique Value* CheckFirstBlockFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 1) { - ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 1) { + ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", + argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* arg_filename = args[0].get(); + const std::unique_ptr<Value>& arg_filename = args[0]; - if (arg_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } + if (arg_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet blk0 {1 /*count*/, 1/*size*/, std::vector<size_t> {0, 1}/*position*/}; - std::vector<uint8_t> block0_buffer(BLOCKSIZE); + RangeSet blk0{ 1 /*count*/, 1 /*size*/, std::vector<size_t>{ 0, 1 } /*position*/ }; + std::vector<uint8_t> block0_buffer(BLOCKSIZE); - if (ReadBlocks(blk0, block0_buffer, fd) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (ReadBlocks(blk0, block0_buffer, fd) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout - // Super block starts from block 0, offset 0x400 - // 0x2C: len32 Mount time - // 0x30: len32 Write time - // 0x34: len16 Number of mounts since the last fsck - // 0x38: len16 Magic signature 0xEF53 + // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout + // Super block starts from block 0, offset 0x400 + // 0x2C: len32 Mount time + // 0x30: len32 Write time + // 0x34: len16 Number of mounts since the last fsck + // 0x38: len16 Magic signature 0xEF53 - time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400+0x2C]); - uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400+0x34]); + time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]); + uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]); - if (mount_count > 0) { - uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); - uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); - } + if (mount_count > 0) { + uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); + uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); + } - return StringValue("t"); + return StringValue("t"); } - Value* BlockImageRecoverFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", + argv.size()); + return StringValue(""); + } - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr<Value>& filename = args[0]; + const std::unique_ptr<Value>& ranges = args[1]; - if (filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - // Output notice to log when recover is attempted - LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; + // Output notice to log when recover is attempted + LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; - // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data, O_RDWR); + // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read + fec::io fh(filename->data, O_RDWR); - if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (!fh) { + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - if (!fh.has_ecc() || !fh.has_verity()) { - ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); - return StringValue(""); - } + if (!fh.has_ecc() || !fh.has_verity()) { + ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); + return StringValue(""); + } - fec_status status; + fec_status status; - if (!fh.get_status(status)) { - ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); - return StringValue(""); - } + if (!fh.get_status(status)) { + ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); + return StringValue(""); + } - RangeSet rs = RangeSet::Parse(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - uint8_t buffer[BLOCKSIZE]; + uint8_t buffer[BLOCKSIZE]; - for (size_t i = 0; i < rs.count; ++i) { - for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { - // Stay within the data area, libfec validates and corrects metadata - if (status.data_size <= (uint64_t)j * BLOCKSIZE) { - continue; - } + for (size_t i = 0; i < rs.count; ++i) { + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + // Stay within the data area, libfec validates and corrects metadata + if (status.data_size <= (uint64_t)j * BLOCKSIZE) { + continue; + } - if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { - ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data.c_str(), j, strerror(errno)); - return StringValue(""); - } + if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { + ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", + filename->data.c_str(), j, strerror(errno)); + return StringValue(""); + } - // If we want to be able to recover from a situation where rewriting a corrected - // block doesn't guarantee the same data will be returned when re-read later, we - // can save a copy of corrected blocks to /cache. Note: - // - // 1. Maximum space required from /cache is the same as the maximum number of - // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, - // this would be ~16 MiB, for example. - // - // 2. To find out if this block was corrupted, call fec_get_status after each - // read and check if the errors field value has increased. - } + // If we want to be able to recover from a situation where rewriting a corrected + // block doesn't guarantee the same data will be returned when re-read later, we + // can save a copy of corrected blocks to /cache. Note: + // + // 1. Maximum space required from /cache is the same as the maximum number of + // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, + // this would be ~16 MiB, for example. + // + // 2. To find out if this block was corrupted, call fec_get_status after each + // read and check if the errors field value has increased. } - LOG(INFO) << "..." << filename->data << " image recovered successfully."; - return StringValue("t"); + } + LOG(INFO) << "..." << filename->data << " image recovered successfully."; + return StringValue("t"); } void RegisterBlockImageFunctions() { - RegisterFunction("block_image_verify", BlockImageVerifyFn); - RegisterFunction("block_image_update", BlockImageUpdateFn); - RegisterFunction("block_image_recover", BlockImageRecoverFn); - RegisterFunction("check_first_block", CheckFirstBlockFn); - RegisterFunction("range_sha1", RangeSha1Fn); + RegisterFunction("block_image_verify", BlockImageVerifyFn); + RegisterFunction("block_image_update", BlockImageUpdateFn); + RegisterFunction("block_image_recover", BlockImageRecoverFn); + RegisterFunction("check_first_block", CheckFirstBlockFn); + RegisterFunction("range_sha1", RangeSha1Fn); } |