aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/ieee80211_ioctl.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 10:27:46 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-03-06 15:30:46 -0500
commitd0709a65181beb787ef3f58cfe45536a2bb254c8 (patch)
tree29e5f36583b0e0a3f11b291347e57672eab41dad /net/mac80211/ieee80211_ioctl.c
parent5cf121c3cdb955583bf0c5d28c992b7968a4aa1a (diff)
mac80211: RCU-ify STA info structure access
This makes access to the STA hash table/list use RCU to protect against freeing of items. However, it's not a true RCU, the copy step is missing: whenever somebody changes a STA item it is simply updated. This is an existing race condition that is now somewhat understandable. This patch also fixes the race key freeing vs. STA destruction by making sure that sta_info_destroy() is always called under RTNL and frees the key. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/ieee80211_ioctl.c')
-rw-r--r--net/mac80211/ieee80211_ioctl.c42
1 files changed, 15 insertions, 27 deletions
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c
index 38e2d83e15f4..5147152b9268 100644
--- a/net/mac80211/ieee80211_ioctl.c
+++ b/net/mac80211/ieee80211_ioctl.c
@@ -33,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
33 size_t key_len) 33 size_t key_len)
34{ 34{
35 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 35 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
36 int ret; 36 struct sta_info *sta;
37 struct sta_info *sta = NULL;
38 struct ieee80211_key *key; 37 struct ieee80211_key *key;
39 struct ieee80211_sub_if_data *sdata; 38 struct ieee80211_sub_if_data *sdata;
40 39
@@ -51,24 +50,23 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
51 key = sdata->keys[idx]; 50 key = sdata->keys[idx];
52 } else { 51 } else {
53 sta = sta_info_get(local, sta_addr); 52 sta = sta_info_get(local, sta_addr);
54 if (!sta) { 53 if (!sta)
55 ret = -ENOENT; 54 return -ENOENT;
56 key = NULL;
57 goto err_out;
58 }
59
60 key = sta->key; 55 key = sta->key;
61 } 56 }
62 57
63 if (!key) 58 if (!key)
64 ret = -ENOENT; 59 return -ENOENT;
65 else 60
66 ret = 0; 61 ieee80211_key_free(key);
62 return 0;
67 } else { 63 } else {
68 key = ieee80211_key_alloc(alg, idx, key_len, _key); 64 key = ieee80211_key_alloc(alg, idx, key_len, _key);
69 if (!key) 65 if (!key)
70 return -ENOMEM; 66 return -ENOMEM;
71 67
68 sta = NULL;
69
72 if (!is_broadcast_ether_addr(sta_addr)) { 70 if (!is_broadcast_ether_addr(sta_addr)) {
73 set_tx_key = 0; 71 set_tx_key = 0;
74 /* 72 /*
@@ -78,14 +76,14 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
78 * work around this. 76 * work around this.
79 */ 77 */
80 if (idx != 0 && alg != ALG_WEP) { 78 if (idx != 0 && alg != ALG_WEP) {
81 ret = -EINVAL; 79 ieee80211_key_free(key);
82 goto err_out; 80 return -EINVAL;
83 } 81 }
84 82
85 sta = sta_info_get(local, sta_addr); 83 sta = sta_info_get(local, sta_addr);
86 if (!sta) { 84 if (!sta) {
87 ret = -ENOENT; 85 ieee80211_key_free(key);
88 goto err_out; 86 return -ENOENT;
89 } 87 }
90 } 88 }
91 89
@@ -93,18 +91,9 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr,
93 91
94 if (set_tx_key || (!sta && !sdata->default_key && key)) 92 if (set_tx_key || (!sta && !sdata->default_key && key))
95 ieee80211_set_default_key(sdata, idx); 93 ieee80211_set_default_key(sdata, idx);
96
97 /* don't free key later */
98 key = NULL;
99
100 ret = 0;
101 } 94 }
102 95
103 err_out: 96 return 0;
104 if (sta)
105 sta_info_put(sta);
106 ieee80211_key_free(key);
107 return ret;
108} 97}
109 98
110static int ieee80211_ioctl_siwgenie(struct net_device *dev, 99static int ieee80211_ioctl_siwgenie(struct net_device *dev,
@@ -625,7 +614,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
625 else 614 else
626 rate->value = 0; 615 rate->value = 0;
627 rate->value *= 100000; 616 rate->value *= 100000;
628 sta_info_put(sta); 617
629 return 0; 618 return 0;
630} 619}
631 620
@@ -1000,7 +989,6 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
1000 wstats->qual.qual = sta->last_signal; 989 wstats->qual.qual = sta->last_signal;
1001 wstats->qual.noise = sta->last_noise; 990 wstats->qual.noise = sta->last_noise;
1002 wstats->qual.updated = local->wstats_flags; 991 wstats->qual.updated = local->wstats_flags;
1003 sta_info_put(sta);
1004 } 992 }
1005 return wstats; 993 return wstats;
1006} 994}