mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add devices cgroups which is partially implemented. It could export the device cgroups to devices.deny and devices.allow, add/remove device cgroups rules will need to be implemented to fully support device cgroups.
PiperOrigin-RevId: 552673967
This commit is contained in:
@@ -40,6 +40,7 @@ go_library(
|
||||
"cpu.go",
|
||||
"cpuacct.go",
|
||||
"cpuset.go",
|
||||
"devices.go",
|
||||
"dir_refs.go",
|
||||
"job.go",
|
||||
"memory.go",
|
||||
|
||||
@@ -89,13 +89,14 @@ var allControllers = []kernel.CgroupControllerType{
|
||||
kernel.CgroupControllerCPU,
|
||||
kernel.CgroupControllerCPUAcct,
|
||||
kernel.CgroupControllerCPUSet,
|
||||
kernel.CgroupControllerDevices,
|
||||
kernel.CgroupControllerJob,
|
||||
kernel.CgroupControllerMemory,
|
||||
kernel.CgroupControllerPIDs,
|
||||
}
|
||||
|
||||
// SupportedMountOptions is the set of supported mount options for cgroupfs.
|
||||
var SupportedMountOptions = []string{"all", "cpu", "cpuacct", "cpuset", "job", "memory", "pids"}
|
||||
var SupportedMountOptions = []string{"all", "cpu", "cpuacct", "cpuset", "devices", "job", "memory", "pids"}
|
||||
|
||||
// FilesystemType implements vfs.FilesystemType.
|
||||
//
|
||||
@@ -227,6 +228,10 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
delete(mopts, "cpuset")
|
||||
wantControllers = append(wantControllers, kernel.CgroupControllerCPUSet)
|
||||
}
|
||||
if _, ok := mopts["devices"]; ok {
|
||||
delete(mopts, "devices")
|
||||
wantControllers = append(wantControllers, kernel.CgroupControllerDevices)
|
||||
}
|
||||
if _, ok := mopts["job"]; ok {
|
||||
delete(mopts, "job")
|
||||
wantControllers = append(wantControllers, kernel.CgroupControllerJob)
|
||||
@@ -341,6 +346,8 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
c = newCPUAcctController(fs)
|
||||
case kernel.CgroupControllerCPUSet:
|
||||
c = newCPUSetController(k, fs)
|
||||
case kernel.CgroupControllerDevices:
|
||||
c = newDevicesController(fs)
|
||||
case kernel.CgroupControllerJob:
|
||||
c = newJobController(fs)
|
||||
case kernel.CgroupControllerMemory:
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cgroupfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
const (
|
||||
allowedDevices = "devices.allow"
|
||||
deniedDevices = "devices.deny"
|
||||
wildcardDevice = 'a'
|
||||
)
|
||||
|
||||
// permission represents a device access, read, write, and mknod.
|
||||
type permission string
|
||||
|
||||
// +stateify savable
|
||||
type deviceRule struct {
|
||||
// Device type, when the type is all, the following fields are ignored.
|
||||
controllerType rune
|
||||
// The device's major number.
|
||||
major *int64
|
||||
// The device's minor number.
|
||||
minor *int64
|
||||
// Cgroup access permission.
|
||||
access permission
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type devicesController struct {
|
||||
controllerCommon
|
||||
controllerStateless
|
||||
controllerNoResource
|
||||
|
||||
// mu protects the fields below.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
|
||||
// Allow or deny the device rules below.
|
||||
allow bool
|
||||
deviceRules []deviceRule
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type allowedDevicesData struct {
|
||||
c *devicesController
|
||||
}
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *allowedDevicesData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
return generate(ctx, buf, d.c, true)
|
||||
}
|
||||
|
||||
// Write implements vfs.WritableDynamicBytesSource.Write.
|
||||
func (d *allowedDevicesData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
|
||||
return write(ctx, src, offset, d.c, true)
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type deniedDevicesData struct {
|
||||
c *devicesController
|
||||
}
|
||||
|
||||
// Generate implements vfs.DynamicBytesSource.Generate.
|
||||
func (d *deniedDevicesData) Generate(ctx context.Context, buf *bytes.Buffer) error {
|
||||
return generate(ctx, buf, d.c, false)
|
||||
}
|
||||
|
||||
// Write implements vfs.WritableDynamicBytesSource.Write.
|
||||
func (d *deniedDevicesData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) {
|
||||
return write(ctx, src, offset, d.c, true)
|
||||
}
|
||||
|
||||
func generate(ctx context.Context, buf *bytes.Buffer, c *devicesController, allow bool) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if allow == c.allow && len(c.deviceRules) > 0 {
|
||||
for i, rule := range c.deviceRules {
|
||||
if rule.controllerType == wildcardDevice {
|
||||
buf.Reset()
|
||||
buf.WriteRune(wildcardDevice)
|
||||
return nil
|
||||
}
|
||||
buf.WriteString(deviceRuleString(rule))
|
||||
if i < len(c.deviceRules)-1 {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.WriteString("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func write(ctx context.Context, src usermem.IOSequence, offset int64, c *devicesController, allow bool) (int64, error) {
|
||||
// TODO(b/289099718): add functions to add and remove rules when writing to device controller data.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var _ controller = (*devicesController)(nil)
|
||||
|
||||
func newDevicesController(fs *filesystem) *devicesController {
|
||||
// The root device cgroup starts with rwm to all.
|
||||
c := &devicesController{
|
||||
allow: true,
|
||||
deviceRules: []deviceRule{},
|
||||
}
|
||||
c.controllerCommon.init(kernel.CgroupControllerJob, fs)
|
||||
return c
|
||||
}
|
||||
|
||||
// Clone implements controller.Clone.
|
||||
func (c *devicesController) Clone() controller {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
newRules := make([]deviceRule, len(c.deviceRules))
|
||||
copy(newRules, c.deviceRules)
|
||||
new := &devicesController{
|
||||
allow: c.allow,
|
||||
deviceRules: newRules,
|
||||
}
|
||||
new.controllerCommon.cloneFromParent(c)
|
||||
return new
|
||||
}
|
||||
|
||||
// AddControlFiles implements controller.AddControlFiles.
|
||||
func (c *devicesController) AddControlFiles(ctx context.Context, creds *auth.Credentials, _ *cgroupInode, contents map[string]kernfs.Inode) {
|
||||
contents[allowedDevices] = c.fs.newControllerWritableFile(ctx, creds, &allowedDevicesData{c: c}, true)
|
||||
contents[deniedDevices] = c.fs.newControllerWritableFile(ctx, creds, &deniedDevicesData{c: c}, true)
|
||||
}
|
||||
|
||||
func deviceRuleString(rule deviceRule) string {
|
||||
return fmt.Sprintf("%c %s:%s %s", rule.controllerType, deviceNumber(rule.major), deviceNumber(rule.minor), rule.access)
|
||||
}
|
||||
|
||||
// deviceNumber converts a device number to string.
|
||||
func deviceNumber(number *int64) string {
|
||||
if number == nil {
|
||||
return "*"
|
||||
}
|
||||
return fmt.Sprint(number)
|
||||
}
|
||||
@@ -42,6 +42,7 @@ const (
|
||||
CgroupControllerCPU = CgroupControllerType("cpu")
|
||||
CgroupControllerCPUAcct = CgroupControllerType("cpuacct")
|
||||
CgroupControllerCPUSet = CgroupControllerType("cpuset")
|
||||
CgroupControllerDevices = CgroupControllerType("devices")
|
||||
CgroupControllerJob = CgroupControllerType("job")
|
||||
CgroupControllerMemory = CgroupControllerType("memory")
|
||||
CgroupControllerPIDs = CgroupControllerType("pids")
|
||||
@@ -56,6 +57,8 @@ func ParseCgroupController(val string) (CgroupControllerType, error) {
|
||||
return CgroupControllerCPUAcct, nil
|
||||
case "cpuset":
|
||||
return CgroupControllerCPUSet, nil
|
||||
case "devices":
|
||||
return CgroupControllerDevices, nil
|
||||
case "job":
|
||||
return CgroupControllerJob, nil
|
||||
case "memory":
|
||||
|
||||
@@ -1428,6 +1428,17 @@ TEST(PIDsCgroup, RaceFSDestructionChargeUncharge) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(DevicesCgroup, ControlFilesExist) {
|
||||
SKIP_IF(!CgroupsAvailable());
|
||||
|
||||
Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()));
|
||||
Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("devices"));
|
||||
|
||||
// The root group starts with allowing rwm to all.
|
||||
EXPECT_THAT(c.ReadControlFile("devices.allow"), IsPosixErrorOkAndHolds(""));
|
||||
EXPECT_THAT(c.ReadControlFile("devices.deny"), IsPosixErrorOkAndHolds(""));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user