rootfs: make /dev initialisation code fd-based

These codepaths are very old and operate on pure paths but before
pivot_root(2), meaning that a bad image with a malicious /dev symlink
could cause us to operate on host paths instead.

In practice this means that we could be tricked into removing a file
called "ptmx" (note that /dev/pts/ptmx and /dev/ptmx are both immune for
different reasons) or creating a very restricted set of symlinks (with
fixed targets and names). The scope of these bugs is thus quite limited,
but we definitely need to harden against it.

These codepaths were unfortunately missed during the fd-based rework in
commit d40b3439a9 ("rootfs: switch to fd-based handling of mountpoint
targets") -- I must've assumed they were called after pivot_root(2)...

Fixes: GHSA-xjvp-4fhw-gc47
Fixes: CVE-2026-41579
Fixes: d40b3439a9 ("rootfs: switch to fd-based handling of mountpoint targets")
Signed-off-by: Aleksa Sarai <aleksa@amutable.com>
This commit is contained in:
Aleksa Sarai
2026-04-15 01:44:09 +10:00
parent fcf04eb41b
commit 864db8042d
3 changed files with 77 additions and 24 deletions
+11 -3
View File
@@ -24,6 +24,14 @@ import (
"path/filepath"
)
func splitPath(path string) (dirPath, filename string, err error) {
dirPath, filename = filepath.Split(path)
if filepath.Join("/", filename) == "/" {
return "", "", fmt.Errorf("root subpath %q has bad trailing component %q", path, filename)
}
return dirPath, filename, nil
}
// MkdirAllParentInRoot is like [MkdirAllInRoot] except that it only creates
// the parent directory of the target path, returning the trailing component so
// the caller has more flexibility around constructing the final inode.
@@ -41,9 +49,9 @@ func MkdirAllParentInRoot(root *os.File, unsafePath string, mode os.FileMode) (*
return nil, "", fmt.Errorf("failed to construct hallucinated target path: %w", err)
}
dirPath, filename := filepath.Split(unsafePath)
if filepath.Join("/", filename) == "/" {
return nil, "", fmt.Errorf("create parent dir in root subpath %q has bad trailing component %q", unsafePath, filename)
dirPath, filename, err := splitPath(unsafePath)
if err != nil {
return nil, "", fmt.Errorf("split path %q for mkdir parent: %w", unsafePath, err)
}
dirFd, err := MkdirAllInRoot(root, dirPath, mode)