From: Takashi Iwai <tiwai@suse.de>
Subject: kABI workaround for cfg80211 changes
Patch-mainline: Never, kABI workaround
References: CVE-2020-24586 bsc#1185859

Some ugly kABI workaround for the recently introduced wireless core
stack changes:
- The modified ieee80211_data_to_8023_exthdr() with an additional
  argument is provided as __ieee80211_data_to_8023_exthdr().
  A compat wrapper is defined.
- The ieee80211_fragment_cache field in struct sta_info is factored
  out into struct __sta_info.  sta_info is embedded there.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 include/net/cfg80211.h  |    7 +++++++
 net/mac80211/rx.c       |    2 +-
 net/mac80211/sta_info.c |   14 +++++++++-----
 net/mac80211/sta_info.h |   11 +++++++++--
 net/wireless/util.c     |   11 ++++++++++-
 5 files changed, 36 insertions(+), 9 deletions(-)

--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4599,9 +4599,17 @@ unsigned int ieee80211_get_mesh_hdrlen(s
  * @data_offset: offset of payload after the 802.11 header
  * Return: 0 on success. Non-zero on error.
  */
+#ifdef __GENKSYMS__
 int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
 				  const u8 *addr, enum nl80211_iftype iftype,
+				  u8 data_offset);
+#else
+/* new version */
+int __ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+				  const u8 *addr, enum nl80211_iftype iftype,
 				  u8 data_offset, bool is_amsdu);
+#define ieee80211_data_to_8023_exthdr __ieee80211_data_to_8023_exthdr
+#endif
 
 /**
  * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2025,7 +2025,7 @@ ieee80211_rx_h_defragment(struct ieee802
 	}
 
 	if (rx->sta)
-		cache = &rx->sta->frags;
+		cache = &sta_frags(rx->sta);
 
 	if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
 		goto out;
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -245,6 +245,8 @@ struct sta_info *sta_info_get_by_idx(str
  */
 void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 {
+	struct __sta_info *__sta = container_of(sta, struct __sta_info, sta);
+
 	/*
 	 * If we had used sta_info_pre_move_state() then we might not
 	 * have gone through the state transitions down again, so do
@@ -255,7 +257,7 @@ void sta_info_free(struct ieee80211_loca
 	kfree(sta->mesh);
 #endif
 	free_percpu(sta->pcpu_rx_stats);
-	kfree(sta);
+	kfree(__sta);
 }
 
 /* Caller must hold local->sta_mtx */
@@ -306,11 +308,13 @@ struct sta_info *sta_info_alloc(struct i
 	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_hw *hw = &local->hw;
 	struct sta_info *sta;
+	struct __sta_info *__sta;
 	int i;
 
-	sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
-	if (!sta)
+	__sta = kzalloc(sizeof(*__sta) + hw->sta_data_size, gfp);
+	if (!__sta)
 		return NULL;
+	sta = &__sta->sta;
 
 	if (ieee80211_hw_check(hw, USES_RSS)) {
 		sta->pcpu_rx_stats =
@@ -348,7 +352,7 @@ struct sta_info *sta_info_alloc(struct i
 
 	u64_stats_init(&sta->rx_stats.syncp);
 
-	ieee80211_init_frag_cache(&sta->frags);
+	ieee80211_init_frag_cache(&__sta->frags);
 
 	sta->sta_state = IEEE80211_STA_NONE;
 
@@ -984,7 +988,7 @@ static void __sta_info_destroy_part2(str
 	rate_control_remove_sta_debugfs(sta);
 	ieee80211_sta_debugfs_remove(sta);
 
-	ieee80211_destroy_frag_cache(&sta->frags);
+	ieee80211_destroy_frag_cache(&sta_frags(sta));
 
 	cleanup_single_sta(sta);
 }
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -585,12 +585,19 @@ struct sta_info {
 
 	struct cfg80211_chan_def tdls_chandef;
 
-	struct ieee80211_fragment_cache frags;
-
 	/* keep last! */
 	struct ieee80211_sta sta;
 };
 
+/* XXX frags is factoroed out for SLE kABI compatibility */
+struct __sta_info {
+	struct ieee80211_fragment_cache frags;
+	void *reserved;
+	struct sta_info sta;
+};
+
+#define sta_frags(s) container_of(s, struct __sta_info, sta)->frags
+
 static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
 {
 #ifdef CONFIG_MAC80211_MESH
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -419,9 +419,9 @@ unsigned int ieee80211_get_mesh_hdrlen(s
 }
 EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
 
-int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
-				  const u8 *addr, enum nl80211_iftype iftype,
-				  u8 data_offset, bool is_amsdu)
+int __ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+				    const u8 *addr, enum nl80211_iftype iftype,
+				    u8 data_offset, bool is_amsdu)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct {
@@ -527,6 +527,17 @@ int ieee80211_data_to_8023_exthdr(struct
 
 	return 0;
 }
+EXPORT_SYMBOL(__ieee80211_data_to_8023_exthdr);
+
+// XXX for SLE kABI compatibility
+#undef ieee80211_data_to_8023_exthdr
+
+int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr,
+				    const u8 *addr, enum nl80211_iftype iftype,
+				    u8 data_offset)
+{
+	return __ieee80211_data_to_8023_exthdr(skb, ehdr, addr, iftype, data_offset, false);
+}
 EXPORT_SYMBOL(ieee80211_data_to_8023_exthdr);
 
 static void
