initramfs: reject a non-regular init and an overlong init path

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 <github@fixeq.qzz.io>
This commit is contained in:
Paweł Sobczak authored and Joachim Wiberg committed 2026-08-16 23:17:57 +02:00
1 parent 42840f0f36
commit 7263641953
1 file changed
+18 -3
+18 -3
View File
@@ -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;
}