Skip to content

runc crashes when creating a container with a small enough unix.RLIMIT_NOFILE #5082

@geofffranks

Description

@geofffranks

Description

We have a test that started failing against runc 1.3.4. It tries to run a process via runc that will exhaust file descriptors, to validate the RLIMIT_NOFILE we provided the container. In 1.3.3 (and 1.3.2), we see this error returned:

runc exec: exit status 255: exec failed: unable to start container process: error loading seccomp filter into kernel: error patching filter: error disassembling original filter: error creating scratch pipe: pipe2: too many open files

However starting in 1.3.4, we get a stack trace:

             runtime: eventfd failed with 18446744073709551592
             fatal error: runtime: eventfd failed

             goroutine 1 gp=0xc000002380 m=0 mp=0x7f1ea67d8ac0 [running, locked to thread]:
             runtime.throw({0x7f1ea61f2c33?, 0x200000003?})
                runtime/panic.go:1094 +0x4a fp=0xc000147520 sp=0xc0001474f0 pc=0x7f1ea5cb7faa
             runtime.netpollinit()
                runtime/netpoll_epoll.go:31 +0x14e fp=0xc000147590 sp=0xc000147520 pc=0x7f1ea5c7c42e
             runtime.netpollGenericInit()
                runtime/netpoll.go:224 +0x35 fp=0xc0001475a8 sp=0xc000147590 pc=0x7f1ea5c7b975
             internal/poll.runtime_pollServerInit()
                runtime/netpoll.go:215 +0xf fp=0xc0001475b8 sp=0xc0001475a8 pc=0x7f1ea5cb6aef
             sync.(*Once).doSlow(0x7f1e00000000?, 0xc000147640?)
                sync/once.go:78 +0xac fp=0xc000147610 sp=0xc0001475b8 pc=0x7f1ea5cd5a4c
             sync.(*Once).Do(...)
                sync/once.go:69
             internal/poll.(*pollDesc).init(0xc000026260, 0xc000026240)
                internal/poll/fd_poll_runtime.go:39 +0x3c fp=0xc000147630 sp=0xc000147610 pc=0x7f1ea5d32cdc
             internal/poll.(*FD).Init(0xc000026240, {0x7f1ea61e7400?, 0x7f1ea5d10254?}, 0x0?)
                internal/poll/fd_unix.go:66 +0x45 fp=0xc000147650 sp=0xc000147630 pc=0x7f1ea5d33bc5
             os.newFile(0x7, {0x7f1ea61e7066, 0x2}, 0x2, 0x0)
                os/file_unix.go:218 +0x15d fp=0xc000147690 sp=0xc000147650 pc=0x7f1ea5d425dd
             os.Pipe()
                os/pipe2_unix.go:21 +0xb8 fp=0xc0001476f0 sp=0xc000147690 pc=0x7f1ea5d44f38
             github.com/opencontainers/runc/libcontainer/seccomp/patchbpf.disassembleFilter(0xc0000a1920)
                github.com/opencontainers/runc@v1.4.0/libcontainer/seccomp/patchbpf/enosys_linux.go:144 +0x53 fp=0xc0001477c8 sp=0xc0001476f0 pc=0x7f1ea5fcd0d3
             github.com/opencontainers/runc/libcontainer/seccomp/patchbpf.enosysPatchFilter(0xc0000e2880, 0xc000147800?)
                github.com/opencontainers/runc@v1.4.0/libcontainer/seccomp/patchbpf/enosys_linux.go:623 +0x2a fp=0xc000147898 sp=0xc0001477c8 pc=0x7f1ea5fcf60a
             github.com/opencontainers/runc/libcontainer/seccomp/patchbpf.PatchAndLoad(0xc0000e2880, 0xc0000a1920)
                github.com/opencontainers/runc@v1.4.0/libcontainer/seccomp/patchbpf/enosys_linux.go:719 +0x25 fp=0xc000147910 sp=0xc000147898 pc=0x7f1ea5fcfd65
             github.com/opencontainers/runc/libcontainer/seccomp.InitSeccomp(0xc0000e2880)
                github.com/opencontainers/runc@v1.4.0/libcontainer/seccomp/seccomp_linux.go:126 +0x518 fp=0xc0001479a8 sp=0xc000147910 pc=0x7f1ea5fd12d8
             github.com/opencontainers/runc/libcontainer.(*linuxSetnsInit).Init(0xc000147b30)
                github.com/opencontainers/runc@v1.4.0/libcontainer/setns_init_linux.go:111 +0x4db fp=0xc000147ac8 sp=0xc0001479a8 pc=0x7f1ea5fff5db
             github.com/opencontainers/runc/libcontainer.containerInit({0xc00002a017, 0x5}, 0xc0000f85a0, 0xc00002c040, 0x0, 0x0, 0x0, 0xc00004a018)
                github.com/opencontainers/runc@v1.4.0/libcontainer/init_linux.go:252 +0xf7 fp=0xc000147b78 sp=0xc000147ac8 pc=0x7f1ea5fe6bd7
             github.com/opencontainers/runc/libcontainer.startInitialization()
                github.com/opencontainers/runc@v1.4.0/libcontainer/init_linux.go:235 +0x81a fp=0xc000147dd0 sp=0xc000147b78 pc=0x7f1ea5fe663a
             github.com/opencontainers/runc/libcontainer.Init()
                github.com/opencontainers/runc@v1.4.0/libcontainer/init_linux.go:109 +0x25 fp=0xc000147e18 sp=0xc000147dd0 pc=0x7f1ea5fe5dc5
             main.init.0()
                github.com/opencontainers/runc@v1.4.0/init.go:14 +0x33 fp=0xc000147e28 sp=0xc000147e18 pc=0x7f1ea60a7e73
             runtime.doInit1(0x7f1ea67ad840)
                runtime/proc.go:7670 +0xd7 fp=0xc000147f50 sp=0xc000147e28 pc=0x7f1ea5c928b7
             runtime.doInit(...)
                runtime/proc.go:7637
             runtime.main()
                runtime/proc.go:256 +0x350 fp=0xc000147fe0 sp=0xc000147f50 pc=0x7f1ea5c83630
             runtime.goexit({})
                runtime/asm_amd64.s:1693 +0x1 fp=0xc000147fe8 sp=0xc000147fe0 pc=0x7f1ea5cbff41

Our tests use NOFILE limit of 10. Increasing this to 20 allows the container to create, and run through the test code, failing on the 11th FD. We can decrease NOFILE down to 14, at which point the container creation failures resume. It would appear that in our case (or maybe all?) we need 14 FDs for runc to create the container, but after creating, 10 remain in use inside the container processes, as at the NOFILE value of 15, we get a container, and the test process is able to open 5 FDs before failing.

Steps to reproduce the issue

  1. Create a container on runc 1.3.4 and 1.3.3 with a NOFILE limit of 10, 14, 15, 20, and 12.
  2. Have the container run the following:
User: "root",
Path: "sh",
Args: "-c", `
set -e

  # must start after fd 2
  exec 10<>file1
  exec 11<>file2
  exec 12<>file3
  exec 13<>file4
  exec 14<>file5
  exec 15<>file6
  exec 16<>file7
  exec 17<>file8
  exec 18<>file9
  exec 19<>file10
  exec 20<>file11

  echo should have died by now
`,

Describe the results you received and expected

At a minimum, runc should return the error it previously returned, rather than panicking.

I don't know if the NOFILE limit should apply to runc while it creates the container or not, but ideally it would at least set a minimum value for that limit which returns an error if it's too small for it to create a container, if that is something that runc can determine.

What version of runc are you using?

1.3.4.

Host OS information

PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

Host kernel information

Linux d56a88a5-8e16-4cb0-a838-91d3fdf523ed 5.15.0-161-generic #171-Ubuntu SMP Sat Oct 11 08:17:01 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions