internal/linux: detect initial user 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.

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: https://github.com/containers/crun/issues/2150
Signed-off-by: Alvaro Leiva Geisse <aleivag@gmail.com>
This commit is contained in:
Alvaro Leiva Geisse 2026-07-28 12:15:24 -07:00 committed by Stéphane Graber
parent 32ef0d98ab
commit 5ae0eb29b6
No known key found for this signature in database
GPG Key ID: C638974D64792D67

View File

@ -1,34 +1,20 @@
package linux
import (
"bufio"
"fmt"
"os"
"github.com/lxc/incus/v7/shared/logger"
"syscall"
)
// See PROC_USER_INIT_INO in include/uapi/linux/nsfs.h.
const procUserInitIno = 0xEFFFFFFD
// RunningInUserNS returns true if the current process is running inside a user namespace.
func RunningInUserNS() bool {
file, err := os.Open("/proc/self/uid_map")
var st syscall.Stat_t
err := syscall.Stat("/proc/self/ns/user", &st)
if err != nil {
return false
}
defer logger.WarnOnError(file.Close, "Failed to close file")
buf := bufio.NewReader(file)
l, _, err := buf.ReadLine()
if err != nil {
return false
}
line := string(l)
var a, b, c int64
_, _ = fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
if a == 0 && b == 0 && c == 4294967295 {
return false
}
return true
return st.Ino != procUserInitIno
}