aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-03-24 04:35:46 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-03-27 20:13:20 -0400
commit4bbf4d56583dd52c429d88f43cb614bdbe5deea6 (patch)
tree7a3f902a08820342254e0d67607fe870b02620b3
parent3832c287f11ba001bbe48e9be8c59cb9f71f6b43 (diff)
cfg80211: fix locking in nl80211_set_wiphy
Luis reports that there's a circular locking dependency; this is because cfg80211_dev_rename() will acquire the cfg80211_mutex while the device mutex is held, while this normally is done the other way around. The solution is to open-code the device-getting in nl80211_set_wiphy and require holding the mutex around cfg80211_dev_rename rather than acquiring it within. Also fix a bug -- rtnl locking is expected by drivers so we need to provide it. Reported-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/wireless/core.c30
-rw-r--r--net/wireless/core.h3
-rw-r--r--net/wireless/nl80211.c28
3 files changed, 33 insertions, 28 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 17fe39049740..d1f556535f6d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -87,7 +87,7 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
87} 87}
88 88
89/* requires cfg80211_mutex to be held! */ 89/* requires cfg80211_mutex to be held! */
90static struct cfg80211_registered_device * 90struct cfg80211_registered_device *
91__cfg80211_drv_from_info(struct genl_info *info) 91__cfg80211_drv_from_info(struct genl_info *info)
92{ 92{
93 int ifindex; 93 int ifindex;
@@ -176,13 +176,14 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv)
176 mutex_unlock(&drv->mtx); 176 mutex_unlock(&drv->mtx);
177} 177}
178 178
179/* requires cfg80211_mutex to be held */
179int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, 180int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
180 char *newname) 181 char *newname)
181{ 182{
182 struct cfg80211_registered_device *drv; 183 struct cfg80211_registered_device *drv;
183 int wiphy_idx, taken = -1, result, digits; 184 int wiphy_idx, taken = -1, result, digits;
184 185
185 mutex_lock(&cfg80211_mutex); 186 assert_cfg80211_lock();
186 187
187 /* prohibit calling the thing phy%d when %d is not its number */ 188 /* prohibit calling the thing phy%d when %d is not its number */
188 sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); 189 sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
@@ -195,30 +196,23 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
195 * deny the name if it is phy<idx> where <idx> is printed 196 * deny the name if it is phy<idx> where <idx> is printed
196 * without leading zeroes. taken == strlen(newname) here 197 * without leading zeroes. taken == strlen(newname) here
197 */ 198 */
198 result = -EINVAL;
199 if (taken == strlen(PHY_NAME) + digits) 199 if (taken == strlen(PHY_NAME) + digits)
200 goto out_unlock; 200 return -EINVAL;
201 } 201 }
202 202
203 203
204 /* Ignore nop renames */ 204 /* Ignore nop renames */
205 result = 0;
206 if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) 205 if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
207 goto out_unlock; 206 return 0;
208 207
209 /* Ensure another device does not already have this name. */ 208 /* Ensure another device does not already have this name. */
210 list_for_each_entry(drv, &cfg80211_drv_list, list) { 209 list_for_each_entry(drv, &cfg80211_drv_list, list)
211 result = -EINVAL;
212 if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0) 210 if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
213 goto out_unlock; 211 return -EINVAL;
214 }
215 212
216 /* this will only check for collisions in sysfs
217 * which is not even always compiled in.
218 */
219 result = device_rename(&rdev->wiphy.dev, newname); 213 result = device_rename(&rdev->wiphy.dev, newname);
220 if (result) 214 if (result)
221 goto out_unlock; 215 return result;
222 216
223 if (rdev->wiphy.debugfsdir && 217 if (rdev->wiphy.debugfsdir &&
224 !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, 218 !debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
@@ -228,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
228 printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", 222 printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
229 newname); 223 newname);
230 224
231 result = 0; 225 nl80211_notify_dev_rename(rdev);
232out_unlock:
233 mutex_unlock(&cfg80211_mutex);
234 if (result == 0)
235 nl80211_notify_dev_rename(rdev);
236 226
237 return result; 227 return 0;
238} 228}
239 229
240/* exported functions */ 230/* exported functions */
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 97a6fd8b2b03..d43daa236ef9 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -99,6 +99,9 @@ struct cfg80211_internal_bss {
99struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx); 99struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx);
100int get_wiphy_idx(struct wiphy *wiphy); 100int get_wiphy_idx(struct wiphy *wiphy);
101 101
102struct cfg80211_registered_device *
103__cfg80211_drv_from_info(struct genl_info *info);
104
102/* 105/*
103 * This function returns a pointer to the driver 106 * This function returns a pointer to the driver
104 * that the genl_info item that is passed refers to. 107 * that the genl_info item that is passed refers to.
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8808431bd581..353e1a4ece83 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -366,16 +366,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
366 int result = 0, rem_txq_params = 0; 366 int result = 0, rem_txq_params = 0;
367 struct nlattr *nl_txq_params; 367 struct nlattr *nl_txq_params;
368 368
369 rdev = cfg80211_get_dev_from_info(info); 369 rtnl_lock();
370 if (IS_ERR(rdev)) 370
371 return PTR_ERR(rdev); 371 mutex_lock(&cfg80211_mutex);
372
373 rdev = __cfg80211_drv_from_info(info);
374 if (IS_ERR(rdev)) {
375 result = PTR_ERR(rdev);
376 goto unlock;
377 }
372 378
373 if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { 379 mutex_lock(&rdev->mtx);
380
381 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
374 result = cfg80211_dev_rename( 382 result = cfg80211_dev_rename(
375 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); 383 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
376 if (result) 384
377 goto bad_res; 385 mutex_unlock(&cfg80211_mutex);
378 } 386
387 if (result)
388 goto bad_res;
379 389
380 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { 390 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
381 struct ieee80211_txq_params txq_params; 391 struct ieee80211_txq_params txq_params;
@@ -471,7 +481,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
471 481
472 482
473 bad_res: 483 bad_res:
474 cfg80211_put_dev(rdev); 484 mutex_unlock(&rdev->mtx);
485 unlock:
486 rtnl_unlock();
475 return result; 487 return result;
476} 488}
477 489