Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: verify virtual interfaces in driver API

The driver is never informed about monitor or
AP_VLAN interfaces, so whenever we pass those
to it later this is a bug. Verify we don't as
there are some cases where this could happen.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

Johannes Berg and committed by
John W. Linville
7b7eab6f 6e3e939f

+69 -11
+62 -6
net/mac80211/driver-ops.h
··· 5 5 #include "ieee80211_i.h" 6 6 #include "driver-trace.h" 7 7 8 + static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) 9 + { 10 + WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)); 11 + } 12 + 8 13 static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) 9 14 { 10 15 local->ops->tx(&local->hw, skb); ··· 74 69 #endif 75 70 76 71 static inline int drv_add_interface(struct ieee80211_local *local, 77 - struct ieee80211_vif *vif) 72 + struct ieee80211_sub_if_data *sdata) 78 73 { 79 74 int ret; 80 75 81 76 might_sleep(); 82 77 83 - trace_drv_add_interface(local, vif_to_sdata(vif)); 84 - ret = local->ops->add_interface(&local->hw, vif); 78 + if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 79 + sdata->vif.type == NL80211_IFTYPE_MONITOR)) 80 + return -EINVAL; 81 + 82 + trace_drv_add_interface(local, sdata); 83 + ret = local->ops->add_interface(&local->hw, &sdata->vif); 85 84 trace_drv_return_int(local, ret); 85 + 86 + if (ret == 0) 87 + sdata->flags |= IEEE80211_SDATA_IN_DRIVER; 88 + 86 89 return ret; 87 90 } 88 91 ··· 102 89 103 90 might_sleep(); 104 91 92 + check_sdata_in_driver(sdata); 93 + 105 94 trace_drv_change_interface(local, sdata, type, p2p); 106 95 ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); 107 96 trace_drv_return_int(local, ret); ··· 111 96 } 112 97 113 98 static inline void drv_remove_interface(struct ieee80211_local *local, 114 - struct ieee80211_vif *vif) 99 + struct ieee80211_sub_if_data *sdata) 115 100 { 116 101 might_sleep(); 117 102 118 - trace_drv_remove_interface(local, vif_to_sdata(vif)); 119 - local->ops->remove_interface(&local->hw, vif); 103 + check_sdata_in_driver(sdata); 104 + 105 + trace_drv_remove_interface(local, sdata); 106 + local->ops->remove_interface(&local->hw, &sdata->vif); 107 + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; 120 108 trace_drv_return_void(local); 121 109 } 122 110 ··· 142 124 { 143 125 might_sleep(); 144 126 127 + check_sdata_in_driver(sdata); 128 + 145 129 trace_drv_bss_info_changed(local, sdata, info, changed); 146 130 if (local->ops->bss_info_changed) 147 131 local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed); ··· 159 139 160 140 might_sleep(); 161 141 142 + check_sdata_in_driver(sdata); 143 + 162 144 trace_drv_tx_sync(local, sdata, bssid, type); 163 145 if (local->ops->tx_sync) 164 146 ret = local->ops->tx_sync(&local->hw, &sdata->vif, ··· 175 153 enum ieee80211_tx_sync_type type) 176 154 { 177 155 might_sleep(); 156 + 157 + check_sdata_in_driver(sdata); 178 158 179 159 trace_drv_finish_tx_sync(local, sdata, bssid, type); 180 160 if (local->ops->finish_tx_sync) ··· 235 211 236 212 might_sleep(); 237 213 214 + check_sdata_in_driver(sdata); 215 + 238 216 trace_drv_set_key(local, cmd, sdata, sta, key); 239 217 ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); 240 218 trace_drv_return_int(local, ret); ··· 254 228 if (sta) 255 229 ista = &sta->sta; 256 230 231 + check_sdata_in_driver(sdata); 232 + 257 233 trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); 258 234 if (local->ops->update_tkip_key) 259 235 local->ops->update_tkip_key(&local->hw, &sdata->vif, conf, ··· 271 243 272 244 might_sleep(); 273 245 246 + check_sdata_in_driver(sdata); 247 + 274 248 trace_drv_hw_scan(local, sdata); 275 249 ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); 276 250 trace_drv_return_int(local, ret); ··· 283 253 struct ieee80211_sub_if_data *sdata) 284 254 { 285 255 might_sleep(); 256 + 257 + check_sdata_in_driver(sdata); 286 258 287 259 trace_drv_cancel_hw_scan(local, sdata); 288 260 local->ops->cancel_hw_scan(&local->hw, &sdata->vif); ··· 301 269 302 270 might_sleep(); 303 271 272 + check_sdata_in_driver(sdata); 273 + 304 274 trace_drv_sched_scan_start(local, sdata); 305 275 ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, 306 276 req, ies); ··· 314 280 struct ieee80211_sub_if_data *sdata) 315 281 { 316 282 might_sleep(); 283 + 284 + check_sdata_in_driver(sdata); 317 285 318 286 trace_drv_sched_scan_stop(local, sdata); 319 287 local->ops->sched_scan_stop(&local->hw, &sdata->vif); ··· 413 377 enum sta_notify_cmd cmd, 414 378 struct ieee80211_sta *sta) 415 379 { 380 + check_sdata_in_driver(sdata); 381 + 416 382 trace_drv_sta_notify(local, sdata, cmd, sta); 417 383 if (local->ops->sta_notify) 418 384 local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta); ··· 428 390 int ret = 0; 429 391 430 392 might_sleep(); 393 + 394 + check_sdata_in_driver(sdata); 431 395 432 396 trace_drv_sta_add(local, sdata, sta); 433 397 if (local->ops->sta_add) ··· 446 406 { 447 407 might_sleep(); 448 408 409 + check_sdata_in_driver(sdata); 410 + 449 411 trace_drv_sta_remove(local, sdata, sta); 450 412 if (local->ops->sta_remove) 451 413 local->ops->sta_remove(&local->hw, &sdata->vif, sta); ··· 462 420 int ret = -EOPNOTSUPP; 463 421 464 422 might_sleep(); 423 + 424 + check_sdata_in_driver(sdata); 465 425 466 426 trace_drv_conf_tx(local, sdata, queue, params); 467 427 if (local->ops->conf_tx) ··· 480 436 481 437 might_sleep(); 482 438 439 + check_sdata_in_driver(sdata); 440 + 483 441 trace_drv_get_tsf(local, sdata); 484 442 if (local->ops->get_tsf) 485 443 ret = local->ops->get_tsf(&local->hw, &sdata->vif); ··· 495 449 { 496 450 might_sleep(); 497 451 452 + check_sdata_in_driver(sdata); 453 + 498 454 trace_drv_set_tsf(local, sdata, tsf); 499 455 if (local->ops->set_tsf) 500 456 local->ops->set_tsf(&local->hw, &sdata->vif, tsf); ··· 507 459 struct ieee80211_sub_if_data *sdata) 508 460 { 509 461 might_sleep(); 462 + 463 + check_sdata_in_driver(sdata); 510 464 511 465 trace_drv_reset_tsf(local, sdata); 512 466 if (local->ops->reset_tsf) ··· 538 488 int ret = -EOPNOTSUPP; 539 489 540 490 might_sleep(); 491 + 492 + check_sdata_in_driver(sdata); 541 493 542 494 trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); 543 495 ··· 696 644 697 645 might_sleep(); 698 646 647 + check_sdata_in_driver(sdata); 648 + 699 649 trace_drv_set_bitrate_mask(local, sdata, mask); 700 650 if (local->ops->set_bitrate_mask) 701 651 ret = local->ops->set_bitrate_mask(&local->hw, ··· 711 657 struct ieee80211_sub_if_data *sdata, 712 658 struct cfg80211_gtk_rekey_data *data) 713 659 { 660 + check_sdata_in_driver(sdata); 661 + 714 662 trace_drv_set_rekey_data(local, sdata, data); 715 663 if (local->ops->set_rekey_data) 716 664 local->ops->set_rekey_data(&local->hw, &sdata->vif, data);
+2
net/mac80211/ieee80211_i.h
··· 543 543 * associated stations and deliver multicast frames both 544 544 * back to wireless media and to the local net stack. 545 545 * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. 546 + * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver 546 547 */ 547 548 enum ieee80211_sub_if_data_flags { 548 549 IEEE80211_SDATA_ALLMULTI = BIT(0), ··· 551 550 IEEE80211_SDATA_OPERATING_GMODE = BIT(2), 552 551 IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), 553 552 IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), 553 + IEEE80211_SDATA_IN_DRIVER = BIT(5), 554 554 }; 555 555 556 556 /**
+3 -3
net/mac80211/iface.c
··· 265 265 break; 266 266 default: 267 267 if (coming_up) { 268 - res = drv_add_interface(local, &sdata->vif); 268 + res = drv_add_interface(local, sdata); 269 269 if (res) 270 270 goto err_stop; 271 271 } ··· 345 345 346 346 return 0; 347 347 err_del_interface: 348 - drv_remove_interface(local, &sdata->vif); 348 + drv_remove_interface(local, sdata); 349 349 err_stop: 350 350 if (!local->open_count) 351 351 drv_stop(local); ··· 520 520 ieee80211_free_keys(sdata); 521 521 522 522 if (going_down) 523 - drv_remove_interface(local, &sdata->vif); 523 + drv_remove_interface(local, sdata); 524 524 } 525 525 526 526 sdata->bss = NULL;
+1 -1
net/mac80211/pm.c
··· 125 125 ieee80211_bss_info_change_notify(sdata, 126 126 BSS_CHANGED_BEACON_ENABLED); 127 127 128 - drv_remove_interface(local, &sdata->vif); 128 + drv_remove_interface(local, sdata); 129 129 } 130 130 131 131 /* stop hardware - this must stop RX */
+1 -1
net/mac80211/util.c
··· 1006 1006 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 1007 1007 sdata->vif.type != NL80211_IFTYPE_MONITOR && 1008 1008 ieee80211_sdata_running(sdata)) 1009 - res = drv_add_interface(local, &sdata->vif); 1009 + res = drv_add_interface(local, sdata); 1010 1010 } 1011 1011 1012 1012 /* add STAs back */