From 7263641953dd55d4b151891e0b6755cc6920395c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Sobczak?= Date: Sun, 16 Aug 2026 23:17:57 +0200 Subject: [PATCH] initramfs: reject a non-regular init and an overlong init path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The precheck accepts a directory as new init: access(path, X_OK) only checks search permission, which directories almost always have, so the mistake is not caught until after teardown. The snprintf() building init_path also never checks for truncation, so an overlong newroot + newinit validates the wrong path. Require a regular file, with EISDIR or ENOEXEC to match what is printed, and fail with ENAMETOOLONG on a truncated init path. Signed-off-by: Paweł Sobczak --- src/initramfs.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/initramfs.c b/src/initramfs.c index b3fea595..40b9fec1 100644 --- a/src/initramfs.c +++ b/src/initramfs.c @@ -170,7 +170,7 @@ switch_root_fail(char *errbuf, size_t errbuflen, int err, const char *fmt, ...) int switch_root_precheck(const char *newroot, const char *newinit, char *errbuf, size_t errbuflen) { - struct stat newroot_st, oldroot_st; + struct stat newroot_st, oldroot_st, st; char init_path[PATH_MAX]; int fd; @@ -218,12 +218,27 @@ int switch_root_precheck(const char *newroot, const char *newinit, return switch_root_fail(errbuf, errbuflen, EINVAL, "%s is not a mount point", newroot); - /* Verify init exists in new root */ - snprintf(init_path, sizeof(init_path), "%s%s", newroot, newinit); + /* Verify init exists in new root and is a regular file */ + if (paste(init_path, sizeof(init_path), + newroot, newinit) >= (int)sizeof(init_path)) + return switch_root_fail(errbuf, errbuflen, ENAMETOOLONG, + "init path too long"); + if (access(init_path, X_OK)) return switch_root_fail(errbuf, errbuflen, ENOENT, "%s not found or not executable", init_path); + if (stat(init_path, &st)) + return switch_root_fail(errbuf, errbuflen, errno, + "cannot stat %s: %s", init_path, strerror(errno)); + if (S_ISDIR(st.st_mode)) + return switch_root_fail(errbuf, errbuflen, EISDIR, + "%s is a directory, not init", init_path); + + if (!S_ISREG(st.st_mode)) + return switch_root_fail(errbuf, errbuflen, ENOEXEC, + "%s is not a regular file", init_path); + return 0; }