blob: a1a5c949b4ce551e03ef1d556aebc038b742ec5e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <lib/fit/defer.h>
#include <lib/zbi-format/zbi.h>
#include <lib/zbi/zbi.h>
#include <lib/zbitl/item.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <zircon/compiler.h>
#include <cstring>
#include <memory>
#include <pretty/hexdump.h>
#include <zxtest/zxtest.h>
const char kTestKernel[] = "4567";
constexpr size_t kKernelPayloadLen =
zbitl::AlignedPayloadLength(static_cast<uint32_t>(sizeof(kTestKernel)));
const char kTestCmdline[] = "0123";
constexpr size_t kCmdlinePayloadLen =
zbitl::AlignedPayloadLength(static_cast<uint32_t>(sizeof(kTestCmdline)));
const char kTestRD[] = "0123456789";
constexpr size_t kRdPayloadLen =
zbitl::AlignedPayloadLength(static_cast<uint32_t>(sizeof(kTestRD)));
const char kTestBootfs[] = "abcdefghijklmnopqrs";
constexpr size_t kBootfsPayloadLen =
zbitl::AlignedPayloadLength(static_cast<uint32_t>(sizeof(kTestBootfs)));
typedef struct test_zbi {
// Bootdata header.
zbi_header_t header;
zbi_header_t kernel_hdr;
char kernel_payload[kKernelPayloadLen];
zbi_header_t cmdline_hdr;
char cmdline_payload[kCmdlinePayloadLen];
zbi_header_t ramdisk_hdr;
char ramdisk_payload[kRdPayloadLen];
zbi_header_t bootfs_hdr;
char bootfs_payload[kBootfsPayloadLen];
} test_zbi_t;
typedef struct single_entry_test_zbi {
zbi_header_t container = zbi_container_header(0);
zbi_header_t entry_header;
int8_t entry_payload[8];
} single_entry_test_zbi_t;
static_assert(offsetof(test_zbi, kernel_hdr) == sizeof(test_zbi::header));
static_assert(offsetof(test_zbi, cmdline_hdr) ==
offsetof(test_zbi, kernel_payload[kKernelPayloadLen]));
static_assert(offsetof(test_zbi, ramdisk_hdr) ==
offsetof(test_zbi, cmdline_payload[kCmdlinePayloadLen]));
static_assert(offsetof(test_zbi, bootfs_hdr) == offsetof(test_zbi, ramdisk_payload[kRdPayloadLen]));
static_assert(sizeof(test_zbi_t) % ZBI_ALIGNMENT == 0, "");
static void init_zbi_header(zbi_header_t* hdr) {
hdr->flags = ZBI_FLAGS_VERSION;
hdr->reserved0 = 0;
hdr->reserved1 = 0;
hdr->magic = ZBI_ITEM_MAGIC;
hdr->crc32 = ZBI_ITEM_NO_CRC32;
hdr->extra = 0;
}
static uint8_t* get_test_zbi_extra(const size_t extra_bytes) {
const size_t kAllocSize = sizeof(test_zbi_t) + extra_bytes;
test_zbi_t* result = reinterpret_cast<test_zbi_t*>(malloc(kAllocSize));
if (!result) {
return nullptr;
}
// Extra bytes are filled with non-zero bytes to test zero padding.
if (extra_bytes > 0) {
memset(result, 0xab, kAllocSize);
}
memset(result, 0, sizeof(*result));
init_zbi_header(&result->header);
result->header.type = ZBI_TYPE_CONTAINER;
result->header.extra = ZBI_CONTAINER_MAGIC;
init_zbi_header(&result->kernel_hdr);
#if defined(__aarch64__)
result->kernel_hdr.type = ZBI_TYPE_KERNEL_ARM64;
#elif defined(__x86_64__) || defined(__i386__)
result->kernel_hdr.type = ZBI_TYPE_KERNEL_X64;
#endif
strcpy(result->kernel_payload, kTestKernel);
result->kernel_hdr.length = static_cast<uint32_t>(sizeof(kTestKernel));
init_zbi_header(&result->cmdline_hdr);
result->cmdline_hdr.type = ZBI_TYPE_CMDLINE;
strcpy(result->cmdline_payload, kTestCmdline);
result->cmdline_hdr.length = static_cast<uint32_t>(sizeof(kTestCmdline));
init_zbi_header(&result->ramdisk_hdr);
result->ramdisk_hdr.type = ZBI_TYPE_STORAGE_RAMDISK;
strcpy(result->ramdisk_payload, kTestRD);
result->ramdisk_hdr.length = static_cast<uint32_t>(sizeof(kTestRD));
init_zbi_header(&result->bootfs_hdr);
result->bootfs_hdr.type = ZBI_TYPE_STORAGE_BOOTFS;
strcpy(result->bootfs_payload, kTestBootfs);
result->bootfs_hdr.length = static_cast<uint32_t>(sizeof(kTestBootfs));
// The container's length is always kept aligned, though each item
// header within the container might have an unaligned length and
// padding bytes after that item's payload so that the following header
// (or the end of the container) is aligned.
result->header.length = static_cast<uint32_t>(sizeof(*result) - sizeof(zbi_header_t));
return reinterpret_cast<uint8_t*>(result);
}
static uint8_t* get_test_zbi() { return get_test_zbi_extra(0); }
static zbi_result_t check_contents(zbi_header_t* hdr, void* payload, void* cookie) {
const char* expected = nullptr;
const char* actual = reinterpret_cast<const char*>(payload);
switch (hdr->type) {
case ZBI_TYPE_KERNEL_X64:
case ZBI_TYPE_KERNEL_ARM64:
expected = kTestKernel;
break;
case ZBI_TYPE_CMDLINE:
expected = kTestCmdline;
break;
case ZBI_TYPE_STORAGE_RAMDISK:
expected = kTestRD;
break;
case ZBI_TYPE_STORAGE_BOOTFS:
expected = kTestBootfs;
break;
default:
return ZBI_RESULT_ERROR;
}
int* itemsProcessed = reinterpret_cast<int*>(cookie);
(*itemsProcessed)++;
if (!strcmp(expected, actual)) {
return ZBI_RESULT_OK;
} else {
return ZBI_RESULT_ERROR;
}
}
TEST(ZbiTests, ZbiTestInit) {
alignas(ZBI_ALIGNMENT) uint8_t buffer[sizeof(zbi_header_t)];
ASSERT_EQ(zbi_init(buffer, sizeof(buffer)), ZBI_RESULT_OK);
auto* zbi = reinterpret_cast<zbi_header_t*>(buffer);
ASSERT_EQ(zbi->type, ZBI_TYPE_CONTAINER);
}
TEST(ZbiTests, ZbiTestInitTooSmall) {
alignas(ZBI_ALIGNMENT) uint8_t buffer[sizeof(zbi_header_t) - 1];
ASSERT_EQ(zbi_init(buffer, sizeof(buffer)), ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestInitNotAligned) {
alignas(ZBI_ALIGNMENT) uint8_t buffer[sizeof(zbi_header_t) + 1];
void* misaligned_buffer = &buffer[1];
ASSERT_EQ(zbi_init(misaligned_buffer, sizeof(zbi_header_t)), ZBI_RESULT_BAD_ALIGNMENT);
}
TEST(ZbiTests, ZbiTestInitNullBuffer) {
ASSERT_EQ(zbi_init(nullptr, sizeof(zbi_header_t)), ZBI_RESULT_ERROR);
}
// TODO(https://fxbug.dev/42129992): Consider pulling out the check logic into a common helper.
TEST(ZbiTests, ZbiTestCheckEmptyContainer) {
zbi_header_t container = zbi_container_header(0);
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_OK);
}
TEST(ZbiTests, ZbiTestCheckEmptyContainerWithErr) {
zbi_header_t container = zbi_container_header(0);
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check(&container, &err), ZBI_RESULT_OK);
ASSERT_EQ(err, nullptr);
}
TEST(ZbiTests, ZbiTestCheckContainerBadType) {
zbi_header_t container = zbi_container_header(0);
container.type = 0;
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_BAD_TYPE);
}
TEST(ZbiTests, ZbiTestCheckContainerBadTypeWithErr) {
zbi_header_t container = zbi_container_header(0);
container.type = 0;
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check(&container, &err), ZBI_RESULT_BAD_TYPE);
ASSERT_EQ(err, &container);
}
TEST(ZbiTests, ZbiTestCheckContainerBadExtra) {
zbi_header_t container = zbi_container_header(0);
container.extra = 0;
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_BAD_MAGIC);
}
TEST(ZbiTests, ZbiTestCheckContainerBadMagic) {
zbi_header_t container = zbi_container_header(0);
container.magic = 0;
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_BAD_MAGIC);
}
TEST(ZbiTests, ZbiTestCheckContainerBadVersion) {
zbi_header_t container = zbi_container_header(0);
container.flags &= ~ZBI_FLAGS_VERSION;
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_BAD_VERSION);
}
TEST(ZbiTests, ZbiTestCheckContainerBadCrc32) {
zbi_header_t container = zbi_container_header(0);
// Entries with no checksum must have the crc32 field set to ZBI_ITEM_NO_CRC32.
container.flags &= ~ZBI_FLAGS_CRC32;
container.crc32 = 0;
ASSERT_EQ(zbi_check(&container, nullptr), ZBI_RESULT_BAD_CRC);
}
TEST(ZbiTests, ZbiTestCheckTestZbi) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
ASSERT_EQ(zbi_check(zbi, nullptr), ZBI_RESULT_OK);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiWithErr) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check(zbi, &err), ZBI_RESULT_OK);
ASSERT_EQ(err, nullptr);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiNull) {
ASSERT_EQ(zbi_check(nullptr, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCheckFirstBadEntryIsMarked) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->cmdline_hdr.magic = 0;
zbi->ramdisk_hdr.magic = 0;
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check(zbi, &err), ZBI_RESULT_BAD_MAGIC);
ASSERT_EQ(err, &zbi->cmdline_hdr);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiBadMagic) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->cmdline_hdr.magic = 0;
EXPECT_EQ(zbi_check(zbi, nullptr), ZBI_RESULT_BAD_MAGIC);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiBadMagicWithErr) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->cmdline_hdr.magic = 0;
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check(zbi, &err), ZBI_RESULT_BAD_MAGIC);
ASSERT_EQ(err, &zbi->cmdline_hdr);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiBadVersion) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->cmdline_hdr.flags &= ~ZBI_FLAGS_VERSION;
EXPECT_EQ(zbi_check(zbi, nullptr), ZBI_RESULT_BAD_VERSION);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiBadCrc32) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->cmdline_hdr.flags &= ~ZBI_FLAGS_CRC32;
zbi->cmdline_hdr.crc32 = 0;
ASSERT_EQ(zbi_check(zbi, nullptr), ZBI_RESULT_BAD_CRC);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckTestZbiTruncated) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->header.length = 1;
ASSERT_EQ(zbi_check(zbi, nullptr), ZBI_RESULT_ERR_TRUNCATED);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbi) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
ASSERT_EQ(zbi_check_bootable(zbi, nullptr), ZBI_RESULT_OK);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbiWithErr) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check_bootable(zbi, &err), ZBI_RESULT_OK);
ASSERT_EQ(err, nullptr);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbiNull) {
ASSERT_EQ(zbi_check_bootable(nullptr, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbiTruncated) {
zbi_header_t container = zbi_container_header(0);
container.length = 0;
ASSERT_EQ(zbi_check_bootable(&container, nullptr), ZBI_RESULT_ERR_TRUNCATED);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbiWrongArch) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->kernel_hdr.type = 0;
ASSERT_EQ(zbi_check_bootable(zbi, nullptr), ZBI_RESULT_INCOMPLETE_KERNEL);
free(zbi);
}
TEST(ZbiTests, ZbiTestCheckCompleteTestZbiWrongArchWithErr) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
zbi->kernel_hdr.type = 0;
zbi_header_t* err = nullptr;
EXPECT_EQ(zbi_check_bootable(zbi, &err), ZBI_RESULT_INCOMPLETE_KERNEL);
ASSERT_EQ(err, &zbi->kernel_hdr);
free(zbi);
}
static zbi_result_t count_items_callback(zbi_header_t* header, void* payload, void* cookie) {
*reinterpret_cast<uint32_t*>(cookie) += 1;
return ZBI_RESULT_OK;
}
TEST(ZbiTests, ZbiTestForEachTestZbiNull) {
ASSERT_EQ(zbi_for_each(nullptr, count_items_callback, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestForEachTestZbiNullCallback) {
zbi_header_t container = zbi_container_header(0);
ASSERT_EQ(zbi_for_each(&container, nullptr, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestForEachTestZbiContainer) {
zbi_header_t container = zbi_container_header(0);
uint32_t count = 0;
// The callback should be invoked with ZBI items and not the container.
EXPECT_EQ(zbi_for_each(&container, count_items_callback, &count), ZBI_RESULT_OK);
ASSERT_EQ(count, 0);
}
TEST(ZbiTests, ZbiTestForEachTestZbiTruncated) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
// Container length does not include the size of the container header
zbi->header.length = offsetof(test_zbi_t, cmdline_payload) - sizeof(zbi_header_t);
uint32_t count = 0;
// Expect the first two entries to be counted.
EXPECT_EQ(zbi_for_each(zbi, count_items_callback, &count), ZBI_RESULT_ERR_TRUNCATED);
ASSERT_EQ(count, 2);
free(zbi);
}
TEST(ZbiTests, ZbiTestForEachTestZbiItems) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
uint32_t count = 0;
EXPECT_EQ(zbi_for_each(zbi, count_items_callback, &count), ZBI_RESULT_OK);
ASSERT_EQ(count, 4);
free(zbi);
}
static zbi_result_t modify_payload_callback(zbi_header_t* header, void* payload, void* cookie) {
if (cookie)
return ZBI_RESULT_ERROR;
std::memset(payload, 'B', 1);
return ZBI_RESULT_OK;
}
TEST(ZbiTests, ZbiTestForEachTestZbiItemsNoCookie) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
std::memset(zbi->kernel_payload, 'A', 1);
std::memset(zbi->cmdline_payload, 'A', 1);
std::memset(zbi->ramdisk_payload, 'A', 1);
std::memset(zbi->bootfs_payload, 'A', 1);
EXPECT_EQ(zbi_for_each(zbi, modify_payload_callback, nullptr), ZBI_RESULT_OK);
EXPECT_EQ(zbi->kernel_payload[0], 'B');
EXPECT_EQ(zbi->cmdline_payload[0], 'B');
EXPECT_EQ(zbi->ramdisk_payload[0], 'B');
EXPECT_EQ(zbi->bootfs_payload[0], 'B');
free(zbi);
}
static zbi_result_t modify_payload_then_error_callback(zbi_header_t* header, void* payload,
void* cookie) {
auto* count = reinterpret_cast<uint32_t*>(cookie);
if (*count > 0) {
return ZBI_RESULT_ERROR;
}
std::memset(payload, 'B', 1);
*count += 1;
return ZBI_RESULT_OK;
}
TEST(ZbiTests, ZbiTestForEachTestZbiItemsCallbackError) {
test_zbi_t* zbi = reinterpret_cast<test_zbi_t*>(get_test_zbi());
std::memset(zbi->kernel_payload, 'A', 1);
std::memset(zbi->cmdline_payload, 'A', 1);
std::memset(zbi->ramdisk_payload, 'A', 1);
std::memset(zbi->bootfs_payload, 'A', 1);
uint32_t count = 0;
// Only the first entry should be modified.
EXPECT_EQ(zbi_for_each(zbi, modify_payload_then_error_callback, &count), ZBI_RESULT_ERROR);
EXPECT_EQ(count, 1);
EXPECT_EQ(zbi->kernel_payload[0], 'B');
EXPECT_EQ(zbi->cmdline_payload[0], 'A');
EXPECT_EQ(zbi->ramdisk_payload[0], 'A');
EXPECT_EQ(zbi->bootfs_payload[0], 'A');
free(zbi);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbi) {
// The ZBI has space for the container and an entry with an 8-byte payload.
single_entry_test_zbi_t zbi;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), ZBI_TYPE_CONTAINER, 0, 0, ZBI_ALIGNMENT, &payload),
ZBI_RESULT_OK);
// Verify the header and confirm the flag version was added.
EXPECT_EQ(zbi.entry_header.type, ZBI_TYPE_CONTAINER);
EXPECT_EQ(zbi.entry_header.flags & ZBI_FLAGS_VERSION, ZBI_FLAGS_VERSION);
// Verify the pointer points to the newly created entry payload.
EXPECT_EQ(payload, zbi.entry_payload);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiNull) {
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(nullptr, sizeof(single_entry_test_zbi_t), 0, 0, 0, 0, &payload),
ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiNullPayload) {
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), ZBI_TYPE_DISCARD, 0, 0, 0, nullptr), ZBI_RESULT_OK);
ASSERT_EQ(zbi.entry_header.type, ZBI_TYPE_DISCARD);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiCrc32NotSupported) {
single_entry_test_zbi_t zbi;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), 0, 0, ZBI_FLAGS_CRC32, 0, &payload),
ZBI_RESULT_ERROR);
}
TEST(ZbiTest, ZbiTestCreateEntryTestPayloadTooBig) {
single_entry_test_zbi_t zbi;
void* payload = nullptr;
size_t payload_length = std::numeric_limits<uint32_t>::max();
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), 0, 0, 0, payload_length + 1, &payload),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiNotContainer) {
single_entry_test_zbi_t zbi;
zbi.container.type = 0;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), 0, 0, 0, 0, &payload), ZBI_RESULT_BAD_TYPE);
}
// create entry tests
TEST(ZbiTests, ZbiTestCreateEntryTestZbiCapacitySmallerThanCurrentSize) {
zbi_header_t container = zbi_container_header(0);
container.length = 2;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(&container, /*capacity=*/1, 0, 0, 0, 0, &payload), ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiFull) {
zbi_header_t container = zbi_container_header(0);
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry(&container, /*capacity=*/sizeof(container), 0, 0, 0,
/*payload_length=*/1, &payload),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryTestZbiPayloadTooLarge) {
single_entry_test_zbi_t zbi;
uint32_t capacity = sizeof(zbi);
void* payload = nullptr;
// Enough space for the entry header but not the payload.
ASSERT_EQ(zbi_create_entry(&zbi, capacity, 0, 0, 0,
/*payload_length=*/capacity, &payload),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbi) {
// The ZBI will have space for the container and an entry with a small payload.
single_entry_test_zbi_t zbi;
uint32_t payload = 0xABCDABCD;
ASSERT_EQ(zbi_create_entry_with_payload(&zbi, sizeof(zbi), ZBI_TYPE_CONTAINER, 0, 0, &payload,
sizeof(payload)),
ZBI_RESULT_OK);
// Verify the contents of the payload.
ASSERT_BYTES_EQ(reinterpret_cast<uint8_t*>(zbi.entry_payload),
reinterpret_cast<uint8_t*>(&payload), sizeof(payload), "Mismatched payloads.");
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiNull) {
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry_with_payload(nullptr, 0, 0, 0, 0, &payload, 0), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiNullPayload) {
zbi_header_t container = zbi_container_header(0);
ASSERT_EQ(zbi_create_entry_with_payload(&container, 0, 0, 0, 0, nullptr, 0), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiCrc32NotSupported) {
zbi_header_t container = zbi_container_header(0);
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry_with_payload(&container, 0, 0, 0, ZBI_FLAGS_CRC32, &payload, 0),
ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestPayloadTooBig) {
zbi_header_t container = zbi_container_header(0);
void* payload = nullptr;
size_t payload_length = std::numeric_limits<uint32_t>::max();
ASSERT_EQ(zbi_create_entry_with_payload(&container, 0, 0, 0, 0, &payload, payload_length + 1),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiNotContainer) {
zbi_header_t container = zbi_container_header(0);
container.type = 0;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry_with_payload(&container, 0, 0, 0, 0, &payload, 0),
ZBI_RESULT_BAD_TYPE);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiCapacitySmallerThanCurrentSize) {
zbi_header_t container = zbi_container_header(0);
container.length = 2;
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry_with_payload(&container, /*capacity=*/1, 0, 0, 0, &payload, 0),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestCreateEntryWithPayloadTestZbiSectionTooLarge) {
zbi_header_t container = zbi_container_header(0);
void* payload = nullptr;
ASSERT_EQ(zbi_create_entry_with_payload(&container, /*capacity=*/1, 0, 0, 0, &payload,
/*payload_length=*/2),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, GetNextEntryPayload) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi), &payload, &max_payload_length),
ZBI_RESULT_OK);
ASSERT_EQ(payload, &zbi.entry_payload);
ASSERT_EQ(max_payload_length, sizeof(zbi.entry_payload));
}
TEST(ZbiTests, GetNextEntryPayloadZeroLength) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(
zbi_get_next_entry_payload(&zbi, 2 * sizeof(zbi_header_t), &payload, &max_payload_length),
ZBI_RESULT_OK);
// Zero-length payload is OK, as long as we can fit the header.
ASSERT_EQ(payload, &zbi.entry_payload);
ASSERT_EQ(max_payload_length, 0);
}
TEST(ZbiTests, GetNextEntryPayloadUnalignedCapacity) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi) + 1, &payload, &max_payload_length),
ZBI_RESULT_OK);
// Max payload length should ignore the extra unaligned byte.
ASSERT_EQ(payload, &zbi.entry_payload);
ASSERT_EQ(max_payload_length, sizeof(zbi.entry_payload));
}
TEST(ZbiTests, GetNextEntryPayloadMaxZbiCapacity) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, SIZE_MAX, &payload, &max_payload_length),
ZBI_RESULT_OK);
// ZBI contents (in this case, 1 header + 1 payload) must fit in a uint32_t.
// Masking out 0x07 bits is for proper ZBI alignment.
ASSERT_EQ(payload, &zbi.entry_payload);
ASSERT_EQ(sizeof(zbi_header_t) + max_payload_length, UINT32_MAX & ~0x07);
}
TEST(ZbiTests, GetNextEntryPayloadAndCreate) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi), &payload, &max_payload_length),
ZBI_RESULT_OK);
const uint32_t data = 0x0123ABCD;
memcpy(payload, &data, sizeof(data));
ASSERT_EQ(zbi_create_entry(&zbi, sizeof(zbi), ZBI_TYPE_DISCARD, 0, 0, sizeof(data), nullptr),
ZBI_RESULT_OK);
// Make sure zbi_create_entry() left the pre-loaded payload intact.
ASSERT_BYTES_EQ(reinterpret_cast<uint8_t*>(zbi.entry_payload),
reinterpret_cast<const uint8_t*>(&data), sizeof(data));
}
TEST(ZbiTests, GetNextEntryPayloadWithNullZbi) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
ASSERT_EQ(zbi_get_next_entry_payload(nullptr, sizeof(single_entry_test_zbi_t), &payload,
&max_payload_length),
ZBI_RESULT_ERROR);
}
TEST(ZbiTests, GetNextEntryPayloadWithNullPayload) {
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi), nullptr, &max_payload_length),
ZBI_RESULT_ERROR);
}
TEST(ZbiTests, GetNextEntryPayloadWithNullMaxPayloadLength) {
void* payload = nullptr;
single_entry_test_zbi_t zbi;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi), &payload, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, GetNextEntryPayloadMissingZbiContainer) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
zbi.container.type = 0;
ASSERT_EQ(zbi_get_next_entry_payload(&zbi, sizeof(zbi), &payload, &max_payload_length),
ZBI_RESULT_BAD_TYPE);
}
// Should never happen, but make sure we can handle if the capacity is smaller
// than the container header.
TEST(ZbiTests, GetNextEntryPayloadCapacitySmallerThanHeaderSize) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
ASSERT_EQ(
zbi_get_next_entry_payload(&zbi, sizeof(zbi_header_t) - 1, &payload, &max_payload_length),
ZBI_RESULT_TOO_BIG);
}
// Should never happen, but make sure we can handle if the capacity is smaller
// than the container length.
TEST(ZbiTests, GetNextEntryPayloadCapacitySmallerThanCurrentSize) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
zbi.container.length = 100;
ASSERT_EQ(
zbi_get_next_entry_payload(&zbi, sizeof(zbi_header_t) + 99, &payload, &max_payload_length),
ZBI_RESULT_TOO_BIG);
}
// Can't fit another header into the ZBI.
TEST(ZbiTests, GetNextEntryPayloadHeaderTooLarge) {
void* payload = nullptr;
uint32_t max_payload_length = 0;
single_entry_test_zbi_t zbi;
zbi.container.length = 100;
ASSERT_EQ(
zbi_get_next_entry_payload(&zbi, sizeof(zbi_header_t) + 101, &payload, &max_payload_length),
ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestExtendTestZbi) {
single_entry_test_zbi_t dst_zbi;
single_entry_test_zbi_t src_zbi;
uint32_t payload = 0xABCDABCD;
ASSERT_EQ(zbi_create_entry_with_payload(&src_zbi, sizeof(src_zbi), ZBI_TYPE_CONTAINER, 0, 0,
&payload, sizeof(payload)),
ZBI_RESULT_OK);
// Extend dst to include src's entry.
ASSERT_EQ(zbi_extend(&dst_zbi, sizeof(dst_zbi), &src_zbi), ZBI_RESULT_OK);
ASSERT_BYTES_EQ(reinterpret_cast<uint8_t*>(dst_zbi.entry_payload),
reinterpret_cast<uint8_t*>(&payload), sizeof(payload), "Mismatched payload.");
}
TEST(ZbiTests, ZbiTestExtendTestZbiDstNull) {
zbi_header_t zbi = zbi_container_header(0);
ASSERT_EQ(zbi_extend(nullptr, 0, &zbi), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestExtendTestZbiSrcNull) {
zbi_header_t zbi = zbi_container_header(0);
ASSERT_EQ(zbi_extend(&zbi, 0, nullptr), ZBI_RESULT_ERROR);
}
TEST(ZbiTests, ZbiTestExtendTestZbiDstNotContainer) {
zbi_header_t src = zbi_container_header(0);
zbi_header_t dst = zbi_container_header(0);
dst.type = 0;
ASSERT_EQ(zbi_extend(&dst, 0, &src), ZBI_RESULT_BAD_TYPE);
}
TEST(ZbiTests, ZbiTestExtendTestZbiSrcNotContainer) {
zbi_header_t src = zbi_container_header(0);
src.type = 0;
zbi_header_t dst = zbi_container_header(0);
ASSERT_EQ(zbi_extend(&dst, 0, &src), ZBI_RESULT_BAD_TYPE);
}
TEST(ZbiTests, ZbiTestExtendTestZbiCapacitySmallerThanDstLength) {
zbi_header_t src = zbi_container_header(0);
zbi_header_t dst = zbi_container_header(1);
ASSERT_EQ(zbi_extend(&dst, 0, &src), ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestExtendTestZbiCapacitySmallerThanDstAlignedLength) {
zbi_header_t src = zbi_container_header(0);
zbi_header_t dst = zbi_container_header(6);
ASSERT_EQ(zbi_extend(&dst, /*capacity=*/7, &src), ZBI_RESULT_TOO_BIG);
}
TEST(ZbiTests, ZbiTestExtendTestZbiSrcTooLarge) {
zbi_header_t src = zbi_container_header(ZBI_ALIGNMENT + 1);
zbi_header_t dst = zbi_container_header(ZBI_ALIGNMENT);
ASSERT_EQ(zbi_extend(&dst, /*capacity=*/ZBI_ALIGNMENT, &src), ZBI_RESULT_TOO_BIG);
}
// Test the happy case.
// Make two zbi containers, extend the first by tacking the second to the back
// of it. Observe that everything went okay.
TEST(ZbiTests, ZbiTestExtendOkay) {
// Create a dst zbi that has enough space to contain the src zbi.
uint8_t* src_buf = get_test_zbi();
const size_t kExtraBytes = (reinterpret_cast<zbi_header_t*>(src_buf))->length;
const size_t kDstCapacity = kExtraBytes + sizeof(test_zbi);
uint8_t* dst_buf = get_test_zbi_extra(kExtraBytes);
auto cleanup = fit::defer([src_buf, dst_buf] {
free(src_buf);
free(dst_buf);
});
// Count the number of sections in the source buffer and the destination
// buffer.
uint32_t src_sections = 0;
uint32_t dst_sections = 0;
uint32_t combined_sections = 0;
ASSERT_EQ(zbi_for_each(src_buf, check_contents, &src_sections), ZBI_RESULT_OK);
ASSERT_EQ(zbi_for_each(dst_buf, check_contents, &dst_sections), ZBI_RESULT_OK);
EXPECT_EQ(zbi_extend(dst_buf, kDstCapacity, src_buf), ZBI_RESULT_OK);
ASSERT_EQ(zbi_for_each(dst_buf, check_contents, &combined_sections), ZBI_RESULT_OK);
ASSERT_EQ(src_sections + dst_sections, combined_sections);
}
TEST(ZbiTests, ZbiTestNoOverflow) {
constexpr size_t kBufferSize = 1024;
constexpr size_t kUsableBufferSize = kBufferSize / 2;
constexpr uint8_t kSentinel = 0xab;
static_assert(kBufferSize % ZBI_ALIGNMENT == 0);
static_assert(kUsableBufferSize % ZBI_ALIGNMENT == 0);
uint8_t* dst_buffer = new uint8_t[kBufferSize];
std::unique_ptr<uint8_t[]> dst_deleter;
dst_deleter.reset(dst_buffer);
memset(dst_buffer, kSentinel, kBufferSize);
uint8_t* src_buffer = new uint8_t[kBufferSize];
std::unique_ptr<uint8_t[]> src_deleter;
src_deleter.reset(src_buffer);
memset(src_buffer, kSentinel, kBufferSize);
uint8_t* test_data = new uint8_t[kUsableBufferSize];
std::unique_ptr<uint8_t[]> test_data_deleter;
test_data_deleter.reset(test_data);
memset(test_data, 0x12, kUsableBufferSize);
ASSERT_EQ(zbi_init(dst_buffer, kUsableBufferSize), ZBI_RESULT_OK);
ASSERT_EQ(zbi_init(src_buffer, kUsableBufferSize), ZBI_RESULT_OK);
ASSERT_EQ(zbi_create_entry_with_payload(
src_buffer, kUsableBufferSize, ZBI_TYPE_CMDLINE,
0, // Extra
0, // Flags
test_data,
kUsableBufferSize -
(sizeof(zbi_header_t) * 2) // Leave room for ZBI header _and_ section header
),
ZBI_RESULT_OK);
ASSERT_EQ(zbi_extend(dst_buffer, kUsableBufferSize, src_buffer), ZBI_RESULT_OK);
// Make sure we haven't trampled any bytes that we shouldn't have.
for (size_t i = kUsableBufferSize; i < kUsableBufferSize; i++) {
ASSERT_EQ(dst_buffer[i], kSentinel);
}
ASSERT_EQ(zbi_init(dst_buffer, kUsableBufferSize), ZBI_RESULT_OK);
ASSERT_EQ(zbi_init(src_buffer, kUsableBufferSize + 1), ZBI_RESULT_OK);
ASSERT_EQ(zbi_create_entry_with_payload(
src_buffer, zbitl::AlignedPayloadLength(kUsableBufferSize + 1), ZBI_TYPE_CMDLINE,
0, // Extra
0, // Flags
test_data,
(kUsableBufferSize + 1) - (sizeof(zbi_header_t) * 2) // This payload is too big.
),
ZBI_RESULT_OK);
ASSERT_NE(zbi_extend(dst_buffer, kUsableBufferSize, src_buffer), ZBI_RESULT_OK);
}