/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" #include "bcd.h" #include "compress.h" #include "fileio.h" #include "tests.h" #include "utf8.h" /* Include the implementation directly, so we can poke at some internals. */ #include "bcd.c" static void load_bcd(const char *path, void **ret_bcd, size_t *ret_bcd_len) { size_t len; _cleanup_free_ char *fn = NULL, *compressed = NULL; assert_se(get_testdata_dir(path, &fn) >= 0); assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &compressed, &len) >= 0); assert_se(decompress_blob_zstd(compressed, len, ret_bcd, ret_bcd_len, SIZE_MAX) >= 0); } static void test_get_bcd_title_one( const char *path, const char16_t *title_expect, size_t title_len_expect) { size_t len; _cleanup_free_ void *bcd = NULL; log_info("/* %s(%s) */", __func__, path); load_bcd(path, &bcd, &len); char16_t *title = get_bcd_title(bcd, len); if (title_expect) { assert_se(title); assert_se(memcmp(title, title_expect, title_len_expect) == 0); } else assert_se(!title); } TEST(get_bcd_title) { test_get_bcd_title_one("test-bcd/win10.bcd.zst", u"Windows 10", sizeof(u"Windows 10")); test_get_bcd_title_one("test-bcd/description-bad-type.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/description-empty.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/description-missing.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/description-too-small.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/displayorder-bad-name.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/displayorder-bad-size.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/displayorder-bad-type.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/empty.bcd.zst", NULL, 0); } TEST(base_block) { size_t len; BaseBlock backup; uint8_t *bcd_base; _cleanup_free_ BaseBlock *bcd = NULL; load_bcd("test-bcd/win10.bcd.zst", (void **) &bcd, &len); backup = *bcd; bcd_base = (uint8_t *) bcd; assert_se(get_bcd_title(bcd_base, len)); /* Try various "corruptions" of the base block. */ assert_se(!get_bcd_title(bcd_base, sizeof(BaseBlock) - 1)); bcd->sig = 0; assert_se(!get_bcd_title(bcd_base, len)); *bcd = backup; bcd->version_minor = 2; assert_se(!get_bcd_title(bcd_base, len)); *bcd = backup; bcd->version_major = 4; assert_se(!get_bcd_title(bcd_base, len)); *bcd = backup; bcd->type = 1; assert_se(!get_bcd_title(bcd_base, len)); *bcd = backup; bcd->primary_seqnum++; assert_se(!get_bcd_title(bcd_base, len)); *bcd = backup; } TEST(bad_bcd) { size_t len; uint8_t *hbins; uint32_t offset; _cleanup_free_ void *bcd = NULL; /* This BCD hive has been manipulated to have bad offsets/sizes at various places. */ load_bcd("test-bcd/corrupt.bcd.zst", &bcd, &len); assert_se(len >= HIVE_CELL_OFFSET); hbins = (uint8_t *) bcd + HIVE_CELL_OFFSET; len -= HIVE_CELL_OFFSET; offset = ((BaseBlock *) bcd)->root_cell_offset; const Key *root = get_key(hbins, len, offset, "\0"); assert_se(root); assert_se(!get_key(hbins, sizeof(Key) - 1, offset, "\0")); assert_se(!get_key(hbins, len, offset, "\0BadOffset\0")); assert_se(!get_key(hbins, len, offset, "\0BadSig\0")); assert_se(!get_key(hbins, len, offset, "\0BadKeyNameLen\0")); assert_se(!get_key(hbins, len, offset, "\0SubkeyBadOffset\0Dummy\0")); assert_se(!get_key(hbins, len, offset, "\0SubkeyBadSig\0Dummy\0")); assert_se(!get_key(hbins, len, offset, "\0SubkeyBadNEntries\0Dummy\0")); assert_se(!get_key_value(hbins, len, root, "Dummy")); const Key *kv_bad_offset = get_key(hbins, len, offset, "\0KeyValuesBadOffset\0"); assert_se(kv_bad_offset); assert_se(!get_key_value(hbins, len, kv_bad_offset, "Dummy")); const Key *kv_bad_n_key_values = get_key(hbins, len, offset, "\0KeyValuesBadNKeyValues\0"); assert_se(kv_bad_n_key_values); assert_se(!get_key_value(hbins, len, kv_bad_n_key_values, "Dummy")); const Key *kv = get_key(hbins, len, offset, "\0KeyValues\0"); assert_se(kv); assert_se(!get_key_value(hbins, len, kv, "BadOffset")); assert_se(!get_key_value(hbins, len, kv, "BadSig")); assert_se(!get_key_value(hbins, len, kv, "BadNameLen")); assert_se(!get_key_value(hbins, len, kv, "InlineData")); assert_se(!get_key_value(hbins, len, kv, "BadDataOffset")); assert_se(!get_key_value(hbins, len, kv, "BadDataSize")); } TEST(argv_bcds) { for (int i = 1; i < saved_argc; i++) { size_t len; _cleanup_free_ void *bcd = NULL; assert_se(read_full_file_full( AT_FDCWD, saved_argv[i], UINT64_MAX, SIZE_MAX, 0, NULL, (char **) &bcd, &len) >= 0); char16_t *title = get_bcd_title(bcd, len); if (title) { _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, char16_strlen(title) * 2); log_info("%s: \"%s\"", saved_argv[i], title_utf8); } else log_info("%s: Bad BCD", saved_argv[i]); } } DEFINE_TEST_MAIN(LOG_INFO);