1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "compress.h"
5 #include "fileio.h"
6 #include "tests.h"
7 #include "utf8.h"
8 
9 /* Include the implementation directly, so we can poke at some internals. */
10 #include "bcd.c"
11 
load_bcd(const char * path,void ** ret_bcd,size_t * ret_bcd_len)12 static void load_bcd(const char *path, void **ret_bcd, size_t *ret_bcd_len) {
13         size_t len;
14         _cleanup_free_ char *fn = NULL, *compressed = NULL;
15 
16         assert_se(get_testdata_dir(path, &fn) >= 0);
17         assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &compressed, &len) >= 0);
18         assert_se(decompress_blob_zstd(compressed, len, ret_bcd, ret_bcd_len, SIZE_MAX) >= 0);
19 }
20 
test_get_bcd_title_one(const char * path,const char16_t * title_expect,size_t title_len_expect)21 static void test_get_bcd_title_one(
22                 const char *path,
23                 const char16_t *title_expect,
24                 size_t title_len_expect) {
25 
26         size_t len;
27         _cleanup_free_ void *bcd = NULL;
28 
29         log_info("/* %s(%s) */", __func__, path);
30 
31         load_bcd(path, &bcd, &len);
32 
33         char16_t *title = get_bcd_title(bcd, len);
34         if (title_expect) {
35                 assert_se(title);
36                 assert_se(memcmp(title, title_expect, title_len_expect) == 0);
37         } else
38                 assert_se(!title);
39 }
40 
TEST(get_bcd_title)41 TEST(get_bcd_title) {
42         test_get_bcd_title_one("test-bcd/win10.bcd.zst", u"Windows 10", sizeof(u"Windows 10"));
43 
44         test_get_bcd_title_one("test-bcd/description-bad-type.bcd.zst", NULL, 0);
45         test_get_bcd_title_one("test-bcd/description-empty.bcd.zst", NULL, 0);
46         test_get_bcd_title_one("test-bcd/description-missing.bcd.zst", NULL, 0);
47         test_get_bcd_title_one("test-bcd/description-too-small.bcd.zst", NULL, 0);
48         test_get_bcd_title_one("test-bcd/displayorder-bad-name.bcd.zst", NULL, 0);
49         test_get_bcd_title_one("test-bcd/displayorder-bad-size.bcd.zst", NULL, 0);
50         test_get_bcd_title_one("test-bcd/displayorder-bad-type.bcd.zst", NULL, 0);
51         test_get_bcd_title_one("test-bcd/empty.bcd.zst", NULL, 0);
52 }
53 
TEST(base_block)54 TEST(base_block) {
55         size_t len;
56         BaseBlock backup;
57         uint8_t *bcd_base;
58         _cleanup_free_ BaseBlock *bcd = NULL;
59 
60         load_bcd("test-bcd/win10.bcd.zst", (void **) &bcd, &len);
61         backup = *bcd;
62         bcd_base = (uint8_t *) bcd;
63 
64         assert_se(get_bcd_title(bcd_base, len));
65 
66         /* Try various "corruptions" of the base block. */
67 
68         assert_se(!get_bcd_title(bcd_base, sizeof(BaseBlock) - 1));
69 
70         bcd->sig = 0;
71         assert_se(!get_bcd_title(bcd_base, len));
72         *bcd = backup;
73 
74         bcd->version_minor = 2;
75         assert_se(!get_bcd_title(bcd_base, len));
76         *bcd = backup;
77 
78         bcd->version_major = 4;
79         assert_se(!get_bcd_title(bcd_base, len));
80         *bcd = backup;
81 
82         bcd->type = 1;
83         assert_se(!get_bcd_title(bcd_base, len));
84         *bcd = backup;
85 
86         bcd->primary_seqnum++;
87         assert_se(!get_bcd_title(bcd_base, len));
88         *bcd = backup;
89 }
90 
TEST(bad_bcd)91 TEST(bad_bcd) {
92         size_t len;
93         uint8_t *hbins;
94         uint32_t offset;
95         _cleanup_free_ void *bcd = NULL;
96 
97         /* This BCD hive has been manipulated to have bad offsets/sizes at various places. */
98         load_bcd("test-bcd/corrupt.bcd.zst", &bcd, &len);
99 
100         assert_se(len >= HIVE_CELL_OFFSET);
101         hbins = (uint8_t *) bcd + HIVE_CELL_OFFSET;
102         len -= HIVE_CELL_OFFSET;
103         offset = ((BaseBlock *) bcd)->root_cell_offset;
104 
105         const Key *root = get_key(hbins, len, offset, "\0");
106         assert_se(root);
107         assert_se(!get_key(hbins, sizeof(Key) - 1, offset, "\0"));
108 
109         assert_se(!get_key(hbins, len, offset, "\0BadOffset\0"));
110         assert_se(!get_key(hbins, len, offset, "\0BadSig\0"));
111         assert_se(!get_key(hbins, len, offset, "\0BadKeyNameLen\0"));
112         assert_se(!get_key(hbins, len, offset, "\0SubkeyBadOffset\0Dummy\0"));
113         assert_se(!get_key(hbins, len, offset, "\0SubkeyBadSig\0Dummy\0"));
114         assert_se(!get_key(hbins, len, offset, "\0SubkeyBadNEntries\0Dummy\0"));
115 
116         assert_se(!get_key_value(hbins, len, root, "Dummy"));
117 
118         const Key *kv_bad_offset = get_key(hbins, len, offset, "\0KeyValuesBadOffset\0");
119         assert_se(kv_bad_offset);
120         assert_se(!get_key_value(hbins, len, kv_bad_offset, "Dummy"));
121 
122         const Key *kv_bad_n_key_values = get_key(hbins, len, offset, "\0KeyValuesBadNKeyValues\0");
123         assert_se(kv_bad_n_key_values);
124         assert_se(!get_key_value(hbins, len, kv_bad_n_key_values, "Dummy"));
125 
126         const Key *kv = get_key(hbins, len, offset, "\0KeyValues\0");
127         assert_se(kv);
128 
129         assert_se(!get_key_value(hbins, len, kv, "BadOffset"));
130         assert_se(!get_key_value(hbins, len, kv, "BadSig"));
131         assert_se(!get_key_value(hbins, len, kv, "BadNameLen"));
132         assert_se(!get_key_value(hbins, len, kv, "InlineData"));
133         assert_se(!get_key_value(hbins, len, kv, "BadDataOffset"));
134         assert_se(!get_key_value(hbins, len, kv, "BadDataSize"));
135 }
136 
TEST(argv_bcds)137 TEST(argv_bcds) {
138         for (int i = 1; i < saved_argc; i++) {
139                 size_t len;
140                 _cleanup_free_ void *bcd = NULL;
141 
142                 assert_se(read_full_file_full(
143                         AT_FDCWD,
144                         saved_argv[i],
145                         UINT64_MAX,
146                         SIZE_MAX,
147                         0,
148                         NULL,
149                         (char **) &bcd,
150                         &len) >= 0);
151 
152                 char16_t *title = get_bcd_title(bcd, len);
153                 if (title) {
154                         _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, char16_strlen(title) * 2);
155                         log_info("%s: \"%s\"", saved_argv[i], title_utf8);
156                 } else
157                         log_info("%s: Bad BCD", saved_argv[i]);
158         }
159 }
160 
161 DEFINE_TEST_MAIN(LOG_INFO);
162