]> git.hungrycats.org Git - linux/commitdiff
dccp: limit sk_filter trim to payload
authorWillem de Bruijn <willemb@google.com>
Tue, 12 Jul 2016 22:18:57 +0000 (18:18 -0400)
committerBen Hutchings <ben@decadent.org.uk>
Thu, 23 Feb 2017 03:54:46 +0000 (03:54 +0000)
commit 4f0c40d94461cfd23893a17335b2ab78ecb333c8 upstream.

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: 7c657876b63c ("[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>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
include/net/sock.h
net/core/sock.c
net/dccp/ipv4.c
net/dccp/ipv6.c

index f957f7c4fa20fe28955737042f70d8b625e01f56..07ede95596bbb720de5ff299e76efa21865d0b5b 100644 (file)
@@ -1669,7 +1669,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)
 {
index 504a2a96ff79026bc4ff0ce06ccdd0f11173142e..3b301b9b9d5f5205b1fbcd7334492518efbd15c2 100644 (file)
@@ -487,11 +487,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;
@@ -527,7 +528,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)
 {
index 6ca645c4b48e8b56a88f7d549b073fb6dc82eeb7..b371341d899dce65f2989113ff6c230fa25f210f 100644 (file)
@@ -890,7 +890,7 @@ static int dccp_v4_rcv(struct sk_buff *skb)
                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))
index 1149e345bba9283470997caa89836552ced2fa0a..9f2dc1d15e43d3fc1a989b31383ee127d9c6a107 100644 (file)
@@ -804,7 +804,7 @@ static int dccp_v6_rcv(struct sk_buff *skb)
        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))