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-YPqQ@mail.gmail.com/ Signed-off-by: Farhad Alemi --- --- 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);