⬢ DragonFlyBSD Kernel Audit
DF-3315 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -2456,19 +2456,42 @@
 		LIST_INSERT_HEAD(&pcbinfo->pcblisthead, marker, inp_list);
 		i = 0;
 		while ((inp = LIST_NEXT(marker, inp_list)) != NULL && i < n) {
+			int skip = 0;
+
+			/*
+			 * The marker advance (read next, unlink marker,
+			 * relink marker after next) is a non-atomic
+			 * two-step sequence.  For pcbinfo's without an
+			 * infotoken (e.g. ripcbinfo) nothing else
+			 * serializes us against a same-cpu netisr tearing
+			 * down the pcb we just stepped to: relinking the
+			 * marker after an unlinked (and possibly freed and
+			 * recycled) inpcb corrupts the list and writes
+			 * into freed memory.  The critical section makes
+			 * the advance + snapshot atomic against same-cpu
+			 * preemption; the marker itself keeps the walk
+			 * coherent across the (sleeping) SYSCTL_OUT below.
+			 */
+			crit_enter();
 			LIST_REMOVE(marker, inp_list);
 			LIST_INSERT_AFTER(inp, marker, inp_list);
 
-			if (inp->inp_flags & INP_PLACEMARKER)
-				continue;
-			if (prison_xinpcb(req->td, inp))
-				continue;
+			if (inp->inp_flags & INP_PLACEMARKER) {
+				skip = 1;
+			} else if (prison_xinpcb(req->td, inp)) {
+				skip = 1;
+			} else {
+				bzero(&xi, sizeof xi);
+				xi.xi_len = sizeof xi;
+				bcopy(inp, &xi.xi_inp, sizeof *inp);
+				if (inp->inp_socket)
+					sotoxsocket(inp->inp_socket,
+					    &xi.xi_socket);
+			}
+			crit_exit();
 
-			bzero(&xi, sizeof xi);
-			xi.xi_len = sizeof xi;
-			bcopy(inp, &xi.xi_inp, sizeof *inp);
-			if (inp->inp_socket)
-				sotoxsocket(inp->inp_socket, &xi.xi_socket);
+			if (skip)
+				continue;
 			if ((error = SYSCTL_OUT(req, &xi, sizeof xi)) != 0)
 				break;
 			++i;