summaryrefslogtreecommitdiff
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorLin Ma <linm@broadcom.com>2011-08-05 12:53:04 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:38:58 -0800
commit0930f16afe839b2f29e234f29620ba3cac0cfddf (patch)
tree3d0944e9ed798881e330216d5c0a7f1d61f36fbf /drivers/net/wireless
parentb0c2d13debf0beab4fa35ae6e80f86aaa0097e08 (diff)
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>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_linux.c33
-rw-r--r--drivers/net/wireless/bcmdhd/wl_android.c2
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfg80211.c49
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfg80211.h3
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 5d35f78cf2a8..0651c256e3c9 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 e054d7b38867..d6471d991065 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 366ce2224ab6..4b7d1047c71c 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 838003bd644a..5b894019f237 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 */