I've spent the past few days in a large hotel and noticed that `iwlist
scan` fails with the error message "Argument list too long".

This seems to be because there are a great many wireless networks in
my vicinity.  See:  https://bugzilla.kernel.org/show_bug.cgi?id=16384

Using `iw` as recommended in that ticket works, but it's not a great
option for me because `wicd` doesn't support it.

If I'm understanding the relevant code correctly, the attached patch
works around the issue by skipping networks that would not fit in the
scan-results buffer and returning partial results instead of just
erroring out.

This is the first time I've modified the kernel code directly.  I have
a couple of questions:

- This seems to be working like I intended, but the condition was a
bit hard to reproduce consistently.  Am I inadvertently breaking
things?
- Is there a better way to accomplish this?  Reading the `iwlist` code
I'm not entirely sure why this is occurring, because it looks like it
should be allocating a dynamic buffer length.
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 21be56b..fc74091 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1699,6 +1699,7 @@ static int ieee80211_scan_results(struct 
cfg80211_registered_device *rdev,
                                  struct iw_request_info *info,
                                  char *buf, size_t len)
 {
+       char *maybe_current_ev;
        char *current_ev = buf;
        char *end_buf = buf + len;
        struct cfg80211_internal_bss *bss;
@@ -1709,14 +1710,20 @@ static int ieee80211_scan_results(struct 
cfg80211_registered_device *rdev,
 
        list_for_each_entry(bss, &rdev->bss_list, list) {
                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       err = -E2BIG;
+                       // Buffer too big to hold another BSS; ignore
                        break;
                }
-               current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
-                                          current_ev, end_buf);
-               if (IS_ERR(current_ev)) {
-                       err = PTR_ERR(current_ev);
+               maybe_current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
+                                                current_ev, end_buf);
+               if (IS_ERR(maybe_current_ev)) {
+                       err = PTR_ERR(maybe_current_ev);
+                       if (err == -E2BIG) {
+                               // Last BSS failed to copy into buffer; ignore
+                               err = 0;
+                       }
                        break;
+               } else {
+                       current_ev = maybe_current_ev;
                }
        }
        spin_unlock_bh(&rdev->bss_lock);

Reply via email to