initramfs_test.c 14 KB

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