Thread (5 messages) 5 messages, 2 authors, 2026-06-02

Re: [PATCH net v2 4/4] netlabel: validate CIPSO option against skb tail in netlbl_skbuff_getattr

From: Paul Moore <paul@paul-moore.com>
Date: 2026-06-02 03:17:34
Also in: netdev, stable

On Sun, May 24, 2026 at 12:15 AM Qi Tang [off-list ref] wrote:
quoted hunk ↗ jump to hunk
netlbl_skbuff_getattr() locates the CIPSO option in the IPv4 IP header
via cipso_v4_optptr() and hands the bare pointer to cipso_v4_getattr().
The consumer re-reads cipso[1] (option length), cipso[6] (tag type),
and then cipso_v4_parsetag_*() re-reads further bytes from the skb.

__ip_options_compile() validates these bytes only at parse time.  An
nftables LOCAL_IN payload write reachable from an unprivileged user
namespace can rewrite them after parse and before the SELinux/Smack
peer-label consume path (selinux_sock_rcv_skb_compat ->
selinux_netlbl_sock_rcv_skb -> netlbl_skbuff_getattr).  This is the
IPv4 analogue of the CALIPSO IPv6 trust-after-modification fixed in
the previous patch: the tag parsers walk the option using attacker-
controlled length bytes, producing slab-out-of-bounds reads whose
contents feed into the MLS access decision.

Validate the option fits within skb_tail_pointer(skb) before invoking
cipso_v4_getattr().  The pre-tag-walk guard "ptr + 8 > tail" covers
the CIPSO option header (type + length + DOI = 6 bytes) plus the
first tag header (type + length = 2 bytes), which are the bytes
cipso_v4_getattr() reads to dispatch on the tag.  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 (Smack peer-label policy + nft LOCAL_IN
mutation of tag_len): UdpInDatagrams increments to 1 and recvfrom
returns the payload, showing netlbl_skbuff_getattr ->
cipso_v4_getattr -> cipso_v4_parsetag_rbm -> 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 <redacted>
Reported-by: Tong Liu <redacted>
Fixes: 04f81f0154e4 ("cipso: don't use IPCB() to locate the CIPSO IP option")
Signed-off-by: Qi Tang <redacted>
---
 net/netlabel/netlabel_kapi.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index d0d6220b8d59d..c2d3ea751f4e1 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -1393,11 +1393,24 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
        unsigned char *ptr;

        switch (family) {
-       case AF_INET:
+       case AF_INET: {
+               const unsigned char *tail = skb_tail_pointer(skb);
+               u8 opt_len, tag_len;
+
                ptr = cipso_v4_optptr(skb);
-               if (ptr && cipso_v4_getattr(ptr, secattr) == 0)
+               if (!ptr)
+                       break;
+               /* CIPSO header (type+len+DOI = 6) + first tag header (type+len = 2) */
+               if (ptr + 8 > tail)
+                       return -EINVAL;
+               opt_len = ptr[1];       /* total CIPSO option length */
+               tag_len = ptr[7];       /* first tag length */
+               if (ptr + opt_len > tail || ptr + 6 + tag_len > tail)
+                       return -EINVAL;
+               if (cipso_v4_getattr(ptr, secattr) == 0)
                        return 0;
I'd strongly prefer if you moved the tag length check into
cipso_v4_getattr().  As you've already validated the CIPSO option
length field it should be a fairly easy check, no need to test it
against the skb's tail pointer, just ensure the tag length doesn't go
past the end of the CIPSO option.
                break;
+       }
 #if IS_ENABLED(CONFIG_IPV6)
        case AF_INET6: {
                const unsigned char *tail = skb_tail_pointer(skb);
--
2.47.3
-- 
paul-moore.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help