/* * Copyright (C) 2016 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include "avb_unittest_util.h" #include "fake_avb_ops.h" namespace avb { static_assert(sizeof(AvbABSlotData) == 4, "AvbABSlotData has wrong size"); static_assert(sizeof(AvbABData) == AVB_AB_DATA_SIZE, "AvbABData has wrong size"); static_assert(offsetof(AvbABData, slots) % 8 == 0, "AvbABData slots member has wrong offset"); // Subclass BaseAvbToolTest to check for memory leaks. class ABTest : public BaseAvbToolTest { public: ABTest() {} }; TEST_F(ABTest, InitData) { AvbABData data; avb_ab_data_init(&data); EXPECT_EQ(0, strncmp(reinterpret_cast(data.magic), AVB_AB_MAGIC, AVB_AB_MAGIC_LEN)); EXPECT_EQ(AVB_AB_MAX_PRIORITY, data.slots[0].priority); EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[0].tries_remaining); EXPECT_EQ(0, data.slots[0].successful_boot); EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, data.slots[1].priority); EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[1].tries_remaining); EXPECT_EQ(0, data.slots[1].successful_boot); EXPECT_EQ(uint32_t(0), data.crc32); } TEST_F(ABTest, DataSerialization) { AvbABData data; AvbABData serialized; AvbABData restored; avb_ab_data_init(&data); EXPECT_EQ(uint32_t(0), data.crc32); avb_ab_data_update_crc_and_byteswap(&data, &serialized); EXPECT_NE(uint32_t(0), serialized.crc32); EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored)); EXPECT_EQ(std::string(reinterpret_cast(data.magic), 4), std::string(reinterpret_cast(restored.magic), 4)); EXPECT_EQ(data.version_major, restored.version_major); EXPECT_EQ(data.version_minor, restored.version_minor); EXPECT_EQ(0, memcmp(reinterpret_cast(data.slots), reinterpret_cast(restored.slots), sizeof(AvbABSlotData) * 2)); } TEST_F(ABTest, CatchBadCRC) { AvbABData data; AvbABData serialized; AvbABData restored; avb_ab_data_init(&data); avb_ab_data_update_crc_and_byteswap(&data, &serialized); serialized.crc32 += 1; EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored)); } TEST_F(ABTest, CatchUnsupportedMajorVersion) { AvbABData data; AvbABData serialized; AvbABData restored; avb_ab_data_init(&data); data.version_major += 1; avb_ab_data_update_crc_and_byteswap(&data, &serialized); EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored)); } TEST_F(ABTest, SupportSameMajorFutureMinorVersion) { AvbABData data; AvbABData serialized; AvbABData restored; avb_ab_data_init(&data); data.version_minor += 1; avb_ab_data_update_crc_and_byteswap(&data, &serialized); EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored)); } #define MISC_PART_SIZE 8 * 1024 // These values are kept short since they are used in SetMD() and it's // helpful if the information for a slot fits in one 80-character // line. enum SlotValidity { SV_OK, // Slot is valid and verified. SV_INV, // Slot is invalid. SV_UNV, // Slot is valid but unverified. }; class AvbABFlowTest : public BaseAvbToolTest { public: AvbABFlowTest() {} virtual void SetUp() override { BaseAvbToolTest::SetUp(); ops_.set_partition_dir(testdir_); ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}, {2, 0}, {3, 0}}); ops_.set_stored_is_device_unlocked(false); // Create large enough 'misc' partition and initialize it with // zeroes. std::vector misc; misc.resize(MISC_PART_SIZE); base::FilePath misc_path = testdir_.Append("misc.img"); EXPECT_EQ(misc.size(), static_cast( base::WriteFile(misc_path, reinterpret_cast(misc.data()), misc.size()))); // We're going to use this key for all images. ops_.set_expected_public_key( PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem"))); } void GenerateSlot(unsigned int slot_number, SlotValidity slot_validity, uint64_t rollback_boot, uint64_t rollback_odm) { std::string boot_name = "boot_a.img"; std::string vbmeta_name = "vbmeta_a.img"; std::string odm_name = "odm_a.img"; if (slot_number > 0) { boot_name = "boot_b.img"; vbmeta_name = "vbmeta_b.img"; odm_name = "odm_b.img"; } // If asked to make an invalid slot, just generate 1MiB garbage // for each the three images in the slot. if (slot_validity == SV_INV) { GenerateImage(boot_name, 1024 * 1024); GenerateImage(vbmeta_name, 1024 * 1024); GenerateImage(odm_name, 1024 * 1024); return; } const size_t boot_partition_size = 16 * 1024 * 1024; const size_t boot_image_size = 5 * 1024 * 1024; base::FilePath boot_path = GenerateImage(boot_name, boot_image_size); EXPECT_COMMAND(0, "./avbtool add_hash_footer" " --image %s" " --rollback_index %" PRIu64 " --partition_name boot" " --partition_size %zd" " --salt deadbeef", boot_path.value().c_str(), rollback_boot, boot_partition_size); const size_t odm_partition_size = 512 * 1024; const size_t odm_image_size = 80 * 1024; base::FilePath odm_path = GenerateImage(odm_name, odm_image_size); EXPECT_COMMAND(0, "./avbtool add_hashtree_footer" " --image %s" " --rollback_index %" PRIu64 " --partition_name odm" " --partition_size %zd" " --salt deadbeef" " --algorithm SHA512_RSA4096 " " --key test/data/testkey_rsa4096.pem" " --do_not_generate_fec", odm_path.value().c_str(), rollback_odm, odm_partition_size); base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey"); EXPECT_COMMAND( 0, "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem" " --output %s", pk_path.value().c_str()); // If requested to make the image unverified, just use another key // in the chain_partition descriptor since this will cause // avb_slot_verify() to return ERROR_PUBLIC_KEY_REJECTED. if (slot_validity == SV_UNV) { pk_path = GenerateImage("dummy.avbpubkey", 32); } GenerateVBMetaImage(vbmeta_name, "SHA256_RSA2048", rollback_boot, base::FilePath("test/data/testkey_rsa2048.pem"), base::StringPrintf("--include_descriptors_from_image %s" " --chain_partition odm:1:%s", boot_path.value().c_str(), pk_path.value().c_str())); } void SetMD(int a_pri, int a_tries, bool a_success, SlotValidity a_slot_validity, uint64_t a_rollback_boot, uint64_t a_rollback_odm, int b_pri, int b_tries, bool b_success, SlotValidity b_slot_validity, uint64_t b_rollback_boot, uint64_t b_rollback_odm, const std::map& stored_rollback_indexes) { AvbABData data; avb_ab_data_init(&data); data.slots[0].priority = a_pri; data.slots[0].tries_remaining = a_tries; data.slots[0].successful_boot = (a_success ? 1 : 0); data.slots[1].priority = b_pri; data.slots[1].tries_remaining = b_tries; data.slots[1].successful_boot = (b_success ? 1 : 0); EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ab_ops()->write_ab_metadata(ops_.avb_ab_ops(), &data)); GenerateSlot(0, a_slot_validity, a_rollback_boot, a_rollback_odm); GenerateSlot(1, b_slot_validity, b_rollback_boot, b_rollback_odm); ops_.set_stored_rollback_indexes(stored_rollback_indexes); } std::map MakeRollbackIndexes(uint64_t slot_0_value, uint64_t slot_1_value) { return std::map{{0, slot_0_value}, {1, slot_1_value}}; } FakeAvbOps ops_; }; #define ExpMD(a_pri, \ a_tries, \ a_success, \ b_pri, \ b_tries, \ b_success, \ stored_rollback_indexes) \ do { \ AvbABData data; \ EXPECT_EQ(AVB_IO_RESULT_OK, \ ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data)); \ EXPECT_EQ(a_pri, data.slots[0].priority); \ EXPECT_EQ(a_tries, data.slots[0].tries_remaining); \ EXPECT_EQ(a_success ? 1 : 0, data.slots[0].successful_boot); \ EXPECT_EQ(b_pri, data.slots[1].priority); \ EXPECT_EQ(b_tries, data.slots[1].tries_remaining); \ EXPECT_EQ(b_success ? 1 : 0, data.slots[1].successful_boot); \ EXPECT_EQ(stored_rollback_indexes, ops_.get_stored_rollback_indexes()); \ } while (0); TEST_F(AvbABFlowTest, MetadataReadAndWrite) { AvbABData data; AvbABData loaded; // First load from an uninitialized 'misc' partition. This should // not fail and just returned initialized data. EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded)); EXPECT_EQ(AVB_AB_MAX_PRIORITY, loaded.slots[0].priority); EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[0].tries_remaining); EXPECT_EQ(0, loaded.slots[0].successful_boot); EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, loaded.slots[1].priority); EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[1].tries_remaining); EXPECT_EQ(0, loaded.slots[1].successful_boot); // Then initialize and save well-known A/B metadata and check we // read back the same thing. avb_ab_data_init(&data); data.slots[0].priority = 2; data.slots[0].tries_remaining = 3; EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_write(ops_.avb_ab_ops(), &data)); EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded)); EXPECT_EQ(2, loaded.slots[0].priority); EXPECT_EQ(3, loaded.slots[0].tries_remaining); } TEST_F(AvbABFlowTest, EverythingIsValid) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(14, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 15, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(14, 0, 1, // A: pri, tries, successful 15, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Also check the other slot. SetMD(15, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, NoBootableSlots) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(0, 0, 0, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 0, 0, 0, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_EQ(nullptr, data); } TEST_F(AvbABFlowTest, TriesRemainingDecreasing) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(15, 3, 0, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 0, 0, 0, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 2, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Keep counting down... EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 1, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Last try... EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // And we're out of tries. At this point, (15, 0, 0) is normalized // to (0, 0, 0) so expect that. EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_EQ(nullptr, data); } TEST_F(AvbABFlowTest, TryingThenFallback) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(15, 2, 0, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 1, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Last try... EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // And we're out of tries. Check we fall back to slot B. EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, TriesRemainingNotDecreasingIfNotPriority) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(15, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 7, 0, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 7, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, InvalidSlotIsMarkedAsSuch) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; // Slot A is invalid. SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Slot B is invalid. SetMD(15, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_INV, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Both slots are invalid. SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_INV, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_EQ(nullptr, data); } TEST_F(AvbABFlowTest, UnverifiedSlotIsMarkedAsSuch) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; // Slot A fails verification. SetMD(15, 0, 1, SV_UNV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Slot B fails verification. SetMD(15, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_UNV, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Both slots fail verification. SetMD(15, 0, 1, SV_UNV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_UNV, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_EQ(nullptr, data); } TEST_F(AvbABFlowTest, RollbackIndexFailures) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; // Slot A rollback index failure for 'boot'. SetMD(15, 0, 1, SV_OK, 0, 2, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 2, 2, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(2, 2)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(2, 2)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Slot A rollback index failure for 'odm'. SetMD(15, 0, 1, SV_OK, 2, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 2, 2, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(2, 2)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(2, 2)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, StoredRollbackIndexBumped) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(15, 0, 1, SV_OK, 3, 3, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 3, 3, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(2, 2)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(3, 3)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // The case where different partitions have different rollback // index values. SetMD(15, 0, 1, SV_OK, 4, 9, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 5, 7, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(4, 7)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // If the slot with the low RI fails verification (or is invalid), // check that these low Rollback Indexs are not taken into account // after marking it as unbootable. SetMD(15, 0, 1, SV_INV, 4, 9, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 5, 7, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(5, 7)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, MarkSlotActive) { SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 11, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 0)); ExpMD(15, 7, 0, // A: pri, tries, successful 11, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes // Note how priority of slot A is altered to make room for newly // activated slot. SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 1)); ExpMD(14, 0, 1, // A: pri, tries, successful 15, 7, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes } TEST_F(AvbABFlowTest, MarkSlotUnbootable) { SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 11, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 0)); ExpMD(0, 0, 0, // A: pri, tries, successful 11, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes SetMD(15, 0, 1, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 1)); ExpMD(15, 0, 1, // A: pri, tries, successful 0, 0, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes } TEST_F(AvbABFlowTest, MarkSlotSuccessful) { SetMD(15, 5, 0, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 11, 3, 0, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0)); ExpMD(15, 0, 1, // A: pri, tries, successful 11, 3, 0, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes SetMD(15, 5, 0, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 1)); ExpMD(15, 5, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes // Marking an unbootable slot (A) as successful won't work (it's a // programmer error to do so)... notice however that the unbootable // slot is normalized in the process. SetMD(0, 3, 2, SV_INV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0)); ExpMD(0, 0, 0, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes } static AvbABData my_serialized_data; static AvbIOResult my_write_ab_metadata(AvbABOps* ops, const struct AvbABData* data) { avb_ab_data_update_crc_and_byteswap(data, &my_serialized_data); return AVB_IO_RESULT_OK; } static AvbIOResult my_read_ab_metadata(AvbABOps* ops, struct AvbABData* data) { if (!avb_ab_data_verify_and_byteswap(&my_serialized_data, data)) { avb_error( "Error validating A/B metadata from persistent storage. " "Resetting and writing new A/B metadata to persistent storage.\n"); avb_ab_data_init(data); return my_write_ab_metadata(ops, data); } return AVB_IO_RESULT_OK; } TEST_F(AvbABFlowTest, OtherMetadataStorage) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; // Use our own A/B storage routines (see above). ops_.avb_ab_ops()->read_ab_metadata = my_read_ab_metadata; ops_.avb_ab_ops()->write_ab_metadata = my_write_ab_metadata; SetMD(14, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 15, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(14, 0, 1, // A: pri, tries, successful 15, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Also check the other slot. SetMD(15, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Check that 'misc' hasn't been written to at all. std::string misc_data; base::FilePath misc_path = testdir_.Append("misc.img"); ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data)); EXPECT_EQ(size_t(MISC_PART_SIZE), misc_data.size()); for (size_t n = 0; n < misc_data.size(); n++) { ASSERT_EQ(uint8_t(misc_data[n]), 0); } } TEST_F(AvbABFlowTest, UnlockedUnverifiedSlot) { AvbSlotVerifyData* data; const char* requested_partitions[] = {"boot", NULL}; SetMD(14, 0, 1, SV_OK, 0, 0, // A: pri, tries, success, slot_validity, RIs 15, 0, 1, SV_UNV, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(14, 0, 1, // A: pri, tries, successful 15, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_b", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); // Also check the other slot. SetMD(15, 0, 1, SV_UNV, 0, 0, // A: pri, tries, success, slot_validity, RIs 14, 0, 1, SV_OK, 0, 0, // B: pri, tries, success, slot_validity, RIs MakeRollbackIndexes(0, 0)); // stored_rollback_indexes EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR, avb_ab_flow(ops_.avb_ab_ops(), requested_partitions, AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR, AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &data)); ExpMD(15, 0, 1, // A: pri, tries, successful 14, 0, 1, // B: pri, tries, successful MakeRollbackIndexes(0, 0)); // stored_rollback_indexes ASSERT_NE(nullptr, data); EXPECT_EQ("_a", std::string(data->ab_suffix)); avb_slot_verify_data_free(data); } TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorEmptyFile) { AvbABData data; base::FilePath misc_path = testdir_.Append("misc.img"); EXPECT_COMMAND(0, "./avbtool set_ab_metadata" " --misc_image %s" " --slot_data 13:3:0:11:2:1", misc_path.value().c_str()); EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data)); EXPECT_EQ(13, data.slots[0].priority); EXPECT_EQ(3, data.slots[0].tries_remaining); EXPECT_EQ(0, data.slots[0].successful_boot); EXPECT_EQ(11, data.slots[1].priority); EXPECT_EQ(2, data.slots[1].tries_remaining); EXPECT_EQ(1, data.slots[1].successful_boot); } TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorExistingFile) { AvbABData data; size_t n; size_t misc_size = 1024 * 1024; base::FilePath misc_path = GenerateImage("misc.img", misc_size); EXPECT_COMMAND(0, "./avbtool set_ab_metadata" " --misc_image %s" " --slot_data 12:2:1:10:5:0", misc_path.value().c_str()); EXPECT_EQ(AVB_IO_RESULT_OK, ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data)); EXPECT_EQ(12, data.slots[0].priority); EXPECT_EQ(2, data.slots[0].tries_remaining); EXPECT_EQ(1, data.slots[0].successful_boot); EXPECT_EQ(10, data.slots[1].priority); EXPECT_EQ(5, data.slots[1].tries_remaining); EXPECT_EQ(0, data.slots[1].successful_boot); std::string misc_data; ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data)); EXPECT_EQ(misc_size, misc_data.size()); for (n = 0; n < 2048; n++) { ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n)); } for (n = 2048 + 32; n < misc_data.size(); n++) { ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n)); } } } // namespace avb