net: wireless: bcmdhd: Fix CFG80211 memory corruption
Lin Ma [Fri, 5 Aug 2011 19:53:04 +0000 (12:53 -0700)]
 * 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
drivers/net/wireless/bcmdhd/wl_android.c
drivers/net/wireless/bcmdhd/wl_cfg80211.c
drivers/net/wireless/bcmdhd/wl_cfg80211.h

index 5d35f78..0651c25 100644 (file)
@@ -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
index e054d7b..d6471d9 100644 (file)
@@ -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);
index 366ce22..4b7d104 100644 (file)
@@ -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;
 }
 
index 838003b..5b89401 100644 (file)
@@ -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 */