Skip to content

[BUG] snap-input crashes in mhash_key_sum_8 when the SNAP protocol table is empty #3735

Description

@nuclearcat

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

    1. abf_plugin.so 26.06-release Access Control List (ACL) Based Forwarding
    2. acl_plugin.so 26.06-release Access Control Lists (ACL)
    3. adl_plugin.so 26.06-release Allow/deny list plugin
    4. af_packet_plugin.so 26.06-release af-packet
    5. arping_plugin.so 26.06-release Arping (arping)
    6. auto_sdl_plugin.so 26.06-release Auto SDL
    7. bpf_trace_filter_plugin.so 26.06-release BPF Trace Filter Plugin
    8. bufmon_plugin.so 26.06-release Buffers monitoring plugin
    9. cdp_plugin.so 26.06-release Cisco Discovery Protocol (CDP)
  1. cnat_plugin.so 26.06-release CNat Translate
  2. crypto_sw_scheduler_plugin.so 26.06-release SW Scheduler Crypto Async Engine plugin
  3. ct6_plugin.so 26.06-release IPv6 Connection Tracker
  4. det44_plugin.so 26.06-release Deterministic NAT (CGN)
  5. dhcp_plugin.so 26.06-release Dynamic Host Configuration Protocol (DHCP)
  6. dispatch_trace_plugin.so 26.06-release Dispatch Trace
  7. dma_intel_plugin.so 26.06-release Intel DSA Backend
  8. dns_plugin.so 26.06-release Simple DNS name resolver
  9. dpdk_plugin.so 26.06-release Data Plane Development Kit (DPDK)
  10. dslite_plugin.so 26.06-release Dual-Stack Lite
  11. flowprobe_plugin.so 26.06-release Flow per Packet
  12. geneve_plugin.so 26.06-release GENEVE Tunnels
  13. gre_plugin.so 26.06-release Generic Routing Encapsulation (GRE) plugin
  14. gtpu_plugin.so 26.06-release GPRS Tunnelling Protocol, User Data (GTPv1-U)
  15. hs_apps_plugin.so 26.06-release Host Stack Applications
  16. hsi_plugin.so 26.06-release Host Stack Intercept (HSI)
  17. http_plugin.so 26.06-release Hypertext Transfer Protocol (HTTP)
  18. http_static_plugin.so 26.06-release HTTP Static Server
  19. igmp_plugin.so 26.06-release Internet Group Management Protocol (IGMP)
  20. ikev2_plugin.so 26.06-release Internet Key Exchange (IKEv2) Protocol
  21. ila_plugin.so 26.06-release Identifier Locator Addressing (ILA) for IPv6
  22. ioam_plugin.so 26.06-release Inbound Operations, Administration, and Maintenance (OAM)
  23. ip_session_redirect_plugin.so 26.06-release IP session redirect
  24. l2tp_plugin.so 26.06-release Layer 2 Tunneling Protocol v3 (L2TP)
  25. l3xc_plugin.so 26.06-release L3 Cross-Connect (L3XC)
  26. lacp_plugin.so 26.06-release Link Aggregation Control Protocol (LACP)
  27. lb_plugin.so 26.06-release Load Balancer (LB)
  28. lisp_plugin.so 26.06-release Locator ID Separation Protocol (LISP)
  29. lldp_plugin.so 26.06-release Link Layer Discovery Protocol (LLDP)
  30. mactime_plugin.so 26.06-release Time-based MAC Source Address Filter
  31. map_plugin.so 26.06-release Mapping of Address and Port (MAP)
  32. mdata_plugin.so 26.06-release Buffer metadata change tracker.
  33. memif_plugin.so 26.06-release Packet Memory Interface (memif) -- Experimental
  34. mss_clamp_plugin.so 26.06-release TCP MSS clamping plugin
  35. nat44_ei_plugin.so 26.06-release IPv4 Endpoint-Independent NAT (NAT44 EI)
  36. nat64_plugin.so 26.06-release NAT64
  37. nat66_plugin.so 26.06-release NAT66
  38. nat_plugin.so 26.06-release Network Address Translation (NAT)
  39. npol_plugin.so 26.06-release Network Policy
  40. nsh_plugin.so 26.06-release Network Service Header (NSH)
  41. nsim_plugin.so 26.06-release Network Delay Simulator
  42. osi_plugin.so 26.06-release OSI plugin
  43. perfmon_plugin.so 26.06-release Performance Monitor
  44. ping_plugin.so 26.06-release Ping (ping)
  45. pnat_plugin.so 26.06-release Policy 1:1 NAT
  46. policer_plugin.so 26.06-release Policer
  47. ppp_plugin.so 26.06-release Point-to-Point Protocol (PPP) plugin
  48. pppoe_plugin.so 26.06-release PPP over Ethernet (PPPoE)
  49. prom_plugin.so 26.06-release Prometheus Stats Exporter
  50. pvti_plugin.so 26.06-release Packet Vector Tunnel Interface plugin
  51. quic_plugin.so 26.06-release QUIC transport protocol
  52. quic_quicly_plugin.so 26.06-release Quicly QUIC Engine
  53. rdma_plugin.so 26.06-release RDMA IBverbs Device Driver
  54. sasc_plugin.so 26.06-release Session Aware Service Chaining (sasc)
  55. selog_plugin.so 26.06-release sFlow random packet sampling
  56. sfdp_services_plugin.so 26.06-release SFDP Services Plugin
  57. sflow_plugin.so 26.06-release sFlow random packet sampling
  58. soft_rss_plugin.so 26.06-release Software RSS feature arc template
  59. srmpls_plugin.so 26.06-release Segment Routing for MPLS plugin
  60. srv6ad_plugin.so 26.06-release Dynamic Segment Routing for IPv6 (SRv6) Proxy
  61. srv6adflow_plugin.so 26.06-release Dynamic Segment Routing for IPv6 (SRv6) Proxy
  62. srv6am_plugin.so 26.06-release Masquerading Segment Routing for IPv6 (SRv6) Proxy
  63. srv6as_plugin.so 26.06-release Static Segment Routing for IPv6 (SRv6) Proxy
  64. srv6mobile_plugin.so 26.06-release SRv6 GTP Endpoint Functions
  65. stn_plugin.so 26.06-release VPP Steals the NIC (STN) for Container Integration
  66. svs_plugin.so 26.06-release Source Virtual Routing and Forwarding (VRF) Select
  67. tap_plugin.so 26.06-release TAP/TUN device (virtio backend)
  68. tcpboost_plugin.so 26.06-release IPv4 TCP tracking, local loss repair, and bounded ACK suppression
  69. tlsopenssl_plugin.so 26.06-release Transport Layer Security (TLS) Engine, OpenSSL Based
  70. tlspicotls_plugin.so 26.06-release Transport Layer Security (TLS) Engine, Picotls Based
  71. tracedump_plugin.so 26.06-release Streaming packet trace dump plugin
  72. tracenode_plugin.so 26.06-release Tracing packet node
  73. tracepath_plugin.so 26.06-release Display traces based on packet paths
  74. udp_echo_plugin.so 26.06-release UDP Echo Plugin
  75. urpf_plugin.so 26.06-release Unicast Reverse Path Forwarding (uRPF)
  76. vhost_plugin.so 26.06-release Vhost-User
  77. vmxnet3_plugin.so 26.06-release VMWare Vmxnet3 Device Driver
  78. vrrp_plugin.so 26.06-release VRRP v3 (RFC 5798)
  79. vxlan-gpe_plugin.so 26.06-release VxLan GPE Tunnels
  80. vxlan_plugin.so 26.06-release VxLan Tunnels
  81. wireguard_plugin.so 26.06-release Wireguard Protocol
  • CDP state: disabled; cdp enable had not been issued

Steps to reproduce

  1. Start VPP with one or more workers.

  2. Leave CDP disabled and ensure no other SNAP consumer has registered an input protocol.

  3. Bring up an Ethernet interface handled by a worker.

  4. 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:

  1. mhash_get() and mhash_get_pair() return NULL immediately after mhash_init();
  2. an empty pair lookup does not invoke the custom key-sum callback;
  3. 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:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions