diff --git a/package/kernel/mac80211/patches/343-cfg80211-limit-scan-results-cache-size.patch b/package/kernel/mac80211/patches/343-cfg80211-limit-scan-results-cache-size.patch new file mode 100644 index 0000000000..f26548a3df --- /dev/null +++ b/package/kernel/mac80211/patches/343-cfg80211-limit-scan-results-cache-size.patch @@ -0,0 +1,150 @@ +From: Johannes Berg +Date: Tue, 15 Nov 2016 12:05:11 +0100 +Subject: [PATCH] cfg80211: limit scan results cache size + +It's possible to make scanning consume almost arbitrary amounts +of memory, e.g. by sending beacon frames with random BSSIDs at +high rates while somebody is scanning. + +Limit the number of BSS table entries we're willing to cache to +1000, limiting maximum memory usage to maybe 4-5MB, but lower +in practice - that would be the case for having both full-sized +beacon and probe response frames for each entry; this seems not +possible in practice, so a limit of 1000 entries will likely be +closer to 0.5 MB. + +Cc: stable@vger.kernel.org +Signed-off-by: Johannes Berg +--- + +--- a/net/wireless/core.h ++++ b/net/wireless/core.h +@@ -71,6 +71,7 @@ struct cfg80211_registered_device { + struct list_head bss_list; + struct rb_root bss_tree; + u32 bss_generation; ++ u32 bss_entries; + struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + struct sk_buff *scan_msg; + struct cfg80211_sched_scan_request __rcu *sched_scan_req; +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -57,6 +57,19 @@ + * also linked into the probe response struct. + */ + ++/* ++ * Limit the number of BSS entries stored in mac80211. Each one is ++ * a bit over 4k at most, so this limits to roughly 4-5M of memory. ++ * If somebody wants to really attack this though, they'd likely ++ * use small beacons, and only one type of frame, limiting each of ++ * the entries to a much smaller size (in order to generate more ++ * entries in total, so overhead is bigger.) ++ */ ++static int bss_entries_limit = 1000; ++module_param(bss_entries_limit, int, 0644); ++MODULE_PARM_DESC(bss_entries_limit, ++ "limit to number of scan BSS entries (per wiphy, default 1000)"); ++ + #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) + + static void bss_free(struct cfg80211_internal_bss *bss) +@@ -137,6 +150,10 @@ static bool __cfg80211_unlink_bss(struct + + list_del_init(&bss->list); + rb_erase(&bss->rbn, &rdev->bss_tree); ++ rdev->bss_entries--; ++ WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), ++ "rdev bss entries[%d]/list[empty:%d] corruption\n", ++ rdev->bss_entries, list_empty(&rdev->bss_list)); + bss_ref_put(rdev, bss); + return true; + } +@@ -163,6 +180,40 @@ static void __cfg80211_bss_expire(struct + rdev->bss_generation++; + } + ++static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev) ++{ ++ struct cfg80211_internal_bss *bss, *oldest = NULL; ++ bool ret; ++ ++ lockdep_assert_held(&rdev->bss_lock); ++ ++ list_for_each_entry(bss, &rdev->bss_list, list) { ++ if (atomic_read(&bss->hold)) ++ continue; ++ ++ if (!list_empty(&bss->hidden_list) && ++ !bss->pub.hidden_beacon_bss) ++ continue; ++ ++ if (oldest && time_before(oldest->ts, bss->ts)) ++ continue; ++ oldest = bss; ++ } ++ ++ if (WARN_ON(!oldest)) ++ return false; ++ ++ /* ++ * The callers make sure to increase rdev->bss_generation if anything ++ * gets removed (and a new entry added), so there's no need to also do ++ * it here. ++ */ ++ ++ ret = __cfg80211_unlink_bss(rdev, oldest); ++ WARN_ON(!ret); ++ return ret; ++} ++ + void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, + bool send_message) + { +@@ -689,6 +740,7 @@ static bool cfg80211_combine_bsses(struc + const u8 *ie; + int i, ssidlen; + u8 fold = 0; ++ u32 n_entries = 0; + + ies = rcu_access_pointer(new->pub.beacon_ies); + if (WARN_ON(!ies)) +@@ -712,6 +764,12 @@ static bool cfg80211_combine_bsses(struc + /* This is the bad part ... */ + + list_for_each_entry(bss, &rdev->bss_list, list) { ++ /* ++ * we're iterating all the entries anyway, so take the ++ * opportunity to validate the list length accounting ++ */ ++ n_entries++; ++ + if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) + continue; + if (bss->pub.channel != new->pub.channel) +@@ -740,6 +798,10 @@ static bool cfg80211_combine_bsses(struc + new->pub.beacon_ies); + } + ++ WARN_ONCE(n_entries != rdev->bss_entries, ++ "rdev bss entries[%d]/list[len:%d] corruption\n", ++ rdev->bss_entries, n_entries); ++ + return true; + } + +@@ -894,7 +956,14 @@ cfg80211_bss_update(struct cfg80211_regi + } + } + ++ if (rdev->bss_entries >= bss_entries_limit && ++ !cfg80211_bss_expire_oldest(rdev)) { ++ kfree(new); ++ goto drop; ++ } ++ + list_add_tail(&new->list, &rdev->bss_list); ++ rdev->bss_entries++; + rb_insert_bss(rdev, new); + found = new; + }