runsc Doesn't Skip Empty BlockIO
Author: colin240215Created Sep 14, 2026Updated Sep 14, 2026
Labelstype: bug
Description
Symptom
runsc fails on a kernel with
- cgroup v1, and
CONFIG_BLK_CGROUPdisabled
Root Cause
992a840889 intends to make cgroup blkio optional. But it skips iff the BlockIO struct is nil.
Docker always populates that struct, even when no I/O limit was requested.
- v24.0.2 effectively emits
"blockIO":{"weight":0} - newer version effectively emits
"blockIO":{}
Inspect Container Config
docker run --rm -v /run:/rc:ro alpine sh -c 'grep -o "\"blockIO\":{[^}]*}" $(find /rc -path "*/io.containerd.runtime.v2.task/moby/$(hostname)*/config.json")'Output
// Docker v24.0.2
"blockIO":{"weight":0}
// Newer Docker
"blockIO":{}Proposal
In addition to nil check, blockIO.skip() checks whether all fields of LinuxBlockIO are default.
func (*blockIO) skip(spec *specs.LinuxResources) error {
if spec == nil || spec.BlockIO == nil {
return nil
}
b := spec.BlockIO
if (b.Weight != nil && *b.Weight != 0) ||
(b.LeafWeight != nil && *b.LeafWeight != 0) ||
len(b.WeightDevice) > 0 ||
len(b.ThrottleReadBpsDevice) > 0 ||
len(b.ThrottleWriteBpsDevice) > 0 ||
len(b.ThrottleReadIOPSDevice) > 0 ||
len(b.ThrottleWriteIOPSDevice) > 0 {
return fmt.Errorf("blkio controller is missing but limits are set in OCI spec")
}
return nil
}Steps to reproduce
Docker Runtime Failure
Requirements (if infeasible, try Unit Test Failure instead)
- cgroup v1
CONFIG_BLK_CGROUPdisabled. i.e. build the kernel withCONFIG_BLK_CGROUP=n
Command
docker run --rm --runtime runsc alpine trueError
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: creating container: cannot set up cgroup for root: configuring cgroup: stat /sys/fs/cgroup/blkio: no such file or directory: unknown.Unit Test Failure
Add extraValid to TestOptional
{
name: "blkio",
ctrlr: &blockIO{},
extraValid: []*specs.LinuxResources{
{BlockIO: &specs.LinuxBlockIO{}}, // Docker 29
{BlockIO: &specs.LinuxBlockIO{Weight: uint16Ptr(0)}}, // Docker ≤ 24
},
invalid: []struct {
name string
spec *specs.LinuxResources
err string
}{
{
name: "weight",
spec: &specs.LinuxResources{BlockIO: &specs.LinuxBlockIO{Weight: uint16Ptr(1)}},
err: "blkio controller is missing but limits are set in OCI spec",
},
},
},The test fails with the current skip().
The proposal fixes it.
Combined with Inspect Container Config, this shows that the current implementation does not skip BlockIO as expected.
runsc version
runsc version VERSION_MISSING
spec: 1.2.1
Built from source at release-20260817.0-43-g80336ad54docker version (if using docker)
Client:
Version: 24.0.2
API version: 1.43
Go version: go1.20.4
Git commit: 610b8d0
Built: Thu Sep 25 10:08:01 2025
OS/Arch: linux/amd64
Context: default
Server:
Engine:
Version: 24.0.2
API version: 1.43 (minimum version 1.12)
Go version: go1.20.4
Git commit: bd0a34d
Built: Thu Sep 25 10:09:08 2025
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.7.1
GitCommit: fea6458b8abe502f6228eab2e5a6678fcb5c3fb0
runc:
Version: v1.1.7
GitCommit: 0320c58
docker-init:
Version: 0.19.0
GitCommit: ed96d00uname
Linux 5.10.55+ SMP x86_64 GNU/Linux
kubectl (if using Kubernetes)
repo state (if built from source)
fatal: No names found, cannot describe anything.
runsc debug logs (if available)
Not applicable: the failure happens in runsc create before the sandbox startsSource: google/gvisor