github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/devices/v2.go (about)

     1  package devices
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"golang.org/x/sys/unix"
     7  
     8  	"github.com/opencontainers/runc/libcontainer/configs"
     9  	"github.com/opencontainers/runc/libcontainer/devices"
    10  	"github.com/opencontainers/runc/libcontainer/userns"
    11  )
    12  
    13  func isRWM(perms devices.Permissions) bool {
    14  	var r, w, m bool
    15  	for _, perm := range perms {
    16  		switch perm {
    17  		case 'r':
    18  			r = true
    19  		case 'w':
    20  			w = true
    21  		case 'm':
    22  			m = true
    23  		}
    24  	}
    25  	return r && w && m
    26  }
    27  
    28  // This is similar to the logic applied in crun for handling errors from bpf(2)
    29  // <https://github.com/containers/crun/blob/0.17/src/libcrun/cgroup.c#L2438-L2470>.
    30  func canSkipEBPFError(r *configs.Resources) bool {
    31  	// If we're running in a user namespace we can ignore eBPF rules because we
    32  	// usually cannot use bpf(2), as well as rootless containers usually don't
    33  	// have the necessary privileges to mknod(2) device inodes or access
    34  	// host-level instances (though ideally we would be blocking device access
    35  	// for rootless containers anyway).
    36  	if userns.RunningInUserNS() {
    37  		return true
    38  	}
    39  
    40  	// We cannot ignore an eBPF load error if any rule if is a block rule or it
    41  	// doesn't permit all access modes.
    42  	//
    43  	// NOTE: This will sometimes trigger in cases where access modes are split
    44  	//       between different rules but to handle this correctly would require
    45  	//       using ".../libcontainer/cgroup/devices".Emulator.
    46  	for _, dev := range r.Devices {
    47  		if !dev.Allow || !isRWM(dev.Permissions) {
    48  			return false
    49  		}
    50  	}
    51  	return true
    52  }
    53  
    54  func setV2(dirPath string, r *configs.Resources) error {
    55  	if r.SkipDevices {
    56  		return nil
    57  	}
    58  	insts, license, err := deviceFilter(r.Devices)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	dirFD, err := unix.Open(dirPath, unix.O_DIRECTORY|unix.O_RDONLY, 0o600)
    63  	if err != nil {
    64  		return fmt.Errorf("cannot get dir FD for %s", dirPath)
    65  	}
    66  	defer unix.Close(dirFD)
    67  	if _, err := loadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil {
    68  		if !canSkipEBPFError(r) {
    69  			return err
    70  		}
    71  	}
    72  	return nil
    73  }