Skip to content

net: icmp: Out of bound memory read

Moderate
ceolin published GHSA-c2vg-hj83-c2vg Jan 30, 2026

Package

zephyr (zephyr)

Affected versions

<= 4.2

Patched versions

None

Description

Zephyr network stack calls icmpv6_handle_echo_request on IPv4 packet with ICMP type 128 (Echo Request type for ICMPv6). This later leads to reinterpretation of IPv4 header as IPv6 header and enables out of bound memory read. It originates from the way ICMP handlers are registered inside Zephyr. Potential possibility of leaking OOB read on network.

Zephyr version: 7823374e872 release: Zephyr 4.1.0

Build with: `west -v build --pristine -b nucleo_h753zi -d stm32h753_echo_server_stock samples/net/sockets/echo_server -- -DOVERLAY_CONFIG="prj.conf"


Additional Details:

  • Start with a minimal IPv4 header (20 Bytes) encapsulating minimal ICMP packet with type 128, code 0 and correct checksums.

  • When the packet is received and processed by the network stack, it eventually calls net_icmp_call_ipv4_handlers:

// from subsys/net/ip/icmpv4.c

enum net_verdict net_icmpv4_input(struct net_pkt *pkt,
                  struct net_ipv4_hdr *ip_hdr)
{
    ...
    ret = net_icmp_call_ipv4_handlers(pkt, ip_hdr, icmp_hdr);
    ...
}
  • net_icmp_call_ipv4_handlers then calls icmp_call_handlers which iterates through handlers and finds the corresponding handler function if there is a match.
// from subsys/net/ip/icmp.c
static int icmp_call_handlers(struct net_pkt *pkt,
                  struct net_icmp_ip_hdr *ip_hdr,
                  struct net_icmp_hdr *icmp_hdr)
{
    ...
    SYS_SLIST_FOR_EACH_CONTAINER(&handlers, ctx, node) {
        if (ctx->type == icmp_hdr->type &&
            (ctx->code == icmp_hdr->code || ctx->code == 0U)) {
            ...
            ret = ctx->handler(ctx, pkt, ip_hdr, icmp_hdr, ctx->user_data);
            ...
        }
    }
...
}
  • The problem arises because icmp.c uses a common static sys_slist_t handlers = SYS_SLIST_STATIC_INIT(&handlers); for both ICMPv4 and ICMPv6. Later Zephyr uses net_icmp_init_ctx to register handlers for different types.
// from subsys/net/ip/icmpv4.c
ret = net_icmp_init_ctx(&ctx, NET_ICMPV4_ECHO_REQUEST, 0, icmpv4_handle_echo_request);

// from subsys/net/ip/icmpv6.c
ret = net_icmp_init_ctx(&ctx, NET_ICMPV6_ECHO_REQUEST, 0, icmpv6_handle_echo_request);

Because of this there is a handler for type 128 even for ICMPv4 packets which should not exist.

  • As a result we get a backtrace like:
+bt
#0  icmpv6_handle_echo_request (ctx=0x2401a558 <ctx>, pkt=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, hdr=0x24000640 <eth0_data+1600>,
    icmp_hdr=0x24034ebe <net_buf_data_rx_bufs+202>, user_data=0x0)
    at zephyr/subsys/net/ip/icmpv6.c:117
#1  0x0803499e in icmp_call_handlers (pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, ip_hdr=ip_hdr@entry=0x24000640 <eth0_data+1600>,
    icmp_hdr=icmp_hdr@entry=0x24034ebe <net_buf_data_rx_bufs+202>) at zephyr/subsys/net/ip/icmp.c:523
#2  0x0803529e in net_icmp_call_ipv4_handlers (pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, ipv4_hdr=ipv4_hdr@entry=0x24034e82 <net_buf_data_rx_bufs+142>,
    icmp_hdr=icmp_hdr@entry=0x24034ebe <net_buf_data_rx_bufs+202>) at zephyr/subsys/net/ip/icmp.c:546
#3  0x08037840 in net_icmpv4_input (pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, ip_hdr=ip_hdr@entry=0x24034e82 <net_buf_data_rx_bufs+142>)
    at zephyr/subsys/net/ip/icmpv4.c:643
#4  0x08038798 in net_ipv4_input (pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, is_loopback=is_loopback@entry=false)
    at zephyr/subsys/net/ip/ipv4.c:388
#5  0x08022f72 in process_data (is_loopback=false, pkt=0x24037adc <_k_mem_slab_buf_rx_pkts+168>)
    at zephyr/subsys/net/ip/net_core.c:156
#6  processing_data (pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, is_loopback=is_loopback@entry=false) at zephyr/subsys/net/ip/net_core.c:174
#7  0x080235f6 in net_rx (pkt=0x24037adc <_k_mem_slab_buf_rx_pkts+168>, iface=<optimized out>) at zephyr/subsys/net/ip/net_core.c:467
#8  0x08023982 in net_queue_rx (iface=<optimized out>, pkt=0x24037adc <_k_mem_slab_buf_rx_pkts+168>) at zephyr/subsys/net/ip/net_core.c:494
#9  net_recv_data (iface=<optimized out>, pkt=pkt@entry=0x24037adc <_k_mem_slab_buf_rx_pkts+168>) at zephyr/subsys/net/ip/net_core.c:604
#10 0x08054034 in rx_thread (arg1=0x80659a8 <__device_dts_ord_118>, unused1=<optimized out>, unused2=<optimized out>) at zephyr/drivers/ethernet/eth_stm32_hal.c:736
#11 0x0800642e in z_thread_entry (entry=0x8053d41 <rx_thread>, p1=0x80659a8 <__device_dts_ord_118>, p2=0x0, p3=0x0) at zephyr/lib/os/thread_entry.c:48
#12 0xaaaaaaaa in ?? ()

Where #0 icmpv6_handle_echo_request gets called from #4 net_ipv4_input. Consequently, IPv4 header gets reinterpreted as IPv6 header inside icmpv6_handle_echo_request which can lead to out of bounds read (refer to length difference between IPv4 header and IPv6).

// from subsys/net/ip/icmpv6.c
static int icmpv6_handle_echo_request(struct net_icmp_ctx *ctx,
                      struct net_pkt *pkt,
                      struct net_icmp_ip_hdr *hdr,
                      struct net_icmp_hdr *icmp_hdr,
                      void *user_data)
{
    ...
    struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
    ...
}
  • It may also be possible to read much more out of bounds and send out on network because of reinterpreted length field inside the new IPv6 header. But it needs careful crafting of payload and passing through net_pkt_copy. I have not done it but would give it a try next month. Would be cool to leak out significant data in outgoing reply.

  • Overall there are multiple instances of net_icmp_init_ctx in the code and I assume there would be a lot of wrong function calls in the network stack in a similar fashion.

net_icmp_init_ctx(&ctx, NET_ICMPV6_ECHO_REQUEST, 0, icmpv6_handle_echo_request);
net_icmp_init_ctx(&ctx, NET_ICMPV6_MLD_QUERY, 0, handle_mld_query);
net_icmp_init_ctx(&ns_ctx, NET_ICMPV6_NS, 0, handle_ns_input);
net_icmp_init_ctx(&na_ctx, NET_ICMPV6_NA, 0, handle_na_input);

Patches

main: #98780
v4.2: #98983
v4.1: #98984
v3.7: #98985

For more information

If you have any questions or comments about this advisory:

embargo: 2026-01-28

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L

CVE ID

CVE-2025-12899

Weaknesses

Access of Resource Using Incompatible Type ('Type Confusion')

The product allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type. Learn more on MITRE.

Credits