From 8288672493196d201c748e23bb287ebf479b26b5 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Thu, 19 Jun 2025 15:24:48 +0200 Subject: [PATCH] Ensure that filemode is set for device nodes This change ensures that the filemode for a CDI device is properly set from the host device node when adding devices to the CDI spec. Without this the device node permissions are 000 in the container under certain conditions (such as Kata). Signed-off-by: Evan Lezar --- pkg/cdi/container-edits_test.go | 22 +++++++++----- pkg/cdi/container-edits_unix.go | 52 ++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/pkg/cdi/container-edits_test.go b/pkg/cdi/container-edits_test.go index 9f01a6eb..1aa66ed6 100644 --- a/pkg/cdi/container-edits_test.go +++ b/pkg/cdi/container-edits_test.go @@ -17,6 +17,7 @@ package cdi import ( + "os" "runtime" "testing" @@ -306,6 +307,9 @@ func TestValidateContainerEdits(t *testing.T) { func TestApplyContainerEdits(t *testing.T) { nullDeviceMajor := int64(1) nullDeviceMinor := int64(3) + + mode := uint32(0666) + nullDeviceFileMode := (*os.FileMode)(&mode) if runtime.GOOS == "darwin" { nullDeviceMajor = 3 nullDeviceMinor = 2 @@ -352,10 +356,11 @@ func TestApplyContainerEdits(t *testing.T) { Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/null", - Type: "c", - Major: nullDeviceMajor, - Minor: nullDeviceMinor, + Path: "/dev/null", + Type: "c", + Major: nullDeviceMajor, + Minor: nullDeviceMinor, + FileMode: nullDeviceFileMode, }, }, Resources: &oci.LinuxResources{ @@ -395,10 +400,11 @@ func TestApplyContainerEdits(t *testing.T) { Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/null", - Type: "c", - Major: nullDeviceMajor, - Minor: nullDeviceMinor, + Path: "/dev/null", + Type: "c", + Major: nullDeviceMajor, + Minor: nullDeviceMinor, + FileMode: nullDeviceFileMode, }, }, Resources: &oci.LinuxResources{ diff --git a/pkg/cdi/container-edits_unix.go b/pkg/cdi/container-edits_unix.go index e0d41a68..5d34c000 100644 --- a/pkg/cdi/container-edits_unix.go +++ b/pkg/cdi/container-edits_unix.go @@ -21,6 +21,7 @@ package cdi import ( "errors" "fmt" + "os" "golang.org/x/sys/unix" ) @@ -31,16 +32,28 @@ const ( fifoDevice = "p" ) +type deviceInfo struct { + // cgroup properties + deviceType string + major int64 + minor int64 + + // device node properties + fileMode os.FileMode +} + // deviceInfoFromPath takes the path to a device and returns its type, // major and minor device numbers. // // It was adapted from https://github.com/opencontainers/runc/blob/v1.1.9/libcontainer/devices/device_unix.go#L30-L69 -func deviceInfoFromPath(path string) (devType string, major, minor int64, _ error) { +func deviceInfoFromPath(path string) (*deviceInfo, error) { var stat unix.Stat_t err := unix.Lstat(path, &stat) if err != nil { - return "", 0, 0, err + return nil, err } + + var devType string switch stat.Mode & unix.S_IFMT { case unix.S_IFBLK: devType = blockDevice @@ -49,10 +62,18 @@ func deviceInfoFromPath(path string) (devType string, major, minor int64, _ erro case unix.S_IFIFO: devType = fifoDevice default: - return "", 0, 0, errors.New("not a device node") + return nil, errors.New("not a device node") } devNumber := uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS. - return devType, int64(unix.Major(devNumber)), int64(unix.Minor(devNumber)), nil + + di := deviceInfo{ + deviceType: devType, + major: int64(unix.Major(devNumber)), + minor: int64(unix.Minor(devNumber)), + fileMode: os.FileMode(stat.Mode &^ unix.S_IFMT), + } + + return &di, nil } // fillMissingInfo fills in missing mandatory attributes from the host device. @@ -65,22 +86,31 @@ func (d *DeviceNode) fillMissingInfo() error { return nil } - deviceType, major, minor, err := deviceInfoFromPath(d.HostPath) + di, err := deviceInfoFromPath(d.HostPath) if err != nil { return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err) } if d.Type == "" { - d.Type = deviceType + d.Type = di.deviceType } else { - if d.Type != deviceType { + if d.Type != di.deviceType { return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)", - d.Path, d.HostPath, d.Type, deviceType) + d.Path, d.HostPath, d.Type, di.deviceType) } } - if d.Major == 0 && d.Type != "p" { - d.Major = major - d.Minor = minor + + if d.FileMode == nil { + d.FileMode = &di.fileMode + } + + if d.Type == "p" { + return nil + } + + if d.Major == 0 { + d.Major = di.major + d.Minor = di.minor } return nil