diff options
author | Willem de Bruijn <willemb@google.com> | 2016-07-12 18:18:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-07-13 14:53:41 -0400 |
commit | 4f0c40d94461cfd23893a17335b2ab78ecb333c8 (patch) | |
tree | bb43f8374b6e157c5a412b30d2e528a808939067 | |
parent | f4979fcea7fd36d8e2f556abef86f80e0d5af1ba (diff) |
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: 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>
-rw-r--r-- | include/net/sock.h | 8 | ||||
-rw-r--r-- | net/core/sock.c | 7 | ||||
-rw-r--r-- | net/dccp/ipv4.c | 2 | ||||
-rw-r--r-- | net/dccp/ipv6.c | 2 |
4 files changed, 13 insertions, 6 deletions
diff --git a/include/net/sock.h b/include/net/sock.h index 649d2a8c17fc..ff5be7e8ddea 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -1576,7 +1576,13 @@ static inline void sock_put(struct sock *sk) | |||
1576 | */ | 1576 | */ |
1577 | void sock_gen_put(struct sock *sk); | 1577 | void sock_gen_put(struct sock *sk); |
1578 | 1578 | ||
1579 | int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested); | 1579 | int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested, |
1580 | unsigned int trim_cap); | ||
1581 | static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb, | ||
1582 | const int nested) | ||
1583 | { | ||
1584 | return __sk_receive_skb(sk, skb, nested, 1); | ||
1585 | } | ||
1580 | 1586 | ||
1581 | static inline void sk_tx_queue_set(struct sock *sk, int tx_queue) | 1587 | static inline void sk_tx_queue_set(struct sock *sk, int tx_queue) |
1582 | { | 1588 | { |
diff --git a/net/core/sock.c b/net/core/sock.c index b7f12639c26a..25dab8b60223 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -452,11 +452,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
452 | } | 452 | } |
453 | EXPORT_SYMBOL(sock_queue_rcv_skb); | 453 | EXPORT_SYMBOL(sock_queue_rcv_skb); |
454 | 454 | ||
455 | int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested) | 455 | int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, |
456 | const int nested, unsigned int trim_cap) | ||
456 | { | 457 | { |
457 | int rc = NET_RX_SUCCESS; | 458 | int rc = NET_RX_SUCCESS; |
458 | 459 | ||
459 | if (sk_filter(sk, skb)) | 460 | if (sk_filter_trim_cap(sk, skb, trim_cap)) |
460 | goto discard_and_relse; | 461 | goto discard_and_relse; |
461 | 462 | ||
462 | skb->dev = NULL; | 463 | skb->dev = NULL; |
@@ -492,7 +493,7 @@ discard_and_relse: | |||
492 | kfree_skb(skb); | 493 | kfree_skb(skb); |
493 | goto out; | 494 | goto out; |
494 | } | 495 | } |
495 | EXPORT_SYMBOL(sk_receive_skb); | 496 | EXPORT_SYMBOL(__sk_receive_skb); |
496 | 497 | ||
497 | struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) | 498 | struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) |
498 | { | 499 | { |
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 25dd25b47d41..345a3aeb8c7e 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c | |||
@@ -868,7 +868,7 @@ lookup: | |||
868 | goto discard_and_relse; | 868 | goto discard_and_relse; |
869 | nf_reset(skb); | 869 | nf_reset(skb); |
870 | 870 | ||
871 | return sk_receive_skb(sk, skb, 1); | 871 | return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4); |
872 | 872 | ||
873 | no_dccp_socket: | 873 | no_dccp_socket: |
874 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) | 874 | if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) |
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index d176f4e66369..3ff137d9471d 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -732,7 +732,7 @@ lookup: | |||
732 | if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) | 732 | if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) |
733 | goto discard_and_relse; | 733 | goto discard_and_relse; |
734 | 734 | ||
735 | return sk_receive_skb(sk, skb, 1) ? -1 : 0; | 735 | return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0; |
736 | 736 | ||
737 | no_dccp_socket: | 737 | no_dccp_socket: |
738 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) | 738 | if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) |