diff options
author | Alexander Duyck <alexander.h.duyck@intel.com> | 2014-09-10 18:05:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-12 17:51:25 -0400 |
commit | bf7fa551e0ce507b82935055f4b4aa229be73eeb (patch) | |
tree | 88068940307b6369cc1f6a2b346f6a070949f4bd /net/mac80211/tx.c | |
parent | cab41c47d92851de71c74b1a7bdbf0fadf6ae4ba (diff) | |
download | lwn-bf7fa551e0ce507b82935055f4b4aa229be73eeb.tar.gz lwn-bf7fa551e0ce507b82935055f4b4aa229be73eeb.zip |
mac80211: Resolve sk_refcnt/sk_wmem_alloc issue in wifi ack path
There is a possible issue with the use, or lack thereof of sk_refcnt and
sk_wmem_alloc in the wifi ack status functionality.
Specifically if a socket were to request acknowledgements, and the socket
were to have sk_refcnt drop to 0 resulting in it waiting on sk_wmem_alloc
to reach 0 it would be possible to have sock_queue_err_skb orphan the last
buffer, resulting in __sk_free being called on the socket. After this the
buffer is enqueued on sk_error_queue, however the queue has already been
flushed resulting in at least a memory leak, if not a data corruption.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r-- | net/mac80211/tx.c | 15 |
1 files changed, 4 insertions, 11 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 925c39f4099e..cf7141452b0f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2072,30 +2072,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (unlikely(!multicast && skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { - struct sk_buff *orig_skb = skb; + struct sk_buff *ack_skb = skb_clone_sk(skb); - skb = skb_clone(skb, GFP_ATOMIC); - if (skb) { + if (ack_skb) { unsigned long flags; int id; spin_lock_irqsave(&local->ack_status_lock, flags); - id = idr_alloc(&local->ack_status_frames, orig_skb, + id = idr_alloc(&local->ack_status_frames, ack_skb, 1, 0x10000, GFP_ATOMIC); spin_unlock_irqrestore(&local->ack_status_lock, flags); if (id >= 0) { info_id = id; info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - } else if (skb_shared(skb)) { - kfree_skb(orig_skb); } else { - kfree_skb(skb); - skb = orig_skb; + kfree_skb(ack_skb); } - } else { - /* couldn't clone -- lose tx status ... */ - skb = orig_skb; } } |