mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									cab41c47d9
								
							
						
					
					
						commit
						bf7fa551e0
					
				
					 2 changed files with 9 additions and 11 deletions
				
			
		| 
						 | 
					@ -3628,9 +3628,14 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
 | 
				
			||||||
	serr->ee.ee_errno = ENOMSG;
 | 
						serr->ee.ee_errno = ENOMSG;
 | 
				
			||||||
	serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
 | 
						serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* take a reference to prevent skb_orphan() from freeing the socket */
 | 
				
			||||||
 | 
						sock_hold(sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = sock_queue_err_skb(sk, skb);
 | 
						err = sock_queue_err_skb(sk, skb);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		kfree_skb(skb);
 | 
							kfree_skb(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sock_put(sk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
 | 
					EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2072,30 +2072,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(!multicast && skb->sk &&
 | 
						if (unlikely(!multicast && skb->sk &&
 | 
				
			||||||
		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
 | 
							     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 (ack_skb) {
 | 
				
			||||||
		if (skb) {
 | 
					 | 
				
			||||||
			unsigned long flags;
 | 
								unsigned long flags;
 | 
				
			||||||
			int id;
 | 
								int id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			spin_lock_irqsave(&local->ack_status_lock, flags);
 | 
								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);
 | 
									       1, 0x10000, GFP_ATOMIC);
 | 
				
			||||||
			spin_unlock_irqrestore(&local->ack_status_lock, flags);
 | 
								spin_unlock_irqrestore(&local->ack_status_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (id >= 0) {
 | 
								if (id >= 0) {
 | 
				
			||||||
				info_id = id;
 | 
									info_id = id;
 | 
				
			||||||
				info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 | 
									info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 | 
				
			||||||
			} else if (skb_shared(skb)) {
 | 
					 | 
				
			||||||
				kfree_skb(orig_skb);
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				kfree_skb(skb);
 | 
									kfree_skb(ack_skb);
 | 
				
			||||||
				skb = orig_skb;
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			/* couldn't clone -- lose tx status ... */
 | 
					 | 
				
			||||||
			skb = orig_skb;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue