* [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
@ 2026-05-14 16:51 Qi Tang
2026-05-14 17:11 ` Casey Schaufler
2026-05-15 2:18 ` Paul Moore
0 siblings, 2 replies; 5+ messages in thread
From: Qi Tang @ 2026-05-14 16:51 UTC (permalink / raw)
To: davem, kuba, pabeni, edumazet
Cc: netdev, lyutoon, stable, Qi Tang, Paul Moore, Simon Horman,
Huw Davies, linux-security-module
netlbl_skbuff_getattr() locates the CALIPSO option in the IPv6 HBH
header via calipso_optptr() and hands the bare pointer to
calipso_getattr() -> calipso_opt_getattr(). The consumer re-reads
calipso[1] (option data length) and calipso[6] (cat_len/4) and walks
calipso + 10 for cat_len bytes via netlbl_bitmap_walk().
ipv6_hop_calipso() validates these bytes only at parse time inside
ipv6_parse_hopopts(). An nftables PRE_ROUTING payload write
reachable from an unprivileged user namespace can rewrite both bytes
between parse and the SELinux/Smack peer-label consume path
(selinux_sock_rcv_skb_compat -> selinux_netlbl_sock_rcv_skb ->
netlbl_skbuff_getattr). The self-consistency check
(cat_len + 8 > len) inside calipso_opt_getattr() is defeated by
mutating both bytes consistently, allowing a ~232-byte
slab-out-of-bounds read from calipso + 10 whose set bits become MLS
categories driving the access decision.
netlbl_skbuff_getattr() has the skb; gate the consume on the option
fitting within skb_tail_pointer(). The IPv6 option layout is
type(1) + length(1) + length bytes of data, so requiring
ptr + 2 + ptr[1] <= skb_tail covers the option and its embedded
bitmap.
Runtime confirmation (Smack peer-label policy + nft HBH mutation):
Udp6InDatagrams increments to 1 with the mutated cat_len, showing
selinux/smack_socket_sock_rcv_skb -> netlbl_skbuff_getattr ->
calipso_opt_getattr -> netlbl_bitmap_walk runs end-to-end past the
option's true bound; with this patch the consume path short-circuits
at the bounds check and the counter stays 0.
Reported-by: Qi Tang <tpluszz77@gmail.com>
Reported-by: Tong Liu <lyutoon@gmail.com>
Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
---
net/netlabel/netlabel_kapi.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 3583fa63dd01f..4af8ab76964e0 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -1399,11 +1399,20 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
return 0;
break;
#if IS_ENABLED(CONFIG_IPV6)
- case AF_INET6:
+ case AF_INET6: {
+ const unsigned char *tail = skb_tail_pointer(skb);
+ u8 opt_data_len;
+
ptr = calipso_optptr(skb);
- if (ptr && calipso_getattr(ptr, secattr) == 0)
+ if (!ptr || ptr + 2 > tail)
+ break;
+ opt_data_len = ptr[1]; /* IPv6 option data length */
+ if (ptr + 2 + opt_data_len > tail)
+ break;
+ if (calipso_getattr(ptr, secattr) == 0)
return 0;
break;
+ }
#endif /* IPv6 */
}
--
2.47.3
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
2026-05-14 16:51 [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr Qi Tang
@ 2026-05-14 17:11 ` Casey Schaufler
2026-05-15 1:54 ` Qi Tang
2026-05-15 2:18 ` Paul Moore
1 sibling, 1 reply; 5+ messages in thread
From: Casey Schaufler @ 2026-05-14 17:11 UTC (permalink / raw)
To: Qi Tang, davem, kuba, pabeni, edumazet
Cc: netdev, lyutoon, stable, Paul Moore, Simon Horman, Huw Davies,
linux-security-module, Casey Schaufler
On 5/14/2026 9:51 AM, Qi Tang wrote:
> netlbl_skbuff_getattr() locates the CALIPSO option in the IPv6 HBH
> header via calipso_optptr() and hands the bare pointer to
> calipso_getattr() -> calipso_opt_getattr(). The consumer re-reads
> calipso[1] (option data length) and calipso[6] (cat_len/4) and walks
> calipso + 10 for cat_len bytes via netlbl_bitmap_walk().
>
> ipv6_hop_calipso() validates these bytes only at parse time inside
> ipv6_parse_hopopts(). An nftables PRE_ROUTING payload write
> reachable from an unprivileged user namespace can rewrite both bytes
> between parse and the SELinux/Smack peer-label consume path
> (selinux_sock_rcv_skb_compat -> selinux_netlbl_sock_rcv_skb ->
> netlbl_skbuff_getattr). The self-consistency check
> (cat_len + 8 > len) inside calipso_opt_getattr() is defeated by
> mutating both bytes consistently, allowing a ~232-byte
> slab-out-of-bounds read from calipso + 10 whose set bits become MLS
> categories driving the access decision.
>
> netlbl_skbuff_getattr() has the skb; gate the consume on the option
> fitting within skb_tail_pointer(). The IPv6 option layout is
> type(1) + length(1) + length bytes of data, so requiring
> ptr + 2 + ptr[1] <= skb_tail covers the option and its embedded
> bitmap.
>
> Runtime confirmation (Smack peer-label policy + nft HBH mutation):
I'm the Smack maintainer and do not understand what you are trying
to say. Smack does not use CALIPSO, although support is on the
wish list.
> Udp6InDatagrams increments to 1 with the mutated cat_len, showing
> selinux/smack_socket_sock_rcv_skb -> netlbl_skbuff_getattr ->
> calipso_opt_getattr -> netlbl_bitmap_walk runs end-to-end past the
> option's true bound; with this patch the consume path short-circuits
> at the bounds check and the counter stays 0.
>
> Reported-by: Qi Tang <tpluszz77@gmail.com>
> Reported-by: Tong Liu <lyutoon@gmail.com>
> Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
> Signed-off-by: Qi Tang <tpluszz77@gmail.com>
> ---
> net/netlabel/netlabel_kapi.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
> index 3583fa63dd01f..4af8ab76964e0 100644
> --- a/net/netlabel/netlabel_kapi.c
> +++ b/net/netlabel/netlabel_kapi.c
> @@ -1399,11 +1399,20 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
> return 0;
> break;
> #if IS_ENABLED(CONFIG_IPV6)
> - case AF_INET6:
> + case AF_INET6: {
> + const unsigned char *tail = skb_tail_pointer(skb);
> + u8 opt_data_len;
> +
> ptr = calipso_optptr(skb);
> - if (ptr && calipso_getattr(ptr, secattr) == 0)
> + if (!ptr || ptr + 2 > tail)
> + break;
> + opt_data_len = ptr[1]; /* IPv6 option data length */
> + if (ptr + 2 + opt_data_len > tail)
> + break;
> + if (calipso_getattr(ptr, secattr) == 0)
> return 0;
> break;
> + }
> #endif /* IPv6 */
> }
>
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
2026-05-14 17:11 ` Casey Schaufler
@ 2026-05-15 1:54 ` Qi Tang
0 siblings, 0 replies; 5+ messages in thread
From: Qi Tang @ 2026-05-15 1:54 UTC (permalink / raw)
To: casey
Cc: davem, kuba, pabeni, edumazet, netdev, lyutoon, paul, horms, huw,
linux-security-module, Qi Tang
Hi Casey,
You're right. "SELinux/Smack peer-label consume path" was wrong
in the CALIPSO patch. Our reasoning was that both LSMs call
netlbl_skbuff_getattr() in their socket-rcv path, but we only
actually verified the OOB read via SELinux's compat path
(selinux=1 enforcing=0, with a CALIPSO DOI installed via
netlabelctl). We never tested with Smack and shouldn't have
included it.
v2 will say "SELinux" only on the CALIPSO patch. The companion
CIPSO patch keeps the Smack mention since Smack does use CIPSO.
Sorry for the noise.
Qi
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
2026-05-14 16:51 [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr Qi Tang
2026-05-14 17:11 ` Casey Schaufler
@ 2026-05-15 2:18 ` Paul Moore
2026-05-15 2:42 ` Qi Tang
1 sibling, 1 reply; 5+ messages in thread
From: Paul Moore @ 2026-05-15 2:18 UTC (permalink / raw)
To: Qi Tang
Cc: davem, kuba, pabeni, edumazet, netdev, lyutoon, stable,
Simon Horman, Huw Davies, linux-security-module
On Thu, May 14, 2026 at 12:52 PM Qi Tang <tpluszz77@gmail.com> wrote:
>
> netlbl_skbuff_getattr() locates the CALIPSO option in the IPv6 HBH
> header via calipso_optptr() and hands the bare pointer to
> calipso_getattr() -> calipso_opt_getattr(). The consumer re-reads
> calipso[1] (option data length) and calipso[6] (cat_len/4) and walks
> calipso + 10 for cat_len bytes via netlbl_bitmap_walk().
>
> ipv6_hop_calipso() validates these bytes only at parse time inside
> ipv6_parse_hopopts(). An nftables PRE_ROUTING payload write
> reachable from an unprivileged user namespace can rewrite both bytes
> between parse and the SELinux/Smack peer-label consume path
> (selinux_sock_rcv_skb_compat -> selinux_netlbl_sock_rcv_skb ->
> netlbl_skbuff_getattr). The self-consistency check
> (cat_len + 8 > len) inside calipso_opt_getattr() is defeated by
> mutating both bytes consistently, allowing a ~232-byte
> slab-out-of-bounds read from calipso + 10 whose set bits become MLS
> categories driving the access decision.
>
> netlbl_skbuff_getattr() has the skb; gate the consume on the option
> fitting within skb_tail_pointer(). The IPv6 option layout is
> type(1) + length(1) + length bytes of data, so requiring
> ptr + 2 + ptr[1] <= skb_tail covers the option and its embedded
> bitmap.
>
> Runtime confirmation (Smack peer-label policy + nft HBH mutation):
> Udp6InDatagrams increments to 1 with the mutated cat_len, showing
> selinux/smack_socket_sock_rcv_skb -> netlbl_skbuff_getattr ->
> calipso_opt_getattr -> netlbl_bitmap_walk runs end-to-end past the
> option's true bound; with this patch the consume path short-circuits
> at the bounds check and the counter stays 0.
>
> Reported-by: Qi Tang <tpluszz77@gmail.com>
> Reported-by: Tong Liu <lyutoon@gmail.com>
> Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
> Signed-off-by: Qi Tang <tpluszz77@gmail.com>
> ---
> net/netlabel/netlabel_kapi.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
> index 3583fa63dd01f..4af8ab76964e0 100644
> --- a/net/netlabel/netlabel_kapi.c
> +++ b/net/netlabel/netlabel_kapi.c
> @@ -1399,11 +1399,20 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
> return 0;
> break;
> #if IS_ENABLED(CONFIG_IPV6)
> - case AF_INET6:
> + case AF_INET6: {
> + const unsigned char *tail = skb_tail_pointer(skb);
> + u8 opt_data_len;
> +
> ptr = calipso_optptr(skb);
> - if (ptr && calipso_getattr(ptr, secattr) == 0)
> + if (!ptr || ptr + 2 > tail)
> + break;
Is there a reason why you simply break here and drop down into the
unlabeled code? I would think we would want to return an error here
since we had packet that was munged.
> + opt_data_len = ptr[1]; /* IPv6 option data length */
> + if (ptr + 2 + opt_data_len > tail)
> + break;
Same thing.
> + if (calipso_getattr(ptr, secattr) == 0)
> return 0;
> break;
> + }
> #endif /* IPv6 */
> }
>
> --
> 2.47.3
--
paul-moore.com
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
2026-05-15 2:18 ` Paul Moore
@ 2026-05-15 2:42 ` Qi Tang
0 siblings, 0 replies; 5+ messages in thread
From: Qi Tang @ 2026-05-15 2:42 UTC (permalink / raw)
To: paul
Cc: davem, kuba, pabeni, edumazet, netdev, lyutoon, horms, huw, casey,
linux-security-module, Qi Tang
Agreed, -EINVAL is right. The bytes passed parse-time
validation, so hitting either bounds check at consume time means
they were mutated after parse. Treating such a packet as "no
label" via netlbl_unlabel_getattr() drops it into the wrong
default. v2 returns -EINVAL on both checks.
Will also drop the Smack mention from the commit message (Casey
flagged that separately).
Qi
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-15 2:42 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-14 16:51 [PATCH net 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr Qi Tang
2026-05-14 17:11 ` Casey Schaufler
2026-05-15 1:54 ` Qi Tang
2026-05-15 2:18 ` Paul Moore
2026-05-15 2:42 ` Qi Tang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.