Re: [PATCH] ipv6: calipso: fix 8-bit hdrlen overflow and missing pskb_may_pull() in hop-by-hop options
From: Paul Moore <paul@paul-moore.com>
Date: 2026-09-20 01:28:05
Also in:
linux-security-module, lkml
On Sat, Sep 19, 2026 at 5:06 PM Hui Peng [off-list ref] wrote:
Two bugs exist in the CALIPSO hop-by-hop option handling in
net/ipv6/calipso.c:
1. When inserting a CALIPSO option into an existing Hop-by-Hop extension
header in calipso_opt_insert() or calipso_skbuff_setattr(), the
resulting header length `buf_len` (or `ipv6_optlen(hop) + len_delta`)
can exceed the maximum size representable by the 8-bit `hdrlen` field
(256 * 8 = 2048 bytes). When `buf_len > 2048`, `new->hdrlen = buf_len
/ 8 - 1` (or `hop->hdrlen = ...`) truncates modulo 256, causing
`ipv6_optlen()` to under-report the allocated header size and
mismatch `opt->opt_nflen` or `skb->len`.
2. In calipso_skbuff_setattr(), when `ip6_hdr->nexthdr == NEXTHDR_HOP`,
`hop` is dereferenced at `(struct ipv6_opt_hdr *)(ip6_hdr + 1)` and
scanned by `calipso_opt_find(hop, &start, &end)` without ensuring
that the Hop-by-Hop header resides in the linear data area of `skb`
via `pskb_may_pull()`, risking an out-of-bounds read past `skb->tail`
on non-linear skbs.
Reject Hop-by-Hop headers that would exceed `8 * 256` bytes with
`-ENOSPC`, and ensure `pskb_may_pull()` linearizes `hop` before
inspecting it in calipso_skbuff_setattr().
Fixes: ceba1832b1b2 ("calipso: Set the calipso socket label to match the secattr.")
Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
Assisted-by: LLM
Signed-off-by: Hui Peng <redacted>
---
net/ipv6/calipso.c | 13 +++++++++++++
1 file changed, 13 insertions(+)Similar to my comment on your other patch, since you are fixing two potential issues in this patch, please break this up into two patches and resubmit. Thank you.
quoted hunk ↗ jump to hunk
diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index c6a34334e657..b8b7451a16ed 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c@@ -944,6 +944,10 @@ calipso_opt_insert(struct ipv6_opt_hdr *hop, memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end); buf_len += hop_len - end; } + if (buf_len > 8 * 256) { + kfree(new); + return ERR_PTR(-ENOSPC); + } new->nexthdr = 0; new->hdrlen = buf_len / 8 - 1;@@ -1323,6 +1327,13 @@ static int calipso_skbuff_setattr(struct sk_buff *skb, ip6_hdr = ipv6_hdr(skb); if (ip6_hdr->nexthdr == NEXTHDR_HOP) { + if (!pskb_may_pull(skb, sizeof(*ip6_hdr) + sizeof(*hop))) + return -EINVAL; + ip6_hdr = ipv6_hdr(skb); + hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); + if (!pskb_may_pull(skb, sizeof(*ip6_hdr) + ipv6_optlen(hop))) + return -EINVAL; + ip6_hdr = ipv6_hdr(skb); hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); ret_val = calipso_opt_find(hop, &start, &end); if (ret_val && ret_val != -ENOENT)@@ -1341,6 +1352,8 @@ static int calipso_skbuff_setattr(struct sk_buff *skb, /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */ pad = ((new_end & 4) + (end & 7)) & 7; len_delta = new_end - (int)end + pad; + if (start && (int)ipv6_optlen(hop) + len_delta > 8 * 256) + return -ENOSPC; ret_val = skb_cow(skb, skb_headroom(skb) + (len_delta > 0 ? len_delta : 0)); if (ret_val < 0) --2.55.0.1082.g2b9226bbc0-goog
-- paul-moore.com