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 @@ -319,16 +319,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) \