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 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. When the bounds check fails the packet has been mutated after parse, so return -EINVAL rather than fall through to the unlabeled path. Runtime confirmation (SELinux compat path with selinux=1 enforcing=0 and a CALIPSO DOI added via netlabelctl): Udp6InDatagrams increments to 1 with the mutated cat_len, showing selinux_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 returns -EINVAL at the bounds check and the counter stays 0. Cc: stable@vger.kernel.org Reported-by: Qi Tang Reported-by: Tong Liu Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.") Signed-off-by: Qi Tang --- net/netlabel/netlabel_kapi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 3583fa63dd01f..d0d6220b8d59d 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1399,11 +1399,22 @@ 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) + break; + if (ptr + 2 > tail) + return -EINVAL; + opt_data_len = ptr[1]; /* IPv6 option data length */ + if (ptr + 2 + opt_data_len > tail) + return -EINVAL; + if (calipso_getattr(ptr, secattr) == 0) return 0; break; + } #endif /* IPv6 */ } -- 2.47.3