)]}'
{
  "commit": "057dac23d329d5c5ed62352f2659a39fd46c6d4a",
  "tree": "432eb28090de9a8e089fda8fe3e2ba49ee99d8f8",
  "parents": [
    "3f4b7d1a49c5c826f3be9b684313eea5b83ac232"
  ],
  "author": {
    "name": "Michal Koutný",
    "email": "mkoutny@suse.com",
    "time": "Mon Sep 14 14:19:10 2026 +0200"
  },
  "committer": {
    "name": "Tejun Heo",
    "email": "tj@kernel.org",
    "time": "Mon Sep 14 12:43:50 2026 -1000"
  },
  "message": "cgroup: Avoid iteration of dying tasks with zero refcount\n\nThe commit 260fbcb92bbea (\"cgroup: Move dying_tasks cleanup from\ncgroup_task_release() to cgroup_task_free()\") extended the lifetime of\ntasks on the dying_tasks list.\nThe iterators have provision to go through dying_tasks because of\ndying threadgroup leaders or explicit CSS_TASK_ITER_WITH_DEAD, however,\nit was expected that such tasks can obtain a new reference (that is\npossible before cgroup_task_release()/put_task_struct_rcu_user()).\nThe tasks after cgroup_task_release() and before cgroup_task_free()\nare subject to race when they may or may not have -\u003eusage count \u003e 0.\n\nThe race window is between css_task_iter_next() invocations\nwhen css_set_lock is released and we may arrive at a new -\u003etask_pos.\nThe iterator should not attempt to resurrect tasks whose -\u003eusage count\ndropped to zero. (When that happens, __put_task_struct_rcu_cb() is\nalready imminent and the returned task_struct would could be used\nafter free.)\n\nAs for the fix, we cannot simply check the signal-\u003elive count of a task\non the dying list because that won\u0027t distinguish regular zombies waiting\nto be reaped from RCU remnant tasks that are going to be free\u0027d.\nTherefore add an extra check to rule out -\u003eusage\u003d\u003d0 tasks from any\niteration.\n\nThe repeat: loop in css_task_iter_advance() doesn\u0027t consider -\u003eusage\ncount, so add a new loop to css_task_iter_next() to skip de-used tasks\non the dying_list.\n\nRough illustration of the possible race\n\n  R (reader of cgroup.procs)         T (thread)                       L (group leader)\n  ---------------------------------  -------------------------------- --------------------------------\n                                                                      L exits, signal-\u003elive \u003e 0\n                                                                      cgroup_task_dead(L)\n                                                                        css_set_skip_task_iters() // skips only cset-\u003etasks\n                                                                        list_add_tail(\u0026L-\u003ecg_list, \u0026cset-\u003edying_tasks)\n  css_task_iter_next()\n    take css_set_lock\n    css_task_iter_advance()\n      leader \u0026\u0026 signal-\u003elive !\u003d 0\n      \u003d\u003e it-\u003etask_pos \u003d \u0026L-\u003ecg_list\n    release css_set_lock\n                                     T exits\n                                     --signal-\u003elive \u003d\u003d 0\n\t\t\t\t     cgroup_task_dead(T) // css_set_lock\n                                     release_task(T)\n                                       cgroup_task_release(T)\n                                       release_task(L) // zap_leader\n                                         cgroup_task_release(L)\n                                         put_task_struct_rcu_user(L)\n                                         ...RCU...\n                                         put_task_struct(L)\n                                           L-\u003eusage \u003d 0\n                                           /* L still on dying_tasks */\n                                           ...RCU...\n                                           __put_task_struct(L)\n  css_task_iter_next() // another iteration\n    take css_set_lock\n    it-\u003etask_pos \u003d \u0026L-\u003ecg_list\n    get_task_struct(L)\n      \u003d\u003e addition on 0\n    drop css_set_lock\n                                           cgroup_task_free(L)\n                                             css_set_skip_task_iters() // dying skip comes too late\n                                           free_task(L)\n  cgroup_procs_show()\n    task_pid_vnr(L)\n\nFixes: 260fbcb92bbea (\"cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()\")\nCc: stable@vger.kernel.org # v6.19+\nLink: https://lists.debian.org/debian-kernel/2026/08/msg00220.html\nReported-by: Noah Elias Feldt \u003cN.Feldt@mittwald.de\u003e\nReported-by: Salvatore Bonaccorso \u003ccarnil@debian.org\u003e\nTested-by: Salvatore Bonaccorso \u003ccarnil@debian.org\u003e\nSigned-off-by: Michal Koutný \u003cmkoutny@suse.com\u003e\nSigned-off-by: Tejun Heo \u003ctj@kernel.org\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "353c8f83439aa7343c50b060215b7595ac3628f2",
      "old_mode": 33188,
      "old_path": "kernel/cgroup/cgroup.c",
      "new_id": "a3d363502b7b84aa1056a730d1d1791196aaaece",
      "new_mode": 33188,
      "new_path": "kernel/cgroup/cgroup.c"
    }
  ]
}
