diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index 5713460e486..2006ba3b7da 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -1060,6 +1060,9 @@ next: filemode = *d.FileMode &^ unix.S_IFMT } device := &devices.Device{ + // NOTE despite the name this is not a device access rule + // (those live under cgroup), but merely a way to specify + // a device type and major:minor for mknod. Rule: devices.Rule{ Type: dt, Major: d.Major, diff --git a/libcontainer/specconv/spec_linux_test.go b/libcontainer/specconv/spec_linux_test.go index 3e532d490fe..184d71cf94b 100644 --- a/libcontainer/specconv/spec_linux_test.go +++ b/libcontainer/specconv/spec_linux_test.go @@ -865,21 +865,22 @@ func TestNullProcess(t *testing.T) { func TestCreateDevices(t *testing.T) { spec := Example() - // dummy uid/gid for /dev/tty; will enable the test to check if createDevices() - // preferred the spec's device over the redundant default device + // Dummy uid/gid for /dev/tty; will enable the test to check if createDevices() + // preferred the spec's device over the redundant default device. ttyUid := uint32(1000) ttyGid := uint32(1000) - fm := os.FileMode(0o666) + ttyMode := os.FileMode(0o666) + ttyPath := "/dev/tty" spec.Linux = &specs.Linux{ Devices: []specs.LinuxDevice{ { // This is purposely redundant with one of runc's default devices - Path: "/dev/tty", + Path: ttyPath, Type: "c", Major: 5, Minor: 0, - FileMode: &fm, + FileMode: &ttyMode, UID: &ttyUid, GID: &ttyGid, }, @@ -900,14 +901,11 @@ func TestCreateDevices(t *testing.T) { t.Errorf("failed to create devices: %v", err) } - // Verify the returned default devices has the /dev/tty entry deduplicated - found := false - for _, d := range defaultDevs { - if d.Path == "/dev/tty" { - if found { - t.Errorf("createDevices failed: returned a duplicated device entry: %v", defaultDevs) - } - found = true + // Verify that /dev/tty is not in defaultDevs, + // because the one in spec is preferred. + for _, dev := range defaultDevs { + if dev.Path == ttyPath { + t.Errorf("%s should not be present in defaultDevs", ttyPath) } } @@ -934,12 +932,12 @@ func TestCreateDevices(t *testing.T) { // Verify that createDevices() deduplicated the /dev/tty entry in the config for _, configDev := range conf.Devices { - if configDev.Path == "/dev/tty" { + if configDev.Path == ttyPath { wantDev := &devices.Device{ - Path: "/dev/tty", - FileMode: 0o666, - Uid: 1000, - Gid: 1000, + Path: ttyPath, + FileMode: ttyMode, + Uid: ttyUid, + Gid: ttyGid, Rule: devices.Rule{ Type: devices.CharDevice, Major: 5, @@ -954,7 +952,7 @@ func TestCreateDevices(t *testing.T) { } // Verify that createDevices() added the entry for /dev/ram0 in the config - found = false + found := false for _, configDev := range conf.Devices { if configDev.Path == "/dev/ram0" { found = true diff --git a/tests/integration/dev.bats b/tests/integration/dev.bats index 6493c1c0388..57bc2f0b538 100644 --- a/tests/integration/dev.bats +++ b/tests/integration/dev.bats @@ -10,6 +10,36 @@ function teardown() { teardown_bundle } +@test "runc run [redundant default /dev/full]" { + requires root # Rootless devices behave differently. + + # 1. This is how a device from the default AllowedDevices should work as is. + # It's /dev/full so it should return "no space left on device" error. + update_config ' .process.args |= ["sh", "-c", "stat /dev/full; echo foo >/dev/full"]' + runc run test_dev + [ "$status" -eq 1 ] + [[ "$output" == *"Device type: 1,7"* ]] + [[ "$output" == *": No space left on device"* ]] + + # 2. Add the device to linux.devices only (but not to linux.resources.devices). + # This way it will be excluded from the cgroup allow rules. + update_config ' .linux.devices += [{"path": "/dev/full", "type": "c", "major": 1, "minor": 7}]' + runc run test_dev + [ "$status" -eq 1 ] + [[ "$output" == *"Device type: 1,7"* ]] + [[ "$output" == *": Operation not permitted"* ]] + + # 3. Also add it to cgroups list. Now it should work like the default one (see 1 above). + update_config ' .linux.resources.devices = [ + {"allow": false, "access": "rwm"}, + {"allow": true, "type": "c", "major": 1, "minor": 7, "access": "rw"} + ]' + runc run test_dev + [ "$status" -eq 1 ] + [[ "$output" == *"Device type: 1,7"* ]] + [[ "$output" == *": No space left on device"* ]] +} + @test "runc run [redundant default /dev/tty]" { update_config ' .linux.devices += [{"path": "/dev/tty", "type": "c", "major": 5, "minor": 0}] | .process.args |= ["ls", "-lLn", "/dev/tty"]'