Skip to content
Fencer Vulns
medium 5.5

CVE-2026-46223 json

cgroup: Defer css percpu_ref kill on rmdir until cgroup is depopulated

published 2026-05-28 5 mo ago modified 2026-06-17 4 mo ago also EUVD-2026-32850

cvss
5.5
version 3.1, NVD
cisa kev
Not listed
not in CISA's catalog
epss
0.1%
EPSS 0.1% chance of exploitation in 30 days, percentile 0%
public exploits
0
none known

Summary

In the Linux kernel, the following vulnerability has been resolved:

cgroup: Defer css percpu_ref kill on rmdir until cgroup is depopulated

A chain of commits going back to v7.0 reworked rmdir to satisfy the controller invariant that a subsystem's ->css_offline() must not run while tasks are still doing kernel-side work in the cgroup.

[1] d245698d727a ("cgroup: Defer task cgroup unlink until after the task is done switching out") [2] a72f73c4dd9b ("cgroup: Don't expose dead tasks in cgroup") [3] 1b164b876c36 ("cgroup: Wait for dying tasks to leave on rmdir") [4] 4c56a8ac6869 ("cgroup: Fix cgroup_drain_dying() testing the wrong condition") [5] 13e786b64bd3 ("cgroup: Increment nr_dying_subsys_* from rmdir context")

[1] moved task cset unlink from do_exit() to finish_task_switch() so a task's cset link drops only after the task has fully stopped scheduling. That made tasks past exit_signals() linger on cset->tasks until their final context switch, which led to a series of problems as what userspace expected to see after rmdir diverged from what the kernel needs to wait for. [2]-[5] tried to bridge that divergence: [2] filtered the exiting tasks from cgroup.procs; [3] had rmdir(2) sleep in TASK_UNINTERRUPTIBLE for them; [4] fixed the wait's condition; [5] made nr_dying_subsys_* visible synchronously.

The cgroup_drain_dying() wait in [3] turned out to be a dead end. When the rmdir caller is also the reaper of a zombie that pins a pidns teardown (e.g. host PID 1 systemd reaping orphan pids that were re-parented to it during the same teardown), rmdir blocks in TASK_UNINTERRUPTIBLE waiting for those pids to free, the pids can't free because PID 1 is the reaper and it's stuck in rmdir, and the system A-A deadlocks. No internal lock ordering breaks this; the wait itself is the bug.

The css killing side that drove the original reorder, however, can be made cleanly asynchronous: ->css_offline() is already async, run from css_killed_work_fn() driven by percpu_ref_kill_and_confirm(). The fix is to make that chain start only after all tasks have left the cgroup. rmdir's user-visible side then returns as soon as cgroup.procs and friends are empty, while ->css_offline() still runs only after the cgroup is fully drained.

Verified by the original reproducer (pidns teardown + zombie reaper, runs under vng) which hangs vanilla and succeeds here, and by per-commit deterministic repros for [2], [3], [4], [5] with a boot parameter that widens the post-exit_signals() window so each state is reliably reachable. Some stress tests on top of that.

cgroup_apply_control_disable() has the same shape of pre-existing race: when a controller is disabled via subtree_control, kill_css() ran synchronously while tasks past exit_signals() could still be linked to the cgroup's csets, and ->css_offline() could fire before they drained. This patch preserves the existing synchronous behavior at that call site (kill_css_sync() + kill_css_finish() back-to-back) and a follow-up patch will defer kill_css_finish() there using a per-css trigger.

This seems like the right approach and I don't see problems with it. The changes are somewhat invasive but not excessively so, so backporting to -stable should be okay. If something does turn out to be wrong, the fallback is to revert the entire chain ([1]-[5]) and rework in the development branch instead.

v2: Pin cgrp across the deferred destroy work with explicit cgroup_get()/cgroup_put() around queue_work() and the work_fn. v1 wasn't actually broken (ordered cgroup_offline_wq + queue_work order in cgroup_task_dead() saved it) but the explicit ref removes the dependency on those non-obvious invariants. Also note the pre-existing cgroup_apply_control_disable() race in the description; a follow-up will defer kill_css_finish() there.

Patch / fix

  • Linux Linux 6.20, 7.0.9, 7.1
  • linux Debian 14 7.0.9-1

debian advisories DEBIAN-CVE-2026-46223

  • linux Ubuntu 26.04 LTS 7.0.0-28.28
  • linux-aws Ubuntu 26.04 LTS 7.0.0-1009.9
  • linux-gcp Ubuntu 26.04 LTS 7.0.0-1008.8
  • linux-gke Ubuntu 26.04 LTS 7.0.0-1003.4
  • linux-ibm Ubuntu 26.04 LTS 7.0.0-1010.10
  • linux-azure Ubuntu 26.04 LTS 7.0.0-1010.10
  • linux-raspi Ubuntu 26.04 LTS 7.0.0-1015.15
  • linux-riscv Ubuntu 26.04 LTS 7.0.0-28.28.1

ubuntu advisories UBUNTU-CVE-2026-46223

Ubuntu security data © Canonical Ltd., from https://github.com/canonical/ubuntu-security-notices via OSV; changed: normalised to fixed package versions per release. CC BY-SA 4.0

  • kernel-source openSUSE Tumbleweed 7.0.11-1.1

opensuse advisories openSUSE-SU-2026:10954-1

Vendor advisories

What the vendors themselves say about their products: affected, fixed or not affected, and what to do.

red hat product security vex CVE-2026-46223 updated

kernel: cgroup: Defer css percpu_ref kill on rmdir until cgroup is depopulated

  • Red Hat Enterprise Linux 6 perf, kernel, python-perf, kernel-kdump, kernel-firmware, kernel-bootwrapper, +1 more Red Hat Enterprise Linux 7 perf, kernel, bpftool, kernel-rt, python-perf, kernel-kdump, +8 more Red Hat Enterprise Linux 8 perf, kernel, bpftool, kernel-rt, kernel-core, kernel-tools, +29 more Red Hat Enterprise Linux 9 rv, perf, rtla, kernel, bpftool, libperf, +83 more Red Hat Enterprise Linux 10 kernel
    not affected: vulnerable code not present

Red Hat CSAF VEX © Red Hat, Inc., from https://security.access.redhat.com/data/csaf/v2/vex/; changed: reduced to product statuses and remediations. CC BY 4.0 · csaf

Exploit prediction

0.08% chance of exploitation in the next 30 days, higher than 0% of all scored CVEs (FIRST EPSS, 2026-11-08).

CVSS scores

assigned by version score vector
NVD 3.1 5.5 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

Affected products

  • Linux Linux

Classification

Weakness
Technology feeds

References

Sources

Fencer Vulns keeps one entry per vulnerability: the CVE id wins, then GHSA, and records that alias each other are merged, except that two CVEs always keep separate entries. The same data is available as JSON.