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

cfg80211: make wmm_rule part of the reg_rule structure

Make wmm_rule be part of the reg_rule structure. This simplifies the
code a lot at the cost of having bigger memory usage. However in most
cases we have only few reg_rule's and when we do have many like in
iwlwifi we do not save memory as it allocates a separate wmm_rule for
each channel anyway.

This also fixes a bug reported in various places where somewhere the
pointers were corrupted and we ended up doing a null-dereference.

Fixes: 230ebaa189af ("cfg80211: read wmm rules from regulatory database")
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
[rephrase commit message slightly]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Stanislaw Gruszka and committed by
Johannes Berg
38cb87ee d7c863a2

+31 -135
+5 -45
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
··· 877 877 const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ? 878 878 iwl_ext_nvm_channels : iwl_nvm_channels; 879 879 struct ieee80211_regdomain *regd, *copy_rd; 880 - int size_of_regd, regd_to_copy, wmms_to_copy; 881 - int size_of_wmms = 0; 880 + int size_of_regd, regd_to_copy; 882 881 struct ieee80211_reg_rule *rule; 883 - struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm; 884 882 struct regdb_ptrs *regdb_ptrs; 885 883 enum nl80211_band band; 886 884 int center_freq, prev_center_freq = 0; 887 - int valid_rules = 0, n_wmms = 0; 888 - int i; 885 + int valid_rules = 0; 889 886 bool new_rule; 890 887 int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ? 891 888 IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS; ··· 901 904 sizeof(struct ieee80211_regdomain) + 902 905 num_of_ch * sizeof(struct ieee80211_reg_rule); 903 906 904 - if (geo_info & GEO_WMM_ETSI_5GHZ_INFO) 905 - size_of_wmms = 906 - num_of_ch * sizeof(struct ieee80211_wmm_rule); 907 - 908 - regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL); 907 + regd = kzalloc(size_of_regd, GFP_KERNEL); 909 908 if (!regd) 910 909 return ERR_PTR(-ENOMEM); 911 910 ··· 914 921 /* set alpha2 from FW. */ 915 922 regd->alpha2[0] = fw_mcc >> 8; 916 923 regd->alpha2[1] = fw_mcc & 0xff; 917 - 918 - wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); 919 924 920 925 for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { 921 926 ch_flags = (u16)__le32_to_cpup(channels + ch_idx); ··· 968 977 band == NL80211_BAND_2GHZ) 969 978 continue; 970 979 971 - if (!reg_query_regdb_wmm(regd->alpha2, center_freq, 972 - &regdb_ptrs[n_wmms].token, wmm_rule)) { 973 - /* Add only new rules */ 974 - for (i = 0; i < n_wmms; i++) { 975 - if (regdb_ptrs[i].token == 976 - regdb_ptrs[n_wmms].token) { 977 - rule->wmm_rule = regdb_ptrs[i].rule; 978 - break; 979 - } 980 - } 981 - if (i == n_wmms) { 982 - rule->wmm_rule = wmm_rule; 983 - regdb_ptrs[n_wmms++].rule = wmm_rule; 984 - wmm_rule++; 985 - } 986 - } 980 + reg_query_regdb_wmm(regd->alpha2, center_freq, rule); 987 981 } 988 982 989 983 regd->n_reg_rules = valid_rules; 990 - regd->n_wmm_rules = n_wmms; 991 984 992 985 /* 993 986 * Narrow down regdom for unused regulatory rules to prevent hole ··· 980 1005 regd_to_copy = sizeof(struct ieee80211_regdomain) + 981 1006 valid_rules * sizeof(struct ieee80211_reg_rule); 982 1007 983 - wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms; 984 - 985 - copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL); 1008 + copy_rd = kzalloc(regd_to_copy, GFP_KERNEL); 986 1009 if (!copy_rd) { 987 1010 copy_rd = ERR_PTR(-ENOMEM); 988 1011 goto out; 989 1012 } 990 1013 991 1014 memcpy(copy_rd, regd, regd_to_copy); 992 - memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd, 993 - wmms_to_copy); 994 - 995 - d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy); 996 - s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); 997 - 998 - for (i = 0; i < regd->n_reg_rules; i++) { 999 - if (!regd->reg_rules[i].wmm_rule) 1000 - continue; 1001 - 1002 - copy_rd->reg_rules[i].wmm_rule = d_wmm + 1003 - (regd->reg_rules[i].wmm_rule - s_wmm); 1004 - } 1005 1015 1006 1016 out: 1007 1017 kfree(regdb_ptrs);
+2 -2
include/net/cfg80211.h
··· 4763 4763 * 4764 4764 * Return: 0 on success. -ENODATA. 4765 4765 */ 4766 - int reg_query_regdb_wmm(char *alpha2, int freq, u32 *ptr, 4767 - struct ieee80211_wmm_rule *rule); 4766 + int reg_query_regdb_wmm(char *alpha2, int freq, 4767 + struct ieee80211_reg_rule *rule); 4768 4768 4769 4769 /* 4770 4770 * callbacks for asynchronous cfg80211 methods, notification
+2 -2
include/net/regulatory.h
··· 217 217 struct ieee80211_reg_rule { 218 218 struct ieee80211_freq_range freq_range; 219 219 struct ieee80211_power_rule power_rule; 220 - struct ieee80211_wmm_rule *wmm_rule; 220 + struct ieee80211_wmm_rule wmm_rule; 221 221 u32 flags; 222 222 u32 dfs_cac_ms; 223 + bool has_wmm; 223 224 }; 224 225 225 226 struct ieee80211_regdomain { 226 227 struct rcu_head rcu_head; 227 228 u32 n_reg_rules; 228 - u32 n_wmm_rules; 229 229 char alpha2[3]; 230 230 enum nl80211_dfs_regions dfs_region; 231 231 struct ieee80211_reg_rule reg_rules[];
+4 -4
net/mac80211/util.c
··· 1120 1120 { 1121 1121 struct ieee80211_chanctx_conf *chanctx_conf; 1122 1122 const struct ieee80211_reg_rule *rrule; 1123 - struct ieee80211_wmm_ac *wmm_ac; 1123 + const struct ieee80211_wmm_ac *wmm_ac; 1124 1124 u16 center_freq = 0; 1125 1125 1126 1126 if (sdata->vif.type != NL80211_IFTYPE_AP && ··· 1139 1139 1140 1140 rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq)); 1141 1141 1142 - if (IS_ERR_OR_NULL(rrule) || !rrule->wmm_rule) { 1142 + if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) { 1143 1143 rcu_read_unlock(); 1144 1144 return; 1145 1145 } 1146 1146 1147 1147 if (sdata->vif.type == NL80211_IFTYPE_AP) 1148 - wmm_ac = &rrule->wmm_rule->ap[ac]; 1148 + wmm_ac = &rrule->wmm_rule.ap[ac]; 1149 1149 else 1150 - wmm_ac = &rrule->wmm_rule->client[ac]; 1150 + wmm_ac = &rrule->wmm_rule.client[ac]; 1151 1151 qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min); 1152 1152 qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max); 1153 1153 qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
+5 -5
net/wireless/nl80211.c
··· 667 667 goto nla_put_failure; 668 668 669 669 if (nla_put_u16(msg, NL80211_WMMR_CW_MIN, 670 - rule->wmm_rule->client[j].cw_min) || 670 + rule->wmm_rule.client[j].cw_min) || 671 671 nla_put_u16(msg, NL80211_WMMR_CW_MAX, 672 - rule->wmm_rule->client[j].cw_max) || 672 + rule->wmm_rule.client[j].cw_max) || 673 673 nla_put_u8(msg, NL80211_WMMR_AIFSN, 674 - rule->wmm_rule->client[j].aifsn) || 674 + rule->wmm_rule.client[j].aifsn) || 675 675 nla_put_u8(msg, NL80211_WMMR_TXOP, 676 - rule->wmm_rule->client[j].cot)) 676 + rule->wmm_rule.client[j].cot)) 677 677 goto nla_put_failure; 678 678 679 679 nla_nest_end(msg, nl_wmm_rule); ··· 766 766 const struct ieee80211_reg_rule *rule = 767 767 freq_reg_info(wiphy, chan->center_freq); 768 768 769 - if (!IS_ERR(rule) && rule->wmm_rule) { 769 + if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) { 770 770 if (nl80211_msg_put_wmm_rules(msg, rule)) 771 771 goto nla_put_failure; 772 772 }
+13 -77
net/wireless/reg.c
··· 425 425 reg_copy_regd(const struct ieee80211_regdomain *src_regd) 426 426 { 427 427 struct ieee80211_regdomain *regd; 428 - int size_of_regd, size_of_wmms; 428 + int size_of_regd; 429 429 unsigned int i; 430 - struct ieee80211_wmm_rule *d_wmm, *s_wmm; 431 430 432 431 size_of_regd = 433 432 sizeof(struct ieee80211_regdomain) + 434 433 src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); 435 - size_of_wmms = src_regd->n_wmm_rules * 436 - sizeof(struct ieee80211_wmm_rule); 437 434 438 - regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL); 435 + regd = kzalloc(size_of_regd, GFP_KERNEL); 439 436 if (!regd) 440 437 return ERR_PTR(-ENOMEM); 441 438 442 439 memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); 443 440 444 - d_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd); 445 - s_wmm = (struct ieee80211_wmm_rule *)((u8 *)src_regd + size_of_regd); 446 - memcpy(d_wmm, s_wmm, size_of_wmms); 447 - 448 - for (i = 0; i < src_regd->n_reg_rules; i++) { 441 + for (i = 0; i < src_regd->n_reg_rules; i++) 449 442 memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i], 450 443 sizeof(struct ieee80211_reg_rule)); 451 - if (!src_regd->reg_rules[i].wmm_rule) 452 - continue; 453 444 454 - regd->reg_rules[i].wmm_rule = d_wmm + 455 - (src_regd->reg_rules[i].wmm_rule - s_wmm); 456 - } 457 445 return regd; 458 446 } 459 447 ··· 847 859 return true; 848 860 } 849 861 850 - static void set_wmm_rule(struct ieee80211_wmm_rule *rule, 862 + static void set_wmm_rule(struct ieee80211_reg_rule *rrule, 851 863 struct fwdb_wmm_rule *wmm) 852 864 { 865 + struct ieee80211_wmm_rule *rule = &rrule->wmm_rule; 853 866 unsigned int i; 854 867 855 868 for (i = 0; i < IEEE80211_NUM_ACS; i++) { ··· 864 875 rule->ap[i].aifsn = wmm->ap[i].aifsn; 865 876 rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot); 866 877 } 878 + 879 + rrule->has_wmm = true; 867 880 } 868 881 869 882 static int __regdb_query_wmm(const struct fwdb_header *db, 870 883 const struct fwdb_country *country, int freq, 871 - u32 *dbptr, struct ieee80211_wmm_rule *rule) 884 + struct ieee80211_reg_rule *rule) 872 885 { 873 886 unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2; 874 887 struct fwdb_collection *coll = (void *)((u8 *)db + ptr); ··· 891 900 wmm_ptr = be16_to_cpu(rrule->wmm_ptr) << 2; 892 901 wmm = (void *)((u8 *)db + wmm_ptr); 893 902 set_wmm_rule(rule, wmm); 894 - if (dbptr) 895 - *dbptr = wmm_ptr; 896 903 return 0; 897 904 } 898 905 } ··· 898 909 return -ENODATA; 899 910 } 900 911 901 - int reg_query_regdb_wmm(char *alpha2, int freq, u32 *dbptr, 902 - struct ieee80211_wmm_rule *rule) 912 + int reg_query_regdb_wmm(char *alpha2, int freq, struct ieee80211_reg_rule *rule) 903 913 { 904 914 const struct fwdb_header *hdr = regdb; 905 915 const struct fwdb_country *country; ··· 912 924 country = &hdr->country[0]; 913 925 while (country->coll_ptr) { 914 926 if (alpha2_equal(alpha2, country->alpha2)) 915 - return __regdb_query_wmm(regdb, country, freq, dbptr, 916 - rule); 927 + return __regdb_query_wmm(regdb, country, freq, rule); 917 928 918 929 country++; 919 930 } ··· 921 934 } 922 935 EXPORT_SYMBOL(reg_query_regdb_wmm); 923 936 924 - struct wmm_ptrs { 925 - struct ieee80211_wmm_rule *rule; 926 - u32 ptr; 927 - }; 928 - 929 - static struct ieee80211_wmm_rule *find_wmm_ptr(struct wmm_ptrs *wmm_ptrs, 930 - u32 wmm_ptr, int n_wmms) 931 - { 932 - int i; 933 - 934 - for (i = 0; i < n_wmms; i++) { 935 - if (wmm_ptrs[i].ptr == wmm_ptr) 936 - return wmm_ptrs[i].rule; 937 - } 938 - return NULL; 939 - } 940 - 941 937 static int regdb_query_country(const struct fwdb_header *db, 942 938 const struct fwdb_country *country) 943 939 { 944 940 unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2; 945 941 struct fwdb_collection *coll = (void *)((u8 *)db + ptr); 946 942 struct ieee80211_regdomain *regdom; 947 - struct ieee80211_regdomain *tmp_rd; 948 - unsigned int size_of_regd, i, n_wmms = 0; 949 - struct wmm_ptrs *wmm_ptrs; 943 + unsigned int size_of_regd, i; 950 944 951 945 size_of_regd = sizeof(struct ieee80211_regdomain) + 952 946 coll->n_rules * sizeof(struct ieee80211_reg_rule); ··· 935 967 regdom = kzalloc(size_of_regd, GFP_KERNEL); 936 968 if (!regdom) 937 969 return -ENOMEM; 938 - 939 - wmm_ptrs = kcalloc(coll->n_rules, sizeof(*wmm_ptrs), GFP_KERNEL); 940 - if (!wmm_ptrs) { 941 - kfree(regdom); 942 - return -ENOMEM; 943 - } 944 970 945 971 regdom->n_reg_rules = coll->n_rules; 946 972 regdom->alpha2[0] = country->alpha2[0]; ··· 974 1012 1000 * be16_to_cpu(rule->cac_timeout); 975 1013 if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) { 976 1014 u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2; 977 - struct ieee80211_wmm_rule *wmm_pos = 978 - find_wmm_ptr(wmm_ptrs, wmm_ptr, n_wmms); 979 - struct fwdb_wmm_rule *wmm; 980 - struct ieee80211_wmm_rule *wmm_rule; 1015 + struct fwdb_wmm_rule *wmm = (void *)((u8 *)db + wmm_ptr); 981 1016 982 - if (wmm_pos) { 983 - rrule->wmm_rule = wmm_pos; 984 - continue; 985 - } 986 - wmm = (void *)((u8 *)db + wmm_ptr); 987 - tmp_rd = krealloc(regdom, size_of_regd + (n_wmms + 1) * 988 - sizeof(struct ieee80211_wmm_rule), 989 - GFP_KERNEL); 990 - 991 - if (!tmp_rd) { 992 - kfree(regdom); 993 - kfree(wmm_ptrs); 994 - return -ENOMEM; 995 - } 996 - regdom = tmp_rd; 997 - 998 - wmm_rule = (struct ieee80211_wmm_rule *) 999 - ((u8 *)regdom + size_of_regd + n_wmms * 1000 - sizeof(struct ieee80211_wmm_rule)); 1001 - 1002 - set_wmm_rule(wmm_rule, wmm); 1003 - wmm_ptrs[n_wmms].ptr = wmm_ptr; 1004 - wmm_ptrs[n_wmms++].rule = wmm_rule; 1017 + set_wmm_rule(rrule, wmm); 1005 1018 } 1006 1019 } 1007 - kfree(wmm_ptrs); 1008 1020 1009 1021 return reg_schedule_apply(regdom); 1010 1022 }