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 | |
parent | cab41c47d92851de71c74b1a7bdbf0fadf6ae4ba (diff) |
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')
-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, | |||
2072 | 2072 | ||
2073 | if (unlikely(!multicast && skb->sk && | 2073 | if (unlikely(!multicast && skb->sk && |
2074 | skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { | 2074 | skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { |
2075 | struct sk_buff *orig_skb = skb; | 2075 | struct sk_buff *ack_skb = skb_clone_sk(skb); |
2076 | 2076 | ||
2077 | skb = skb_clone(skb, GFP_ATOMIC); | 2077 | if (ack_skb) { |
2078 | if (skb) { | ||
2079 | unsigned long flags; | 2078 | unsigned long flags; |
2080 | int id; | 2079 | int id; |
2081 | 2080 | ||
2082 | spin_lock_irqsave(&local->ack_status_lock, flags); | 2081 | spin_lock_irqsave(&local->ack_status_lock, flags); |
2083 | id = idr_alloc(&local->ack_status_frames, orig_skb, | 2082 | id = idr_alloc(&local->ack_status_frames, ack_skb, |
2084 | 1, 0x10000, GFP_ATOMIC); | 2083 | 1, 0x10000, GFP_ATOMIC); |
2085 | spin_unlock_irqrestore(&local->ack_status_lock, flags); | 2084 | spin_unlock_irqrestore(&local->ack_status_lock, flags); |
2086 | 2085 | ||
2087 | if (id >= 0) { | 2086 | if (id >= 0) { |
2088 | info_id = id; | 2087 | info_id = id; |
2089 | info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; | 2088 | info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; |
2090 | } else if (skb_shared(skb)) { | ||
2091 | kfree_skb(orig_skb); | ||
2092 | } else { | 2089 | } else { |
2093 | kfree_skb(skb); | 2090 | kfree_skb(ack_skb); |
2094 | skb = orig_skb; | ||
2095 | } | 2091 | } |
2096 | } else { | ||
2097 | /* couldn't clone -- lose tx status ... */ | ||
2098 | skb = orig_skb; | ||
2099 | } | 2092 | } |
2100 | } | 2093 | } |
2101 | 2094 | ||