From 76517a27d8f1be974d03a7d967631186dfc91fed Mon Sep 17 00:00:00 2001 From: Eugene Gvozdetsky Date: Tue, 13 Oct 2026 16:25:04 +0200 Subject: [PATCH] config-linux: add linux.ioUring Restrict the io_uring operations a container can submit, using the io_uring task restrictions added in Linux 7.0. io_uring operations are not system calls, so seccomp cannot filter them. ops is an allowlist that doesn't depend on submission flags, so runtimes behave the same whether they use BPF filters or the task allowlist. An empty socketFamilies is not allowed: Go's omitempty would turn it into "any family". To deny all sockets, leave IORING_OP_SOCKET out of ops. Closes #1323. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Eugene Gvozdetsky --- config-linux.md | 46 +++++++++++++++++++ features-linux.md | 38 +++++++++++++++ features.md | 12 +++++ schema/config-linux.json | 21 +++++++++ schema/defs-linux.json | 8 ++++ schema/features-linux.json | 20 ++++++++ .../bad/linux-io-uring-empty-families.json | 14 ++++++ .../config/bad/linux-io-uring-invalid-op.json | 13 ++++++ schema/test/config/bad/linux-io-uring.json | 13 ++++++ schema/test/config/good/linux-io-uring.json | 19 ++++++++ specs-go/config.go | 13 ++++++ specs-go/features/features.go | 16 +++++++ 12 files changed, 233 insertions(+) create mode 100644 schema/test/config/bad/linux-io-uring-empty-families.json create mode 100644 schema/test/config/bad/linux-io-uring-invalid-op.json create mode 100644 schema/test/config/bad/linux-io-uring.json create mode 100644 schema/test/config/good/linux-io-uring.json diff --git a/config-linux.md b/config-linux.md index c71757494..2d8f2e49a 100644 --- a/config-linux.md +++ b/config-linux.md @@ -1040,6 +1040,51 @@ Example sending a single `seccompFd` file descriptor in the `SCM_RIGHTS` array: } ``` +## io_uring + +**`ioUring`** (object, OPTIONAL) restricts the [io_uring(7)][io_uring.7] operations that the container process can submit. +io_uring operations are not system calls, so [seccomp](#configLinuxSeccomp) cannot filter them. +Whether the io_uring system calls themselves are allowed is controlled by seccomp. + +* **`ops`** *(array of strings, REQUIRED)* - the io_uring operations that are allowed. + All other operations are denied, and an empty array denies all operations. + Whether an operation is allowed does not depend on its submission flags (`IOSQE_*`). + Values are names of the `IORING_OP_*` constants from ``, for example `IORING_OP_READ`. + The runtime MUST generate an error if it does not recognize a value. + The runtime MUST ignore a recognized value that the kernel does not support. +* **`socketFamilies`** *(array of strings, OPTIONAL)* - the address families that `IORING_OP_SOCKET` can create. + If this property is not set, any address family is allowed. + If it is set, it MUST NOT be empty; to deny all sockets, leave `IORING_OP_SOCKET` out of `ops`. + Values are names of the `AF_*` constants from ``, for example `AF_INET`. + The runtime MUST generate an error if it does not recognize a value. + The runtime MUST generate an error if this property is set and `ops` does not contain `IORING_OP_SOCKET`. + +The runtime MUST apply the restrictions before the container process is executed, so that they apply to the container process and all of its descendants. +If the runtime cannot apply the restrictions, it MUST generate an error. +Applying `ioUring` SHOULD NOT prevent the container process from registering further io_uring restrictions of its own. +The restrictions only apply to io_uring instances created after they are applied. +An io_uring instance passed into the container, for example with `SCM_RIGHTS`, is not restricted. + +### Example + +```json +"ioUring": { + "ops": [ + "IORING_OP_READ", + "IORING_OP_WRITE", + "IORING_OP_SEND", + "IORING_OP_RECV", + "IORING_OP_ACCEPT", + "IORING_OP_SOCKET" + ], + "socketFamilies": [ + "AF_UNIX", + "AF_INET", + "AF_INET6" + ] +} +``` + ## Rootfs Mount Propagation **`rootfsPropagation`** (string, OPTIONAL) sets the rootfs's mount propagation. @@ -1140,6 +1185,7 @@ subset of the available options. [full.4]: https://man7.org/linux/man-pages/man4/full.4.html [set_mempolicy.2]: https://man7.org/linux/man-pages/man2/set_mempolicy.2.html +[io_uring.7]: https://man7.org/linux/man-pages/man7/io_uring.7.html [mknod.1]: https://man7.org/linux/man-pages/man1/mknod.1.html [mknod.2]: https://man7.org/linux/man-pages/man2/mknod.2.html [namespaces.7_2]: https://man7.org/linux/man-pages/man7/namespaces.7.html diff --git a/features-linux.md b/features-linux.md index 3331266dd..9ec66ce75 100644 --- a/features-linux.md +++ b/features-linux.md @@ -165,6 +165,44 @@ Irrelevant to the kernel version of the host operating system. } ``` +## io_uring + +**`ioUring`** (object, OPTIONAL) represents the runtime's implementation status of io_uring restrictions. +Irrelevant to the kernel version of the host operating system. + +* **`enabled`** (bool, OPTIONAL) represents whether the runtime supports io_uring restrictions. +* **`ops`** (array of strings, OPTIONAL) The recognized names of the io_uring operations. + The runtime MUST recognize the elements in this array in the [`ops` property of the `linux.ioUring` object in `config.json`](config-linux.md#configLinuxIoUring). +* **`socketFamilies`** (array of strings, OPTIONAL) The recognized names of the address families. + The runtime MUST recognize the elements in this array in the [`socketFamilies` property of the `linux.ioUring` object in `config.json`](config-linux.md#configLinuxIoUring). + +### Example + +```json +"ioUring": { + "enabled": true, + "ops": [ + "IORING_OP_NOP", + "IORING_OP_READV", + "IORING_OP_WRITEV", + "IORING_OP_READ", + "IORING_OP_WRITE", + "IORING_OP_SEND", + "IORING_OP_RECV", + "IORING_OP_ACCEPT", + "IORING_OP_SOCKET" + ], + "socketFamilies": [ + "AF_UNIX", + "AF_INET", + "AF_INET6", + "AF_NETLINK", + "AF_PACKET", + "AF_VSOCK" + ] +} +``` + ## AppArmor **`apparmor`** (object, OPTIONAL) represents the runtime's implementation status of AppArmor. diff --git a/features.md b/features.md index 9c729591a..136feb940 100644 --- a/features.md +++ b/features.md @@ -348,6 +348,18 @@ Here is a full example for reference. "SECCOMP_FILTER_FLAG_LOG" ] }, + "ioUring": { + "enabled": true, + "ops": [ + "IORING_OP_READ", + "IORING_OP_WRITE" + ], + "socketFamilies": [ + "AF_UNIX", + "AF_INET", + "AF_INET6" + ] + }, "apparmor": { "enabled": true }, diff --git a/schema/config-linux.json b/schema/config-linux.json index 778561d89..613b14a00 100644 --- a/schema/config-linux.json +++ b/schema/config-linux.json @@ -250,6 +250,27 @@ "defaultAction" ] }, + "ioUring": { + "type": "object", + "properties": { + "ops": { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/IoUringOp" + } + }, + "socketFamilies": { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/SocketFamily" + }, + "minItems": 1 + } + }, + "required": [ + "ops" + ] + }, "sysctl": { "$ref": "defs.json#/definitions/mapStringString" }, diff --git a/schema/defs-linux.json b/schema/defs-linux.json index edc0a523c..a0e7545ac 100644 --- a/schema/defs-linux.json +++ b/schema/defs-linux.json @@ -266,6 +266,14 @@ "allow" ] }, + "IoUringOp": { + "type": "string", + "pattern": "^IORING_OP_[A-Z0-9_]+$" + }, + "SocketFamily": { + "type": "string", + "pattern": "^AF_[A-Z0-9_]+$" + }, "MemoryPolicyMode": { "type": "string", "enum": [ diff --git a/schema/features-linux.json b/schema/features-linux.json index fcf3df7d6..43204ea62 100644 --- a/schema/features-linux.json +++ b/schema/features-linux.json @@ -74,6 +74,26 @@ } } }, + "ioUring": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "ops": { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/IoUringOp" + } + }, + "socketFamilies": { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/SocketFamily" + } + } + } + }, "apparmor": { "type": "object", "properties": { diff --git a/schema/test/config/bad/linux-io-uring-empty-families.json b/schema/test/config/bad/linux-io-uring-empty-families.json new file mode 100644 index 000000000..789638c36 --- /dev/null +++ b/schema/test/config/bad/linux-io-uring-empty-families.json @@ -0,0 +1,14 @@ +{ + "ociVersion": "1.0.0", + "root": { + "path": "rootfs" + }, + "linux": { + "ioUring": { + "ops": [ + "IORING_OP_SOCKET" + ], + "socketFamilies": [] + } + } +} diff --git a/schema/test/config/bad/linux-io-uring-invalid-op.json b/schema/test/config/bad/linux-io-uring-invalid-op.json new file mode 100644 index 000000000..c79455729 --- /dev/null +++ b/schema/test/config/bad/linux-io-uring-invalid-op.json @@ -0,0 +1,13 @@ +{ + "ociVersion": "1.0.0", + "root": { + "path": "rootfs" + }, + "linux": { + "ioUring": { + "ops": [ + "read" + ] + } + } +} diff --git a/schema/test/config/bad/linux-io-uring.json b/schema/test/config/bad/linux-io-uring.json new file mode 100644 index 000000000..2e63cfdd4 --- /dev/null +++ b/schema/test/config/bad/linux-io-uring.json @@ -0,0 +1,13 @@ +{ + "ociVersion": "1.0.0", + "root": { + "path": "rootfs" + }, + "linux": { + "ioUring": { + "socketFamilies": [ + "AF_INET" + ] + } + } +} diff --git a/schema/test/config/good/linux-io-uring.json b/schema/test/config/good/linux-io-uring.json new file mode 100644 index 000000000..18817ab4e --- /dev/null +++ b/schema/test/config/good/linux-io-uring.json @@ -0,0 +1,19 @@ +{ + "ociVersion": "1.0.0", + "root": { + "path": "rootfs" + }, + "linux": { + "ioUring": { + "ops": [ + "IORING_OP_READ", + "IORING_OP_WRITE", + "IORING_OP_SOCKET" + ], + "socketFamilies": [ + "AF_UNIX", + "AF_INET6" + ] + } + } +} diff --git a/specs-go/config.go b/specs-go/config.go index 711eb9e17..18e04fbcf 100644 --- a/specs-go/config.go +++ b/specs-go/config.go @@ -242,6 +242,8 @@ type Linux struct { NetDevices map[string]LinuxNetDevice `json:"netDevices,omitempty"` // Seccomp specifies the seccomp security settings for the container. Seccomp *LinuxSeccomp `json:"seccomp,omitempty"` + // IOUring restricts the io_uring operations available to the container. + IOUring *LinuxIOUring `json:"ioUring,omitempty"` // RootfsPropagation is the rootfs mount propagation mode for the container. RootfsPropagation string `json:"rootfsPropagation,omitempty"` // MaskedPaths masks over the provided paths inside the container. @@ -892,6 +894,17 @@ type LinuxIntelRdt struct { EnableMonitoring bool `json:"enableMonitoring,omitempty"` } +// LinuxIOUring restricts the io_uring operations available to the container. +type LinuxIOUring struct { + // Ops is the list of allowed io_uring operations, e.g., "IORING_OP_READ". + // All other operations are denied. + Ops []string `json:"ops"` + // SocketFamilies is the list of address families that IORING_OP_SOCKET + // can create, e.g., "AF_INET". Nil or empty means any address family; + // to deny all sockets, leave IORING_OP_SOCKET out of Ops. + SocketFamilies []string `json:"socketFamilies,omitempty"` +} + // LinuxMemoryPolicy represents input for the set_mempolicy syscall. type LinuxMemoryPolicy struct { // Mode for the set_mempolicy syscall. diff --git a/specs-go/features/features.go b/specs-go/features/features.go index 7b4c40640..d7bf626f4 100644 --- a/specs-go/features/features.go +++ b/specs-go/features/features.go @@ -44,6 +44,7 @@ type Linux struct { Cgroup *Cgroup `json:"cgroup,omitempty"` Seccomp *Seccomp `json:"seccomp,omitempty"` + IOUring *IOUring `json:"ioUring,omitempty"` Apparmor *Apparmor `json:"apparmor,omitempty"` Selinux *Selinux `json:"selinux,omitempty"` IntelRdt *IntelRdt `json:"intelRdt,omitempty"` @@ -109,6 +110,21 @@ type Seccomp struct { SupportedFlags []string `json:"supportedFlags,omitempty"` } +// IOUring represents the "ioUring" field. +type IOUring struct { + // Enabled is true if io_uring restrictions support is compiled in. + // Nil value means "unknown", not "false". + Enabled *bool `json:"enabled,omitempty"` + + // Ops is the list of the recognized io_uring operations, e.g., "IORING_OP_READ". + // Nil value means "unknown", not "no support for any operation". + Ops []string `json:"ops,omitempty"` + + // SocketFamilies is the list of the recognized address families, e.g., "AF_INET". + // Nil value means "unknown", not "no support for any address family". + SocketFamilies []string `json:"socketFamilies,omitempty"` +} + // Apparmor represents the "apparmor" field. type Apparmor struct { // Enabled is true if AppArmor support is compiled in.