Summary
A VPP worker can crash while processing an IEEE 802.3 LLC/SNAP frame when no SNAP input protocol has been registered.
The issue was discovered when an ordinary MikroTik CDP advertisement reached an interface handled by VPP. CDP was disabled, so the SNAP protocol table was empty. Instead of treating the protocol as unknown and dropping the packet, the
worker crashed in mhash_key_sum_8().
This was observed in an FNM-OS image built from the VPP v26.06 tag. The complete crash path is unmodified upstream VPP code. No downstream component calls or modifies the SNAP or mhash APIs.
Environment
-
VPP source: tag v26.06
-
Architecture: x86_64
-
Operating system: Custom busybox-based Linux (FNM-OS)
-
Worker configuration:
vpp# show threads
ID Name Type LWP Sched Policy (Priority) lcore Core Socket State
0 vpp_main 395 other (0) 0 0 0
1 vpp_wk_0 workers 406 other (0) 2 4 0
2 vpp_wk_1 workers 407 other (0) 4 8 0
3 vpp_wk_2 workers 408 other (0) 6 12 0
4 vpp_wk_3 workers 409 other (0) 8 16 0
5 vpp_wk_4 workers 410 other (0) 10 20 0
6 vpp_wk_5 workers 411 other (0) 12 24 0
7 vpp_wk_6 workers 412 other (0) 14 28 0
-
Version details:
Version: v26.06-release
Compiler: GCC 15.2.0
-
Plugin list:
vpp# show plugins
Plugin path is: /usr/lib/x86_64-linux-gnu/vpp_plugins, plugins shown in load order
Plugin Version Description
- abf_plugin.so 26.06-release Access Control List (ACL) Based Forwarding
- acl_plugin.so 26.06-release Access Control Lists (ACL)
- adl_plugin.so 26.06-release Allow/deny list plugin
- af_packet_plugin.so 26.06-release af-packet
- arping_plugin.so 26.06-release Arping (arping)
- auto_sdl_plugin.so 26.06-release Auto SDL
- bpf_trace_filter_plugin.so 26.06-release BPF Trace Filter Plugin
- bufmon_plugin.so 26.06-release Buffers monitoring plugin
- cdp_plugin.so 26.06-release Cisco Discovery Protocol (CDP)
- cnat_plugin.so 26.06-release CNat Translate
- crypto_sw_scheduler_plugin.so 26.06-release SW Scheduler Crypto Async Engine plugin
- ct6_plugin.so 26.06-release IPv6 Connection Tracker
- det44_plugin.so 26.06-release Deterministic NAT (CGN)
- dhcp_plugin.so 26.06-release Dynamic Host Configuration Protocol (DHCP)
- dispatch_trace_plugin.so 26.06-release Dispatch Trace
- dma_intel_plugin.so 26.06-release Intel DSA Backend
- dns_plugin.so 26.06-release Simple DNS name resolver
- dpdk_plugin.so 26.06-release Data Plane Development Kit (DPDK)
- dslite_plugin.so 26.06-release Dual-Stack Lite
- flowprobe_plugin.so 26.06-release Flow per Packet
- geneve_plugin.so 26.06-release GENEVE Tunnels
- gre_plugin.so 26.06-release Generic Routing Encapsulation (GRE) plugin
- gtpu_plugin.so 26.06-release GPRS Tunnelling Protocol, User Data (GTPv1-U)
- hs_apps_plugin.so 26.06-release Host Stack Applications
- hsi_plugin.so 26.06-release Host Stack Intercept (HSI)
- http_plugin.so 26.06-release Hypertext Transfer Protocol (HTTP)
- http_static_plugin.so 26.06-release HTTP Static Server
- igmp_plugin.so 26.06-release Internet Group Management Protocol (IGMP)
- ikev2_plugin.so 26.06-release Internet Key Exchange (IKEv2) Protocol
- ila_plugin.so 26.06-release Identifier Locator Addressing (ILA) for IPv6
- ioam_plugin.so 26.06-release Inbound Operations, Administration, and Maintenance (OAM)
- ip_session_redirect_plugin.so 26.06-release IP session redirect
- l2tp_plugin.so 26.06-release Layer 2 Tunneling Protocol v3 (L2TP)
- l3xc_plugin.so 26.06-release L3 Cross-Connect (L3XC)
- lacp_plugin.so 26.06-release Link Aggregation Control Protocol (LACP)
- lb_plugin.so 26.06-release Load Balancer (LB)
- lisp_plugin.so 26.06-release Locator ID Separation Protocol (LISP)
- lldp_plugin.so 26.06-release Link Layer Discovery Protocol (LLDP)
- mactime_plugin.so 26.06-release Time-based MAC Source Address Filter
- map_plugin.so 26.06-release Mapping of Address and Port (MAP)
- mdata_plugin.so 26.06-release Buffer metadata change tracker.
- memif_plugin.so 26.06-release Packet Memory Interface (memif) -- Experimental
- mss_clamp_plugin.so 26.06-release TCP MSS clamping plugin
- nat44_ei_plugin.so 26.06-release IPv4 Endpoint-Independent NAT (NAT44 EI)
- nat64_plugin.so 26.06-release NAT64
- nat66_plugin.so 26.06-release NAT66
- nat_plugin.so 26.06-release Network Address Translation (NAT)
- npol_plugin.so 26.06-release Network Policy
- nsh_plugin.so 26.06-release Network Service Header (NSH)
- nsim_plugin.so 26.06-release Network Delay Simulator
- osi_plugin.so 26.06-release OSI plugin
- perfmon_plugin.so 26.06-release Performance Monitor
- ping_plugin.so 26.06-release Ping (ping)
- pnat_plugin.so 26.06-release Policy 1:1 NAT
- policer_plugin.so 26.06-release Policer
- ppp_plugin.so 26.06-release Point-to-Point Protocol (PPP) plugin
- pppoe_plugin.so 26.06-release PPP over Ethernet (PPPoE)
- prom_plugin.so 26.06-release Prometheus Stats Exporter
- pvti_plugin.so 26.06-release Packet Vector Tunnel Interface plugin
- quic_plugin.so 26.06-release QUIC transport protocol
- quic_quicly_plugin.so 26.06-release Quicly QUIC Engine
- rdma_plugin.so 26.06-release RDMA IBverbs Device Driver
- sasc_plugin.so 26.06-release Session Aware Service Chaining (sasc)
- selog_plugin.so 26.06-release sFlow random packet sampling
- sfdp_services_plugin.so 26.06-release SFDP Services Plugin
- sflow_plugin.so 26.06-release sFlow random packet sampling
- soft_rss_plugin.so 26.06-release Software RSS feature arc template
- srmpls_plugin.so 26.06-release Segment Routing for MPLS plugin
- srv6ad_plugin.so 26.06-release Dynamic Segment Routing for IPv6 (SRv6) Proxy
- srv6adflow_plugin.so 26.06-release Dynamic Segment Routing for IPv6 (SRv6) Proxy
- srv6am_plugin.so 26.06-release Masquerading Segment Routing for IPv6 (SRv6) Proxy
- srv6as_plugin.so 26.06-release Static Segment Routing for IPv6 (SRv6) Proxy
- srv6mobile_plugin.so 26.06-release SRv6 GTP Endpoint Functions
- stn_plugin.so 26.06-release VPP Steals the NIC (STN) for Container Integration
- svs_plugin.so 26.06-release Source Virtual Routing and Forwarding (VRF) Select
- tap_plugin.so 26.06-release TAP/TUN device (virtio backend)
- tcpboost_plugin.so 26.06-release IPv4 TCP tracking, local loss repair, and bounded ACK suppression
- tlsopenssl_plugin.so 26.06-release Transport Layer Security (TLS) Engine, OpenSSL Based
- tlspicotls_plugin.so 26.06-release Transport Layer Security (TLS) Engine, Picotls Based
- tracedump_plugin.so 26.06-release Streaming packet trace dump plugin
- tracenode_plugin.so 26.06-release Tracing packet node
- tracepath_plugin.so 26.06-release Display traces based on packet paths
- udp_echo_plugin.so 26.06-release UDP Echo Plugin
- urpf_plugin.so 26.06-release Unicast Reverse Path Forwarding (uRPF)
- vhost_plugin.so 26.06-release Vhost-User
- vmxnet3_plugin.so 26.06-release VMWare Vmxnet3 Device Driver
- vrrp_plugin.so 26.06-release VRRP v3 (RFC 5798)
- vxlan-gpe_plugin.so 26.06-release VxLan GPE Tunnels
- vxlan_plugin.so 26.06-release VxLan Tunnels
- wireguard_plugin.so 26.06-release Wireguard Protocol
- CDP state: disabled;
cdp enable had not been issued
Steps to reproduce
-
Start VPP with one or more workers.
-
Leave CDP disabled and ensure no other SNAP consumer has registered an input protocol.
-
Bring up an Ethernet interface handled by a worker.
-
Send or replay an IEEE 802.3 LLC/SNAP frame containing:
- LLC DSAP:
0xaa
- LLC SSAP:
0xaa
- LLC control:
0x03
- SNAP OUI:
00:00:0c
- SNAP protocol:
0x2000 (CDP)
A normal MikroTik CDP advertisement triggered the observed failure.
Expected behavior
snap-input should classify the frame as an unknown SNAP protocol and send it to error-drop with SNAP_ERROR_UNKNOWN_PROTOCOL.
Actual behavior
The VPP worker receives SIGSEGV. The symbolic crash path is:
snap_input
snap_get_protocol_info
mhash_get
mhash_get_pair
_hash_get_pair
lookup
mhash_key_sum_8
In the core:
- the SNAP protocol hash has zero entries;
- the mhash lookup key is the temporary-key sentinel,
~0;
mhash_key_sum_8() resolves the corresponding key data to 0x0 and faults while hashing it.
Analysis
snap_init() initializes snap_main.protocol_hash as an empty mhash.
The CDP plugin does not register its SNAP protocols during plugin initialization. Registration of Cisco OUI 0x00000c, protocols 0x2000 and 0x2004, occurs only after CDP_EVENT_ENABLE.
However, snap_input() calls snap_get_protocol_info() unconditionally for incoming SNAP traffic. snap_get_protocol_info() then calls mhash_get() even when sm->protocols and the underlying hash contain no entries.
mhash_get_pair() establishes the temporary ~0 key and calls hash_get_pair(). The underlying _hash_get_pair() calls lookup() unconditionally. This differs from _hash_get(), which explicitly returns without searching when
h->elts == 0.
Consequently, an empty-table lookup reaches the custom mhash key-sum callback even though no match is possible. In the observed worker core, the per-thread temporary key data resolved to NULL.
The empty-table asymmetry is confirmed from the source. The exact reason the worker's temporary-key data was NULL may warrant separate review of mhash's per-thread temporary-key handling.
Downstream mitigation
The following guard in snap_get_protocol_info() prevents the crash and has been deployed successfully:
if (PREDICT_FALSE (vec_len (sm->protocols) == 0))
return 0;
This is logically safe because an empty protocol vector cannot contain a matching protocol.
Suggested upstream fix
A generic fix would make pair lookups return immediately for an empty hash, matching _hash_get():
__clib_export hash_pair_t *
_hash_get_pair (void *v, uword key)
{
hash_t *h = hash_header (v);
if (!v || h->elts == 0)
return 0;
return lookup (v, key, GET, 0, 0);
}
Alternatively, mhash_get_pair() could check mhash_elts(h) == 0 before constructing its temporary key. That narrower fix would also avoid unnecessary per-thread temporary-key work.
The SNAP guard could be retained as defense in depth and as a fast path.
Regression coverage
Please add an empty-hash/mhash regression test that verifies:
mhash_get() and mhash_get_pair() return NULL immediately after mhash_init();
- an empty pair lookup does not invoke the custom key-sum callback;
- the behavior is safe when the lookup is performed from a VPP worker thread.
Impact
An L2-adjacent sender can crash a VPP worker by transmitting SNAP traffic when the SNAP protocol table is empty. A valid CDP advertisement is sufficient; malformed traffic is not required.
Downstream modifications ruled out
- The crash path consists entirely of upstream VPP functions.
- No downstream patch modified the affected VPP files before the mitigation was added.
- The trigger was a normal MikroTik CDP advertisement.
The relevant upstream references are:
Summary
A VPP worker can crash while processing an IEEE 802.3 LLC/SNAP frame when no SNAP input protocol has been registered.
The issue was discovered when an ordinary MikroTik CDP advertisement reached an interface handled by VPP. CDP was disabled, so the SNAP protocol table was empty. Instead of treating the protocol as unknown and dropping the packet, the
worker crashed in
mhash_key_sum_8().This was observed in an FNM-OS image built from the VPP v26.06 tag. The complete crash path is unmodified upstream VPP code. No downstream component calls or modifies the SNAP or mhash APIs.
Environment
VPP source: tag
v26.06Architecture: x86_64
Operating system: Custom busybox-based Linux (FNM-OS)
Worker configuration:
vpp# show threads
ID Name Type LWP Sched Policy (Priority) lcore Core Socket State
0 vpp_main 395 other (0) 0 0 0
1 vpp_wk_0 workers 406 other (0) 2 4 0
2 vpp_wk_1 workers 407 other (0) 4 8 0
3 vpp_wk_2 workers 408 other (0) 6 12 0
4 vpp_wk_3 workers 409 other (0) 8 16 0
5 vpp_wk_4 workers 410 other (0) 10 20 0
6 vpp_wk_5 workers 411 other (0) 12 24 0
7 vpp_wk_6 workers 412 other (0) 14 28 0
Version details:
Version: v26.06-release
Compiler: GCC 15.2.0
Plugin list:
vpp# show plugins
Plugin path is: /usr/lib/x86_64-linux-gnu/vpp_plugins, plugins shown in load order
Plugin Version Description
cdp enablehad not been issuedSteps to reproduce
Start VPP with one or more workers.
Leave CDP disabled and ensure no other SNAP consumer has registered an input protocol.
Bring up an Ethernet interface handled by a worker.
Send or replay an IEEE 802.3 LLC/SNAP frame containing:
0xaa0xaa0x0300:00:0c0x2000(CDP)A normal MikroTik CDP advertisement triggered the observed failure.
Expected behavior
snap-inputshould classify the frame as an unknown SNAP protocol and send it toerror-dropwithSNAP_ERROR_UNKNOWN_PROTOCOL.Actual behavior
The VPP worker receives SIGSEGV. The symbolic crash path is:
In the core:
~0;mhash_key_sum_8()resolves the corresponding key data to0x0and faults while hashing it.Analysis
snap_init()initializessnap_main.protocol_hashas an empty mhash.The CDP plugin does not register its SNAP protocols during plugin initialization. Registration of Cisco OUI
0x00000c, protocols0x2000and0x2004, occurs only afterCDP_EVENT_ENABLE.However,
snap_input()callssnap_get_protocol_info()unconditionally for incoming SNAP traffic.snap_get_protocol_info()then callsmhash_get()even whensm->protocolsand the underlying hash contain no entries.mhash_get_pair()establishes the temporary~0key and callshash_get_pair(). The underlying_hash_get_pair()callslookup()unconditionally. This differs from_hash_get(), which explicitly returns without searching whenh->elts == 0.Consequently, an empty-table lookup reaches the custom mhash key-sum callback even though no match is possible. In the observed worker core, the per-thread temporary key data resolved to NULL.
The empty-table asymmetry is confirmed from the source. The exact reason the worker's temporary-key data was NULL may warrant separate review of mhash's per-thread temporary-key handling.
Downstream mitigation
The following guard in
snap_get_protocol_info()prevents the crash and has been deployed successfully:This is logically safe because an empty protocol vector cannot contain a matching protocol.
Suggested upstream fix
A generic fix would make pair lookups return immediately for an empty hash, matching
_hash_get():Alternatively,
mhash_get_pair()could checkmhash_elts(h) == 0before constructing its temporary key. That narrower fix would also avoid unnecessary per-thread temporary-key work.The SNAP guard could be retained as defense in depth and as a fast path.
Regression coverage
Please add an empty-hash/mhash regression test that verifies:
mhash_get()andmhash_get_pair()return NULL immediately aftermhash_init();Impact
An L2-adjacent sender can crash a VPP worker by transmitting SNAP traffic when the SNAP protocol table is empty. A valid CDP advertisement is sufficient; malformed traffic is not required.
Downstream modifications ruled out
The relevant upstream references are: