⬢ DragonFlyBSD Kernel Audit
DF-4505 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/ipmi/ipmi.c
+++ b/sys/dev/misc/ipmi/ipmi.c
@@ -77,6 +77,16 @@
 	.d_kqfilter =	ipmi_kqfilter,
 };
 
+/*
+ * Cap on outstanding requests per open /dev/ipmiN handle.  Each request
+ * kmallocs sizeof(struct ipmi_request) + ir_requestlen + IPMI_MAX_RX
+ * bytes; the interface kthread drains the queue serially, so without a
+ * cap an operator-group user can park an unbounded backlog of in-flight
+ * requests with an async IPMICTL_SEND_COMMAND flood and exhaust kernel
+ * memory ("panic: ipmi: malloc limit exceeded").
+ */
+#define	IPMI_MAX_REQUESTS	128
+
 static int ipmi_watchdog_sysctl_enable(SYSCTL_HANDLER_ARGS);
 static int ipmi_watchdog_sysctl_period(SYSCTL_HANDLER_ARGS);
 
@@ -408,11 +418,22 @@
 			return (error);
 		}
 		IPMI_LOCK(sc);
+		if (dev->ipmi_requests >= IPMI_MAX_REQUESTS) {
+			/* Too many requests still in flight for this handle. */
+			IPMI_UNLOCK(sc);
+			ipmi_free_request(kreq);
+			return (EBUSY);
+		}
 		dev->ipmi_requests++;
 		error = sc->ipmi_enqueue_request(sc, kreq);
-		IPMI_UNLOCK(sc);
-		if (error)
+		if (error != 0) {
+			/* Not queued: this request will never complete. */
+			dev->ipmi_requests--;
+			IPMI_UNLOCK(sc);
+			ipmi_free_request(kreq);
 			return (error);
+		}
+		IPMI_UNLOCK(sc);
 		break;
 #ifdef IPMICTL_SEND_COMMAND_32
 	case IPMICTL_RECEIVE_MSG_TRUNC_32: