Skip to content

Commit

Permalink
dccp: limit sk_filter trim to payload
Browse files Browse the repository at this point in the history
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: 7c65787 ("[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>
  • Loading branch information
Willem de Bruijn authored and David S. Miller committed Jul 13, 2016
1 parent f4979fc commit 4f0c40d
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 6 deletions.
8 changes: 7 additions & 1 deletion include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
7 changes: 4 additions & 3 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -492,7 +493,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
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)
{
Expand Down
2 changes: 1 addition & 1 deletion net/dccp/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,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))
Expand Down
2 changes: 1 addition & 1 deletion net/dccp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,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))
Expand Down

0 comments on commit 4f0c40d

Please sign in to comment.