⬢ DragonFlyBSD Kernel Audit
DF-4657 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/plip/if_plip.c
+++ b/sys/dev/netif/plip/if_plip.c
@@ -90,6 +90,7 @@
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/rman.h>
+#include <sys/sysctl.h>
 #include <sys/thread2.h>
 
 #include <machine/clock.h>
@@ -124,6 +125,32 @@
 #define	LPMAXERRS	100
 #endif
 
+
+#ifndef LPMAXSPINTOTAL		/* hard cap: total port polls per call */
+#define	LPMAXSPINTOTAL	4000
+#endif
+
+/*
+ * DF-4657 fix: lpoutput()/lp_intr() spin on the status port inside
+ * crit_enter() with only per-byte budgets; the aggregate time with
+ * interrupts disabled was unbounded (LPMAXSPIN1 + payload*LPMAXSPIN2
+ * port polls per packet, looped).  Any unprivileged user routing
+ * packets out lp# could pin a CPU indefinitely.  Impose a hard wall on
+ * the total handshake polls per if_output/if_intr invocation; tunable
+ * via hw.plip.maxspin for large MTUs on healthy links.
+ */
+static int lp_maxspin = LPMAXSPINTOTAL;
+SYSCTL_INT(_hw, OID_AUTO, plip_maxspin, CTLFLAG_RW, &lp_maxspin, 0,
+	"max total plip handshake polls per output/intr");
+static int lp_spin_budget;
+
+static __inline int lp_spin_ok (void);
+static __inline int
+lp_spin_ok (void)
+{
+	return (lp_spin_budget-- > 0);
+}
+
 #define CLPIPHDRLEN	14	/* We send dummy ethernet addresses (two) + packet type in front of packet */
 #define	CLPIP_SHAKE	0x80	/* This bit toggles between nibble reception */
 #define MLPIPHDRLEN	CLPIPHDRLEN
@@ -391,12 +418,12 @@
 {
 	ppb_wdtr(ppbus, ctxmitl[byte]);
 	while (ppb_rstr(ppbus) & CLPIP_SHAKE)
-		if (--spin == 0) {
+		if (!lp_spin_ok() || --spin == 0) {
 			return 1;
 		}
 	ppb_wdtr(ppbus, ctxmith[byte]);
 	while (!(ppb_rstr(ppbus) & CLPIP_SHAKE))
-		if (--spin == 0) {
+		if (!lp_spin_ok() || --spin == 0) {
 			return 1;
 		}
 	return 0;
@@ -408,14 +435,14 @@
 	u_char c, cl;
 
 	while((ppb_rstr(ppbus) & CLPIP_SHAKE))
-	    if(!--spin) {
+	    if(!lp_spin_ok() || !--spin) {
 		return -1;
 	    }
 	cl = ppb_rstr(ppbus);
 	ppb_wdtr(ppbus, 0x10);
 
 	while(!(ppb_rstr(ppbus) & CLPIP_SHAKE))
-	    if(!--spin) {
+	    if(!lp_spin_ok() || !--spin) {
 		return -1;
 	    }
 	c = ppb_rstr(ppbus);
@@ -453,6 +480,10 @@
 
 	crit_enter();
 
+	lp_spin_budget = lp_maxspin;
+
+	crit_enter();
+
 	if (sc->sc_if.if_flags & IFF_LINK0) {
 
 	    /* Ack. the request */
@@ -512,7 +543,7 @@
 
 		j = LPMAXSPIN2;
 		while((ppb_rstr(ppbus) & LPIP_SHAKE))
-		    if(!--j) goto err;
+		    if(!lp_spin_ok() || !--j) goto err;
 
 		c = ppb_rstr(ppbus);
 		ppb_wdtr(ppbus, 0);
@@ -521,6 +552,8 @@
 
 		j = LPMAXSPIN2;
 		while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) {
+		    if (!lp_spin_ok())
+			goto err;
 		    if (cl != c &&
 			(((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) ==
 			  (c & 0xf8))
@@ -575,11 +608,11 @@
 {
     ppb_wdtr(ppbus, txmith[byte]);
     while (!(ppb_rstr(ppbus) & LPIP_SHAKE))
-	if (--spin == 0)
+	if (!lp_spin_ok() || --spin == 0)
 		return 1;
     ppb_wdtr(ppbus, txmitl[byte]);
     while (ppb_rstr(ppbus) & LPIP_SHAKE)
-	if (--spin == 0)
+	if (!lp_spin_ok() || --spin == 0)
 		return 1;
     return 0;
 }
@@ -603,6 +636,8 @@
 
     err = 1;			/* assume we're aborting because of an error */
 
+    lp_spin_budget = lp_maxspin;
+
     crit_enter();
 
     /* Suspend (on laptops) or receive-errors might have taken us offline */
@@ -619,7 +654,7 @@
 	spin = LPMAXSPIN1;
 	ppb_wdtr(ppbus, 0x08);
 	while ((ppb_rstr(ppbus) & 0x08) == 0)
-		if (--spin == 0) {
+		if (!lp_spin_ok() || --spin == 0) {
 			goto nend;
 		}