Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ipv6: ioam: fix heap buffer overflow in __ioam6_fill_trace_data()

On the receive path, __ioam6_fill_trace_data() uses trace->nodelen
to decide how much data to write for each node. It trusts this field
as-is from the incoming packet, with no consistency check against
trace->type (the 24-bit field that tells which data items are
present). A crafted packet can set nodelen=0 while setting type bits
0-21, causing the function to write ~100 bytes past the allocated
region (into skb_shared_info), which corrupts adjacent heap memory
and leads to a kernel panic.

Add a shared helper ioam6_trace_compute_nodelen() in ioam6.c to
derive the expected nodelen from the type field, and use it:

- in ioam6_iptunnel.c (send path, existing validation) to replace
the open-coded computation;
- in exthdrs.c (receive path, ipv6_hop_ioam) to drop packets whose
nodelen is inconsistent with the type field, before any data is
written.

Per RFC 9197, bits 12-21 are each short (4-octet) fields, so they
are included in IOAM6_MASK_SHORT_FIELDS (changed from 0xff100000 to
0xff1ffc00).

Fixes: 9ee11f0fff20 ("ipv6: ioam: Data plane support for Pre-allocated Trace")
Cc: stable@vger.kernel.org
Signed-off-by: Junxi Qian <qjx1298677004@gmail.com>
Reviewed-by: Justin Iurman <justin.iurman@gmail.com>
Link: https://patch.msgid.link/20260211040412.86195-1-qjx1298677004@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Qanux and committed by
Jakub Kicinski
6db8b56e a68a9bd0

+22 -9
+2
include/net/ioam6.h
··· 60 60 struct ioam6_trace_hdr *trace, 61 61 bool is_input); 62 62 63 + u8 ioam6_trace_compute_nodelen(u32 trace_type); 64 + 63 65 int ioam6_init(void); 64 66 void ioam6_exit(void); 65 67
+5
net/ipv6/exthdrs.c
··· 931 931 if (hdr->opt_len < 2 + sizeof(*trace) + trace->remlen * 4) 932 932 goto drop; 933 933 934 + /* Inconsistent Pre-allocated Trace header */ 935 + if (trace->nodelen != 936 + ioam6_trace_compute_nodelen(be32_to_cpu(trace->type_be32))) 937 + goto drop; 938 + 934 939 /* Ignore if the IOAM namespace is unknown */ 935 940 ns = ioam6_namespace(dev_net(skb->dev), trace->namespace_id); 936 941 if (!ns)
+14
net/ipv6/ioam6.c
··· 690 690 return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params); 691 691 } 692 692 693 + #define IOAM6_MASK_SHORT_FIELDS 0xff1ffc00 694 + #define IOAM6_MASK_WIDE_FIELDS 0x00e00000 695 + 696 + u8 ioam6_trace_compute_nodelen(u32 trace_type) 697 + { 698 + u8 nodelen = hweight32(trace_type & IOAM6_MASK_SHORT_FIELDS) 699 + * (sizeof(__be32) / 4); 700 + 701 + nodelen += hweight32(trace_type & IOAM6_MASK_WIDE_FIELDS) 702 + * (sizeof(__be64) / 4); 703 + 704 + return nodelen; 705 + } 706 + 693 707 static void __ioam6_fill_trace_data(struct sk_buff *skb, 694 708 struct ioam6_namespace *ns, 695 709 struct ioam6_trace_hdr *trace,
+1 -9
net/ipv6/ioam6_iptunnel.c
··· 22 22 #include <net/ip6_route.h> 23 23 #include <net/addrconf.h> 24 24 25 - #define IOAM6_MASK_SHORT_FIELDS 0xff100000 26 - #define IOAM6_MASK_WIDE_FIELDS 0xe00000 27 - 28 25 struct ioam6_lwt_encap { 29 26 struct ipv6_hopopt_hdr eh; 30 27 u8 pad[2]; /* 2-octet padding for 4n-alignment */ ··· 90 93 trace->type.bit21 | trace->type.bit23) 91 94 return false; 92 95 93 - trace->nodelen = 0; 94 96 fields = be32_to_cpu(trace->type_be32); 95 - 96 - trace->nodelen += hweight32(fields & IOAM6_MASK_SHORT_FIELDS) 97 - * (sizeof(__be32) / 4); 98 - trace->nodelen += hweight32(fields & IOAM6_MASK_WIDE_FIELDS) 99 - * (sizeof(__be64) / 4); 97 + trace->nodelen = ioam6_trace_compute_nodelen(fields); 100 98 101 99 return true; 102 100 }