From b7a82d40f1d9dc7a69281fc3b2a42a489f2a1619 Mon Sep 17 00:00:00 2001
From: Lin Ma <linm@broadcom.com>
Date: Fri, 5 Aug 2011 12:53:04 -0700
Subject: [PATCH 635/696] net: wireless: bcmdhd: Fix CFG80211 memory corruption

 * Sends event/data packets to kernel while net_device interface
   has not been created or registered yet
 * Timer gets freed twice
 * The primary net_device interface never gets freed
 * Memory corruption in scan buffer
 * Memory corruption in cfg80211 wiphy structure fix for kthred_stop crash

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
---
 drivers/net/wireless/bcmdhd/dhd_linux.c   |   33 ++++++++++++-------
 drivers/net/wireless/bcmdhd/wl_android.c  |    2 +-
 drivers/net/wireless/bcmdhd/wl_cfg80211.c |   49 +++++++++++++++--------------
 drivers/net/wireless/bcmdhd/wl_cfg80211.h |    3 +-
 4 files changed, 48 insertions(+), 39 deletions(-)

diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c
index 5d35f78..0651c25 100644
--- a/drivers/net/wireless/bcmdhd/dhd_linux.c
+++ b/drivers/net/wireless/bcmdhd/dhd_linux.c
@@ -1385,7 +1385,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
 	struct sk_buff *skb;
 	uchar *eth;
 	uint len;
-	void *data, *pnext, *save_pktbuf;
+	void *data, *pnext = NULL, *save_pktbuf;
 	int i;
 	dhd_if_t *ifp;
 	wl_event_msg_t event;
@@ -1398,6 +1398,16 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
 		struct ether_header *eh;
 		struct dot11_llc_snap_header *lsh;
 
+		ifp = dhd->iflist[ifidx];
+
+		/* Dropping packets before registering net device to avoid kernel panic */
+		if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) {
+			DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n",
+			__FUNCTION__));
+			PKTFREE(dhdp->osh, pktbuf, TRUE);
+			continue;
+		}
+
 		pnext = PKTNEXT(dhdp->osh, pktbuf);
 		PKTSETNEXT(wl->sh.osh, pktbuf, NULL);
 
@@ -1487,14 +1497,6 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
 		dhdp->dstats.rx_bytes += skb->len;
 		dhdp->rx_packets++; /* Local count */
 
-		/* Dropping packets before registering net device to avoid kernel panic */
-		if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) {
-			DHD_ERROR(("%s: net device is NOT registered yet. drop [%s] packet\n",
-			__FUNCTION__, (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) ? "event" : "data"));
-			PKTFREE(dhdp->osh, pktbuf, TRUE);
-			continue;
-		}
-
 		if (in_interrupt()) {
 			netif_rx(skb);
 		} else {
@@ -3057,6 +3059,7 @@ void dhd_detach(dhd_pub_t *dhdp)
 {
 	dhd_info_t *dhd;
 	unsigned long flags;
+	int timer_valid = FALSE;
 
 	if (!dhdp)
 		return;
@@ -3118,17 +3121,23 @@ void dhd_detach(dhd_pub_t *dhdp)
 		if (ifp->net->netdev_ops == &dhd_ops_pri)
 #endif
 		{
-			unregister_netdev(ifp->net);
+			if (ifp->net) {
+				unregister_netdev(ifp->net);
+				free_netdev(ifp->net);
+				ifp->net = NULL;
+			}
 			MFREE(dhd->pub.osh, ifp, sizeof(*ifp));
-
+			dhd->iflist[0] = NULL;
 		}
 	}
 
 	/* Clear the watchdog timer */
 	flags = dhd_os_spin_lock(&dhd->pub);
+	timer_valid = dhd->wd_timer_valid;
 	dhd->wd_timer_valid = FALSE;
 	dhd_os_spin_unlock(&dhd->pub, flags);
-	del_timer_sync(&dhd->timer);
+	if (timer_valid)
+		del_timer_sync(&dhd->timer);
 
 	if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) {
 #ifdef DHDTHREAD
diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c
index e054d7b..d6471d9 100644
--- a/drivers/net/wireless/bcmdhd/wl_android.c
+++ b/drivers/net/wireless/bcmdhd/wl_android.c
@@ -338,10 +338,10 @@ int wl_android_wifi_off(struct net_device *dev)
 	dhd_net_if_lock(dev);
 	if (g_wifi_on) {
 		dhd_dev_reset(dev, 1);
-		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
 		sdioh_stop(NULL);
 		/* clean up dtim_skip setting */
 		net_os_set_dtim_skip(dev, TRUE);
+		dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF);
 		g_wifi_on = 0;
 	}
 	dhd_net_if_unlock(dev);
diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c
index 366ce22..4b7d104 100644
--- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c
+++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c
@@ -3913,9 +3913,9 @@ static void wl_free_wdev(struct wl_priv *wl)
 		}
 	}
 	wiphy_unregister(wdev->wiphy);
+	wdev->wiphy->dev.parent = NULL;
 	wiphy_free(wdev->wiphy);
 	kfree(wdev);
-	wl_to_wdev(wl) = NULL;
 }
 
 static s32 wl_inform_bss(struct wl_priv *wl)
@@ -4740,7 +4740,7 @@ static s32 wl_init_priv_mem(struct wl_priv *wl)
 		WL_ERR(("Ioctl buf alloc failed\n"));
 		goto init_priv_mem_out;
 	}
-	wl->escan_ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL);
+	wl->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
 	if (unlikely(!wl->escan_ioctl_buf)) {
 		WL_ERR(("Ioctl buf alloc failed\n"));
 		goto init_priv_mem_out;
@@ -4809,24 +4809,20 @@ static void wl_deinit_priv_mem(struct wl_priv *wl)
 
 static s32 wl_create_event_handler(struct wl_priv *wl)
 {
+	int ret = 0;
 	WL_DBG(("Enter \n"));
-	sema_init(&wl->event_sync, 0);
-	wl->event_tsk = kthread_run(wl_event_handler, wl, "wl_event_handler");
-	if (IS_ERR(wl->event_tsk)) {
-		wl->event_tsk = NULL;
-		WL_ERR(("failed to create event thread\n"));
-		return -ENOMEM;
-	}
-	return 0;
+
+	wl->event_tsk.thr_pid = DHD_PID_KT_INVALID;
+	PROC_START(wl_event_handler, wl, &wl->event_tsk, 0);
+	if (wl->event_tsk.thr_pid < 0)
+		ret = -ENOMEM;
+	return ret;
 }
 
 static void wl_destroy_event_handler(struct wl_priv *wl)
 {
-	if (wl->event_tsk) {
-		send_sig(SIGTERM, wl->event_tsk, 1);
-		kthread_stop(wl->event_tsk);
-		wl->event_tsk = NULL;
-	}
+	if (wl->event_tsk.thr_pid >= 0)
+		PROC_STOP(&wl->event_tsk);
 }
 
 static void wl_term_iscan(struct wl_priv *wl)
@@ -5367,29 +5363,32 @@ void wl_cfg80211_detach(void)
 	if (wl->p2p_supported)
 		wl_cfgp2p_deinit_priv(wl);
 	wl_deinit_priv(wl);
-	wl_free_wdev(wl);
 	wl_set_drvdata(wl_cfg80211_dev, NULL);
 	kfree(wl_cfg80211_dev);
 	wl_cfg80211_dev = NULL;
 	wl_clear_sdio_func();
+	wl_free_wdev(wl);
 }
 
 static void wl_wakeup_event(struct wl_priv *wl)
 {
-	up(&wl->event_sync);
+	if (wl->event_tsk.thr_pid >= 0)
+		up(&wl->event_tsk.sema);
 }
 
 static s32 wl_event_handler(void *data)
 {
 	struct net_device *netdev;
-	struct wl_priv *wl = (struct wl_priv *)data;
-	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 };
+	struct wl_priv *wl = NULL;
 	struct wl_event_q *e;
+	tsk_ctl_t *tsk = (tsk_ctl_t *)data;
 
-	sched_setscheduler(current, SCHED_FIFO, &param);
-	allow_signal(SIGTERM);
-	while (likely(!down_interruptible(&wl->event_sync))) {
-		if (kthread_should_stop())
+	wl = (struct wl_priv *)tsk->parent;
+	complete(&tsk->completed);
+
+	while (down_interruptible (&tsk->sema) == 0) {
+		SMP_RD_BARRIER_DEPENDS();
+		if (tsk->terminated)
 			break;
 		e = wl_deq_event(wl);
 		if (unlikely(!e)) {
@@ -5400,14 +5399,16 @@ static s32 wl_event_handler(void *data)
 		netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx);
 		if (!netdev)
 			netdev = wl_to_prmry_ndev(wl);
-		if (wl->evt_handler[e->etype]) {
+		if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) {
 			wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata);
 		} else {
 			WL_DBG(("Unknown Event (%d): ignoring\n", e->etype));
 		}
 		wl_put_event(e);
 	}
+
 	WL_DBG(("%s was terminated\n", __func__));
+	complete_and_exit(&tsk->completed, 0);
 	return 0;
 }
 
diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h
index 838003b..5b89401 100644
--- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h
+++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h
@@ -347,7 +347,6 @@ struct wl_priv {
 	struct ether_addr bssid;	/* bssid of currently engaged network */
 
 	/* for synchronization of main event thread */
-	struct semaphore event_sync;
 	struct wl_profile *profile;	/* holding dongle profile */
 	struct wl_iscan_ctrl *iscan;	/* iscan controller */
 
@@ -357,7 +356,7 @@ struct wl_priv {
 	/* control firwmare and nvram paramter downloading */
 	struct wl_fw_ctrl *fw;
 	struct wl_pmk_list *pmk_list;	/* wpa2 pmk list */
-	struct task_struct *event_tsk;	/* task of main event handler thread */
+	tsk_ctl_t event_tsk;		/* task of main event handler thread */
 	unsigned long status;		/* current dongle status */
 	void *pub;
 	u32 channel;		/* current channel */
-- 
1.7.1


