Skip to content

Commit 8995662

Browse files
1 parent 962e73f commit 8995662

File tree

4 files changed

+522
-1
lines changed

4 files changed

+522
-1
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
From ab41549ce869d942c4f03f34454992f198cd7bde Mon Sep 17 00:00:00 2001
2+
From: AllSpark <[email protected]>
3+
Date: Mon, 24 Nov 2025 07:13:56 +0000
4+
Subject: [PATCH] Backport: handle /dev/null checks and mask multiple paths;
5+
adjust stdio and devnull reopening logic
6+
7+
Signed-off-by: Azure Linux Security Servicing Account <[email protected]>
8+
Upstream-reference: AI Backport from existing Build 989296 of https://github.com/opencontainers/runc/commit/8476df83b534a2522b878c0507b3491def48db9f.diff
9+
---
10+
.../runc/libcontainer/init_linux.go | 11 ++---
11+
.../runc/libcontainer/rootfs_linux.go | 49 +++++++++++++++----
12+
.../runc/libcontainer/standard_init_linux.go | 8 +--
13+
3 files changed, 47 insertions(+), 21 deletions(-)
14+
15+
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
16+
index d9f18139..50c7a129 100644
17+
--- a/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
18+
+++ b/vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
19+
@@ -432,19 +432,16 @@ func setupUser(config *initConfig) error {
20+
// The ownership needs to match because it is created outside of the container and needs to be
21+
// localized.
22+
func fixStdioPermissions(u *user.ExecUser) error {
23+
- var null unix.Stat_t
24+
- if err := unix.Stat("/dev/null", &null); err != nil {
25+
- return &os.PathError{Op: "stat", Path: "/dev/null", Err: err}
26+
- }
27+
for _, file := range []*os.File{os.Stdin, os.Stdout, os.Stderr} {
28+
var s unix.Stat_t
29+
if err := unix.Fstat(int(file.Fd()), &s); err != nil {
30+
return &os.PathError{Op: "fstat", Path: file.Name(), Err: err}
31+
}
32+
33+
- // Skip chown if uid is already the one we want or any of the STDIO descriptors
34+
- // were redirected to /dev/null.
35+
- if int(s.Uid) == u.Uid || s.Rdev == null.Rdev {
36+
+ // Skip chown if:
37+
+ // - uid is already the one we want, or
38+
+ // - fd is opened to /dev/null.
39+
+ if int(s.Uid) == u.Uid || isDevNull(&s) {
40+
continue
41+
}
42+
43+
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
44+
index c701d6a2..abc7c5ea 100644
45+
--- a/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
46+
+++ b/vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
47+
@@ -360,7 +360,7 @@ func mountCgroupV2(m *configs.Mount, c *mountConfig) error {
48+
// Mask `/sys/fs/cgroup` to ensure it is read-only, even when `/sys` is mounted
49+
// with `rbind,ro` (`runc spec --rootless` produces `rbind,ro` for `/sys`).
50+
err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error {
51+
- return maskPath(procfd, c.label)
52+
+ return maskPaths([]string{procfd}, c.label)
53+
})
54+
}
55+
return err
56+
@@ -653,27 +653,23 @@ func setupDevSymlinks(rootfs string) error {
57+
// needs to be called after we chroot/pivot into the container's rootfs so that any
58+
// symlinks are resolved locally.
59+
func reOpenDevNull() error {
60+
- var stat, devNullStat unix.Stat_t
61+
file, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
62+
if err != nil {
63+
return err
64+
}
65+
defer file.Close() //nolint: errcheck
66+
- if err := unix.Fstat(int(file.Fd()), &devNullStat); err != nil {
67+
- return &os.PathError{Op: "fstat", Path: file.Name(), Err: err}
68+
+ if err := verifyDevNull(file); err != nil {
69+
+ return fmt.Errorf("can't reopen /dev/null: %w", err)
70+
}
71+
for fd := 0; fd < 3; fd++ {
72+
+ var stat unix.Stat_t
73+
if err := unix.Fstat(fd, &stat); err != nil {
74+
return &os.PathError{Op: "fstat", Path: "fd " + strconv.Itoa(fd), Err: err}
75+
}
76+
- if stat.Rdev == devNullStat.Rdev {
77+
+ if isDevNull(&stat) {
78+
// Close and re-open the fd.
79+
if err := unix.Dup3(int(file.Fd()), fd, 0); err != nil {
80+
- return &os.PathError{
81+
- Op: "dup3",
82+
- Path: "fd " + strconv.Itoa(int(file.Fd())),
83+
- Err: err,
84+
- }
85+
+ return err
86+
}
87+
}
88+
}
89+
@@ -1063,6 +1059,39 @@ func remountReadonly(m *configs.Mount) error {
90+
// security issues from processes reading information from non-namespace aware
91+
// mounts ( proc/kcore ).
92+
// For files, maskPath bind mounts /dev/null over the top of the specified path.
93+
+
94+
+func isDevNull(st *unix.Stat_t) bool {
95+
+ return st.Mode&unix.S_IFMT == unix.S_IFCHR && st.Rdev == unix.Mkdev(1, 3)
96+
+}
97+
+
98+
+func verifyDevNull(f *os.File) error {
99+
+ var st unix.Stat_t
100+
+ if err := unix.Fstat(int(f.Fd()), &st); err != nil {
101+
+ return &os.PathError{Op: "fstat", Path: f.Name(), Err: err}
102+
+ }
103+
+ if !isDevNull(&st) {
104+
+ return errors.New("container's /dev/null is invalid")
105+
+ }
106+
+ return nil
107+
+}
108+
+
109+
+// maskPaths masks the top of the specified paths inside a container to avoid
110+
+func maskPaths(paths []string, mountLabel string) error {
111+
+ for _, path := range paths {
112+
+ if err := mount("/dev/null", path, "", "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
113+
+ if errors.Is(err, unix.ENOTDIR) {
114+
+ if err := mount("tmpfs", path, "", "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel)); err != nil {
115+
+ return err
116+
+ }
117+
+ continue
118+
+ }
119+
+ return err
120+
+ }
121+
+ }
122+
+ return nil
123+
+}
124+
+
125+
+
126+
// For directories, maskPath mounts read-only tmpfs over the top of the specified path.
127+
func maskPath(path string, mountLabel string) error {
128+
if err := mount("/dev/null", path, "", "", unix.MS_BIND, ""); err != nil && !errors.Is(err, os.ErrNotExist) {
129+
diff --git a/vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
130+
index d1d94352..da407620 100644
131+
--- a/vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
132+
+++ b/vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
133+
@@ -141,11 +141,11 @@ func (l *linuxStandardInit) Init() error {
134+
return fmt.Errorf("can't make %q read-only: %w", path, err)
135+
}
136+
}
137+
- for _, path := range l.config.Config.MaskPaths {
138+
- if err := maskPath(path, l.config.Config.MountLabel); err != nil {
139+
- return fmt.Errorf("can't mask path %s: %w", path, err)
140+
- }
141+
+
142+
+ if err := maskPaths(l.config.Config.MaskPaths, l.config.Config.MountLabel); err != nil {
143+
+ return err
144+
}
145+
+
146+
pdeath, err := system.GetParentDeathSignal()
147+
if err != nil {
148+
return fmt.Errorf("can't get pdeath signal: %w", err)
149+
--
150+
2.45.4
151+

0 commit comments

Comments
 (0)