skb->transport_header is a 16-bit offset from skb->head, and U16_MAX is reserved as the "not set" value. skb_set_transport_header() silently truncates an offset that does not fit. sctp_v6_err() stores @offset, computed by icmpv6_notify() from the extension headers of the packet embedded in an ICMPv6 error. Should the value be truncated, sctp_err_lookup() would read the SCTP header at the wrong place. Since commit 3744b0964d52 ("ipv6: Implement limits on extension header parsing") a single chain is bounded to IP6_MAX_EXT_HDRS_CNT headers (24576 bytes), so this needs a large skb->data - skb->head on top of it, for instance after several nested decapsulations, each of them possibly pulling its own extension header chain, since @offset is relative to the IPv6 header embedded in the ICMPv6 error. No reproducer was written, this is hardening. Use skb_set_transport_header_careful() and ignore the ICMPv6 error if the offset can not be stored. Do this before changing the network header, so that nothing has to be restored on failure. Fixes: 1a37e412a022 ("net: Use 16bits for *_headers fields of struct skbuff") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet --- net/sctp/ipv6.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 97390569c4bd..ad5c951efdf0 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -170,8 +170,11 @@ static int sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, /* Fix up skb to look at the embedded net header. */ saveip = skb->network_header; savesctp = skb->transport_header; + if (unlikely(!skb_set_transport_header_careful(skb, offset))) { + __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS); + return -EINVAL; + } skb_reset_network_header(skb); - skb_set_transport_header(skb, offset); sk = sctp_err_lookup(net, AF_INET6, skb, sctp_hdr(skb), &asoc, &transport); /* Put back, the original pointers. */ skb->network_header = saveip; -- 2.53.0