lkml.org 
[lkml]   [2020]   [May]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.4 057/312] dccp: limit sk_filter trim to payload
    Date
    From: Willem de Bruijn <willemb@google.com>

    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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    include/net/sock.h | 8 +++++++-
    net/core/sock.c | 7 ++++---
    net/dccp/ipv4.c | 2 +-
    net/dccp/ipv6.c | 2 +-
    4 files changed, 13 insertions(+), 6 deletions(-)

    --- a/include/net/sock.h
    +++ b/include/net/sock.h
    @@ -1651,7 +1651,13 @@ static inline void sock_put(struct sock
    */
    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)
    {
    --- a/net/core/sock.c
    +++ b/net/core/sock.c
    @@ -484,11 +484,12 @@ int sock_queue_rcv_skb(struct sock *sk,
    }
    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;
    @@ -524,7 +525,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)
    {
    --- a/net/dccp/ipv4.c
    +++ b/net/dccp/ipv4.c
    @@ -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))
    --- a/net/dccp/ipv6.c
    +++ b/net/dccp/ipv6.c
    @@ -741,7 +741,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))

    \
     
     \ /
      Last update: 2020-05-08 15:40    [W:4.080 / U:0.352 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site