123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- From: Johannes Berg <johannes.berg@intel.com>
- 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 <johannes.berg@intel.com>
- ---
- --- 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;
- }
|