mwifiex: dump station can panic if device is reset

cfg80211_dump_station semantics are such that the entry point is invoked
repeatedly for each station in the list, and the driver returns ENOENT
once all the station entries have been visited. This forces the entry
point to maintain state, which is currently implemented with a static
local variable.

If the driver is reset while in the middle of dumping stations, a stale
pointer will be referenced the next time dump_station is called, resulting
in an oops with a misleading stack:

	Call Trace:
	[<e063090c>] wifiex_del_virtual_intf+0x403c/0x44ac [mwifiex]
	[<80426d00>] serial8250_handle_irq.part.21+0x100/0x124

And rarely, a more accurate oops:

	Call Trace:
	[<e062d1c0>] mwifiex_cfg80211_dump_station+0xc0/0x130 [mwifiex]
	[<e050e130>] nl80211_dump_station+0xe0/0x310 [cfg80211]
	[<80640ac4>] genl_lock_dumpit+0x4c/0x74
	[<8063d2dc>] netlink_dump+0x148/0x2b8
	[<8063da2c>] __netlink_dump_start+0x1e4/0x260
	[<80641804>] genl_rcv_msg+0x354/0x3e8
	[<80640634>] netlink_rcv_skb+0x110/0x138
	[<80641498>] genl_rcv+0x40/0x58
	[<8063fb70>] netlink_unicast+0x1fc/0x34c
	[<80640270>] netlink_sendmsg+0x430/0x4f4
	[<805eb244>] ___sys_sendmsg+0x11c/0x254
	[<805ec32c>] __sys_sendmsg+0x58/0xa0
	[<80016e54>] syscall_common+0x30/0x54

The fix moves the state variable into mwifiex_private.

See b/27275899

Change-Id: I9f944b63581567603d7f9d8d51a9620ee18ee186
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 85b5df0..4431836 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -1388,7 +1388,7 @@
 			      int idx, u8 *mac, struct station_info *sinfo)
 {
 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
-	static struct mwifiex_sta_node *node;
+	struct mwifiex_sta_node **node_p = &priv->dump_station_node;
 
 	if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
 	    priv->media_connected && idx == 0) {
@@ -1398,15 +1398,15 @@
 		mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST,
 				 HostCmd_ACT_GEN_GET, 0, NULL, true);
 
-		if (node && (&node->list == &priv->sta_list)) {
-			node = NULL;
+		if (*node_p && (&(*node_p)->list == &priv->sta_list)) {
+			*node_p = NULL;
 			return -ENOENT;
 		}
 
-		node = list_prepare_entry(node, &priv->sta_list, list);
-		list_for_each_entry_continue(node, &priv->sta_list, list) {
-			ether_addr_copy(mac, node->mac_addr);
-			return mwifiex_dump_station_info(priv, node, sinfo);
+		*node_p = list_prepare_entry((*node_p), &priv->sta_list, list);
+		list_for_each_entry_continue((*node_p), &priv->sta_list, list) {
+			ether_addr_copy(mac, (*node_p)->mac_addr);
+			return mwifiex_dump_station_info(priv, *node_p, sinfo);
 		}
 	}
 
diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
index 2f7f478..6d719fe 100644
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -578,6 +578,7 @@
 	struct mwifiex_wmm_desc wmm;
 	atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
 	struct list_head sta_list;
+	struct mwifiex_sta_node *dump_station_node;
 	/* spin lock for associated station/TDLS peers list */
 	spinlock_t sta_list_spinlock;
 	struct list_head auto_tdls_list;