lowpan_header_compress() pays for the IPHC header it pushes by first
calling skb_pull(skb, sizeof(struct ipv6hdr)), but that pull is a no-op
when skb->len is shorter than an IPv6 header, so the unpaid skb_push() can
drive skb->data below skb->head and into skb_under_panic().
lowpan_nhc_check_compression() has the same missing length check,
committing to the next-header compression path without requiring the
nhc->nexthdrlen transport bytes that nhc->compress() reads and
lowpan_nhc_do_compression() then pulls.  Return -EINVAL from
lowpan_header_compress() when pskb_may_pull() cannot produce a full IPv6
header, and return -ENOENT from lowpan_nhc_check_compression() unless the
IPv6 header plus nhc->nexthdrlen bytes are present, so that the nexthdr
falls back to its inline encoding.

Closes: 
https://lore.kernel.org/all/CA+0ovCjTsygN76s2o=tzqpqw8v2gbhmrnz+q6g-nab3cq-y...@mail.gmail.com/
Signed-off-by: Farhad Alemi <[email protected]>
---
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -1140,6 +1140,10 @@ int lowpan_header_compress(struct sk_buff *skb,
const struct net_device *dev,
        if (skb->protocol != htons(ETH_P_IPV6))
                return -EINVAL;

+       /* The IPHC header pushed below is paid for by pulling this header. */
+       if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+               return -EINVAL;
+
        hdr = ipv6_hdr(skb);
        hc_ptr = head + 2;

--- a/net/6lowpan/nhc.c
+++ b/net/6lowpan/nhc.c
@@ -47,7 +47,9 @@ int lowpan_nhc_check_compression(struct sk_buff *skb,
        spin_lock_bh(&lowpan_nhc_lock);

        nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
-       if (!(nhc && nhc->compress))
+       /* nhc->compress() reads and then pulls nexthdrlen transport bytes. */
+       if (!(nhc && nhc->compress) ||
+           !pskb_may_pull(skb, sizeof(struct ipv6hdr) + nhc->nexthdrlen))
                ret = -ENOENT;

        spin_unlock_bh(&lowpan_nhc_lock);

Reply via email to