Sending packets to master interface directly causes dereferencing of pointer
containing random data. The same problem happens when originating virtual
interface is removed while a packet is queued.

We really shouldn't store pointer to ieee80211_sub_if_data in skb->cb. Store
ifindex there instead.

Also, there is no need for internal ieee80211_tx_packet_data structure to be
declared in d80211.h. As this patch touches this structure anyway, let's
move it to ieee80211_i.h.

Signed-off-by: Jiri Benc <[EMAIL PROTECTED]>

---

 include/net/d80211.h       |   11 -----
 net/d80211/ieee80211.c     |   89 +++++++++++++++++++++++++++++++++++---------
 net/d80211/ieee80211_i.h   |   12 ++++++
 net/d80211/ieee80211_sta.c |    3 +
 net/d80211/wme.c           |    2 -
 5 files changed, 85 insertions(+), 32 deletions(-)

8d3a2dea3e4cefad23f3dad3e2eeeb5bdcff3dbb
diff --git a/include/net/d80211.h b/include/net/d80211.h
index 23bcbfa..e44e21c 100644
--- a/include/net/d80211.h
+++ b/include/net/d80211.h
@@ -188,17 +188,6 @@ struct ieee80211_tx_control {
        struct ieee80211_sub_if_data *sdata; /* internal */
 };
 
-/* Stored in sk_buff->cb */
-struct ieee80211_tx_packet_data {
-        struct ieee80211_sub_if_data *sdata;
-       unsigned long jiffies;
-       unsigned int req_tx_status:1;
-       unsigned int do_not_encrypt:1;
-       unsigned int pkt_probe_resp:1;
-       unsigned int requeue:1;
-       unsigned int queue:4;
-};
-
 #define RX_FLAG_MMIC_ERROR       0x1
 #define RX_FLAG_DECRYPTED        0x2
 #define RX_FLAG_XR_DOUBLE_CHIRP  0x4
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index eb701ac..f768de5 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -1051,23 +1051,21 @@ ieee80211_tx_h_ps_buf(struct ieee80211_t
 }
 
 
-static void inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
-                                       struct sk_buff *skb,
-                                       struct net_device *dev,
-                                       struct ieee80211_tx_control *control)
+static void inline
+__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+                      struct sk_buff *skb,
+                      struct net_device *dev,
+                      struct ieee80211_tx_control *control)
 {
        struct ieee80211_local *local = dev->priv;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-        struct ieee80211_tx_packet_data *pkt_data;
        int hdrlen;
 
-        pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
-       tx->dev = pkt_data->sdata->dev; /* use original interface */
+       tx->dev = dev; /* use original interface */
        tx->local = local;
-        tx->sdata = pkt_data->sdata;
+       tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        tx->sta = sta_info_get(local, hdr->addr1);
        tx->fc = le16_to_cpu(hdr->frame_control);
         control->power_level = local->conf.power_level;
@@ -1095,6 +1093,37 @@ static void inline ieee80211_tx_prepare(
 
 }
 
+/* FIXME: This is not nice but currently there doesn't exist more elegant way 
*/
+static int inline is_ieee80211_device(struct net_device *dev)
+{
+       return (dev->wireless_handlers ==
+               (struct iw_handler_def *) &ieee80211_iw_handler_def);
+}
+
+/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
+ * finished with it. */
+static void inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+                                       struct sk_buff *skb,
+                                       struct net_device *mdev,
+                                       struct ieee80211_tx_control *control)
+{
+       struct ieee80211_tx_packet_data *pkt_data;
+       struct net_device *dev;
+
+       pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+       dev = dev_get_by_index(pkt_data->ifindex);
+       if (unlikely(dev && !is_ieee80211_device(dev))) {
+               dev_put(dev);
+               dev = NULL;
+       }
+       if (unlikely(!dev)) {
+               printk(KERN_WARNING "%s: NULL ifindex in pkt_data\n",
+                      mdev->name);
+               dev = mdev;
+               dev_hold(dev);
+       }
+       __ieee80211_tx_prepare(tx, skb, dev, control);
+}
 
 static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
                        struct ieee80211_tx_control *control, int mgmt)
@@ -1111,7 +1140,7 @@ static int ieee80211_tx(struct net_devic
                return 0;
        }
 
-       ieee80211_tx_prepare(&tx, skb, dev, control);
+       __ieee80211_tx_prepare(&tx, skb, dev, control);
        sta = tx.sta;
        tx.u.tx.mgmt_interface = mgmt;
 
@@ -1136,8 +1165,8 @@ static int ieee80211_tx(struct net_devic
                return 0;
        }
 
-       ieee80211_dump_frame(dev->name, "TX to low-level driver", skb);
-       ret = local->hw->tx(dev, skb, control);
+       ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb);
+       ret = local->hw->tx(local->mdev, skb, control);
 #ifdef IEEE80211_LEDS
        if (!ret && local->tx_led_counter++ == 0) {
                 ieee80211_tx_led(1, dev);
@@ -1177,9 +1206,9 @@ #endif /* IEEE80211_LEDS */
                        dur = ieee80211_duration(&tx, 0, next_len);
                        hdr->duration_id = cpu_to_le16(dur);
 
-                       ieee80211_dump_frame(dev->name,
+                       ieee80211_dump_frame(local->mdev->name,
                                             "TX to low-level driver", skb);
-                       ret = local->hw->tx(dev, tx.u.tx.extra_frag[i],
+                       ret = local->hw->tx(local->mdev, tx.u.tx.extra_frag[i],
                                            control);
                        if (ret > 0)
                                goto drop;
@@ -1212,6 +1241,7 @@ static int ieee80211_master_start_xmit(s
 {
        struct ieee80211_tx_control control;
        struct ieee80211_tx_packet_data *pkt_data;
+       struct net_device *odev = NULL;
        struct ieee80211_sub_if_data *sdata;
        int ret;
 
@@ -1222,7 +1252,23 @@ static int ieee80211_master_start_xmit(s
         */
        pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
        memset(&control, 0, sizeof(struct ieee80211_tx_control));
-       control.sdata = pkt_data->sdata;
+
+       if (pkt_data->ifindex)
+               odev = dev_get_by_index(pkt_data->ifindex);
+       if (unlikely(odev && !is_ieee80211_device(odev))) {
+               dev_put(odev);
+               odev = NULL;
+       }
+       if (unlikely(!odev)) {
+#ifdef CONFIG_D80211_VERBOSE_DEBUG
+               printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
+                      "originating device\n", dev->name);
+#endif
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       control.sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        control.req_tx_status = pkt_data->req_tx_status;
        control.do_not_encrypt = pkt_data->do_not_encrypt;
        control.pkt_type =
@@ -1230,8 +1276,9 @@ static int ieee80211_master_start_xmit(s
        control.requeue = pkt_data->requeue;
        control.queue = pkt_data->queue;
 
-       ret = ieee80211_tx(dev, skb, &control,
+       ret = ieee80211_tx(odev, skb, &control,
                           control.sdata->type == IEEE80211_IF_TYPE_MGMT);
+       dev_put(odev);
 
         return ret;
 }
@@ -1401,7 +1448,8 @@ #endif
 
        pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
        memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-       pkt_data->sdata = sdata;
+       pkt_data->ifindex = sdata->dev->ifindex;
+       pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
        pkt_data->do_not_encrypt = no_encrypt;
 
        skb->dev = sdata->master;
@@ -1452,7 +1500,8 @@ ieee80211_mgmt_start_xmit(struct sk_buff
 
        pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
        memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-        pkt_data->sdata = sdata;
+        pkt_data->ifindex = sdata->dev->ifindex;
+       pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
 
        if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
            WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP)
@@ -1680,6 +1729,7 @@ ieee80211_get_buffered_bc(struct net_dev
                if (res == TXRX_DROP || res == TXRX_QUEUED)
                        break;
        }
+       dev_put(tx.dev);
 
        if (res == TXRX_DROP) {
                I802_DEBUG_INC(local->tx_handlers_drop);
@@ -3644,7 +3694,8 @@ static void ieee80211_remove_tx_extra(st
        struct ieee80211_tx_packet_data *pkt_data;
 
        pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-       pkt_data->sdata = control->sdata;
+       pkt_data->ifindex = control->sdata->dev->ifindex;
+       pkt_data->mgmt_iface = (control->sdata->type == IEEE80211_IF_TYPE_MGMT);
        pkt_data->req_tx_status = control->req_tx_status;
        pkt_data->do_not_encrypt = control->do_not_encrypt;
        pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP);
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index 400c675..b29a5e8 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -143,6 +143,18 @@ #ifdef CONFIG_HOSTAPD_WPA_TESTING
 #endif /* CONFIG_HOSTAPD_WPA_TESTING */
 };
 
+/* Stored in sk_buff->cb */
+struct ieee80211_tx_packet_data {
+       int ifindex;
+       unsigned long jiffies;
+       unsigned int req_tx_status:1;
+       unsigned int do_not_encrypt:1;
+       unsigned int pkt_probe_resp:1;
+       unsigned int requeue:1;
+       unsigned int queue:4;
+       unsigned int mgmt_iface:1;
+};
+
 struct ieee80211_passive_scan {
         unsigned int in_scan:1; /* this must be cleared before calling
                                 * netif_oper(WAKEUP) */
diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index d9c3d67..2720f1d 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -399,7 +399,8 @@ static void ieee80211_sta_tx(struct net_
 
        pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
        memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
-       pkt_data->sdata = sdata;
+       pkt_data->ifindex = sdata->dev->ifindex;
+       pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
        pkt_data->do_not_encrypt = !encrypt;
        if (probe_resp)
                pkt_data->pkt_probe_resp = 1;
diff --git a/net/d80211/wme.c b/net/d80211/wme.c
index 51a197a..f20d3e0 100644
--- a/net/d80211/wme.c
+++ b/net/d80211/wme.c
@@ -190,7 +190,7 @@ static inline int classify80211(struct s
                return IEEE80211_TX_QUEUE_DATA0;
        }
 
-       if (unlikely(pkt_data->sdata->type == IEEE80211_IF_TYPE_MGMT)) {
+       if (unlikely(pkt_data->mgmt_iface)) {
                /* Data frames from hostapd (mainly, EAPOL) use AC_VO
                * and they will include QoS control fields if
                * the target STA is using WME. */
-- 
1.3.0

-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to