/*
* 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 <string.h>
#include <map>
#include <vector>
#include <gtest/gtest.h>
#include <libavb_ab/libavb_ab.h>
#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<const char*>(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<const char*>(data.magic), 4),
std::string(reinterpret_cast<const char*>(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<void*>(data.slots),
reinterpret_cast<void*>(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<uint8_t> misc;
misc.resize(MISC_PART_SIZE);
base::FilePath misc_path = testdir_.Append("misc.img");
EXPECT_EQ(misc.size(),
static_cast<const size_t>(
base::WriteFile(misc_path,
reinterpret_cast<const char*>(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<size_t, uint64_t>& 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<size_t, uint64_t> MakeRollbackIndexes(uint64_t slot_0_value,
uint64_t slot_1_value) {
return std::map<size_t, uint64_t>{{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