madwifi: fix ACL race condition (patch by Sebastian Gottschall)
authorFelix Fietkau <nbd@openwrt.org>
Sun, 2 Nov 2008 13:05:46 +0000 (13:05 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 2 Nov 2008 13:05:46 +0000 (13:05 +0000)
SVN-Revision: 13097

package/madwifi/patches/386-acl_crashfix.patch [new file with mode: 0644]

diff --git a/package/madwifi/patches/386-acl_crashfix.patch b/package/madwifi/patches/386-acl_crashfix.patch
new file mode 100644 (file)
index 0000000..12f3273
--- /dev/null
@@ -0,0 +1,116 @@
+fixes ACL race condition caused by acl list modifications at run time
+
+Signed-off-by: Sebastian Gottschall <brainslayer@dd-wrt.com>
+
+--- a/net80211/ieee80211_acl.c
++++ b/net80211/ieee80211_acl.c
+@@ -112,9 +112,9 @@ acl_detach(struct ieee80211vap *vap)
+ {
+       struct aclstate *as = vap->iv_as;
+-      ACL_LOCK(as);
++      ACL_LOCK_IRQ(as);
+       acl_free_all_locked(as);
+-      ACL_UNLOCK(as);
++      ACL_UNLOCK_IRQ(as);
+       vap->iv_as = NULL;
+       ACL_LOCK_DESTROY(as);
+       FREE(as, M_DEVBUF);
+@@ -128,11 +128,18 @@ _find_acl(struct aclstate *as, const u_i
+       struct acl *acl;
+       int hash;
++      /* locking needed, as inserts are not atomic */
++      ACL_LOCK_IRQ(as);
+       hash = ACL_HASH(macaddr);
+       LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+-              if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
+-                      return acl;
++              if (!IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
++                      continue;
++
++              ACL_UNLOCK_IRQ_EARLY(as);
++              return acl;
+       }
++      ACL_UNLOCK_IRQ(as);
++
+       return NULL;
+ }
+@@ -176,11 +183,11 @@ acl_add(struct ieee80211vap *vap, const 
+               return -ENOMEM;
+       }
+-      ACL_LOCK(as);
++      ACL_LOCK_IRQ(as);
+       hash = ACL_HASH(mac);
+       LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+               if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
+-                      ACL_UNLOCK_EARLY(as);
++                      ACL_UNLOCK_IRQ_EARLY(as);
+                       FREE(new, M_80211_ACL);
+                       IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+                               "ACL: add " MAC_FMT " failed, already present\n",
+@@ -191,7 +198,7 @@ acl_add(struct ieee80211vap *vap, const 
+       IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
+       TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
+       LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
+-      ACL_UNLOCK(as);
++      ACL_UNLOCK_IRQ(as);
+       IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+               "ACL: add " MAC_FMT "\n", MAC_ADDR(mac));
+@@ -204,11 +211,11 @@ acl_remove(struct ieee80211vap *vap, con
+       struct aclstate *as = vap->iv_as;
+       struct acl *acl;
+-      ACL_LOCK(as);
++      ACL_LOCK_IRQ(as);
+       acl = _find_acl(as, mac);
+       if (acl != NULL)
+               _acl_free(as, acl);
+-      ACL_UNLOCK(as);
++      ACL_UNLOCK_IRQ(as);
+       IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
+               "ACL: remove " MAC_FMT "%s\n", MAC_ADDR(mac),
+@@ -235,9 +242,9 @@ acl_free_all(struct ieee80211vap *vap)
+       IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+-      ACL_LOCK(as);
++      ACL_LOCK_IRQ(as);
+       acl_free_all_locked(vap->iv_as);
+-      ACL_UNLOCK(as);
++      ACL_UNLOCK_IRQ(as);
+       return 0;
+ }
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -311,16 +311,15 @@ typedef spinlock_t ieee80211_scan_lock_t
+ typedef spinlock_t acl_lock_t;
+ #define       ACL_LOCK_INIT(_as, _name)       spin_lock_init(&(_as)->as_lock)
+ #define       ACL_LOCK_DESTROY(_as)
+-#define       ACL_LOCK(_as)                   do {    \
+-      ACL_LOCK_CHECK(_as);            \
+-      spin_lock(&(_as)->as_lock);
+-#define       ACL_UNLOCK(_as)                         \
+-      ACL_LOCK_ASSERT(_as);                   \
+-      spin_unlock(&(_as)->as_lock);           \
+-} while(0)
+-#define ACL_UNLOCK_EARLY(_as)                 \
+-      ACL_LOCK_ASSERT(_as);                   \
+-      spin_unlock(&(_as)->as_lock);
++#define       ACL_LOCK_IRQ(_as)       do {    \
++      unsigned long __acl_lockflags;          \
++      spin_lock_irqsave(&(_as)->as_lock, __acl_lockflags);
++#define       ACL_UNLOCK_IRQ(_as) \
++      spin_unlock_irqrestore(&(_as)->as_lock, __acl_lockflags); \
++} while (0)
++#define       ACL_UNLOCK_IRQ_EARLY(_as)       do { \
++      spin_unlock_irqrestore(&(_as)->as_lock, __acl_lockflags); \
++} while (0)
+ #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
+ #define       ACL_LOCK_ASSERT(_as) \