Re: [PATCH nf] netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jun 21, 2023 at 5:44 PM Florian Westphal <fw@xxxxxxxxx> wrote:
>
> Eric Dumazet says:
>   nf_conntrack_dccp_packet() has an unique:
>
>   dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
>
>   And nothing more is 'pulled' from the packet, depending on the content.
>   dh->dccph_doff, and/or dh->dccph_x ...)
>   So dccp_ack_seq() is happily reading stuff past the _dh buffer.
>
> BUG: KASAN: stack-out-of-bounds in nf_conntrack_dccp_packet+0x1134/0x11c0
> Read of size 4 at addr ffff000128f66e0c by task syz-executor.2/29371
> [..]
>
> Fix this by increasing the stack buffer to also include room for
> the extra sequence numbers and all the known dccp packet type headers,
> then pull again after the initial validation of the basic header.
>
> While at it, mark packets invalid that lack 48bit sequence bit but
> where RFC says the type MUST use them.
>
> Compile tested only.
>
> Heads-up: I intend to remove dccp conntrack support later this year.
>
> Fixes: 2bc780499aa3 ("[NETFILTER]: nf_conntrack: add DCCP protocol support")
> Reported-by: Eric Dumazet <edumazet@xxxxxxxxxx>
> Signed-off-by: Florian Westphal <fw@xxxxxxxxx>
> ---
>  net/netfilter/nf_conntrack_proto_dccp.c | 50 ++++++++++++++++++++++++-
>  1 file changed, 48 insertions(+), 2 deletions(-)
>
> diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c
> index c1557d47ccd1..745a1f76c249 100644
> --- a/net/netfilter/nf_conntrack_proto_dccp.c
> +++ b/net/netfilter/nf_conntrack_proto_dccp.c
> @@ -432,9 +432,19 @@ static bool dccp_error(const struct dccp_hdr *dh,
>                        struct sk_buff *skb, unsigned int dataoff,
>                        const struct nf_hook_state *state)
>  {
> +       static const unsigned long require_seq48 = 1 << DCCP_PKT_REQUEST |
> +                                                  1 << DCCP_PKT_RESPONSE |
> +                                                  1 << DCCP_PKT_CLOSEREQ |
> +                                                  1 << DCCP_PKT_CLOSE |
> +                                                  1 << DCCP_PKT_RESET |
> +                                                  1 << DCCP_PKT_SYNC |
> +                                                  1 << DCCP_PKT_SYNCACK;
>         unsigned int dccp_len = skb->len - dataoff;
>         unsigned int cscov;
>         const char *msg;
> +       u8 type;
> +
> +       BUILD_BUG_ON(DCCP_PKT_INVALID >= BITS_PER_LONG);
>
>         if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) ||
>             dh->dccph_doff * 4 > dccp_len) {
> @@ -459,26 +469,57 @@ static bool dccp_error(const struct dccp_hdr *dh,
>                 goto out_invalid;
>         }
>
> -       if (dh->dccph_type >= DCCP_PKT_INVALID) {
> +       type = dh->dccph_type;
> +       if (type >= DCCP_PKT_INVALID) {
>                 msg = "nf_ct_dccp: reserved packet type ";
>                 goto out_invalid;
>         }
> +
> +       if (test_bit(type, &require_seq48) && !dh->dccph_x) {
> +               msg = "nf_ct_dccp: type lacks 48bit sequence numbers";
> +               goto out_invalid;
> +       }
> +
>         return false;
>  out_invalid:
>         nf_l4proto_log_invalid(skb, state, IPPROTO_DCCP, "%s", msg);
>         return true;
>  }
>
> +struct nf_conntrack_dccp_buf {
> +       struct dccp_hdr _dh;     /* must be first */
> +       struct dccp_hdr_ext ext; /* optional depending dh->dccph_x */
> +       union {                  /* depends on header type */
> +               struct dccp_hdr_ack_bits ack;
> +               struct dccp_hdr_request req;
> +               struct dccp_hdr_response response;
> +               struct dccp_hdr_reset rst;
> +       } u;
> +};
> +
> +static struct dccp_hdr *
> +dccp_header_pointer(const struct sk_buff *skb, int offset, const struct dccp_hdr *dh,
> +                   struct nf_conntrack_dccp_buf *buf)
> +{
> +       unsigned int hdrlen = __dccp_hdr_len(dh);
> +
> +       if (hdrlen > sizeof(*buf))
> +               return NULL;
> +
> +       return skb_header_pointer(skb, offset, hdrlen, buf);
> +}
> +
>  int nf_conntrack_dccp_packet(struct nf_conn *ct, struct sk_buff *skb,
>                              unsigned int dataoff,
>                              enum ip_conntrack_info ctinfo,
>                              const struct nf_hook_state *state)
>  {
>         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
> -       struct dccp_hdr _dh, *dh;
> +       struct nf_conntrack_dccp_buf _dh;
>         u_int8_t type, old_state, new_state;
>         enum ct_dccp_roles role;
>         unsigned int *timeouts;
> +       struct dccp_hdr *dh;
>
>         dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);

sizeof(struct dccp_hdr) , or sizeof(_dh._dh) ?

>         if (!dh)
> @@ -487,6 +528,11 @@ int nf_conntrack_dccp_packet(struct nf_conn *ct, struct sk_buff *skb,
>         if (dccp_error(dh, skb, dataoff, state))
>                 return -NF_ACCEPT;
>
> +       /* pull again, including possible 48 bit sequences and subtype header */
> +       dh = dccp_header_pointer(skb, dataoff, dh, &_dh);
> +       if (!dh)
> +               return NF_DROP;
> +
>         type = dh->dccph_type;
>         if (!nf_ct_is_confirmed(ct) && !dccp_new(ct, skb, dh, state))
>                 return -NF_ACCEPT;
> --
> 2.39.3
>




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux