mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
Merge patch series "initramfs: test and improve cpio hex header validation"
David Disseldorp <ddiss@suse.de> says: The series that introduced simple_strntoul() had passed into kernel without proper review and hence reinvented a wheel that's not needed. Here is the refactoring to show that. It can go via PRINTK or VFS tree. I have tested this on x86, but I believe the same result will be on big-endian CPUs (I deduced that from how strtox() works). I also run KUnit tests. * patches from https://patch.msgid.link/20260331070519.5974-1-ddiss@suse.de: kstrtox: Drop extern keyword in the simple_strtox() declarations vsprintf: Revert "add simple_strntoul" initramfs: Refactor to use hex2bin() instead of custom approach initramfs: Sort headers alphabetically initramfs_test: test header fields with 0x hex prefix initramfs_test: add fill_cpio() inject_ox parameter Link: https://patch.msgid.link/20260331070519.5974-1-ddiss@suse.de Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
commit
1ca179a853
|
|
@ -142,10 +142,9 @@ static inline int __must_check kstrtos32_from_user(const char __user *s, size_t
|
|||
* Keep in mind above caveat.
|
||||
*/
|
||||
|
||||
extern unsigned long simple_strtoul(const char *,char **,unsigned int);
|
||||
extern unsigned long simple_strntoul(const char *,char **,unsigned int,size_t);
|
||||
extern long simple_strtol(const char *,char **,unsigned int);
|
||||
extern unsigned long long simple_strtoull(const char *,char **,unsigned int);
|
||||
extern long long simple_strtoll(const char *,char **,unsigned int);
|
||||
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base);
|
||||
long simple_strtol(const char *cp, char **endp, unsigned int base);
|
||||
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
|
||||
long long simple_strtoll(const char *cp, char **endp, unsigned int base);
|
||||
|
||||
#endif /* _LINUX_KSTRTOX_H */
|
||||
|
|
|
|||
|
|
@ -1,25 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/init.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dirent.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/utime.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/init_syscalls.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/init_syscalls.h>
|
||||
#include <linux/umh.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/umh.h>
|
||||
#include <linux/utime.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "do_mounts.h"
|
||||
#include "initramfs_internal.h"
|
||||
|
|
@ -190,26 +193,30 @@ static __initdata gid_t gid;
|
|||
static __initdata unsigned rdev;
|
||||
static __initdata u32 hdr_csum;
|
||||
|
||||
static void __init parse_header(char *s)
|
||||
static int __init parse_header(char *s)
|
||||
{
|
||||
unsigned long parsed[13];
|
||||
int i;
|
||||
__be32 header[13];
|
||||
int ret;
|
||||
|
||||
for (i = 0, s += 6; i < 13; i++, s += 8)
|
||||
parsed[i] = simple_strntoul(s, NULL, 16, 8);
|
||||
ret = hex2bin((u8 *)header, s + 6, sizeof(header));
|
||||
if (ret) {
|
||||
error("damaged header");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ino = parsed[0];
|
||||
mode = parsed[1];
|
||||
uid = parsed[2];
|
||||
gid = parsed[3];
|
||||
nlink = parsed[4];
|
||||
mtime = parsed[5]; /* breaks in y2106 */
|
||||
body_len = parsed[6];
|
||||
major = parsed[7];
|
||||
minor = parsed[8];
|
||||
rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));
|
||||
name_len = parsed[11];
|
||||
hdr_csum = parsed[12];
|
||||
ino = be32_to_cpu(header[0]);
|
||||
mode = be32_to_cpu(header[1]);
|
||||
uid = be32_to_cpu(header[2]);
|
||||
gid = be32_to_cpu(header[3]);
|
||||
nlink = be32_to_cpu(header[4]);
|
||||
mtime = be32_to_cpu(header[5]); /* breaks in y2106 */
|
||||
body_len = be32_to_cpu(header[6]);
|
||||
major = be32_to_cpu(header[7]);
|
||||
minor = be32_to_cpu(header[8]);
|
||||
rdev = new_encode_dev(MKDEV(be32_to_cpu(header[9]), be32_to_cpu(header[10])));
|
||||
name_len = be32_to_cpu(header[11]);
|
||||
hdr_csum = be32_to_cpu(header[12]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finite-state machine */
|
||||
|
|
@ -289,7 +296,8 @@ static int __init do_header(void)
|
|||
error("no cpio magic");
|
||||
return 1;
|
||||
}
|
||||
parse_header(collected);
|
||||
if (parse_header(collected))
|
||||
return 1;
|
||||
next_header = this_header + N_ALIGN(name_len) + body_len;
|
||||
next_header = (next_header + 3) & ~3;
|
||||
state = SkipIt;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,18 @@ struct initramfs_test_cpio {
|
|||
char *data;
|
||||
};
|
||||
|
||||
static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, char *out)
|
||||
/* regular newc header format */
|
||||
#define CPIO_HDR_FMT "%s%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%s"
|
||||
/*
|
||||
* Bogus newc header with "0x" prefixes on the uid, gid, and namesize values.
|
||||
* parse_header()/simple_str[n]toul() accepted this, contrary to the initramfs
|
||||
* specification. hex2bin() now fails.
|
||||
*/
|
||||
#define CPIO_HDR_OX_INJECT \
|
||||
"%s%08x%08x0x%06x0X%06x%08x%08x%08x%08x%08x%08x%08x0x%06x%08x%s"
|
||||
|
||||
static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz,
|
||||
bool inject_ox, char *out)
|
||||
{
|
||||
int i;
|
||||
size_t off = 0;
|
||||
|
|
@ -38,9 +49,8 @@ static size_t fill_cpio(struct initramfs_test_cpio *cs, size_t csz, char *out)
|
|||
size_t thislen;
|
||||
|
||||
/* +1 to account for nulterm */
|
||||
thislen = sprintf(pos, "%s"
|
||||
"%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x"
|
||||
"%s",
|
||||
thislen = sprintf(pos,
|
||||
inject_ox ? CPIO_HDR_OX_INJECT : CPIO_HDR_FMT,
|
||||
c->magic, c->ino, c->mode, c->uid, c->gid, c->nlink,
|
||||
c->mtime, c->filesize, c->devmajor, c->devminor,
|
||||
c->rdevmajor, c->rdevminor, c->namesize, c->csum,
|
||||
|
|
@ -102,7 +112,7 @@ static void __init initramfs_test_extract(struct kunit *test)
|
|||
/* +3 to cater for any 4-byte end-alignment */
|
||||
cpio_srcbuf = kzalloc(ARRAY_SIZE(c) * (CPIO_HDRLEN + PATH_MAX + 3),
|
||||
GFP_KERNEL);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
|
||||
ktime_get_real_ts64(&ts_before);
|
||||
err = unpack_to_rootfs(cpio_srcbuf, len);
|
||||
|
|
@ -177,7 +187,7 @@ static void __init initramfs_test_fname_overrun(struct kunit *test)
|
|||
/* limit overrun to avoid crashes / filp_open() ENAMETOOLONG */
|
||||
cpio_srcbuf[CPIO_HDRLEN + strlen(c[0].fname) + 20] = '\0';
|
||||
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
/* overwrite trailing fname terminator and padding */
|
||||
suffix_off = len - 1;
|
||||
while (cpio_srcbuf[suffix_off] == '\0') {
|
||||
|
|
@ -219,7 +229,7 @@ static void __init initramfs_test_data(struct kunit *test)
|
|||
cpio_srcbuf = kmalloc(CPIO_HDRLEN + c[0].namesize + c[0].filesize + 6,
|
||||
GFP_KERNEL);
|
||||
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
|
||||
err = unpack_to_rootfs(cpio_srcbuf, len);
|
||||
KUNIT_EXPECT_NULL(test, err);
|
||||
|
|
@ -274,7 +284,7 @@ static void __init initramfs_test_csum(struct kunit *test)
|
|||
|
||||
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
|
||||
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
|
||||
err = unpack_to_rootfs(cpio_srcbuf, len);
|
||||
KUNIT_EXPECT_NULL(test, err);
|
||||
|
|
@ -284,7 +294,7 @@ static void __init initramfs_test_csum(struct kunit *test)
|
|||
|
||||
/* mess up the csum and confirm that unpack fails */
|
||||
c[0].csum--;
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
|
||||
err = unpack_to_rootfs(cpio_srcbuf, len);
|
||||
KUNIT_EXPECT_NOT_NULL(test, err);
|
||||
|
|
@ -306,7 +316,7 @@ static void __init initramfs_test_hardlink(struct kunit *test)
|
|||
{
|
||||
char *err, *cpio_srcbuf;
|
||||
size_t len;
|
||||
struct kstat st0, st1;
|
||||
struct kstat st0 = {}, st1 = {};
|
||||
struct initramfs_test_cpio c[] = { {
|
||||
.magic = "070701",
|
||||
.ino = 1,
|
||||
|
|
@ -330,7 +340,7 @@ static void __init initramfs_test_hardlink(struct kunit *test)
|
|||
|
||||
cpio_srcbuf = kmalloc(8192, GFP_KERNEL);
|
||||
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, cpio_srcbuf);
|
||||
|
||||
err = unpack_to_rootfs(cpio_srcbuf, len);
|
||||
KUNIT_EXPECT_NULL(test, err);
|
||||
|
|
@ -371,7 +381,7 @@ static void __init initramfs_test_many(struct kunit *test)
|
|||
};
|
||||
|
||||
c.namesize = 1 + sprintf(thispath, "initramfs_test_many-%d", i);
|
||||
p += fill_cpio(&c, 1, p);
|
||||
p += fill_cpio(&c, 1, false, p);
|
||||
}
|
||||
|
||||
len = p - cpio_srcbuf;
|
||||
|
|
@ -425,7 +435,7 @@ static void __init initramfs_test_fname_pad(struct kunit *test)
|
|||
} };
|
||||
|
||||
memcpy(tbufs->padded_fname, "padded_fname", sizeof("padded_fname"));
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), tbufs->cpio_srcbuf);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_srcbuf);
|
||||
|
||||
err = unpack_to_rootfs(tbufs->cpio_srcbuf, len);
|
||||
KUNIT_EXPECT_NULL(test, err);
|
||||
|
|
@ -451,7 +461,7 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
|
|||
{
|
||||
char *err;
|
||||
size_t len;
|
||||
struct kstat st0, st1;
|
||||
struct kstat st0 = {}, st1 = {};
|
||||
char fdata[] = "this file data will not be unpacked";
|
||||
struct test_fname_path_max {
|
||||
char fname_oversize[PATH_MAX + 1];
|
||||
|
|
@ -481,7 +491,7 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
|
|||
memcpy(tbufs->fname_oversize, "fname_oversize",
|
||||
sizeof("fname_oversize") - 1);
|
||||
memcpy(tbufs->fname_ok, "fname_ok", sizeof("fname_ok") - 1);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), tbufs->cpio_src);
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), false, tbufs->cpio_src);
|
||||
|
||||
/* unpack skips over fname_oversize instead of returning an error */
|
||||
err = unpack_to_rootfs(tbufs->cpio_src, len);
|
||||
|
|
@ -494,6 +504,45 @@ static void __init initramfs_test_fname_path_max(struct kunit *test)
|
|||
kfree(tbufs);
|
||||
}
|
||||
|
||||
static void __init initramfs_test_hdr_hex(struct kunit *test)
|
||||
{
|
||||
char *err;
|
||||
size_t len;
|
||||
char fdata[] = "this file data will not be unpacked";
|
||||
struct initramfs_test_bufs {
|
||||
char cpio_src[(CPIO_HDRLEN + PATH_MAX + 3 + sizeof(fdata)) * 2];
|
||||
} *tbufs = kzalloc(sizeof(struct initramfs_test_bufs), GFP_KERNEL);
|
||||
struct initramfs_test_cpio c[] = { {
|
||||
.magic = "070701",
|
||||
.ino = 1,
|
||||
.mode = S_IFREG | 0777,
|
||||
.uid = 0x123456,
|
||||
.gid = 0x123457,
|
||||
.nlink = 1,
|
||||
.namesize = sizeof("initramfs_test_hdr_hex_0"),
|
||||
.fname = "initramfs_test_hdr_hex_0",
|
||||
.filesize = sizeof(fdata),
|
||||
.data = fdata,
|
||||
}, {
|
||||
.magic = "070701",
|
||||
.ino = 2,
|
||||
.mode = S_IFDIR | 0777,
|
||||
.uid = 0x000056,
|
||||
.gid = 0x000057,
|
||||
.nlink = 1,
|
||||
.namesize = sizeof("initramfs_test_hdr_hex_1"),
|
||||
.fname = "initramfs_test_hdr_hex_1",
|
||||
} };
|
||||
|
||||
/* inject_ox=true to add "0x" cpio field prefixes */
|
||||
len = fill_cpio(c, ARRAY_SIZE(c), true, tbufs->cpio_src);
|
||||
|
||||
err = unpack_to_rootfs(tbufs->cpio_src, len);
|
||||
KUNIT_EXPECT_NOT_NULL(test, err);
|
||||
|
||||
kfree(tbufs);
|
||||
}
|
||||
|
||||
/*
|
||||
* The kunit_case/_suite struct cannot be marked as __initdata as this will be
|
||||
* used in debugfs to retrieve results after test has run.
|
||||
|
|
@ -507,6 +556,7 @@ static struct kunit_case __refdata initramfs_test_cases[] = {
|
|||
KUNIT_CASE(initramfs_test_many),
|
||||
KUNIT_CASE(initramfs_test_fname_pad),
|
||||
KUNIT_CASE(initramfs_test_fname_path_max),
|
||||
KUNIT_CASE(initramfs_test_hdr_hex),
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -129,13 +129,6 @@ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
|
|||
}
|
||||
EXPORT_SYMBOL(simple_strtoul);
|
||||
|
||||
unsigned long simple_strntoul(const char *cp, char **endp, unsigned int base,
|
||||
size_t max_chars)
|
||||
{
|
||||
return simple_strntoull(cp, endp, base, max_chars);
|
||||
}
|
||||
EXPORT_SYMBOL(simple_strntoul);
|
||||
|
||||
/**
|
||||
* simple_strtol - convert a string to a signed long
|
||||
* @cp: The start of the string
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user