)]}'
{
  "commit": "a6dc643c69311677c574a0f17a3f4d66a5f3744b",
  "tree": "94f10bc43ea4f7a04de9996aa6131d82e726b678",
  "parents": [
    "86e87059e6d1fd5115a31949726450ed03c1073b"
  ],
  "author": {
    "name": "Christian Brauner",
    "email": "brauner@kernel.org",
    "time": "Thu Apr 23 11:56:09 2026 +0200"
  },
  "committer": {
    "name": "Christian Brauner",
    "email": "brauner@kernel.org",
    "time": "Fri Apr 24 00:36:29 2026 +0200"
  },
  "message": "eventpoll: fix ep_remove struct eventpoll / struct file UAF\n\nep_remove() (via ep_remove_file()) cleared file-\u003ef_ep under\nfile-\u003ef_lock but then kept using @file inside the critical section\n(is_file_epoll(), hlist_del_rcu() through the head, spin_unlock).\nA concurrent __fput() taking the eventpoll_release() fastpath in\nthat window observed the transient NULL, skipped\neventpoll_release_file() and ran to f_op-\u003erelease / file_free().\n\nFor the epoll-watches-epoll case, f_op-\u003erelease is\nep_eventpoll_release() -\u003e ep_clear_and_put() -\u003e ep_free(), which\nkfree()s the watched struct eventpoll. Its embedded -\u003erefs\nhlist_head is exactly where epi-\u003efllink.pprev points, so the\nsubsequent hlist_del_rcu()\u0027s \"*pprev \u003d next\" scribbles into freed\nkmalloc-192 memory.\n\nIn addition, struct file is SLAB_TYPESAFE_BY_RCU, so the slot\nbacking @file could be recycled by alloc_empty_file() --\nreinitializing f_lock and f_ep -- while ep_remove() is still\nnominally inside that lock. The upshot is an attacker-controllable\nkmem_cache_free() against the wrong slab cache.\n\nPin @file via epi_fget() at the top of ep_remove() and gate the\ncritical section on the pin succeeding. With the pin held @file\ncannot reach refcount zero, which holds __fput() off and\ntransitively keeps the watched struct eventpoll alive across the\nhlist_del_rcu() and the f_lock use, closing both UAFs.\n\nIf the pin fails @file has already reached refcount zero and its\n__fput() is in flight. Because we bailed before clearing f_ep,\nthat path takes the eventpoll_release() slow path into\neventpoll_release_file() and blocks on ep-\u003emtx until the waiter\nside\u0027s ep_clear_and_put() drops it. The bailed epi\u0027s share of\nep-\u003erefcount stays intact, so the trailing ep_refcount_dec_and_test()\nin ep_clear_and_put() cannot free the eventpoll out from under\neventpoll_release_file(); the orphaned epi is then cleaned up\nthere.\n\nA successful pin also proves we are not racing\neventpoll_release_file() on this epi, so drop the now-redundant\nre-check of epi-\u003edying under f_lock. The cheap lockless\nREAD_ONCE(epi-\u003edying) fast-path bailout stays.\n\nFixes: 58c9b016e128 (\"epoll: use refcount to reduce ep_mutex contention\")\nReported-by: Jaeyoung Chung \u003cjjy600901@snu.ac.kr\u003e\nLink: https://patch.msgid.link/20260423-work-epoll-uaf-v1-6-2470f9eec0f5@kernel.org\nSigned-off-by: Christian Brauner (Amutable) \u003cbrauner@kernel.org\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "5ee4398a6cb84fcf1bd52b366395e3f8ad02500a",
      "old_mode": 33188,
      "old_path": "fs/eventpoll.c",
      "new_id": "0f785c0a1544f3c3e884622b6154f70808fe6aa4",
      "new_mode": 33188,
      "new_path": "fs/eventpoll.c"
    }
  ]
}
