cfg80211: add and use strongly typed element iteration macros
authorJohannes Berg <johannes.berg@intel.com>
Thu, 7 Feb 2019 20:44:41 +0000 (21:44 +0100)
committerchenyt9 <chenyt9@lenovo.com>
Wed, 14 Oct 2020 07:32:47 +0000 (15:32 +0800)
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.

Mot-CRs-fixed: (CR)
CVE-Fixed: CVE-2019-16746
Bug: 145728612

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jignesh Patel <jignesh@motorola.com>
Change-Id: I260ca9826af7434b9b9e6ae3db4461c0d9ec2965
Reviewed-on: https://gerrit.mot.com/1681823
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com>
Submit-Approved: Jira Key
(cherry picked from commit 43b94d892e255e7082834c3b72637d56b9cb78ed)

include/linux/ieee80211.h
net/wireless/scan.c

index 55a604ad459f6e105ac3856f3b298b053365277c..cf41820f279493ae215b36c8cccbabc6768b04d0 100644 (file)
@@ -2743,4 +2743,57 @@ static inline bool ieee80211_action_contains_tpc(struct sk_buff *skb)
        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 */
index 715cc728ae565c4801efb7f65e85d791ae47c488..f143fc13713e7d997c1b1a0b454e7c1bd84c09d3 100644 (file)
@@ -484,6 +484,8 @@ const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
                                 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.
         */
@@ -491,14 +493,10 @@ const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len,
                    (!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;