⬢ DragonFlyBSD Kernel Audit
DF-5806 / fix.diff
← back to finding ↓ download raw
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;
 	}