mac80211: stop using pointers as userspace cookies
Even if the pointers are really only accessible to root and used pretty much only by wpa_supplicant, this is still not great; even for debugging it'd be easier to have something that's easier to read and guaranteed to never get reused. With the recent change to make mac80211 create an ack_skb for the mgmt-tx path this becomes possible, only the client probe method needs to also allocate an ack_skb, and we can store the cookie in that skb. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
b2eb0ee6d0
commit
3b79af973c
|
@ -874,6 +874,9 @@ struct ieee80211_tx_info {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
/* 4 bytes free */
|
/* 4 bytes free */
|
||||||
} control;
|
} control;
|
||||||
|
struct {
|
||||||
|
u64 cookie;
|
||||||
|
} ack;
|
||||||
struct {
|
struct {
|
||||||
struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
|
struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
|
||||||
s32 ack_signal;
|
s32 ack_signal;
|
||||||
|
|
|
@ -2546,6 +2546,19 @@ static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
|
||||||
|
{
|
||||||
|
lockdep_assert_held(&local->mtx);
|
||||||
|
|
||||||
|
local->roc_cookie_counter++;
|
||||||
|
|
||||||
|
/* wow, you wrapped 64 bits ... more likely a bug */
|
||||||
|
if (WARN_ON(local->roc_cookie_counter == 0))
|
||||||
|
local->roc_cookie_counter++;
|
||||||
|
|
||||||
|
return local->roc_cookie_counter;
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_channel *channel,
|
struct ieee80211_channel *channel,
|
||||||
|
@ -2583,7 +2596,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
||||||
roc->req_duration = duration;
|
roc->req_duration = duration;
|
||||||
roc->frame = txskb;
|
roc->frame = txskb;
|
||||||
roc->type = type;
|
roc->type = type;
|
||||||
roc->mgmt_tx_cookie = (unsigned long)txskb;
|
|
||||||
roc->sdata = sdata;
|
roc->sdata = sdata;
|
||||||
INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
|
INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
|
||||||
INIT_LIST_HEAD(&roc->dependents);
|
INIT_LIST_HEAD(&roc->dependents);
|
||||||
|
@ -2593,17 +2605,10 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
|
||||||
* or the SKB (for mgmt TX)
|
* or the SKB (for mgmt TX)
|
||||||
*/
|
*/
|
||||||
if (!txskb) {
|
if (!txskb) {
|
||||||
/* local->mtx protects this */
|
roc->cookie = ieee80211_mgmt_tx_cookie(local);
|
||||||
local->roc_cookie_counter++;
|
|
||||||
roc->cookie = local->roc_cookie_counter;
|
|
||||||
/* wow, you wrapped 64 bits ... more likely a bug */
|
|
||||||
if (WARN_ON(roc->cookie == 0)) {
|
|
||||||
roc->cookie = 1;
|
|
||||||
local->roc_cookie_counter++;
|
|
||||||
}
|
|
||||||
*cookie = roc->cookie;
|
*cookie = roc->cookie;
|
||||||
} else {
|
} else {
|
||||||
*cookie = (unsigned long)txskb;
|
roc->mgmt_tx_cookie = *cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if there's one pending or we're scanning, queue this one */
|
/* if there's one pending or we're scanning, queue this one */
|
||||||
|
@ -3284,6 +3289,36 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
|
||||||
|
struct sk_buff *skb, u64 *cookie,
|
||||||
|
gfp_t gfp)
|
||||||
|
{
|
||||||
|
unsigned long spin_flags;
|
||||||
|
struct sk_buff *ack_skb;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
ack_skb = skb_copy(skb, gfp);
|
||||||
|
if (!ack_skb)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&local->ack_status_lock, spin_flags);
|
||||||
|
id = idr_alloc(&local->ack_status_frames, ack_skb,
|
||||||
|
1, 0x10000, GFP_ATOMIC);
|
||||||
|
spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
|
||||||
|
|
||||||
|
if (id < 0) {
|
||||||
|
kfree_skb(ack_skb);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEEE80211_SKB_CB(skb)->ack_frame_id = id;
|
||||||
|
|
||||||
|
*cookie = ieee80211_mgmt_tx_cookie(local);
|
||||||
|
IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
|
||||||
|
|
||||||
|
return ack_skb;
|
||||||
|
}
|
||||||
|
|
||||||
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||||
struct cfg80211_mgmt_tx_params *params,
|
struct cfg80211_mgmt_tx_params *params,
|
||||||
u64 *cookie)
|
u64 *cookie)
|
||||||
|
@ -3429,40 +3464,22 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||||
skb->dev = sdata->dev;
|
skb->dev = sdata->dev;
|
||||||
|
|
||||||
if (!params->dont_wait_for_ack) {
|
if (!params->dont_wait_for_ack) {
|
||||||
unsigned long spin_flags;
|
/* make a copy to preserve the frame contents
|
||||||
int id;
|
|
||||||
|
|
||||||
/* make a copy to preserve the original cookie (in case the
|
|
||||||
* driver decides to reallocate the skb) and the frame contents
|
|
||||||
* in case of encryption.
|
* in case of encryption.
|
||||||
*/
|
*/
|
||||||
ack_skb = skb_copy(skb, GFP_KERNEL);
|
ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
|
||||||
if (!ack_skb) {
|
GFP_KERNEL);
|
||||||
ret = -ENOMEM;
|
if (IS_ERR(ack_skb)) {
|
||||||
|
ret = PTR_ERR(ack_skb);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&local->ack_status_lock, spin_flags);
|
|
||||||
id = idr_alloc(&local->ack_status_frames, ack_skb,
|
|
||||||
1, 0x10000, GFP_ATOMIC);
|
|
||||||
spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
|
|
||||||
|
|
||||||
if (id < 0) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
kfree_skb(ack_skb);
|
|
||||||
kfree_skb(skb);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEEE80211_SKB_CB(skb)->ack_frame_id = id;
|
|
||||||
} else {
|
} else {
|
||||||
/* for cookie below */
|
/* for cookie below */
|
||||||
ack_skb = skb;
|
ack_skb = skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!need_offchan) {
|
if (!need_offchan) {
|
||||||
*cookie = (unsigned long)ack_skb;
|
|
||||||
ieee80211_tx_skb(sdata, skb);
|
ieee80211_tx_skb(sdata, skb);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
@ -3555,7 +3572,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
struct ieee80211_qos_hdr *nullfunc;
|
struct ieee80211_qos_hdr *nullfunc;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb, *ack_skb;
|
||||||
int size = sizeof(*nullfunc);
|
int size = sizeof(*nullfunc);
|
||||||
__le16 fc;
|
__le16 fc;
|
||||||
bool qos;
|
bool qos;
|
||||||
|
@ -3563,20 +3580,24 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||||
struct sta_info *sta;
|
struct sta_info *sta;
|
||||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
enum ieee80211_band band;
|
enum ieee80211_band band;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* the lock is needed to assign the cookie later */
|
||||||
|
mutex_lock(&local->mtx);
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||||
if (WARN_ON(!chanctx_conf)) {
|
if (WARN_ON(!chanctx_conf)) {
|
||||||
rcu_read_unlock();
|
ret = -EINVAL;
|
||||||
return -EINVAL;
|
goto unlock;
|
||||||
}
|
}
|
||||||
band = chanctx_conf->def.chan->band;
|
band = chanctx_conf->def.chan->band;
|
||||||
sta = sta_info_get_bss(sdata, peer);
|
sta = sta_info_get_bss(sdata, peer);
|
||||||
if (sta) {
|
if (sta) {
|
||||||
qos = sta->sta.wme;
|
qos = sta->sta.wme;
|
||||||
} else {
|
} else {
|
||||||
rcu_read_unlock();
|
ret = -ENOLINK;
|
||||||
return -ENOLINK;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qos) {
|
if (qos) {
|
||||||
|
@ -3592,8 +3613,8 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
|
||||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
rcu_read_unlock();
|
ret = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb->dev = dev;
|
skb->dev = dev;
|
||||||
|
@ -3619,13 +3640,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||||
if (qos)
|
if (qos)
|
||||||
nullfunc->qos_ctrl = cpu_to_le16(7);
|
nullfunc->qos_ctrl = cpu_to_le16(7);
|
||||||
|
|
||||||
|
ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
|
||||||
|
if (IS_ERR(ack_skb)) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
ret = PTR_ERR(ack_skb);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
local_bh_disable();
|
local_bh_disable();
|
||||||
ieee80211_xmit(sdata, sta, skb);
|
ieee80211_xmit(sdata, sta, skb);
|
||||||
local_bh_enable();
|
local_bh_enable();
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
*cookie = (unsigned long) skb;
|
ret = 0;
|
||||||
return 0;
|
unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
|
mutex_unlock(&local->mtx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
||||||
|
|
|
@ -471,15 +471,23 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||||
|
u64 cookie = IEEE80211_SKB_CB(skb)->ack.cookie;
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
sdata = ieee80211_sdata_from_skb(local, skb);
|
sdata = ieee80211_sdata_from_skb(local, skb);
|
||||||
if (sdata)
|
if (sdata) {
|
||||||
cfg80211_mgmt_tx_status(&sdata->wdev,
|
if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||||
(unsigned long)skb,
|
ieee80211_is_qos_nullfunc(hdr->frame_control))
|
||||||
|
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||||
|
cookie, acked,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
else
|
||||||
|
cfg80211_mgmt_tx_status(&sdata->wdev, cookie,
|
||||||
skb->data, skb->len,
|
skb->data, skb->len,
|
||||||
acked, GFP_ATOMIC);
|
acked, GFP_ATOMIC);
|
||||||
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
dev_kfree_skb_any(skb);
|
dev_kfree_skb_any(skb);
|
||||||
|
@ -499,11 +507,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||||
if (dropped)
|
if (dropped)
|
||||||
acked = false;
|
acked = false;
|
||||||
|
|
||||||
if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
|
||||||
IEEE80211_TX_INTFL_MLME_CONN_TX) &&
|
|
||||||
!info->ack_frame_id) {
|
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
u64 cookie = (unsigned long)skb;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
|
||||||
|
@ -525,10 +530,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||||
ieee80211_mgd_conn_tx_status(sdata,
|
ieee80211_mgd_conn_tx_status(sdata,
|
||||||
hdr->frame_control,
|
hdr->frame_control,
|
||||||
acked);
|
acked);
|
||||||
} else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
|
||||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
|
||||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
|
||||||
cookie, acked, GFP_ATOMIC);
|
|
||||||
} else {
|
} else {
|
||||||
/* we assign ack frame ID for the others */
|
/* we assign ack frame ID for the others */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
|
|
Loading…
Reference in New Issue