DragonFlyBSD Kernel Audit
DF-0351 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netproto/802_11/wlan/ieee80211_hwmp.c b/sys/netproto/802_11/wlan/ieee80211_hwmp.c
--- a/sys/netproto/802_11/wlan/ieee80211_hwmp.c
+++ b/sys/netproto/802_11/wlan/ieee80211_hwmp.c
@@ -134,6 +134,26 @@
 #define HWMP_SEQ_MAX(a, b)	(a > b ? a : b)
 
 /*
+ * Saturating add for HWMP path/airtime metrics.
+ *
+ * HWMP frame metrics (preq_metric/prep_metric/rann_metric) are read verbatim
+ * from attacker-controlled on-air frames via le32dec() with no range check, so
+ * a crafted value near UINT32_MAX can make `accumulated += link_metric` wrap
+ * modulo 2^32 to a small value.  That wrapped value then beats every honest
+ * path metric in the route-accept comparison and silently poisons / hijacks
+ * the route table.  The 802.11s HWMP spec mandates a monotonically increasing,
+ * non-wrapping metric accumulation; clamp the sum at UINT32_MAX to honour that
+ * and defeat the wrap.  See DF-0351.
+ */
+static __inline uint32_t
+hwmp_metric_add(uint32_t a, uint32_t b)
+{
+	uint64_t s = (uint64_t)a + (uint64_t)b;
+
+	return (s > UINT32_MAX) ? UINT32_MAX : (uint32_t)s;
+}
+
+/*
  * Private extension of ieee80211_mesh_route.
  */
 struct ieee80211_hwmp_route {
@@ -1086,7 +1106,8 @@
 	/* Data creation and update of forwarding information
 	 * according to Table 11C-8 for originator mesh STA.
 	 */
-	metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni);
+	metric = hwmp_metric_add(preq->preq_metric,
+	    ms->ms_pmetric->mpm_metric(ni));
 	if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) ||
 	    (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) &&
 	    metric < rtorig->rt_metric)) {
@@ -1322,7 +1343,8 @@
 #endif
 		ppreq.preq_hopcount += 1;
 		ppreq.preq_ttl -= 1;
-		ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni);
+		ppreq.preq_metric = hwmp_metric_add(ppreq.preq_metric,
+		    ms->ms_pmetric->mpm_metric(ni));
 
 		/* don't do PREQ ratecheck when we propagate */
 		hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL);
@@ -1450,7 +1472,8 @@
 	}
 	hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route);
 	/* update path metric */
-	metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni);
+	metric = hwmp_metric_add(prep->prep_metric,
+	    ms->ms_pmetric->mpm_metric(ni));
 	if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) {
 		if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) {
 #if defined(__DragonFly__)
@@ -1554,7 +1577,8 @@
 		memcpy(&pprep, prep, sizeof(pprep));
 		pprep.prep_hopcount += 1;
 		pprep.prep_ttl -= 1;
-		pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni);
+		pprep.prep_metric = hwmp_metric_add(pprep.prep_metric,
+		    ms->ms_pmetric->mpm_metric(ni));
 		hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep);
 
 		/* precursor list for the Target Mesh STA Address is updated */
@@ -1969,7 +1993,8 @@
 	/* RANN ACCEPTED */
 
 	ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */
-	metric = rann->rann_metric + ms->ms_pmetric->mpm_metric(ni);
+	metric = hwmp_metric_add(rann->rann_metric,
+	    ms->ms_pmetric->mpm_metric(ni));
 
 	if (rt == NULL) {
 		rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);