blob: 4718a9fbe06131fdecc6f6a69c1a5010746beb22 [file] [edit]
/* Copyright 2025 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "unit_tests.h"
#include "updater.h"
#include "cgptlib_internal.h"
#include "file_type.h"
#include "futility_options.h"
#include "host_misc.h"
#include <sys/mman.h>
#define IMAGE_MAIN GET_WORK_COPY_TEST_DATA_FILE_PATH("image.bin")
#define FILE_TEMP GET_WORK_COPY_TEST_DATA_FILE_PATH("file-temp")
#define FILE_SMALL GET_WORK_COPY_TEST_DATA_FILE_PATH("file-small")
#define FILE_NONEXISTENT GET_WORK_COPY_TEST_DATA_FILE_PATH("nonexistent")
#define FILE_READONLY GET_WORK_COPY_TEST_DATA_FILE_PATH("read-only")
const char small_data[] = "small";
#define FILE_SMALL_SIZE (ARRAY_SIZE(small_data) - 1)
static enum unit_result prepare_test_data(void)
{
UNIT_TEST_BEGIN;
UNIT_ASSERT(system("rm -rf " WORK_COPY_TEST_DATA_DIR) == 0);
UNIT_ASSERT(system("mkdir -p " WORK_COPY_TEST_DATA_DIR) == 0);
UNIT_ASSERT(futil_copy_file(GET_SOURCE_TEST_DATA_FILE_PATH("image-steelix.bin"),
IMAGE_MAIN) != -1);
UNIT_ASSERT(vb2_write_file(FILE_SMALL, small_data, FILE_SMALL_SIZE) == VB2_SUCCESS);
remove(FILE_NONEXISTENT);
UNIT_ASSERT(system("touch " FILE_READONLY) == 0);
UNIT_ASSERT(system("chmod 444 " FILE_READONLY) == 0);
unit_cleanup:
UNIT_TEST_RETURN;
}
static enum unit_result test_gbb(void)
{
UNIT_TEST_BEGIN;
struct firmware_image image = {0};
FmapAreaHeader *ah = NULL;
uint8_t *ptr = NULL;
uint32_t maxlen;
struct vb2_gbb_header *gbb, good_gbb = {0};
char *str = NULL;
UNIT_ASSERT(load_firmware_image(&image, IMAGE_MAIN, NULL) == 0);
ptr = fmap_find_by_name(image.data, image.size, image.fmap_header, "GBB", &ah);
UNIT_ASSERT(ptr != NULL);
gbb = (struct vb2_gbb_header *)ptr;
good_gbb = *gbb;
TEST_EQ(ft_recognize_gbb(ptr, ah->area_size), FILE_TYPE_GBB,
"File type recognize GBB: correct");
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, &maxlen), 1,
"Futil valid GBB header: correct");
TEST_EQ(ft_recognize_gbb(ptr, sizeof(struct vb2_gbb_header) - 1), FILE_TYPE_UNKNOWN,
"File type recognize GBB: too small");
TEST_EQ(futil_valid_gbb_header(gbb, gbb->header_size - 1, NULL), 0,
"Futil valid GBB header: too small");
memset(gbb->signature, 0, sizeof(gbb->signature));
TEST_EQ(ft_recognize_gbb(ptr, ah->area_size), FILE_TYPE_UNKNOWN,
"File type recognize GBB: invalid signature");
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid signature");
*gbb = good_gbb;
gbb->major_version = UINT16_MAX;
TEST_EQ(ft_recognize_gbb(ptr, ah->area_size), FILE_TYPE_UNKNOWN,
"File type recognize GBB: invalid major version");
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid major version");
*gbb = good_gbb;
gbb->header_size = ah->area_size + 1;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid header_size");
*gbb = good_gbb;
gbb->hwid_offset = EXPECTED_VB2_GBB_HEADER_SIZE - 1;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid hwid_offset");
*gbb = good_gbb;
gbb->hwid_offset = ah->area_size + 1;
gbb->hwid_size = 0;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid hwid_offset or hwid_size");
*gbb = good_gbb;
gbb->rootkey_offset = ah->area_size + 1;
gbb->rootkey_size = 0;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid rootkey_offset or rootkey_size");
*gbb = good_gbb;
gbb->bmpfv_offset = ah->area_size + 1;
gbb->bmpfv_size = 0;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid bmpfv_offset or bmpfv_size");
*gbb = good_gbb;
gbb->recovery_key_offset = EXPECTED_VB2_GBB_HEADER_SIZE - 1;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid recovery_key_offset");
*gbb = good_gbb;
gbb->recovery_key_size = ah->area_size + 1;
gbb->recovery_key_offset = EXPECTED_VB2_GBB_HEADER_SIZE;
TEST_EQ(futil_valid_gbb_header(gbb, ah->area_size, NULL), 0,
"Futil valid GBB header: invalid recovery_key_offset or recovery_key_offset");
*gbb = good_gbb;
str = calloc(1, gbb->hwid_size + 2);
UNIT_ASSERT(str != NULL);
memset(str, 'X', gbb->hwid_size + 1);
TEST_EQ(futil_set_gbb_hwid(gbb, str), -1, "Futil set GBB HWID: too big");
free(str);
*gbb = good_gbb;
str = (char *)"M";
TEST_EQ(futil_set_gbb_hwid(gbb, str), 0, "Futil set GBB HWID: valid");
TEST_EQ(strncmp((char *)gbb + gbb->hwid_offset, str, 2), 0, "Verifying");
*gbb = good_gbb;
gbb->minor_version = 1;
str = (char *)"N";
TEST_EQ(futil_set_gbb_hwid(gbb, str), 0, "Futil set GBB HWID: minor < 2");
TEST_EQ(strncmp((char *)gbb + gbb->hwid_offset, str, 2), 0, "Verifying");
str = NULL;
unit_cleanup:
free_firmware_image(&image);
UNIT_TEST_RETURN;
}
static enum unit_result test_futil_file_helpers(void)
{
UNIT_TEST_BEGIN;
int fd = -1;
TEST_EQ(futil_copy_file(FILE_SMALL, FILE_TEMP), FILE_SMALL_SIZE,
"Futil copy file: valid");
TEST_NEQ(futil_copy_file(FILE_NONEXISTENT, FILE_TEMP), 0,
"Futil copy file: nonexistent");
TEST_NEQ(futil_copy_file(FILE_TEMP, FILE_READONLY), 0, "Futil copy file: invalid");
TEST_EQ(futil_open_file(FILE_TEMP, &fd, FILE_RW), FILE_ERR_NONE, "Futil open file: rw");
TEST_TRUE(fd >= 0, "Verifying fd");
TEST_EQ(futil_close_file(fd), FILE_ERR_NONE, "Futil close file: rw");
TEST_EQ(futil_open_file(FILE_NONEXISTENT, &fd, FILE_RW), FILE_ERR_OPEN,
"Futil open file: rw nonexistent");
fd = -1;
TEST_EQ(futil_open_file(FILE_TEMP, &fd, FILE_RO), FILE_ERR_NONE, "Futil open file: ro");
TEST_TRUE(fd >= 0, "Verifying fd");
TEST_EQ(futil_close_file(fd), FILE_ERR_NONE, "Futil close file: ro");
TEST_EQ(futil_open_file(FILE_NONEXISTENT, &fd, FILE_RO), FILE_ERR_OPEN,
"Futil open file: ro nonexistent");
fd = -1;
UNIT_ASSERT(futil_open_file(FILE_TEMP, &fd, FILE_RW) == FILE_ERR_NONE);
TEST_TRUE(fd >= 0, "Verifying fd");
UNIT_ASSERT(futil_close_file(fd) == FILE_ERR_NONE);
TEST_EQ(futil_close_file(fd), FILE_ERR_CLOSE, "Futil close file: invalid");
unit_cleanup:
UNIT_TEST_RETURN;
}
static enum unit_result test_files_mmap(void)
{
UNIT_TEST_BEGIN;
int fd = -1;
uint8_t *data = NULL;
uint32_t size = 0;
TEST_EQ(futil_map_file(-1, FILE_RO, &data, &size), FILE_ERR_STAT,
"Futil map file: invalid fd");
fd = -1;
data = NULL;
size = 0;
UNIT_ASSERT(futil_open_file(FILE_TEMP, &fd, FILE_RO) == FILE_ERR_NONE);
TEST_TRUE(fd >= 0, "Verifying fd");
TEST_EQ(futil_map_file(fd, FILE_RO, &data, &size), FILE_ERR_NONE, "Futil map file");
TEST_EQ(futil_unmap_file(fd, FILE_RO, data, size), FILE_ERR_NONE, "Futil unmap file");
UNIT_ASSERT(futil_close_file(fd) == FILE_ERR_NONE);
fd = -1;
data = NULL;
size = 0;
TEST_NEQ(futil_open_and_map_file(FILE_NONEXISTENT, &fd, FILE_RO, &data, &size),
FILE_ERR_NONE, "Futil open and map file: nonexistent");
fd = -1;
data = NULL;
size = 0;
TEST_EQ(futil_open_and_map_file(FILE_TEMP, &fd, FILE_RO, &data, &size), FILE_ERR_NONE,
"Futil open and map file");
TEST_TRUE(fd >= 0, "Verifying fd");
TEST_EQ(futil_unmap_and_close_file(fd, FILE_RO, data, size), FILE_ERR_NONE,
"Futil unmap and close file");
TEST_NEQ(futil_unmap_and_close_file(fd, FILE_RO, data, size), FILE_ERR_NONE,
"Futil unmap and close file: invalid fd");
unit_cleanup:
UNIT_TEST_RETURN;
}
static enum unit_result test_misc(void)
{
UNIT_TEST_BEGIN;
size_t len = 4096;
uint8_t *ptr = malloc(4096);
GptHeader *h = (GptHeader *)(ptr + 512);
uint8_t *data = (uint8_t *)strdup("test");
UNIT_ASSERT(ptr != NULL && data != NULL);
/* Pretend we have a valid GPT. */
memcpy(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
h->revision = GPT_HEADER_REVISION;
h->size = MIN_SIZE_OF_HEADER + 1;
h->header_crc32 = HeaderCrc(h);
TEST_EQ(ft_recognize_gpt(ptr, len), FILE_TYPE_CHROMIUMOS_DISK,
"File type recognize GPT: valid");
memcpy(h->signature, "12345678", GPT_HEADER_SIGNATURE_SIZE);
TEST_EQ(ft_recognize_gpt(ptr, len), FILE_TYPE_UNKNOWN,
"File type recognize GPT: invalid signature");
memcpy(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE);
h->revision = ~GPT_HEADER_REVISION;
TEST_EQ(ft_recognize_gpt(ptr, len), FILE_TYPE_UNKNOWN,
"File type recognize GPT: invalid revision");
h->revision = GPT_HEADER_REVISION;
h->size = MAX_SIZE_OF_HEADER + 1;
TEST_EQ(ft_recognize_gpt(ptr, len), FILE_TYPE_UNKNOWN,
"File type recognize GPT: invalid size");
h->size = MIN_SIZE_OF_HEADER + 1;
h->header_crc32 = ~h->header_crc32; /* To change the checksum. */
TEST_EQ(ft_recognize_gpt(ptr, len), FILE_TYPE_UNKNOWN,
"File type recognize GPT: invalid crc32");
len = strlen((char *)data);
TEST_NEQ(write_to_file("test", FILE_READONLY, data, len), 0,
"Write to file: invalid file");
TEST_EQ(write_to_file("test", FILE_TEMP, data, 0), 0, "Write to file: zero bytes");
TEST_EQ(write_to_file("test", FILE_TEMP, data, len), 0, "Write to file: valid");
unit_cleanup:
free(data);
free(ptr);
UNIT_TEST_RETURN;
}
int main(int argc, char *argv[])
{
if (prepare_test_data() == UNIT_FAIL) {
ERROR("Failed to prepare data.\n");
return 1;
}
test_gbb();
test_futil_file_helpers();
test_files_mmap();
test_misc();
return !gTestSuccess;
}