MEDIUM 5.5

CVE-2026-46223: Linux Kernel Cgroup Deadlock During Container Teardown

This Linux kernel vulnerability centers on a deadlock condition in cgroup resource management during container shutdown. When a system administrator removes a cgroup (via rmdir), the kernel's cleanup logic can become stuck waiting for tasks to exit under certain conditions—specifically when the process performing the removal is also responsible for reaping zombie processes. This creates a circular dependency where the cleanup cannot proceed because the reaper is blocked, and the zombies cannot be cleaned because the reaper is stuck. The fix defers the actual cleanup work to run asynchronously after tasks have already left the cgroup, allowing the rmdir operation to return promptly while kernel-side cleanup continues in the background.

Source data · NVD / CISA · public domain

CVSS
3.1 · 5.5 MEDIUM · CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weaknesses (CWE)
CWE-667
Affected products
5 configuration(s)
Published / Modified
2026-05-28 / 2026-06-17

NVD description (verbatim)

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.

2 reference(s) · View on NVD →

SEC.co analysis · AI-assisted, reviewed against source

Technical summary

The vulnerability stems from a synchronous blocking mechanism in cgroup_drain_dying() that was introduced to enforce the controller invariant requiring ->css_offline() to not execute while tasks remain in a cgroup. The problematic flow occurs when: (1) a task past exit_signals() remains linked to a cset due to earlier architectural changes that deferred task unlink; (2) rmdir() calls cgroup_drain_dying() to wait for these tasks; (3) if the rmdir caller is also the reaper of zombies (e.g., PID 1), a deadlock ensues because the reaper blocks in TASK_UNINTERRUPTIBLE, preventing zombie cleanup, which in turn prevents PID namespace teardown and task exit. The fix transforms css killing from a synchronous operation to an asynchronous one driven by percpu_ref_kill_and_confirm(), allowing rmdir to return after cgroup.procs empties while ->css_offline() runs post-drainage. This eliminates the lock-ordering deadlock by breaking the circular wait dependency.

Business impact

Production systems running containerized workloads are at risk of hung processes and system hangs during container lifecycle operations. A hung rmdir operation can cascade into application deployment failures, orchestration system timeouts, and potential system-wide availability degradation if multiple cgroup removals are queued. Systems using PID namespaces for container isolation—particularly Kubernetes nodes, systemd-managed systems, or custom container runtimes—face the highest exposure. The MEDIUM severity reflects local-only access requirements and the specific trigger conditions needed, though the impact of a hang can be severe for availability.

Affected systems

The vulnerability affects the Linux kernel across all supported versions impacted by the chain of commits dating back to v7.0 that introduced the problematic rmdir behavior. Any distribution shipping an affected kernel version is in scope, with particular risk in systems actively removing cgroups during container teardown or PID namespace cleanup. The vulnerability requires local access and the ability to trigger cgroup removal operations, limiting the attack surface to local users or container management systems.

Exploitability

Exploitation requires local access and the ability to trigger cgroup rmdir operations while conditions align to cause the deadlock—specifically, the rmdir caller must also be managing zombie processes undergoing reaping. This is not a trivial coincidence in uncontrolled environments but occurs reliably in PID namespace teardown scenarios involving re-parented orphan processes. The original vulnerability report includes a reproducible test case (pidns teardown with zombie reaper) that reliably triggers the hang. Weaponization would involve crafting PID namespace and container lifecycle scenarios to force the deadlock state, placing this at moderate exploitability for targeted local denial-of-service.

Remediation

Patch the Linux kernel to include the fix deferring css percpu_ref kill until after cgroup depopulation. The solution involves asynchronously scheduling css cleanup via percpu_ref_kill_and_confirm() rather than waiting synchronously in rmdir context. Verify that the patch includes explicit cgroup_get()/cgroup_put() reference pinning around the deferred work queue to avoid dependency on implicit ordering invariants. For stable backports, the change set is somewhat invasive but localized to cgroup management code; test thoroughly in your container runtime environment before rolling to production.

Patch guidance

Apply the kernel patch that implements the asynchronous css killing behavior. The fix should be verified against the vendor advisory for your specific Linux distribution to confirm the patch version and any dependent commits. Key aspects to verify: (1) cgroup_drain_dying() no longer blocks rmdir on task lingering; (2) css_offline() runs asynchronously via percpu_ref_kill_and_confirm() after cgroup drains; (3) rmdir returns promptly once cgroup.procs is empty; (4) explicit cgroup reference counting wraps the deferred work. Test the patch in a staging environment that exercises container creation and deletion cycles, particularly with namespace teardown scenarios.

Detection guidance

Monitor for hung rmdir processes using process state inspection (ps showing processes in TASK_UNINTERRUPTIBLE state within cgroup operations). Check kernel logs for timeouts or hangs during cgroup deletion, particularly in container orchestration or systemd teardown paths. Correlate rmdir hangs with active zombie reaping or PID namespace teardown operations. On vulnerable systems under specific trigger conditions, you may observe: systemctl/orchestrator delays during service/container shutdown, accumulated zombie processes unable to be reaped, or system-wide slowdowns during mass container termination. Implement timeout monitoring around container lifecycle operations as a temporary detection control.

Why prioritize this

While CVSS 5.5 (MEDIUM) reflects the local-only requirement and specificity of trigger conditions, the availability impact in production container environments warrants prioritization. This is not a widespread remote threat, but a targeted local denial-of-service affecting system stability during normal container lifecycle operations. Organizations running Kubernetes, Docker, or systemd-managed containers should prioritize patching to prevent availability incidents. The deadlock scenario is deterministic and reliably reproducible in PID namespace contexts, making it a realistic threat in multi-tenant or dynamic container environments. Balance this against your kernel update cycle; backport availability and testing effort may influence timing.

Risk score, explained

The CVSS 3.1 score of 5.5 (MEDIUM) reflects: Attack Vector Local (cannot be exploited remotely), Access Complexity Low (no special conditions beyond normal cgroup removal), Privilege Level Low (local user can trigger via container operations), User Interaction None, Scope Unchanged, Confidentiality None, Integrity None, Availability High (denial of service via hang). The score appropriately penalizes the local-only attack vector but acknowledges the complete availability impact. In container environments, the practical risk may feel higher due to the centrality of container lifecycle operations, but the scorer's requirement for local access and specific trigger alignment justifies MEDIUM classification.

Frequently asked questions

Does this vulnerability allow arbitrary code execution or privilege escalation?

No. The vulnerability is limited to denial-of-service via system hang. It does not enable code execution, data exfiltration, or privilege escalation. The impact is exclusively on system availability during cgroup management operations.

Which container runtimes and orchestration platforms are affected?

Any container runtime or orchestration system (Docker, containerd, Kubernetes, LXC, systemd-nspawn) that removes cgroups during normal operation can trigger the vulnerability under the specific condition of concurrent zombie reaping. However, the deadlock requires a precise ordering of events—the rmdir caller must be the zombie reaper during PID namespace teardown—making it more likely in complex container lifecycle scenarios than in simple delete operations.

What is the practical impact of a hung rmdir operation?

A hung rmdir blocks the calling process indefinitely in TASK_UNINTERRUPTIBLE state, which cannot be interrupted even by signals. In orchestration contexts, this causes timeouts in container shutdown sequences, potential orchestrator hangs, and cascading failures if multiple removals queue up. System-wide PID namespace teardown can stall, preventing proper cleanup of containerized workloads.

Should I apply this patch immediately or can I defer it?

Prioritize patching based on your container workload density and lifecycle churn. High-velocity environments (frequent container creation/deletion, Kubernetes clusters with autoscaling) should patch sooner; stable or static environments can defer within your normal kernel update cycle. Verify patch availability from your Linux distribution before committing to a timeline.

This analysis is based on publicly available vulnerability data and the provided advisory description. CVSS scores and affected product versions are sourced from official CVE records; verify patch availability and specific version applicability against your Linux distribution's security advisory. Exploitation requires local access and specific trigger conditions; this is not a remote vulnerability. Organizations should validate patch compatibility with their kernel version and container runtime before deployment. Security patches should be tested in non-production environments first. This information is provided for defensive security purposes; do not use for offensive activities. Source: NVD (public-domain), retrieved 2026-07-07. Analysis generated by SEC.co (claude-haiku-4-5).