Skip to content

Commit c1c9830

Browse files
committed
Merge tag 'vfs-6.15-rc1.initramfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull vfs initramfs updates from Christian Brauner: "This adds basic kunit test coverage for initramfs unpacking and cleans up some buffer handling issues and inefficiencies" * tag 'vfs-6.15-rc1.initramfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: MAINTAINERS: append initramfs files to the VFS section initramfs: avoid static buffer for error message initramfs: fix hardlink hash leak without TRAILER initramfs: reuse name_len for dir mtime tracking initramfs: allocate heap buffers together initramfs: avoid memcpy for hex header fields vsprintf: add simple_strntoul initramfs_test: kunit tests for initramfs unpacking init: add initramfs_internal.h
2 parents e63046a + 0054b43 commit c1c9830

File tree

9 files changed

+475
-28
lines changed

9 files changed

+475
-28
lines changed

MAINTAINERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8903,6 +8903,9 @@ F: include/linux/fs.h
89038903
F: include/linux/fs_types.h
89048904
F: include/uapi/linux/fs.h
89058905
F: include/uapi/linux/openat2.h
8906+
F: Documentation/driver-api/early-userspace/buffer-format.rst
8907+
F: init/do_mounts*
8908+
F: init/*initramfs*
89068909

89078910
FILESYSTEMS [EXPORTFS]
89088911
M: Chuck Lever <[email protected]>

include/linux/kstrtox.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ static inline int __must_check kstrtos32_from_user(const char __user *s, size_t
143143
*/
144144

145145
extern unsigned long simple_strtoul(const char *,char **,unsigned int);
146+
extern unsigned long simple_strntoul(const char *,char **,unsigned int,size_t);
146147
extern long simple_strtol(const char *,char **,unsigned int);
147148
extern unsigned long long simple_strtoull(const char *,char **,unsigned int);
148149
extern long long simple_strtoll(const char *,char **,unsigned int);

init/.kunitconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_KUNIT=y
2+
CONFIG_BLK_DEV_INITRD=y
3+
CONFIG_INITRAMFS_TEST=y

init/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,13 @@ config INITRAMFS_PRESERVE_MTIME
14541454

14551455
If unsure, say Y.
14561456

1457+
config INITRAMFS_TEST
1458+
bool "Test initramfs cpio archive extraction" if !KUNIT_ALL_TESTS
1459+
depends on BLK_DEV_INITRD && KUNIT=y
1460+
default KUNIT_ALL_TESTS
1461+
help
1462+
Build KUnit tests for initramfs. See Documentation/dev-tools/kunit
1463+
14571464
choice
14581465
prompt "Compiler optimization level"
14591466
default CC_OPTIMIZE_FOR_PERFORMANCE

init/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ else
1212
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
1313
endif
1414
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
15+
obj-$(CONFIG_INITRAMFS_TEST) += initramfs_test.o
1516

1617
obj-y += init_task.o
1718

init/initramfs.c

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/security.h>
2121

2222
#include "do_mounts.h"
23+
#include "initramfs_internal.h"
2324

2425
static __initdata bool csum_present;
2526
static __initdata u32 io_csum;
@@ -75,6 +76,7 @@ static __initdata struct hash {
7576
struct hash *next;
7677
char name[N_ALIGN(PATH_MAX)];
7778
} *head[32];
79+
static __initdata bool hardlink_seen;
7880

7981
static inline int hash(int major, int minor, int ino)
8082
{
@@ -108,19 +110,21 @@ static char __init *find_link(int major, int minor, int ino,
108110
strcpy(q->name, name);
109111
q->next = NULL;
110112
*p = q;
113+
hardlink_seen = true;
111114
return NULL;
112115
}
113116

114117
static void __init free_hash(void)
115118
{
116119
struct hash **p, *q;
117-
for (p = head; p < head + 32; p++) {
120+
for (p = head; hardlink_seen && p < head + 32; p++) {
118121
while (*p) {
119122
q = *p;
120123
*p = q->next;
121124
kfree(q);
122125
}
123126
}
127+
hardlink_seen = false;
124128
}
125129

126130
#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
@@ -143,9 +147,8 @@ struct dir_entry {
143147
char name[];
144148
};
145149

146-
static void __init dir_add(const char *name, time64_t mtime)
150+
static void __init dir_add(const char *name, size_t nlen, time64_t mtime)
147151
{
148-
size_t nlen = strlen(name) + 1;
149152
struct dir_entry *de;
150153

151154
de = kmalloc(sizeof(struct dir_entry) + nlen, GFP_KERNEL);
@@ -169,7 +172,7 @@ static void __init dir_utime(void)
169172
#else
170173
static void __init do_utime(char *filename, time64_t mtime) {}
171174
static void __init do_utime_path(const struct path *path, time64_t mtime) {}
172-
static void __init dir_add(const char *name, time64_t mtime) {}
175+
static void __init dir_add(const char *name, size_t nlen, time64_t mtime) {}
173176
static void __init dir_utime(void) {}
174177
#endif
175178

@@ -188,14 +191,11 @@ static __initdata u32 hdr_csum;
188191
static void __init parse_header(char *s)
189192
{
190193
unsigned long parsed[13];
191-
char buf[9];
192194
int i;
193195

194-
buf[8] = '\0';
195-
for (i = 0, s += 6; i < 13; i++, s += 8) {
196-
memcpy(buf, s, 8);
197-
parsed[i] = simple_strtoul(buf, NULL, 16);
198-
}
196+
for (i = 0, s += 6; i < 13; i++, s += 8)
197+
parsed[i] = simple_strntoul(s, NULL, 16, 8);
198+
199199
ino = parsed[0];
200200
mode = parsed[1];
201201
uid = parsed[2];
@@ -256,7 +256,7 @@ static __initdata char *header_buf, *symlink_buf, *name_buf;
256256

257257
static int __init do_start(void)
258258
{
259-
read_into(header_buf, 110, GotHeader);
259+
read_into(header_buf, CPIO_HDRLEN, GotHeader);
260260
return 0;
261261
}
262262

@@ -396,7 +396,7 @@ static int __init do_name(void)
396396
init_mkdir(collected, mode);
397397
init_chown(collected, uid, gid, 0);
398398
init_chmod(collected, mode);
399-
dir_add(collected, mtime);
399+
dir_add(collected, name_len, mtime);
400400
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
401401
S_ISFIFO(mode) || S_ISSOCK(mode)) {
402402
if (maybe_link() == 0) {
@@ -497,20 +497,33 @@ static unsigned long my_inptr __initdata; /* index of next byte to be processed
497497

498498
#include <linux/decompress/generic.h>
499499

500-
static char * __init unpack_to_rootfs(char *buf, unsigned long len)
500+
/**
501+
* unpack_to_rootfs - decompress and extract an initramfs archive
502+
* @buf: input initramfs archive to extract
503+
* @len: length of initramfs data to process
504+
*
505+
* Returns: NULL for success or an error message string
506+
*
507+
* This symbol shouldn't be used externally. It's available for unit tests.
508+
*/
509+
char * __init unpack_to_rootfs(char *buf, unsigned long len)
501510
{
502511
long written;
503512
decompress_fn decompress;
504513
const char *compress_name;
505-
static __initdata char msg_buf[64];
514+
struct {
515+
char header[CPIO_HDRLEN];
516+
char symlink[PATH_MAX + N_ALIGN(PATH_MAX) + 1];
517+
char name[N_ALIGN(PATH_MAX)];
518+
} *bufs = kmalloc(sizeof(*bufs), GFP_KERNEL);
506519

507-
header_buf = kmalloc(110, GFP_KERNEL);
508-
symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
509-
name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
510-
511-
if (!header_buf || !symlink_buf || !name_buf)
520+
if (!bufs)
512521
panic_show_mem("can't allocate buffers");
513522

523+
header_buf = bufs->header;
524+
symlink_buf = bufs->symlink;
525+
name_buf = bufs->name;
526+
514527
state = Start;
515528
this_header = 0;
516529
message = NULL;
@@ -538,12 +551,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
538551
if (res)
539552
error("decompressor failed");
540553
} else if (compress_name) {
541-
if (!message) {
542-
snprintf(msg_buf, sizeof msg_buf,
543-
"compression method %s not configured",
544-
compress_name);
545-
message = msg_buf;
546-
}
554+
pr_err("compression method %s not configured\n",
555+
compress_name);
556+
error("decompressor failed");
547557
} else
548558
error("invalid magic at start of compressed archive");
549559
if (state != Reset)
@@ -553,9 +563,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
553563
len -= my_inptr;
554564
}
555565
dir_utime();
556-
kfree(name_buf);
557-
kfree(symlink_buf);
558-
kfree(header_buf);
566+
/* free any hardlink state collected without optional TRAILER!!! */
567+
free_hash();
568+
kfree(bufs);
559569
return message;
560570
}
561571

init/initramfs_internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#ifndef __INITRAMFS_INTERNAL_H__
3+
#define __INITRAMFS_INTERNAL_H__
4+
5+
char *unpack_to_rootfs(char *buf, unsigned long len);
6+
#define CPIO_HDRLEN 110
7+
8+
#endif

0 commit comments

Comments
 (0)