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