mac80211: split ieee80211_key_alloc/free
In order to RCU-ify sta_info, we need to be able to allocate a key without linking it to an sdata/sta structure (because allocation cannot be done in an rcu critical section). This patch splits up ieee80211_key_alloc() and updates all users appropriately. While at it, this patch fixes a number of race conditions such as finally making key replacement atomic, unfortunately at the expense of more complex code. Note that this patch documents /existing/ bugs with sta info and key interaction, there is currently a race condition when a sta info is freed without holding the RTNL. This will finally be fixed by a followup patch. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:

committed by
John W. Linville

parent
6f48422a29
commit
db4d1169d0
@@ -123,6 +123,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct sta_info *sta = NULL;
|
||||
enum ieee80211_key_alg alg;
|
||||
int ret;
|
||||
struct ieee80211_key *key;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
@@ -141,16 +142,21 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
if (mac_addr) {
|
||||
sta = sta_info_get(sdata->local, mac_addr);
|
||||
if (!sta)
|
||||
if (!sta) {
|
||||
ieee80211_key_free(key);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_key_link(key, sdata, sta);
|
||||
|
||||
ret = 0;
|
||||
if (!ieee80211_key_alloc(sdata, sta, alg, key_idx,
|
||||
params->key_len, params->key))
|
||||
ret = -ENOMEM;
|
||||
|
||||
if (sta)
|
||||
sta_info_put(sta);
|
||||
@@ -164,6 +170,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct sta_info *sta;
|
||||
int ret;
|
||||
struct ieee80211_key *key;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
@@ -173,9 +180,11 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
return -ENOENT;
|
||||
|
||||
ret = 0;
|
||||
if (sta->key)
|
||||
ieee80211_key_free(sta->key);
|
||||
else
|
||||
if (sta->key) {
|
||||
key = sta->key;
|
||||
ieee80211_key_free(key);
|
||||
WARN_ON(sta->key);
|
||||
} else
|
||||
ret = -ENOENT;
|
||||
|
||||
sta_info_put(sta);
|
||||
@@ -185,7 +194,9 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (!sdata->keys[key_idx])
|
||||
return -ENOENT;
|
||||
|
||||
ieee80211_key_free(sdata->keys[key_idx]);
|
||||
key = sdata->keys[key_idx];
|
||||
ieee80211_key_free(key);
|
||||
WARN_ON(sdata->keys[key_idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user