Skip to main content
Vulnerability Database/CVE-2026-19938

CVE-2026-19938: Bluetooth ATT Use-After-Free Vulnerability

CVE-2026-19938 is a use-after-free flaw in Bluetooth ATT transmission that causes memory corruption when signing fails. This post explains its technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-19938 Overview

CVE-2026-19938 is a use-after-free vulnerability [CWE-416] in the Zephyr RTOS Bluetooth host stack. The flaw lives in the Attribute Protocol (ATT) transmit path in subsys/bluetooth/host/att.c, where chan_send() violates the net_buf ownership contract on a signing failure. When bt_smp_sign() returns an error for a Signed Write Command, the code calls net_buf_unref(buf) and then returns the error, causing callers to re-queue, retry, or dereference an already-freed pooled buffer. The condition is reachable by a remote Bluetooth peer that pairs without accepting the Connection Signature Resolving Key (CSRK), pairs with No Bonding, or leaves the link unpaired or unencrypted.

Critical Impact

Remote adjacent attackers can trigger host-side memory corruption in the Bluetooth stack, resulting in list corruption, double free, out-of-bounds writes, and crashes on devices with CONFIG_BT_SIGNING enabled.

Affected Products

  • Zephyr RTOS Bluetooth host (subsys/bluetooth/host/att.c)
  • Builds with the deprecated CONFIG_BT_SIGNING option enabled
  • Applications invoking bt_gatt_write_without_response_cb() with the sign flag set on unencrypted connections

Discovery Timeline

  • 2026-10-13 - CVE-2026-19938 published to NVD
  • 2026-10-14 - Last updated in NVD database

Technical Details for CVE-2026-19938

Vulnerability Analysis

The defect is a buffer ownership contract violation on the ATT signed-write error path. Every other error exit from chan_send() leaves the caller holding the net_buf reference, but the BT_ATT_OP_SIGNED_WRITE_CMD branch unreferenced the buffer before returning the error. Downstream callers assume they still own the buffer and act accordingly: process_queue() re-queues it with k_queue_prepend(), bt_att_chan_send_rsp() places it back on chan->tx_queue, bt_att_chan_req_send() restores it into the request structure, and att_req_retry() performs its own unref.

The damage multiplies because struct net_buf's node field serves triple duty as the ATT TX FIFO linkage word, the deferred-destroy list used by att_tx_destroy(), and the pool free list. A freed-and-re-queued buffer corrupts all three lists simultaneously. The buffer can also be reissued by bt_att_create_pdu() to a new PDU while the ATT TX path still holds it, producing a pooled buffer with two owners.

A secondary bug on the retry path saved net_buf_simple_state only after signing. A buffer restored after a bt_l2cap_send_pdu() failure retained its 12-octet signature, and a subsequent retry re-signed and appended a second signature via net_buf_add(buf, 12) on a buffer sized for one, writing past the end of the pooled buffer.

Root Cause

The root cause is an incorrect net_buf_unref(buf) call in the signed-write error handler combined with mis-ordered state saving. bt_smp_sign() returns -ENOENT whenever no local CSRK exists for the peer. smp_distribute_keys() only stores a local CSRK when the peer's pairing key-distribution field requests the signing key and bonding was agreed, so the failing path is under remote peer control.

Attack Vector

A remote Bluetooth peer within radio range pairs with the victim without accepting the CSRK, pairs with No Bonding, remains unpaired, or keeps the link unencrypted. When the victim application subsequently calls bt_gatt_write_without_response_cb() with the sign flag, bt_smp_sign() fails and the mishandled free occurs. Later queue drain at disconnect or PDU reuse triggers memory corruption.

c
 	}
 
 	if (hdr->code == BT_ATT_OP_SIGNED_WRITE_CMD) {
+		net_buf_simple_save(&buf->b, &state);
+
 		err = bt_smp_sign(chan->att->conn, buf);
-		if (err) {
+		if (err != 0) {
 			LOG_ERR("Error signing data");
-			net_buf_unref(buf);
+			/* Restore the buffer state, since the callers retain
+			 * ownership of the buffer on failure and may retry
+			 * sending it later.
+			 */
+			net_buf_simple_restore(&buf->b, &state);
 			return err;
 		}
-	}
 
-	net_buf_simple_save(&buf->b, &state);
+		/* Keep the pre-signing state saved above: bt_smp_sign()
+		 * already consumed and persisted a sign counter value, so if
+		 * the send below fails the buffer must be restored to its
+		 * unsigned form. Retrying will then sign it again (consuming
+		 * the next counter value, which is fine since the spec only
+		 * requires the counter to strictly increase). Restoring the
+		 * already-signed state instead would let a retry append a
+		 * second signature onto a buffer sized for only one.
+		 */
+	} else {

Source: Zephyr commit ba8627d. The patch removes the incorrect net_buf_unref(buf), saves net_buf_simple_state before signing, and restores the pre-signing state on failure so retries sign exactly once.

Detection Methods for CVE-2026-19938

Indicators of Compromise

  • Kernel assertion failures or crash logs referencing net_buf, att_tx_destroy(), or bt_att_create_pdu()
  • LOG_ERR("Error signing data") entries from chan_send() immediately preceding instability
  • Unexpected Bluetooth disconnections or stack resets after pairing attempts that decline CSRK distribution

Detection Strategies

  • Audit Zephyr builds for CONFIG_BT_SIGNING=y combined with GATT clients that set the sign flag on bt_gatt_write_without_response_cb()
  • Enable net_buf tailroom assertions during testing to surface the 12-octet out-of-bounds write on the retry path
  • Fuzz Bluetooth pairing flows with peers that negotiate No Bonding or omit the signing key from the key-distribution field

Monitoring Recommendations

  • Collect crash dumps and serial logs from fleet devices running Zephyr Bluetooth firmware
  • Track firmware versions in asset inventory to identify devices still shipping the vulnerable att.c logic
  • Alert on repeated SMP sign failures correlated with downstream Bluetooth stack faults

How to Mitigate CVE-2026-19938

Immediate Actions Required

  • Apply the upstream Zephyr fix in commit ba8627d91c8ce15c26224cbc7f7f92bb66f56b03 to all affected firmware images
  • Rebuild and reflash devices that enable CONFIG_BT_SIGNING and ship applications using signed GATT writes
  • Review whether CONFIG_BT_SIGNING is actually required; the option is deprecated and can be disabled where unused

Patch Information

The fix is published in the Zephyr commit ba8627d and documented in GitHub Security Advisory GHSA-p3r4-qw98-j59m. The patch saves buffer state before signing, restores it on failure, and removes the incorrect net_buf_unref() call so callers retain ownership per the documented contract.

Workarounds

  • Disable CONFIG_BT_SIGNING in Zephyr configuration if the application does not require ATT signed writes
  • Modify application code to avoid setting the sign flag on bt_gatt_write_without_response_cb() over unencrypted or unbonded links
  • Require encrypted and bonded connections with full key distribution before permitting signed-write operations
bash
# Disable the deprecated signing feature in prj.conf
CONFIG_BT_SIGNING=n

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.