A serial-port driver that drops one extra byte on the floor — and the floor happens to be your neighbor's stack frame
CVE-2026-10644 lives in Zephyr RTOS's drivers/serial/uart_mchp_sercom_g1.c, the SERCOM-G1 UART driver used by the Microchip PIC32CM-JH SoC family. When a consumer calls uart_rx_enable() with a one-byte receive buffer while CONFIG_UART_MCHP_ASYNC=y, the RX-complete ISR kicks off a single-beat DMA transfer *while a byte is already pending in the SERCOM DATA register*. The result: one attacker-controlled byte written exactly one address past the end of the caller's RX buffer. Affected: Zephyr v4.4.0. Fixed: v4.5.0 (commit 5251d2b), which reads the first byte via CPU and only DMAs len-1 for larger buffers.
Vendor severity is MEDIUM (4.2) with vector AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L — adjacent network, high complexity, low integrity, low availability. That matches reality, and arguably overstates it. The bug is single-byte, the value is bounded by what a wired UART peer can transmit, the location is fixed (immediately after the buffer), the async path is not enabled in default in-tree PIC32CM-JH board configs, and the consumer code must independently choose to pass a 1-byte buffer. Calling this MEDIUM is generous; we're downgrading to LOW.
5 steps from start to impact.
Physical/adjacent UART access
AV:A in CVSS terms but in practice means wired access to a board's UART pins, a USB-serial bridge, a connected modem/sensor, or a compromised peripheral on the same bus.- Physical or board-adjacent access to the UART line
- Target firmware built on Zephyr v4.4.0 SERCOM-G1 driver
- Embedded devices using this SoC are typically inside sealed enclosures
- UART is rarely exposed beyond debug headers in production
Target enables async RX with len == 1
CONFIG_UART_MCHP_ASYNC=y (an opt-in Kconfig) AND must call uart_rx_enable(dev, buf, 1, timeout) with a one-byte buffer. Most realistic async consumers use ring buffers larger than one byte; one-byte async RX is an unusual API pattern.CONFIG_UART_MCHP_ASYNC=yin the firmware build- Caller passes len == 1 to
uart_rx_enable()
- Default in-tree PIC32CM-JH board configs do not enable async UART
- One-byte async DMA RX is a degenerate use case — most consumers batch
uart_rx_enable(..., 1, ...)Race the ISR
AC:H.- Ability to drive precise UART timing
- Knowledge of target baud rate and interrupt latency
- Race window is a few microseconds at typical UART baud rates
- Most real peers (sensors, modems) cannot emit bytes with sub-symbol precision
One-byte adjacent corruption
- Knowledge or guess of memory layout adjacent to the RX buffer
- No control over destination address
- No primitive to chain into code execution from a single fixed-offset byte
- Most adjacent bytes are padding, canaries, or unrelated state
Crash or transient state corruption
- Adjacent byte is functionally meaningful
- Cortex-M0+ on PIC32CM-JH has no W^X enforcement to bypass, but also no useful gadget chain from a single byte
- Watchdog reset is the most common outcome
The supporting signals.
| In-the-wild exploitation | None observed. No public reports of exploitation against PIC32CM-JH Zephyr deployments. |
|---|---|
| Public PoC | No weaponized PoC. The fix commit 5251d2b describes the race precisely enough to reproduce in a lab. |
| EPSS | 0.00143 (~0.14%) — bottom percentile, consistent with embedded-driver DoS bugs that have no internet attack surface. |
| KEV status | Not listed. Extremely unlikely to ever appear — CISA KEV catalogs internet-reachable bugs. |
| CVSS vector | AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L → adjacent, high complexity, no auth, no UI, scope unchanged, low integrity + low availability. No confidentiality impact at all. |
| Affected versions | Zephyr RTOS v4.4.0 with CONFIG_UART_MCHP_ASYNC=y, SERCOM-G1 UART driver, PIC32CM-JH SoC family |
| Fixed version | Zephyr v4.5.0 (mainline commit 5251d2bc0070be801769fb7ce7b9066fef5d9f81); backport to any 4.4-LTS forks must be picked manually |
| Exposure data | Not applicable — no public scanner (Shodan, Censys, GreyNoise) enumerates embedded SERCOM peripherals. Exposure is bounded by your PIC32CM-JH BOM. |
| Disclosed | 2026-06-28 via Zephyr GHSA GHSA-xv2x-56j7-6wc3 |
| Reporter | d3zd3z (Zephyr community contributor); reported and patched through the Zephyr security advisory process |
noisgate verdict.
The single decisive factor is attacker position: this bug requires wired UART-peer access to a specific Microchip SoC running an opt-in async config with a degenerate 1-byte buffer, and the primitive is a single fixed-offset byte with no path to code execution. Blast radius is one device, capped at crash or watchdog reset.
Why this verdict
- Attacker position is adjacent/wired, not network. No remote reachability. The attacker is already on the UART line — a position from which countless simpler attacks (replay, fuzzing, JTAG if exposed) exist.
- Three independent preconditions must all hold: (a) firmware built from Zephyr v4.4.0 SERCOM-G1, (b)
CONFIG_UART_MCHP_ASYNC=y(not default), (c) a consumer that callsuart_rx_enablewith len == 1 (an unusual API shape). The intersection is small. - Primitive ceiling is one byte at a fixed offset. Attacker controls the value, not the address. There is no documented or plausible chain from this primitive to RCE on Cortex-M0+ class parts — outcome is HardFault / watchdog / transient state flip.
- Role multiplier: PIC32CM-JH is a general-purpose Cortex-M0+ MCU used in motor control, IoT endpoints, and consumer devices — not identity, hypervisor, PKI, or supply-chain roles. There is no canonical high-value deployment role for this SoC that elevates blast radius beyond one device. The deployment-role floor does not trigger.
- EPSS 0.00143 + no KEV + no public PoC weaponization — all empirical signals align with the technical floor.
Why not higher?
MEDIUM would imply a believable path to disrupt multiple devices or a privilege transition. Neither exists: the primitive is one byte at one fixed offset on one device, requires wired access plus a hard race, and the affected config is opt-in. Vendor's 4.2 is itself charitable.
Why not lower?
IGNORE is wrong because the bug is real, the patch is straightforward, and embedded firmware updates are operationally expensive — so teams shipping PIC32CM-JH devices should still pick up the fix on their normal Zephyr LTS cadence rather than declare it unfixable noise.
What to do — in priority order.
- Audit firmware for
CONFIG_UART_MCHP_ASYNCand len==1 callers — Grep your Zephyr application tree forCONFIG_UART_MCHP_ASYNCinprj.conf/Kconfig fragments and foruart_rx_enable(.*,\s*1\s*,call sites. If neither is present, you are not vulnerable regardless of Zephyr version. noisgate MEDIUM/LOW SLA: no mitigation deadline — fold into the 365-day remediation window. - Pin async UART consumers to len ≥ 2 — If you must keep async mode on Zephyr 4.4.0, refactor consumers to use a minimum 2-byte RX buffer. The bug is specifically the len == 1 single-beat DMA path; len ≥ 2 follows the safe code path and is immune.
- Disable physical UART access on production units — Conformal-coat or remove debug headers, disable test-mode pins via fuse settings on PIC32CM-JH, and ensure production firmware does not expose a privileged UART console. This caps any future UART-driver bug at the same time.
- Schedule rebase to Zephyr 4.5.0 or backport commit 5251d2b — On your normal Zephyr LTS cadence, rebase to 4.5.0 or cherry-pick
5251d2bc0070be801769fb7ce7b9066fef5d9f81into your 4.4-based fork. Roll into the next scheduled firmware OTA — no emergency drop needed.
- Network segmentation / firewalls — the attack vector is
AV:Aover a wired UART line, not IP. Nothing on your network plane changes the attack surface. - ASLR / stack canaries — Cortex-M0+ Zephyr builds typically run from flash with static .bss/.data layouts and no meaningful ASLR; canaries don't cover a single-byte adjacent write to a non-stack object.
- EDR / SIEM rules — there is no host telemetry from a SERCOM DMA peripheral. No detection content exists or can be reasonably authored.
- SBOM scanners alone — they will flag Zephyr 4.4.0 but cannot tell you whether
CONFIG_UART_MCHP_ASYNCis enabled or whether any caller uses a 1-byte buffer. You still need source-level audit.
Crowdsourced verification payload.
Run this on your build host (or in CI) against the root of a Zephyr application tree. Invoke as ./check_cve_2026_10644.sh /path/to/zephyr-app. No special privileges required — it greps source files and Kconfig fragments to determine whether the vulnerable async + len==1 combination is reachable in your build.
#!/usr/bin/env bash
# check_cve_2026_10644.sh — Zephyr SERCOM-G1 UART async OOB write check
# Usage: ./check_cve_2026_10644.sh <path-to-zephyr-app-or-tree>
# Exit: 0 PATCHED, 1 VULNERABLE, 2 UNKNOWN
set -u
ROOT="${1:-.}"
if [ ! -d "$ROOT" ]; then
echo "UNKNOWN: path '$ROOT' is not a directory" >&2
exit 2
fi
# 1. Detect Zephyr version (VERSION file at repo root)
VER_FILE="$(find "$ROOT" -maxdepth 4 -name VERSION -path '*/zephyr/VERSION' 2>/dev/null | head -n1)"
if [ -z "$VER_FILE" ]; then
VER_FILE="$(find "$ROOT" -maxdepth 2 -name VERSION 2>/dev/null | head -n1)"
fi
ZVER="unknown"
if [ -n "$VER_FILE" ] && [ -f "$VER_FILE" ]; then
MAJ=$(grep -E '^VERSION_MAJOR' "$VER_FILE" | awk -F'=' '{print $2}' | tr -d ' ')
MIN=$(grep -E '^VERSION_MINOR' "$VER_FILE" | awk -F'=' '{print $2}' | tr -d ' ')
PAT=$(grep -E '^PATCHLEVEL' "$VER_FILE" | awk -F'=' '{print $2}' | tr -d ' ')
ZVER="${MAJ}.${MIN}.${PAT}"
fi
echo "Detected Zephyr version: $ZVER"
# 2. Look for the vulnerable driver file
DRV="$(find "$ROOT" -type f -name 'uart_mchp_sercom_g1.c' 2>/dev/null | head -n1)"
if [ -z "$DRV" ]; then
echo "PATCHED: SERCOM-G1 UART driver not present in tree"
exit 0
fi
# 3. Check whether async UART is enabled anywhere in the application config
ASYNC_HITS=$(grep -RIl --include='*.conf' --include='Kconfig*' --include='*.cmake' \
-E 'CONFIG_UART_MCHP_ASYNC[[:space:]]*=[[:space:]]*y' "$ROOT" 2>/dev/null | wc -l)
# 4. Look for len==1 uart_rx_enable call sites
LEN1_HITS=$(grep -RInE 'uart_rx_enable[[:space:]]*\([^,]+,[^,]+,[[:space:]]*1[[:space:]]*,' "$ROOT" 2>/dev/null | wc -l)
# 5. Confirm patch presence: fixed driver reads first byte via CPU
if grep -qE 'uart_poll_in|read.*first.*byte.*CPU|len[[:space:]]*-[[:space:]]*1' "$DRV" 2>/dev/null; then
PATCH_PRESENT=1
else
PATCH_PRESENT=0
fi
echo "async enabled in configs: $ASYNC_HITS file(s)"
echo "uart_rx_enable len==1 call sites: $LEN1_HITS"
echo "patch markers present in driver: $PATCH_PRESENT"
# Decision
if [ "$ZVER" = "4.4.0" ] && [ "$ASYNC_HITS" -gt 0 ] && [ "$LEN1_HITS" -gt 0 ] && [ "$PATCH_PRESENT" -eq 0 ]; then
echo "VULNERABLE: Zephyr 4.4.0 + async UART + 1-byte RX buffer + unpatched driver"
exit 1
fi
if [ "$PATCH_PRESENT" -eq 1 ] || [ "$ASYNC_HITS" -eq 0 ] || [ "$LEN1_HITS" -eq 0 ]; then
echo "PATCHED: vulnerable combination not reachable in this build"
exit 0
fi
echo "UNKNOWN: version=$ZVER async=$ASYNC_HITS len1=$LEN1_HITS patch=$PATCH_PRESENT — manual review needed"
exit 2
If you remember one thing.
5251d2b) into your next routine firmware release. Before then, run the verification script across your application repos: if you don't ship PIC32CM-JH devices, or you don't set CONFIG_UART_MCHP_ASYNC=y, or no caller uses a 1-byte uart_rx_enable, you can document this as not-applicable and move on. If you do meet all three conditions, refactor offending callers to ≥2-byte buffers as a same-day interim and ship the patched driver on your normal OTA cadence.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.