Commit 49bbb1dc authored by Willem de Bruijn's avatar Willem de Bruijn Committed by Ben Hutchings

dccp: limit sk_filter trim to payload

commit 4f0c40d9 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: 7c657876 ("[DCCP]: Initial implementation")
Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>
Acked-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent d0fb92f2
...@@ -1669,7 +1669,13 @@ static inline void sock_put(struct sock *sk) ...@@ -1669,7 +1669,13 @@ static inline void sock_put(struct sock *sk)
*/ */
void sock_gen_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) static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
{ {
......
...@@ -487,11 +487,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -487,11 +487,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
} }
EXPORT_SYMBOL(sock_queue_rcv_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; int rc = NET_RX_SUCCESS;
if (sk_filter(sk, skb)) if (sk_filter_trim_cap(sk, skb, trim_cap))
goto discard_and_relse; goto discard_and_relse;
skb->dev = NULL; skb->dev = NULL;
...@@ -527,7 +528,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested) ...@@ -527,7 +528,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
kfree_skb(skb); kfree_skb(skb);
goto out; goto out;
} }
EXPORT_SYMBOL(sk_receive_skb); EXPORT_SYMBOL(__sk_receive_skb);
struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
{ {
......
...@@ -890,7 +890,7 @@ static int dccp_v4_rcv(struct sk_buff *skb) ...@@ -890,7 +890,7 @@ static int dccp_v4_rcv(struct sk_buff *skb)
goto discard_and_relse; goto discard_and_relse;
nf_reset(skb); nf_reset(skb);
return sk_receive_skb(sk, skb, 1); return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
no_dccp_socket: no_dccp_socket:
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
......
...@@ -804,7 +804,7 @@ static int dccp_v6_rcv(struct sk_buff *skb) ...@@ -804,7 +804,7 @@ static int dccp_v6_rcv(struct sk_buff *skb)
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse; 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: no_dccp_socket:
if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment