Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

io_uring/poll: fix multishot recv missing EOF on wakeup race

When a socket send and shutdown() happen back-to-back, both fire
wake-ups before the receiver's task_work has a chance to run. The first
wake gets poll ownership (poll_refs=1), and the second bumps it to 2.
When io_poll_check_events() runs, it calls io_poll_issue() which does a
recv that reads the data and returns IOU_RETRY. The loop then drains all
accumulated refs (atomic_sub_return(2) -> 0) and exits, even though only
the first event was consumed. Since the shutdown is a persistent state
change, no further wakeups will happen, and the multishot recv can hang
forever.

Check specifically for HUP in the poll loop, and ensure that another
loop is done to check for status if more than a single poll activation
is pending. This ensures we don't lose the shutdown event.

Cc: stable@vger.kernel.org
Fixes: dbc2564cfe0f ("io_uring: let fast poll support multishot")
Reported-by: Francis Brosseau <francis@malagauche.com>
Link: https://github.com/axboe/liburing/issues/1549
Signed-off-by: Jens Axboe <axboe@kernel.dk>

+7 -2
+7 -2
io_uring/poll.c
··· 272 272 atomic_andnot(IO_POLL_RETRY_FLAG, &req->poll_refs); 273 273 v &= ~IO_POLL_RETRY_FLAG; 274 274 } 275 + v &= IO_POLL_REF_MASK; 275 276 } 276 277 277 278 /* the mask was stashed in __io_poll_execute */ ··· 305 304 return IOU_POLL_REMOVE_POLL_USE_RES; 306 305 } 307 306 } else { 308 - int ret = io_poll_issue(req, tw); 307 + int ret; 309 308 309 + /* multiple refs and HUP, ensure we loop once more */ 310 + if ((req->cqe.res & (POLLHUP | POLLRDHUP)) && v != 1) 311 + v--; 312 + 313 + ret = io_poll_issue(req, tw); 310 314 if (ret == IOU_COMPLETE) 311 315 return IOU_POLL_REMOVE_POLL_USE_RES; 312 316 else if (ret == IOU_REQUEUE) ··· 327 321 * Release all references, retry if someone tried to restart 328 322 * task_work while we were executing it. 329 323 */ 330 - v &= IO_POLL_REF_MASK; 331 324 } while (atomic_sub_return(v, &req->poll_refs) & IO_POLL_REF_MASK); 332 325 333 326 io_napi_add(req);