CVE-2026-15891 Overview
CVE-2026-15891 is a NULL pointer dereference vulnerability [CWE-476] in the Zephyr RTOS MQTT-SN client keepalive handler. The defect resides in process_ping() within subsys/net/lib/mqtt_sn/mqtt_sn.c. When PINGREQ retries are exhausted, the code invokes SYS_SLIST_PEEK_HEAD_CONTAINER(&client->gateways, gw, next) as a pure expression and discards the return value, leaving gw as its NULL initializer.
The subsequent dereference of gw->gw_id and call chain into k_mem_slab_free(&gateways, NULL) triggers a kernel panic or, on targets where address zero is writable, silent slab allocator corruption. A remote or on-path attacker can force the condition by advertising as a gateway and refusing to answer keepalives.
Critical Impact
Remotely triggerable denial of service against MQTT-SN clients over unauthenticated UDP, resulting in kernel panic or memory allocator corruption.
Affected Products
- Zephyr RTOS MQTT-SN client (subsys/net/lib/mqtt_sn/mqtt_sn.c)
- Zephyr Project builds incorporating the vulnerable process_ping() handler prior to commit bd21e954
- Embedded and IoT devices using Zephyr's MQTT-SN client over UDP
Discovery Timeline
- 2026-09-13 - CVE-2026-15891 published to NVD
- 2026-09-14 - Last updated in NVD database
Technical Details for CVE-2026-15891
Vulnerability Analysis
The vulnerability lives in the MQTT-SN client's keepalive retry logic. When client->ping_retries reaches zero, process_ping() attempts to remove the non-responsive gateway from the client's gateway list. The removal path calls SYS_SLIST_PEEK_HEAD_CONTAINER intending to obtain a pointer to the head gateway record.
That macro is a pure expression in Zephyr's linked list API. It does not assign to the gw argument. Because gw was initialized to NULL earlier in the function, it remains NULL regardless of list contents. The code then dereferences gw->gw_id in a log statement and passes NULL into mqtt_sn_gw_destroy(), which eventually invokes k_mem_slab_free(&gateways, NULL).
With CONFIG_MEM_SLAB_POINTER_VALIDATE enabled, the invalid pointer triggers k_panic(). In the default configuration, the free path executes *(char **)mem = slab->free_list; — a write through NULL. On MMU-equipped targets this faults; on targets where address zero is writable, the slab free list is silently corrupted.
Root Cause
The root cause is misuse of SYS_SLIST_PEEK_HEAD_CONTAINER as a statement rather than an expression whose return value must be captured. The sibling function process_advertise() correctly uses SYS_SLIST_FOR_EACH_CONTAINER_SAFE and is not affected. This is a classic NULL pointer dereference [CWE-476] resulting from an unchecked API contract.
Attack Vector
MQTT-SN runs over UDP with no authentication requirement. Any peer capable of reaching the client's MQTT-SN port can act as a gateway. An attacker advertises itself as a gateway via ADVERTISE messages, allows the client to connect, then stops responding to PINGREQ traffic. Once ping_retries decrements to zero the vulnerable branch executes.
An adjacent attacker on the same broadcast domain can also blackhole PINGRESP traffic from a legitimate gateway to reach the same code path. No user interaction and no privileges are required.
// Patch from Zephyr commit bd21e954c36be105a374bbc090623810f1a17ee3
// subsys/net/lib/mqtt_sn/mqtt_sn.c - process_ping()
if (!client->ping_retries--) {
LOG_WRN("Ping ran out of retries");
mqtt_sn_disconnect_internal(client);
- SYS_SLIST_PEEK_HEAD_CONTAINER(&client->gateways, gw, next);
+ gw = SYS_SLIST_PEEK_HEAD_CONTAINER(&client->gateways, gw, next);
LOG_DBG("Removing non-responsive GW 0x%02x", gw->gw_id);
mqtt_sn_gw_destroy(client, gw);
return -ETIMEDOUT;
Source: Zephyr commit bd21e954
Detection Methods for CVE-2026-15891
Indicators of Compromise
- Unexpected kernel panic logs on Zephyr devices containing the string Ping ran out of retries immediately preceding the crash
- MQTT-SN clients that repeatedly connect to gateways which never issue PINGRESP replies
- Malformed or spoofed MQTT-SN ADVERTISE frames on UDP originating from unexpected LAN peers
Detection Strategies
- Audit firmware images for the vulnerable pattern in subsys/net/lib/mqtt_sn/mqtt_sn.c where SYS_SLIST_PEEK_HEAD_CONTAINER is called without capturing the return value
- Enable CONFIG_MEM_SLAB_POINTER_VALIDATE in Zephyr builds to convert silent slab corruption into an observable panic that can be logged
- Correlate device-side crash telemetry with network flow records showing dropped or missing MQTT-SN keepalive responses
Monitoring Recommendations
- Log and alert on MQTT-SN client disconnect events followed by device reboots within the keepalive retry window
- Monitor UDP traffic on MQTT-SN ports for unexpected ADVERTISE broadcasts and asymmetric PINGREQ/PINGRESP counts
- Track repeated crash-loop behavior on fleets of Zephyr-based IoT devices as a signal of remote exploitation attempts
How to Mitigate CVE-2026-15891
Immediate Actions Required
- Rebuild affected Zephyr firmware from a tree that includes commit bd21e954c36be105a374bbc090623810f1a17ee3 and deploy to all impacted devices
- Restrict MQTT-SN traffic to trusted network segments and block untrusted UDP peers from reaching client ports
- Enable CONFIG_MEM_SLAB_POINTER_VALIDATE on devices where silent memory corruption at address zero is a plausible outcome
Patch Information
The upstream fix assigns the return value of SYS_SLIST_PEEK_HEAD_CONTAINER to gw, ensuring the pointer references the actual head gateway record before dereference. Details are published in GitHub Security Advisory GHSA-c4g8-4f9p-4746 and the corresponding Zephyr commit bd21e954.
Workarounds
- Disable the MQTT-SN client subsystem in Zephyr builds where the protocol is not strictly required
- Place devices behind a network filter that permits MQTT-SN UDP traffic only from an allow-listed gateway address
- Increase ping_retries is not a remediation; only network isolation or patching prevents the vulnerable branch from executing
# Verify Zephyr source tree contains the fix
git -C zephyr log --oneline bd21e954c36be105a374bbc090623810f1a17ee3 \
-- subsys/net/lib/mqtt_sn/mqtt_sn.c
# Disable MQTT-SN in prj.conf if the protocol is not required
# CONFIG_MQTT_SN=n
# Enable slab pointer validation to convert silent corruption into a panic
# CONFIG_MEM_SLAB_POINTER_VALIDATE=y
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.