DragonFlyBSD Kernel Audit
DF-0755 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netinet/tcp_debug.c b/sys/netinet/tcp_debug.c
--- a/sys/netinet/tcp_debug.c
+++ b/sys/netinet/tcp_debug.c
@@ -47,6 +47,8 @@
 #include <sys/systm.h>
 #include <sys/protosw.h>
 #include <sys/socket.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
 
 #include <net/netisr.h>
 #include <net/netmsg.h>
@@ -67,6 +69,15 @@
 
 static int	tcpconsdebug = 0;
 static struct tcp_debug tcp_debug[TCP_NDEBUG];
+/*
+ * tcp_debx is a process-global index into tcp_debug[].  It is read/modified
+ * from concurrent TCP trace events on every CPU (per-tcpcb tokens do not
+ * serialize it).  Take a spinlock around the increment + wrap so the index
+ * cannot race past TCP_NDEBUG and write struct tcp_debug off the end of the
+ * array into BSS (DF-0755).  This is a cold, debug-only path, so the lock
+ * cost is irrelevant.
+ */
+static struct spinlock tcp_debx_spin = SPINLOCK_INITIALIZER(tcp_debx_spin, "tcp_debx");
 static int	tcp_debx;
 
 /*
@@ -81,7 +92,15 @@
 #endif /* INET6 */
 	tcp_seq seq, ack;
 	int len, flags;
-	struct tcp_debug *td = &tcp_debug[tcp_debx++];
+	int slot;
+	struct tcp_debug *td;
+
+	spin_lock(&tcp_debx_spin);
+	slot = tcp_debx++;
+	if (tcp_debx == TCP_NDEBUG)
+		tcp_debx = 0;
+	spin_unlock(&tcp_debx_spin);
+	td = &tcp_debug[slot];
 
 #ifdef INET6
 	isipv6 = (ipgen != NULL && ((struct ip *)ipgen)->ip_v == 6) ? 1 : 0;
@@ -91,8 +110,6 @@
 		(isipv6 != 0) ? AF_INET6 :
 #endif
 		AF_INET;
-	if (tcp_debx == TCP_NDEBUG)
-		tcp_debx = 0;
 	td->td_time = iptime();
 	td->td_act = act;
 	td->td_ostate = ostate;