Skip to content

Commit 54934d6

Browse files
committed
userns: detect initial namespace by inode
The current uid_map heuristic treats a private user namespace with an identity uid_map as the initial user namespace. systemd 260 can create that shape with PrivateUsers=full, causing callers to believe they can perform initial-namespace-only operations such as cgroup device BPF setup. Use /proc/self/ns/user's inode instead and compare it with the kernel's PROC_USER_INIT_INO value. This detects the initial user namespace directly instead of inferring it from the uid_map layout. Related: containers/crun#2150 Signed-off-by: Alvaro Leiva Geisse <aleivag@gmail.com>
1 parent d7c3b9e commit 54934d6

2 files changed

Lines changed: 11 additions & 39 deletions

File tree

userns/userns_linux.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import (
55
"fmt"
66
"os"
77
"sync"
8+
"syscall"
89
)
910

11+
// See PROC_USER_INIT_INO in https://github.com/torvalds/linux/blob/v7.1/include/uapi/linux/nsfs.h#L50.
12+
const procUserInitIno = 0xEFFFFFFD
13+
1014
var inUserNS = sync.OnceValue(runningInUserNS)
1115

1216
// runningInUserNS detects whether we are currently running in a user namespace.
@@ -17,6 +21,11 @@ var inUserNS = sync.OnceValue(runningInUserNS)
1721
// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49
1822
// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700
1923
func runningInUserNS() bool {
24+
var st syscall.Stat_t
25+
if err := syscall.Stat("/proc/self/ns/user", &st); err == nil {
26+
return st.Ino != procUserInitIno
27+
}
28+
2029
file, err := os.Open("/proc/self/uid_map")
2130
if err != nil {
2231
// This kernel-provided file only exists if user namespaces are supported.
@@ -30,12 +39,9 @@ func runningInUserNS() bool {
3039
return false
3140
}
3241

33-
return uidMapInUserNS(string(l))
34-
}
35-
36-
func uidMapInUserNS(uidMap string) bool {
42+
uidMap := string(l)
3743
if uidMap == "" {
38-
// File exist but empty (the initial state when userns is created,
44+
// File exists but is empty (the initial state when userns is created,
3945
// see user_namespaces(7)).
4046
return true
4147
}

userns/userns_linux_test.go

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)