🇰🇷 한글 설명
Linux 커널 io_uring의 waitid 구현에서, io_waitid_wait()가 요청의 소유권을 확보하기 전에 ->head를 먼저 NULL로 지워 발생하는 레이스 컨디션입니다. 이로 인해 이미 완료되어 해제·재사용된 요청에 대해 refs가 증가하고 콜백이 큐잉되어 slab use-after-free가 발생할 수 있습니다. 수정은 핸들러가 요청을 소유한 뒤에만 ->head를 지우도록 변경하는 것입니다.
💡 쉬운 설명
리눅스 커널의 비동기 입출력 기능에서 작업 순서가 꼬이는 버그로, 이미 끝난 작업의 메모리를 잘못 건드릴 수 있습니다. 이를 악용하면 시스템 메모리가 손상되어 권한 상승이나 시스템 불안정으로 이어질 수 있습니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
io_uring/waitid: own the request before clearing ->head
io_waitid_remove_wq() only takes the wait queue lock if ->head is set.
io_waitid_wait() clears ->head first thing, before it tries to become the
owner of the request by bumping ->refs. If somebody else owns the request
at that point, nothing keeps it from completing the request while the
wake up handler is still going to touch it:
CPU0 (io_waitid_wait) CPU1 (io_waitid, holds a ref)
list_del_init(&wait->entry)
smp_store_release(&iw->head, NULL)
__do_wait() finds another child
io_waitid_remove_wq()
// ->head == NULL, no locking
io_waitid_finish(), IOU_COMPLETE
// request freed and reused
atomic_fetch_inc(&iw->refs)
The same goes for __io_waitid_cancel() if it claims the request in that
window. ->refs is part of the per opcode data of the request, so this
increments whatever the next user of the request keeps there. If that
happens to be zero, the handler concludes that it owns the request, and
queues io_waitid_cb() for it. With the window held open by a delay, and
the request reused for a receive that is waiting for data:
BUG: KASAN: slab-use-after-free in __wake_up_common+0x1ca/0x1f0
__wake_up_sync_key+0x36/0x50
sock_def_readable+0xf1/0x420
unix_stream_sendmsg+0x60b/0x1110
Allocated by task 87:
io_arm_apoll+0x1ed/0x940
Freed by task 87:
__io_submit_flush_completions+0x118d/0x1a10
tctx_task_work_run+0x327/0x950
Only clear ->head once the handler owns the request. If it doesn't, the
owner finds ->head set and has to wait for the lock, which is held by the
caller of the handler. Deleting the already deleted entry again is
harmless.
📍 영향받는 시스템
io_uring waitid 기능을 포함하는 수정 이전 Linux 커널 버전 (정확한 영향 버전은 해당 커널 배포판의 보안 권고 확인 필요)
⚔️ 공격 벡터
로컬 사용자가 io_uring waitid 요청과 소켓 작업을 조작해 레이스 윈도우를 넓히고, 해제된 메모리를 재사용하여 커널 메모리를 손상시키는 방식
🛡️ 대응 방법
패치된 커널로 업데이트하거나 배포판 벤더가 제공하는 보안 업데이트를 적용하고, 필요 시 io_uring을 비활성화(sysctl kernel.io_uring_disabled 설정)
🔧 검증 코드
출력된 커널 버전을 배포판 보안 권고에서 CVE-2026-99231 수정 패치가 포함된 버전과 비교하여, 같거나 높으면 패치 적용됨, 낮으면 취약할 가능성이 있음으로 판단