1 // SPDX-License-Identifier: GPL-2.0 2 #include <kunit/test.h> 3 #include <linux/fcntl.h> 4 #include <linux/file.h> 5 #include <linux/fs.h> 6 #include <linux/init.h> 7 #include <linux/init_syscalls.h> 8 #include <linux/initrd.h> 9 #include <linux/stringify.h> 10 #include <linux/timekeeping.h> 11 #include "initramfs_internal.h" 12 13 struct initramfs_test_cpio { 14 char *magic; 15 unsigned int ino; 16 unsigned int mode; 17 unsigned int uid; 18 unsigned int gid; 19 unsigned int nlink; 20 unsigned int mtime; 21 unsigned int filesize; 22 unsigned int devmajor; 23 unsigned int devminor; 24 unsigned int rdevmajor; 25 unsigned int rdevminor; 26 unsigned int namesize; 27 unsigned int csum; 28 char *fname; 29 char *data; 30 }; 31 32 /* regular newc header format */ 33 #define CPIO_HDR_FMT "%s%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%s" 34 /* 35 * Bogus newc header with "0x" prefixes on the uid, gid, and namesize values. 36 * parse_header()/simple_str[n]toul() accepted this, contrary to the initramfs 37 * specification. hex2bin() now fails. 38 */ 39 #define CPIO_HDR_OX_INJECT \ 40 "%s%08x%08x0x%06x0X%06x%08x%08x%08x%08x%08x%08x%08x0x%06x%08x%s" 41 42 static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, 43 bool inject_ox, char *out) 44 { 45 int i; 46 size_t off = 0; 47 48 for (i = 0; i < csz; i++) { 49 char *pos = &out[off]; 50 struct initramfs_test_cpio *c = &cs[i]; 51 size_t thislen; 52 53 /* +1 to account for nulterm */ 54 thislen = sprintf(pos, 55 inject_ox ? CPIO_HDR_OX_INJECT : CPIO_HDR_FMT, 56 c->magic, c->ino, c->mode, c->uid, c->gid, c->nlink, 57 c->mtime, c->filesize, c->devmajor, c->devminor, 58 c->rdevmajor, c->rdevminor, c->namesize, c->csum, 59 c->fname) + 1; 60 61 pr_debug("packing (%zu): %.*s\n", thislen, (int)thislen, pos); 62 if (thislen != CPIO_HDRLEN + c->namesize) 63 pr_debug("padded to: %u\n", CPIO_HDRLEN + c->namesize); 64 off += CPIO_HDRLEN + c->namesize; 65 while (off & 3) 66 out[off++] = '\0'; 67 68 memcpy(&out[off], c->data, c->filesize); 69 off += c->filesize; 70 while (off & 3) 71 out[off++] = '\0'; 72 } 73 74 return off; 75 } 76 77 static void __init initramfs_test_extract(struct kunit *test) 78 { 79 char *err, *cpio_srcbuf; 80 size_t len; 81 struct timespec64 ts_before, ts_after; 82 struct kstat st = {}; 83 struct initramfs_test_cpio c[] = { { 84 .magic = "070701", 85 .ino = 1, 86 .mode = S_IFREG | 0777, 87 .uid = 12, 88 .gid = 34, 89 .nlink = 1, 90 .mtime = 56, 91 .filesize = 0, 92 .devmajor = 0, 93 .devminor = 1, 94 .rdevmajor = 0, 95 .rdevminor = 0, 96 .namesize = sizeof("initramfs_test_extract"), 97 .csum = 0, 98 .fname = "initramfs_test_extract", 99 }, { 100 .magic = "070701", 101 .ino = 2, 102 .mode = S_IFDIR | 0777, 103 .nlink = 1, 104 .mtime = 57, 105 .devminor = 1, 106 .namesize = sizeof("initramfs_test_extract_dir"), 107 .fname = "initramfs_test_extract_dir", 108 }, { 109 .magic = "070701", 110 .namesize = sizeof("TRAILER!!!"), 111 .fname = "TRAILER!!!", 112 } }; 113 114 /* +3 to cater for any 4-byte end-alignment */ 115 cpio_srcbuf = kzalloc(ARRAY_SIZE(c) * (CPIO_HDRLEN + PATH_MAX + 3), 116 GFP_KERNEL); 117 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 118 119 ktime_get_real_ts64(&ts_before); 120 err = unpack_to_rootfs(cpio_srcbuf, len); 121 ktime_get_real_ts64(&ts_after); 122 if (err) { 123 KUNIT_FAIL(test, "unpack failed %s", err); 124 goto out; 125 } 126 127 KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st, 0), 0); 128 KUNIT_EXPECT_TRUE(test, S_ISREG(st.mode)); 129 KUNIT_EXPECT_TRUE(test, uid_eq(st.uid, KUIDT_INIT(c[0].uid))); 130 KUNIT_EXPECT_TRUE(test, gid_eq(st.gid, KGIDT_INIT(c[0].gid))); 131 KUNIT_EXPECT_EQ(test, st.nlink, 1); 132 if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) { 133 KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[0].mtime); 134 } else { 135 KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec); 136 KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec); 137 } 138 KUNIT_EXPECT_EQ(test, st.blocks, c[0].filesize); 139 140 KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st, 0), 0); 141 KUNIT_EXPECT_TRUE(test, S_ISDIR(st.mode)); 142 if (IS_ENABLED(CONFIG_INITRAMFS_PRESERVE_MTIME)) { 143 KUNIT_EXPECT_EQ(test, st.mtime.tv_sec, c[1].mtime); 144 } else { 145 KUNIT_EXPECT_GE(test, st.mtime.tv_sec, ts_before.tv_sec); 146 KUNIT_EXPECT_LE(test, st.mtime.tv_sec, ts_after.tv_sec); 147 } 148 149 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 150 KUNIT_EXPECT_EQ(test, init_rmdir(c[1].fname), 0); 151 out: 152 kfree(cpio_srcbuf); 153 } 154 155 /* 156 * Don't terminate filename. Previously, the cpio filename field was passed 157 * directly to filp_open(collected, O_CREAT|..) without nulterm checks. See 158 * https://lore.kernel.org/linux-fsdevel/20241030035509.20194-2-ddiss@suse.de 159 */ 160 static void __init initramfs_test_fname_overrun(struct kunit *test) 161 { 162 char *err, *cpio_srcbuf; 163 size_t len, suffix_off; 164 struct initramfs_test_cpio c[] = { { 165 .magic = "070701", 166 .ino = 1, 167 .mode = S_IFREG | 0777, 168 .uid = 0, 169 .gid = 0, 170 .nlink = 1, 171 .mtime = 1, 172 .filesize = 0, 173 .devmajor = 0, 174 .devminor = 1, 175 .rdevmajor = 0, 176 .rdevminor = 0, 177 .namesize = sizeof("initramfs_test_fname_overrun"), 178 .csum = 0, 179 .fname = "initramfs_test_fname_overrun", 180 } }; 181 182 /* 183 * poison cpio source buffer, so we can detect overrun. source 184 * buffer is used by read_into() when hdr or fname 185 * are already available (e.g. no compression). 186 */ 187 cpio_srcbuf = kmalloc(CPIO_HDRLEN + PATH_MAX + 3, GFP_KERNEL); 188 memset(cpio_srcbuf, 'B', CPIO_HDRLEN + PATH_MAX + 3); 189 /* limit overrun to avoid crashes / filp_open() ENAMETOOLONG */ 190 cpio_srcbuf[CPIO_HDRLEN + strlen(c[0].fname) + 20] = '\0'; 191 192 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 193 /* overwrite trailing fname terminator and padding */ 194 suffix_off = len - 1; 195 while (cpio_srcbuf[suffix_off] == '\0') { 196 cpio_srcbuf[suffix_off] = 'P'; 197 suffix_off--; 198 } 199 200 err = unpack_to_rootfs(cpio_srcbuf, len); 201 KUNIT_EXPECT_NOT_NULL(test, err); 202 203 kfree(cpio_srcbuf); 204 } 205 206 static void __init initramfs_test_data(struct kunit *test) 207 { 208 char *err, *cpio_srcbuf; 209 size_t len; 210 struct file *file; 211 struct initramfs_test_cpio c[] = { { 212 .magic = "070701", 213 .ino = 1, 214 .mode = S_IFREG | 0777, 215 .uid = 0, 216 .gid = 0, 217 .nlink = 1, 218 .mtime = 1, 219 .filesize = sizeof("ASDF") - 1, 220 .devmajor = 0, 221 .devminor = 1, 222 .rdevmajor = 0, 223 .rdevminor = 0, 224 .namesize = sizeof("initramfs_test_data"), 225 .csum = 0, 226 .fname = "initramfs_test_data", 227 .data = "ASDF", 228 } }; 229 230 /* +6 for max name and data 4-byte padding */ 231 cpio_srcbuf = kmalloc(CPIO_HDRLEN + c[0].namesize + c[0].filesize + 6, 232 GFP_KERNEL); 233 234 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 235 236 err = unpack_to_rootfs(cpio_srcbuf, len); 237 KUNIT_EXPECT_NULL(test, err); 238 239 file = filp_open(c[0].fname, O_RDONLY, 0); 240 if (IS_ERR(file)) { 241 KUNIT_FAIL(test, "open failed"); 242 goto out; 243 } 244 245 /* read back file contents into @cpio_srcbuf and confirm match */ 246 len = kernel_read(file, cpio_srcbuf, c[0].filesize, NULL); 247 KUNIT_EXPECT_EQ(test, len, c[0].filesize); 248 KUNIT_EXPECT_MEMEQ(test, cpio_srcbuf, c[0].data, len); 249 250 fput(file); 251 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 252 out: 253 kfree(cpio_srcbuf); 254 } 255 256 static void __init initramfs_test_csum(struct kunit *test) 257 { 258 char *err, *cpio_srcbuf; 259 size_t len; 260 struct initramfs_test_cpio c[] = { { 261 /* 070702 magic indicates a valid csum is present */ 262 .magic = "070702", 263 .ino = 1, 264 .mode = S_IFREG | 0777, 265 .nlink = 1, 266 .filesize = sizeof("ASDF") - 1, 267 .devminor = 1, 268 .namesize = sizeof("initramfs_test_csum"), 269 .csum = 'A' + 'S' + 'D' + 'F', 270 .fname = "initramfs_test_csum", 271 .data = "ASDF", 272 }, { 273 /* mix csum entry above with no-csum entry below */ 274 .magic = "070701", 275 .ino = 2, 276 .mode = S_IFREG | 0777, 277 .nlink = 1, 278 .filesize = sizeof("ASDF") - 1, 279 .devminor = 1, 280 .namesize = sizeof("initramfs_test_csum_not_here"), 281 /* csum ignored */ 282 .csum = 5555, 283 .fname = "initramfs_test_csum_not_here", 284 .data = "ASDF", 285 } }; 286 287 cpio_srcbuf = kmalloc(8192, GFP_KERNEL); 288 289 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 290 291 err = unpack_to_rootfs(cpio_srcbuf, len); 292 KUNIT_EXPECT_NULL(test, err); 293 294 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 295 KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0); 296 297 /* mess up the csum and confirm that unpack fails */ 298 c[0].csum--; 299 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 300 301 err = unpack_to_rootfs(cpio_srcbuf, len); 302 KUNIT_EXPECT_NOT_NULL(test, err); 303 304 /* 305 * file (with content) is still retained in case of bad-csum abort. 306 * Perhaps we should change this. 307 */ 308 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 309 KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), -ENOENT); 310 kfree(cpio_srcbuf); 311 } 312 313 /* 314 * hardlink hashtable may leak when the archive omits a trailer: 315 * https://lore.kernel.org/r/20241107002044.16477-10-ddiss@suse.de/ 316 */ 317 static void __init initramfs_test_hardlink(struct kunit *test) 318 { 319 char *err, *cpio_srcbuf; 320 size_t len; 321 struct kstat st0 = {}, st1 = {}; 322 struct initramfs_test_cpio c[] = { { 323 .magic = "070701", 324 .ino = 1, 325 .mode = S_IFREG | 0777, 326 .nlink = 2, 327 .devminor = 1, 328 .namesize = sizeof("initramfs_test_hardlink"), 329 .fname = "initramfs_test_hardlink", 330 }, { 331 /* hardlink data is present in last archive entry */ 332 .magic = "070701", 333 .ino = 1, 334 .mode = S_IFREG | 0777, 335 .nlink = 2, 336 .filesize = sizeof("ASDF") - 1, 337 .devminor = 1, 338 .namesize = sizeof("initramfs_test_hardlink_link"), 339 .fname = "initramfs_test_hardlink_link", 340 .data = "ASDF", 341 } }; 342 343 cpio_srcbuf = kmalloc(8192, GFP_KERNEL); 344 345 len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf); 346 347 err = unpack_to_rootfs(cpio_srcbuf, len); 348 KUNIT_EXPECT_NULL(test, err); 349 350 KUNIT_EXPECT_EQ(test, init_stat(c[0].fname, &st0, 0), 0); 351 KUNIT_EXPECT_EQ(test, init_stat(c[1].fname, &st1, 0), 0); 352 KUNIT_EXPECT_EQ(test, st0.ino, st1.ino); 353 KUNIT_EXPECT_EQ(test, st0.nlink, 2); 354 KUNIT_EXPECT_EQ(test, st1.nlink, 2); 355 356 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 357 KUNIT_EXPECT_EQ(test, init_unlink(c[1].fname), 0); 358 359 kfree(cpio_srcbuf); 360 } 361 362 #define INITRAMFS_TEST_MANY_LIMIT 1000 363 #define INITRAMFS_TEST_MANY_PATH_MAX (sizeof("initramfs_test_many-") \ 364 + sizeof(__stringify(INITRAMFS_TEST_MANY_LIMIT))) 365 static void __init initramfs_test_many(struct kunit *test) 366 { 367 char *err, *cpio_srcbuf, *p; 368 size_t len = INITRAMFS_TEST_MANY_LIMIT * 369 (CPIO_HDRLEN + INITRAMFS_TEST_MANY_PATH_MAX + 3); 370 char thispath[INITRAMFS_TEST_MANY_PATH_MAX]; 371 int i; 372 373 p = cpio_srcbuf = kmalloc(len, GFP_KERNEL); 374 375 for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) { 376 struct initramfs_test_cpio c = { 377 .magic = "070701", 378 .ino = i, 379 .mode = S_IFREG | 0777, 380 .nlink = 1, 381 .devminor = 1, 382 .fname = thispath, 383 }; 384 385 c.namesize = 1 + sprintf(thispath, "initramfs_test_many-%d", i); 386 p += fill_cpio(&c, 1, false, p); 387 } 388 389 len = p - cpio_srcbuf; 390 err = unpack_to_rootfs(cpio_srcbuf, len); 391 KUNIT_EXPECT_NULL(test, err); 392 393 for (i = 0; i < INITRAMFS_TEST_MANY_LIMIT; i++) { 394 sprintf(thispath, "initramfs_test_many-%d", i); 395 KUNIT_EXPECT_EQ(test, init_unlink(thispath), 0); 396 } 397 398 kfree(cpio_srcbuf); 399 } 400 401 /* 402 * An initramfs filename is namesize in length, including the zero-terminator. 403 * A filename can be zero-terminated prior to namesize, with the remainder used 404 * as padding. This can be useful for e.g. alignment of file data segments with 405 * a 4KB filesystem block, allowing for extent sharing (reflinks) between cpio 406 * source and destination. This hack works with both GNU cpio and initramfs, as 407 * long as PATH_MAX isn't exceeded. 408 */ 409 static void __init initramfs_test_fname_pad(struct kunit *test) 410 { 411 char *err; 412 size_t len; 413 struct file *file; 414 char fdata[] = "this file data is aligned at 4K in the archive"; 415 struct test_fname_pad { 416 char padded_fname[4096 - CPIO_HDRLEN]; 417 char cpio_srcbuf[CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)]; 418 } *tbufs = kzalloc_obj(struct test_fname_pad); 419 struct initramfs_test_cpio c[] = { { 420 .magic = "070701", 421 .ino = 1, 422 .mode = S_IFREG | 0777, 423 .uid = 0, 424 .gid = 0, 425 .nlink = 1, 426 .mtime = 1, 427 .filesize = sizeof(fdata), 428 .devmajor = 0, 429 .devminor = 1, 430 .rdevmajor = 0, 431 .rdevminor = 0, 432 /* align file data at 4K archive offset via padded fname */ 433 .namesize = 4096 - CPIO_HDRLEN, 434 .csum = 0, 435 .fname = tbufs->padded_fname, 436 .data = fdata, 437 } }; 438 439 memcpy(tbufs->padded_fname, "padded_fname", sizeof("padded_fname")); 440 len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_srcbuf); 441 442 err = unpack_to_rootfs(tbufs->cpio_srcbuf, len); 443 KUNIT_EXPECT_NULL(test, err); 444 445 file = filp_open(c[0].fname, O_RDONLY, 0); 446 if (IS_ERR(file)) { 447 KUNIT_FAIL(test, "open failed"); 448 goto out; 449 } 450 451 /* read back file contents into @cpio_srcbuf and confirm match */ 452 len = kernel_read(file, tbufs->cpio_srcbuf, c[0].filesize, NULL); 453 KUNIT_EXPECT_EQ(test, len, c[0].filesize); 454 KUNIT_EXPECT_MEMEQ(test, tbufs->cpio_srcbuf, c[0].data, len); 455 456 fput(file); 457 KUNIT_EXPECT_EQ(test, init_unlink(c[0].fname), 0); 458 out: 459 kfree(tbufs); 460 } 461 462 static void __init initramfs_test_fname_path_max(struct kunit *test) 463 { 464 char *err; 465 size_t len; 466 struct kstat st0 = {}, st1 = {}; 467 char fdata[] = "this file data will not be unpacked"; 468 struct test_fname_path_max { 469 char fname_oversize[PATH_MAX + 1]; 470 char fname_ok[PATH_MAX]; 471 char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2]; 472 } *tbufs = kzalloc_obj(struct test_fname_path_max); 473 struct initramfs_test_cpio c[] = { { 474 .magic = "070701", 475 .ino = 1, 476 .mode = S_IFDIR | 0777, 477 .nlink = 1, 478 .namesize = sizeof(tbufs->fname_oversize), 479 .fname = tbufs->fname_oversize, 480 .filesize = sizeof(fdata), 481 .data = fdata, 482 }, { 483 .magic = "070701", 484 .ino = 2, 485 .mode = S_IFDIR | 0777, 486 .nlink = 1, 487 .namesize = sizeof(tbufs->fname_ok), 488 .fname = tbufs->fname_ok, 489 } }; 490 491 memset(tbufs->fname_oversize, '/', sizeof(tbufs->fname_oversize) - 1); 492 memset(tbufs->fname_ok, '/', sizeof(tbufs->fname_ok) - 1); 493 memcpy(tbufs->fname_oversize, "fname_oversize", 494 sizeof("fname_oversize") - 1); 495 memcpy(tbufs->fname_ok, "fname_ok", sizeof("fname_ok") - 1); 496 len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_src); 497 498 /* unpack skips over fname_oversize instead of returning an error */ 499 err = unpack_to_rootfs(tbufs->cpio_src, len); 500 KUNIT_EXPECT_NULL(test, err); 501 502 KUNIT_EXPECT_EQ(test, init_stat("fname_oversize", &st0, 0), -ENOENT); 503 KUNIT_EXPECT_EQ(test, init_stat("fname_ok", &st1, 0), 0); 504 KUNIT_EXPECT_EQ(test, init_rmdir("fname_ok"), 0); 505 506 kfree(tbufs); 507 } 508 509 static void __init initramfs_test_hdr_hex(struct kunit *test) 510 { 511 char *err; 512 size_t len; 513 char fdata[] = "this file data will not be unpacked"; 514 struct initramfs_test_bufs { 515 char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2]; 516 } *tbufs = kzalloc(sizeof(struct initramfs_test_bufs), GFP_KERNEL); 517 struct initramfs_test_cpio c[] = { { 518 .magic = "070701", 519 .ino = 1, 520 .mode = S_IFREG | 0777, 521 .uid = 0x123456, 522 .gid = 0x123457, 523 .nlink = 1, 524 .namesize = sizeof("initramfs_test_hdr_hex_0"), 525 .fname = "initramfs_test_hdr_hex_0", 526 .filesize = sizeof(fdata), 527 .data = fdata, 528 }, { 529 .magic = "070701", 530 .ino = 2, 531 .mode = S_IFDIR | 0777, 532 .uid = 0x000056, 533 .gid = 0x000057, 534 .nlink = 1, 535 .namesize = sizeof("initramfs_test_hdr_hex_1"), 536 .fname = "initramfs_test_hdr_hex_1", 537 } }; 538 539 /* inject_ox=true to add "0x" cpio field prefixes */ 540 len = fill_cpio(c, ARRAY_SIZE(c), true, tbufs->cpio_src); 541 542 err = unpack_to_rootfs(tbufs->cpio_src, len); 543 KUNIT_EXPECT_NOT_NULL(test, err); 544 545 kfree(tbufs); 546 } 547 548 /* 549 * The kunit_case/_suite struct cannot be marked as __initdata as this will be 550 * used in debugfs to retrieve results after test has run. 551 */ 552 static struct kunit_case __refdata initramfs_test_cases[] = { 553 KUNIT_CASE(initramfs_test_extract), 554 KUNIT_CASE(initramfs_test_fname_overrun), 555 KUNIT_CASE(initramfs_test_data), 556 KUNIT_CASE(initramfs_test_csum), 557 KUNIT_CASE(initramfs_test_hardlink), 558 KUNIT_CASE(initramfs_test_many), 559 KUNIT_CASE(initramfs_test_fname_pad), 560 KUNIT_CASE(initramfs_test_fname_path_max), 561 KUNIT_CASE(initramfs_test_hdr_hex), 562 {}, 563 }; 564 565 static int __init initramfs_test_init(struct kunit_suite *suite) 566 { 567 /* 568 * unpack_to_rootfs() uses module-static state (victim, byte_count, 569 * state, ...). The boot-time async do_populate_rootfs() may still be 570 * running, so wait for it to finish before we call unpack_to_rootfs() 571 * from the test thread, otherwise the two writers race and crash. 572 */ 573 wait_for_initramfs(); 574 return 0; 575 } 576 577 static struct kunit_suite __refdata initramfs_test_suite = { 578 .name = "initramfs", 579 .suite_init = initramfs_test_init, 580 .test_cases = initramfs_test_cases, 581 }; 582 kunit_test_init_section_suites(&initramfs_test_suite); 583 584 MODULE_DESCRIPTION("Initramfs KUnit test suite"); 585 MODULE_LICENSE("GPL v2"); 586