diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-02-21 08:09:30 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:41:34 -0500 |
commit | 43ba7e958f2ca05e4e9171a15402288419289d71 (patch) | |
tree | 15b7a04a7db402dd286f83cc56c21b336189da09 /net/mac80211/sta_info.c | |
parent | d46e144b65bf053b25d134ec9f52a38e63e04bb4 (diff) |
mac80211: atomically check whether STA exists already
When a STA structure is added, it is often checked whether it
already exists before adding it. This, however, isn't done
atomically so there is a race condition that could lead to two
STA structures being added with the same MAC address. This
patch changes sta_info_add() to return an ERR_PTR in case
of failure and adds the failure mode -EEXIST when the STA
already exists.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Luis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r-- | net/mac80211/sta_info.c | 38 |
1 files changed, 27 insertions, 11 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index b31a627ff97f..c6c0df4bbd2c 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -55,19 +55,29 @@ static int sta_info_hash_del(struct ieee80211_local *local, | |||
55 | return -ENOENT; | 55 | return -ENOENT; |
56 | } | 56 | } |
57 | 57 | ||
58 | struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) | 58 | /* must hold local->sta_lock */ |
59 | static struct sta_info *__sta_info_find(struct ieee80211_local *local, | ||
60 | u8 *addr) | ||
59 | { | 61 | { |
60 | struct sta_info *sta; | 62 | struct sta_info *sta; |
61 | 63 | ||
62 | read_lock_bh(&local->sta_lock); | ||
63 | sta = local->sta_hash[STA_HASH(addr)]; | 64 | sta = local->sta_hash[STA_HASH(addr)]; |
64 | while (sta) { | 65 | while (sta) { |
65 | if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { | 66 | if (compare_ether_addr(sta->addr, addr) == 0) |
66 | __sta_info_get(sta); | ||
67 | break; | 67 | break; |
68 | } | ||
69 | sta = sta->hnext; | 68 | sta = sta->hnext; |
70 | } | 69 | } |
70 | return sta; | ||
71 | } | ||
72 | |||
73 | struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) | ||
74 | { | ||
75 | struct sta_info *sta; | ||
76 | |||
77 | read_lock_bh(&local->sta_lock); | ||
78 | sta = __sta_info_find(local, addr); | ||
79 | if (sta) | ||
80 | __sta_info_get(sta); | ||
71 | read_unlock_bh(&local->sta_lock); | 81 | read_unlock_bh(&local->sta_lock); |
72 | 82 | ||
73 | return sta; | 83 | return sta; |
@@ -110,8 +120,8 @@ void sta_info_put(struct sta_info *sta) | |||
110 | EXPORT_SYMBOL(sta_info_put); | 120 | EXPORT_SYMBOL(sta_info_put); |
111 | 121 | ||
112 | 122 | ||
113 | struct sta_info * sta_info_add(struct ieee80211_local *local, | 123 | struct sta_info *sta_info_add(struct ieee80211_local *local, |
114 | struct net_device *dev, u8 *addr, gfp_t gfp) | 124 | struct net_device *dev, u8 *addr, gfp_t gfp) |
115 | { | 125 | { |
116 | struct sta_info *sta; | 126 | struct sta_info *sta; |
117 | int i; | 127 | int i; |
@@ -119,7 +129,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
119 | 129 | ||
120 | sta = kzalloc(sizeof(*sta), gfp); | 130 | sta = kzalloc(sizeof(*sta), gfp); |
121 | if (!sta) | 131 | if (!sta) |
122 | return NULL; | 132 | return ERR_PTR(-ENOMEM); |
123 | 133 | ||
124 | kref_init(&sta->kref); | 134 | kref_init(&sta->kref); |
125 | 135 | ||
@@ -128,7 +138,7 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
128 | if (!sta->rate_ctrl_priv) { | 138 | if (!sta->rate_ctrl_priv) { |
129 | rate_control_put(sta->rate_ctrl); | 139 | rate_control_put(sta->rate_ctrl); |
130 | kfree(sta); | 140 | kfree(sta); |
131 | return NULL; | 141 | return ERR_PTR(-ENOMEM); |
132 | } | 142 | } |
133 | 143 | ||
134 | memcpy(sta->addr, addr, ETH_ALEN); | 144 | memcpy(sta->addr, addr, ETH_ALEN); |
@@ -158,9 +168,15 @@ struct sta_info * sta_info_add(struct ieee80211_local *local, | |||
158 | } | 168 | } |
159 | skb_queue_head_init(&sta->ps_tx_buf); | 169 | skb_queue_head_init(&sta->ps_tx_buf); |
160 | skb_queue_head_init(&sta->tx_filtered); | 170 | skb_queue_head_init(&sta->tx_filtered); |
161 | __sta_info_get(sta); /* sta used by caller, decremented by | ||
162 | * sta_info_put() */ | ||
163 | write_lock_bh(&local->sta_lock); | 171 | write_lock_bh(&local->sta_lock); |
172 | /* mark sta as used (by caller) */ | ||
173 | __sta_info_get(sta); | ||
174 | /* check if STA exists already */ | ||
175 | if (__sta_info_find(local, addr)) { | ||
176 | write_unlock_bh(&local->sta_lock); | ||
177 | sta_info_put(sta); | ||
178 | return ERR_PTR(-EEXIST); | ||
179 | } | ||
164 | list_add(&sta->list, &local->sta_list); | 180 | list_add(&sta->list, &local->sta_list); |
165 | local->num_sta++; | 181 | local->num_sta++; |
166 | sta_info_hash_add(local, sta); | 182 | sta_info_hash_add(local, sta); |