The IPv6 IOAM Pre-allocated Trace receive path (ipv6_hop_ioam in net/ipv6/exthdrs.c) does not validate that the nodelen field in the trace header is consistent with the type bitmap. When a crafted packet arrives with nodelen=0 but with type bits set, __ioam6_fill_trace_data() computes the write pointer at the end of the trace data buffer and then writes past it, overflowing into adjacent memory (skb_shared_info). This corrupts critical SKB metadata, leading to a kernel panic when the SKB is freed. The send path properly validates the trace header via ioam6_validate_trace_hdr() which recomputes nodelen from the type bitmap. However, calling that function from the receive path would break forward compatibility, as it rejects packets with undefined type bits (bit 12-21) that the receive path intentionally accepts. Fix by recomputing nodelen directly from the type bitmap inside ioam6_fill_trace_data() before using it, preserving the receive path's forward compatibility while preventing the overflow. Fixes: 9ee11f0fff20 ("ipv6: ioam: Data plane support for Pre-allocated Trace") Cc: stable@vger.kernel.org Signed-off-by: Junxi Qian --- include/net/ioam6.h | 4 ++++ net/ipv6/ioam6.c | 10 +++++++++- net/ipv6/ioam6_iptunnel.c | 3 --- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/net/ioam6.h b/include/net/ioam6.h index abfa518cd917..a3f2a1e4fd19 100644 --- a/include/net/ioam6.h +++ b/include/net/ioam6.h @@ -14,6 +14,10 @@ #include #include +#define IOAM6_MASK_SHORT_FIELDS 0xff100000 +#define IOAM6_MASK_WIDE_FIELDS 0x00e00000 +#define IOAM6_MASK_UNDEF_FIELDS 0x000ffc00 + struct ioam6_namespace { struct rhash_head head; struct rcu_head rcu; diff --git a/net/ipv6/ioam6.c b/net/ipv6/ioam6.c index 02aboriginal1..02bfixedcode 100644 --- a/net/ipv6/ioam6.c +++ b/net/ipv6/ioam6.c @@ -929,12 +929,20 @@ void ioam6_fill_trace_data(struct sk_buff *skb, bool is_input) { struct ioam6_schema *sc; + u32 fields; u8 sclen = 0; /* Skip if Overflow flag is set */ if (trace->overflow) return; + trace->nodelen = 0; + fields = be32_to_cpu(trace->type_be32); + trace->nodelen += hweight32(fields & IOAM6_MASK_SHORT_FIELDS) + * (sizeof(__be32) / 4); + trace->nodelen += hweight32(fields & IOAM6_MASK_WIDE_FIELDS) + * (sizeof(__be64) / 4); + trace->nodelen += hweight32(fields & IOAM6_MASK_UNDEF_FIELDS) + * (sizeof(__be32) / 4); /* NodeLen does not include Opaque State Snapshot length. We need to * take it into account if the corresponding bit is set (bit 22) and diff --git a/net/ipv6/ioam6_iptunnel.c b/net/ipv6/ioam6_iptunnel.c index 7abcdef12345..7abcfixedcode 100644 --- a/net/ipv6/ioam6_iptunnel.c +++ b/net/ipv6/ioam6_iptunnel.c @@ -23,9 +23,6 @@ #include #include -#define IOAM6_MASK_SHORT_FIELDS 0xff100000 -#define IOAM6_MASK_WIDE_FIELDS 0x00e00000 - struct ioam6_lwt_encap { struct ipv6_hopopt_hdr eh; u8 pad[2]; /* 2-octet padding for 4n-alignment */ -- 2.39.0