| From 0f3b07f027f87a38ebe5c436490095df762819be Mon Sep 17 00:00:00 2001 |
| From: Johannes Berg <johannes.berg@intel.com> |
| Date: Thu, 7 Feb 2019 21:44:41 +0100 |
| Subject: cfg80211: add and use strongly typed element iteration macros |
| |
| From: Johannes Berg <johannes.berg@intel.com> |
| |
| commit 0f3b07f027f87a38ebe5c436490095df762819be upstream. |
| |
| Rather than always iterating elements from frames with pure |
| u8 pointers, add a type "struct element" that encapsulates |
| the id/datalen/data format of them. |
| |
| Then, add the element iteration macros |
| * for_each_element |
| * for_each_element_id |
| * for_each_element_extid |
| |
| which take, as their first 'argument', such a structure and |
| iterate through a given u8 array interpreting it as elements. |
| |
| While at it and since we'll need it, also add |
| * for_each_subelement |
| * for_each_subelement_id |
| * for_each_subelement_extid |
| |
| which instead of taking data/length just take an outer element |
| and use its data/datalen. |
| |
| Also add for_each_element_completed() to determine if any of |
| the loops above completed, i.e. it was able to parse all of |
| the elements successfully and no data remained. |
| |
| Use for_each_element_id() in cfg80211_find_ie_match() as the |
| first user of this. |
| |
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| include/linux/ieee80211.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++ |
| net/wireless/scan.c | 14 +++++------- |
| 2 files changed, 59 insertions(+), 8 deletions(-) |
| |
| --- a/include/linux/ieee80211.h |
| +++ b/include/linux/ieee80211.h |
| @@ -2630,4 +2630,57 @@ static inline bool ieee80211_action_cont |
| return true; |
| } |
| |
| +struct element { |
| + u8 id; |
| + u8 datalen; |
| + u8 data[]; |
| +}; |
| + |
| +/* element iteration helpers */ |
| +#define for_each_element(element, _data, _datalen) \ |
| + for (element = (void *)(_data); \ |
| + (u8 *)(_data) + (_datalen) - (u8 *)element >= \ |
| + sizeof(*element) && \ |
| + (u8 *)(_data) + (_datalen) - (u8 *)element >= \ |
| + sizeof(*element) + element->datalen; \ |
| + element = (void *)(element->data + element->datalen)) |
| + |
| +#define for_each_element_id(element, _id, data, datalen) \ |
| + for_each_element(element, data, datalen) \ |
| + if (element->id == (_id)) |
| + |
| +#define for_each_element_extid(element, extid, data, datalen) \ |
| + for_each_element(element, data, datalen) \ |
| + if (element->id == WLAN_EID_EXTENSION && \ |
| + element->datalen > 0 && \ |
| + element->data[0] == (extid)) |
| + |
| +#define for_each_subelement(sub, element) \ |
| + for_each_element(sub, (element)->data, (element)->datalen) |
| + |
| +#define for_each_subelement_id(sub, id, element) \ |
| + for_each_element_id(sub, id, (element)->data, (element)->datalen) |
| + |
| +#define for_each_subelement_extid(sub, extid, element) \ |
| + for_each_element_extid(sub, extid, (element)->data, (element)->datalen) |
| + |
| +/** |
| + * for_each_element_completed - determine if element parsing consumed all data |
| + * @element: element pointer after for_each_element() or friends |
| + * @data: same data pointer as passed to for_each_element() or friends |
| + * @datalen: same data length as passed to for_each_element() or friends |
| + * |
| + * This function returns %true if all the data was parsed or considered |
| + * while walking the elements. Only use this if your for_each_element() |
| + * loop cannot be broken out of, otherwise it always returns %false. |
| + * |
| + * If some data was malformed, this returns %false since the last parsed |
| + * element will not fill the whole remaining data. |
| + */ |
| +static inline bool for_each_element_completed(const struct element *element, |
| + const void *data, size_t datalen) |
| +{ |
| + return (u8 *)element == (u8 *)data + datalen; |
| +} |
| + |
| #endif /* LINUX_IEEE80211_H */ |
| --- a/net/wireless/scan.c |
| +++ b/net/wireless/scan.c |
| @@ -407,6 +407,8 @@ const u8 *cfg80211_find_ie_match(u8 eid, |
| const u8 *match, int match_len, |
| int match_offset) |
| { |
| + const struct element *elem; |
| + |
| /* match_offset can't be smaller than 2, unless match_len is |
| * zero, in which case match_offset must be zero as well. |
| */ |
| @@ -414,14 +416,10 @@ const u8 *cfg80211_find_ie_match(u8 eid, |
| (!match_len && match_offset))) |
| return NULL; |
| |
| - while (len >= 2 && len >= ies[1] + 2) { |
| - if ((ies[0] == eid) && |
| - (ies[1] + 2 >= match_offset + match_len) && |
| - !memcmp(ies + match_offset, match, match_len)) |
| - return ies; |
| - |
| - len -= ies[1] + 2; |
| - ies += ies[1] + 2; |
| + for_each_element_id(elem, eid, ies, len) { |
| + if (elem->datalen >= match_offset - 2 + match_len && |
| + !memcmp(elem->data + match_offset - 2, match, match_len)) |
| + return (void *)elem; |
| } |
| |
| return NULL; |