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

mac80211: mesh: embedd mesh_paths and mpp_paths into ieee80211_if_mesh

Syzbot hit NULL deref in rhashtable_free_and_destroy(). The problem was
in mesh_paths and mpp_paths being NULL.

mesh_pathtbl_init() could fail in case of memory allocation failure, but
nobody cared, since ieee80211_mesh_init_sdata() returns void. It led to
leaving 2 pointers as NULL. Syzbot has found null deref on exit path,
but it could happen anywhere else, because code assumes these pointers are
valid.

Since all ieee80211_*_setup_sdata functions are void and do not fail,
let's embedd mesh_paths and mpp_paths into parent struct to avoid
adding error handling on higher levels and follow the pattern of others
setup_sdata functions

Fixes: 60854fd94573 ("mac80211: mesh: convert path table to rhashtable")
Reported-and-tested-by: syzbot+860268315ba86ea6b96b@syzkaller.appspotmail.com
Signed-off-by: Pavel Skripkin <paskripkin@gmail.com>
Link: https://lore.kernel.org/r/20211230195547.23977-1-paskripkin@gmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Pavel Skripkin and committed by
Johannes Berg
8b5cb7e4 68a18ad7

+54 -81
+22 -2
net/mac80211/ieee80211_i.h
··· 647 647 struct cfg80211_csa_settings settings; 648 648 }; 649 649 650 + /** 651 + * struct mesh_table 652 + * 653 + * @known_gates: list of known mesh gates and their mpaths by the station. The 654 + * gate's mpath may or may not be resolved and active. 655 + * @gates_lock: protects updates to known_gates 656 + * @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr 657 + * @walk_head: linked list containing all mesh_path objects 658 + * @walk_lock: lock protecting walk_head 659 + * @entries: number of entries in the table 660 + */ 661 + struct mesh_table { 662 + struct hlist_head known_gates; 663 + spinlock_t gates_lock; 664 + struct rhashtable rhead; 665 + struct hlist_head walk_head; 666 + spinlock_t walk_lock; 667 + atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */ 668 + }; 669 + 650 670 struct ieee80211_if_mesh { 651 671 struct timer_list housekeeping_timer; 652 672 struct timer_list mesh_path_timer; ··· 741 721 /* offset from skb->data while building IE */ 742 722 int meshconf_offset; 743 723 744 - struct mesh_table *mesh_paths; 745 - struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ 724 + struct mesh_table mesh_paths; 725 + struct mesh_table mpp_paths; /* Store paths for MPP&MAP */ 746 726 int mesh_paths_generation; 747 727 int mpp_paths_generation; 748 728 };
+1 -21
net/mac80211/mesh.h
··· 127 127 u32 path_change_count; 128 128 }; 129 129 130 - /** 131 - * struct mesh_table 132 - * 133 - * @known_gates: list of known mesh gates and their mpaths by the station. The 134 - * gate's mpath may or may not be resolved and active. 135 - * @gates_lock: protects updates to known_gates 136 - * @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr 137 - * @walk_head: linked list containing all mesh_path objects 138 - * @walk_lock: lock protecting walk_head 139 - * @entries: number of entries in the table 140 - */ 141 - struct mesh_table { 142 - struct hlist_head known_gates; 143 - spinlock_t gates_lock; 144 - struct rhashtable rhead; 145 - struct hlist_head walk_head; 146 - spinlock_t walk_lock; 147 - atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */ 148 - }; 149 - 150 130 /* Recent multicast cache */ 151 131 /* RMC_BUCKETS must be a power of 2, maximum 256 */ 152 132 #define RMC_BUCKETS 256 ··· 288 308 void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta); 289 309 void mesh_path_flush_pending(struct mesh_path *mpath); 290 310 void mesh_path_tx_pending(struct mesh_path *mpath); 291 - int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata); 311 + void mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata); 292 312 void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata); 293 313 int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr); 294 314 void mesh_path_timer(struct timer_list *t);
+31 -58
net/mac80211/mesh_pathtbl.c
··· 47 47 mesh_path_free_rcu(tbl, mpath); 48 48 } 49 49 50 - static struct mesh_table *mesh_table_alloc(void) 50 + static void mesh_table_init(struct mesh_table *tbl) 51 51 { 52 - struct mesh_table *newtbl; 52 + INIT_HLIST_HEAD(&tbl->known_gates); 53 + INIT_HLIST_HEAD(&tbl->walk_head); 54 + atomic_set(&tbl->entries, 0); 55 + spin_lock_init(&tbl->gates_lock); 56 + spin_lock_init(&tbl->walk_lock); 53 57 54 - newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); 55 - if (!newtbl) 56 - return NULL; 57 - 58 - INIT_HLIST_HEAD(&newtbl->known_gates); 59 - INIT_HLIST_HEAD(&newtbl->walk_head); 60 - atomic_set(&newtbl->entries, 0); 61 - spin_lock_init(&newtbl->gates_lock); 62 - spin_lock_init(&newtbl->walk_lock); 63 - if (rhashtable_init(&newtbl->rhead, &mesh_rht_params)) { 64 - kfree(newtbl); 65 - return NULL; 66 - } 67 - 68 - return newtbl; 58 + /* rhashtable_init() may fail only in case of wrong 59 + * mesh_rht_params 60 + */ 61 + WARN_ON(rhashtable_init(&tbl->rhead, &mesh_rht_params)); 69 62 } 70 63 71 64 static void mesh_table_free(struct mesh_table *tbl) 72 65 { 73 66 rhashtable_free_and_destroy(&tbl->rhead, 74 67 mesh_path_rht_free, tbl); 75 - kfree(tbl); 76 68 } 77 69 78 70 /** ··· 230 238 struct mesh_path * 231 239 mesh_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) 232 240 { 233 - return mpath_lookup(sdata->u.mesh.mesh_paths, dst, sdata); 241 + return mpath_lookup(&sdata->u.mesh.mesh_paths, dst, sdata); 234 242 } 235 243 236 244 struct mesh_path * 237 245 mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) 238 246 { 239 - return mpath_lookup(sdata->u.mesh.mpp_paths, dst, sdata); 247 + return mpath_lookup(&sdata->u.mesh.mpp_paths, dst, sdata); 240 248 } 241 249 242 250 static struct mesh_path * ··· 273 281 struct mesh_path * 274 282 mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) 275 283 { 276 - return __mesh_path_lookup_by_idx(sdata->u.mesh.mesh_paths, idx); 284 + return __mesh_path_lookup_by_idx(&sdata->u.mesh.mesh_paths, idx); 277 285 } 278 286 279 287 /** ··· 288 296 struct mesh_path * 289 297 mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) 290 298 { 291 - return __mesh_path_lookup_by_idx(sdata->u.mesh.mpp_paths, idx); 299 + return __mesh_path_lookup_by_idx(&sdata->u.mesh.mpp_paths, idx); 292 300 } 293 301 294 302 /** ··· 301 309 int err; 302 310 303 311 rcu_read_lock(); 304 - tbl = mpath->sdata->u.mesh.mesh_paths; 312 + tbl = &mpath->sdata->u.mesh.mesh_paths; 305 313 306 314 spin_lock_bh(&mpath->state_lock); 307 315 if (mpath->is_gate) { ··· 410 418 if (!new_mpath) 411 419 return ERR_PTR(-ENOMEM); 412 420 413 - tbl = sdata->u.mesh.mesh_paths; 421 + tbl = &sdata->u.mesh.mesh_paths; 414 422 spin_lock_bh(&tbl->walk_lock); 415 423 mpath = rhashtable_lookup_get_insert_fast(&tbl->rhead, 416 424 &new_mpath->rhash, ··· 452 460 return -ENOMEM; 453 461 454 462 memcpy(new_mpath->mpp, mpp, ETH_ALEN); 455 - tbl = sdata->u.mesh.mpp_paths; 463 + tbl = &sdata->u.mesh.mpp_paths; 456 464 457 465 spin_lock_bh(&tbl->walk_lock); 458 466 ret = rhashtable_lookup_insert_fast(&tbl->rhead, ··· 481 489 void mesh_plink_broken(struct sta_info *sta) 482 490 { 483 491 struct ieee80211_sub_if_data *sdata = sta->sdata; 484 - struct mesh_table *tbl = sdata->u.mesh.mesh_paths; 492 + struct mesh_table *tbl = &sdata->u.mesh.mesh_paths; 485 493 static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 486 494 struct mesh_path *mpath; 487 495 ··· 540 548 void mesh_path_flush_by_nexthop(struct sta_info *sta) 541 549 { 542 550 struct ieee80211_sub_if_data *sdata = sta->sdata; 543 - struct mesh_table *tbl = sdata->u.mesh.mesh_paths; 551 + struct mesh_table *tbl = &sdata->u.mesh.mesh_paths; 544 552 struct mesh_path *mpath; 545 553 struct hlist_node *n; 546 554 ··· 555 563 static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata, 556 564 const u8 *proxy) 557 565 { 558 - struct mesh_table *tbl = sdata->u.mesh.mpp_paths; 566 + struct mesh_table *tbl = &sdata->u.mesh.mpp_paths; 559 567 struct mesh_path *mpath; 560 568 struct hlist_node *n; 561 569 ··· 589 597 */ 590 598 void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) 591 599 { 592 - table_flush_by_iface(sdata->u.mesh.mesh_paths); 593 - table_flush_by_iface(sdata->u.mesh.mpp_paths); 600 + table_flush_by_iface(&sdata->u.mesh.mesh_paths); 601 + table_flush_by_iface(&sdata->u.mesh.mpp_paths); 594 602 } 595 603 596 604 /** ··· 636 644 /* flush relevant mpp entries first */ 637 645 mpp_flush_by_proxy(sdata, addr); 638 646 639 - err = table_path_del(sdata->u.mesh.mesh_paths, sdata, addr); 647 + err = table_path_del(&sdata->u.mesh.mesh_paths, sdata, addr); 640 648 sdata->u.mesh.mesh_paths_generation++; 641 649 return err; 642 650 } ··· 674 682 struct mesh_path *gate; 675 683 bool copy = false; 676 684 677 - tbl = sdata->u.mesh.mesh_paths; 685 + tbl = &sdata->u.mesh.mesh_paths; 678 686 679 687 rcu_read_lock(); 680 688 hlist_for_each_entry_rcu(gate, &tbl->known_gates, gate_list) { ··· 754 762 mesh_path_tx_pending(mpath); 755 763 } 756 764 757 - int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata) 765 + void mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata) 758 766 { 759 - struct mesh_table *tbl_path, *tbl_mpp; 760 - int ret; 761 - 762 - tbl_path = mesh_table_alloc(); 763 - if (!tbl_path) 764 - return -ENOMEM; 765 - 766 - tbl_mpp = mesh_table_alloc(); 767 - if (!tbl_mpp) { 768 - ret = -ENOMEM; 769 - goto free_path; 770 - } 771 - 772 - sdata->u.mesh.mesh_paths = tbl_path; 773 - sdata->u.mesh.mpp_paths = tbl_mpp; 774 - 775 - return 0; 776 - 777 - free_path: 778 - mesh_table_free(tbl_path); 779 - return ret; 767 + mesh_table_init(&sdata->u.mesh.mesh_paths); 768 + mesh_table_init(&sdata->u.mesh.mpp_paths); 780 769 } 781 770 782 771 static ··· 779 806 780 807 void mesh_path_expire(struct ieee80211_sub_if_data *sdata) 781 808 { 782 - mesh_path_tbl_expire(sdata, sdata->u.mesh.mesh_paths); 783 - mesh_path_tbl_expire(sdata, sdata->u.mesh.mpp_paths); 809 + mesh_path_tbl_expire(sdata, &sdata->u.mesh.mesh_paths); 810 + mesh_path_tbl_expire(sdata, &sdata->u.mesh.mpp_paths); 784 811 } 785 812 786 813 void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata) 787 814 { 788 - mesh_table_free(sdata->u.mesh.mesh_paths); 789 - mesh_table_free(sdata->u.mesh.mpp_paths); 815 + mesh_table_free(&sdata->u.mesh.mesh_paths); 816 + mesh_table_free(&sdata->u.mesh.mpp_paths); 790 817 }