mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-11-01 09:13:37 +00:00 
			
		
		
		
	dccp: limit sk_filter trim to payload
Dccp verifies packet integrity, including length, at initial rcv in
dccp_invalid_packet, later pulls headers in dccp_enqueue_skb.
A call to sk_filter in-between can cause __skb_pull to wrap skb->len.
skb_copy_datagram_msg interprets this as a negative value, so
(correctly) fails with EFAULT. The negative length is reported in
ioctl SIOCINQ or possibly in a DCCP_WARN in dccp_close.
Introduce an sk_receive_skb variant that caps how small a filter
program can trim packets, and call this in dccp with the header
length. Excessively trimmed packets are now processed normally and
queued for reception as 0B payloads.
Fixes: 7c657876b6 ("[DCCP]: Initial implementation")
Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									f4979fcea7
								
							
						
					
					
						commit
						4f0c40d944
					
				
					 4 changed files with 13 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -1576,7 +1576,13 @@ static inline void sock_put(struct sock *sk)
 | 
			
		|||
 */
 | 
			
		||||
void sock_gen_put(struct sock *sk);
 | 
			
		||||
 | 
			
		||||
int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
 | 
			
		||||
int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
 | 
			
		||||
		     unsigned int trim_cap);
 | 
			
		||||
static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
 | 
			
		||||
				 const int nested)
 | 
			
		||||
{
 | 
			
		||||
	return __sk_receive_skb(sk, skb, nested, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -452,11 +452,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL(sock_queue_rcv_skb);
 | 
			
		||||
 | 
			
		||||
int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
 | 
			
		||||
int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
 | 
			
		||||
		     const int nested, unsigned int trim_cap)
 | 
			
		||||
{
 | 
			
		||||
	int rc = NET_RX_SUCCESS;
 | 
			
		||||
 | 
			
		||||
	if (sk_filter(sk, skb))
 | 
			
		||||
	if (sk_filter_trim_cap(sk, skb, trim_cap))
 | 
			
		||||
		goto discard_and_relse;
 | 
			
		||||
 | 
			
		||||
	skb->dev = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -492,7 +493,7 @@ discard_and_relse:
 | 
			
		|||
	kfree_skb(skb);
 | 
			
		||||
	goto out;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sk_receive_skb);
 | 
			
		||||
EXPORT_SYMBOL(__sk_receive_skb);
 | 
			
		||||
 | 
			
		||||
struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -868,7 +868,7 @@ lookup:
 | 
			
		|||
		goto discard_and_relse;
 | 
			
		||||
	nf_reset(skb);
 | 
			
		||||
 | 
			
		||||
	return sk_receive_skb(sk, skb, 1);
 | 
			
		||||
	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
 | 
			
		||||
 | 
			
		||||
no_dccp_socket:
 | 
			
		||||
	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -732,7 +732,7 @@ lookup:
 | 
			
		|||
	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 | 
			
		||||
		goto discard_and_relse;
 | 
			
		||||
 | 
			
		||||
	return sk_receive_skb(sk, skb, 1) ? -1 : 0;
 | 
			
		||||
	return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0;
 | 
			
		||||
 | 
			
		||||
no_dccp_socket:
 | 
			
		||||
	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue