diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1593,8 +1593,18 @@ so_pru_rcvd(so, flags); } + /* + * NOTE: Never take the zero-progress restart path when + * MSG_PEEK is set. A peeking receive consumes nothing, so + * orig_resid == resid would otherwise be an unbreakable + * in-kernel spin (the loop never sleeps and cannot even be + * killed). A peek of a record that delivers zero bytes + * (zero-length datagram, or control-only record with + * zero-length data) must return a short read instead. + */ if (orig_resid == resid && orig_resid && - (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { + (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0 && + (flags & MSG_PEEK) == 0) { ssb_unlock(&so->so_rcv); goto restart; } @@ -1923,8 +1933,11 @@ so_pru_rcvd_async(so); } + /* NOTE: See soreceive() -- MSG_PEEK must not take the + * zero-progress restart path (unbreakable kernel spin). */ if (orig_resid == resid && orig_resid && - (so->so_state & SS_CANTRCVMORE) == 0) { + (so->so_state & SS_CANTRCVMORE) == 0 && + (flags & MSG_PEEK) == 0) { ssb_unlock(&so->so_rcv); goto restart; }