Where
-Infinity
0
Severity
8.8
AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H

The Zephyr ADIN2111/ADIN1110 10BASE-T1S/T1L Ethernet driver (drivers/ethernet/ethadin2111.c) reassembles received Ethernet frames in OPEN Alliance (OA) SPI mode by copying device-supplied 64-byte data chunks into a fixed static buffer ctx->buf of size CONFIGETHADIN2111BUFFERSIZE (default 1524 bytes). In ethadin2111oadataread(), each valid chunk was memcpy'd into ctx->buf[ctx->scur] and the write cursor scur advanced, with no check that scur + len stayed within the buffer. The number of chunks (up to 255, from the BUFSTS RCA field) and the per-chunk length are taken entirely from the frame data received off the wire; the cursor is only reset on a start-of-frame chunk. An attacker on the single-pair Ethernet segment can therefore send a frame whose reassembled size exceeds the configured buffer, causing the driver's RX offload thread to write attacker-controlled frame bytes past the end of the static buffer into adjacent driver/kernel memory (up to roughly 14.8 KB in the worst case). This is a remotely/adjacently reachable out-of-bounds write (CWE-787) that can corrupt memory and cause denial of service or potentially code execution. The defect was introduced when OA SPI support was added (commit 0ca8b0756b1) and shipped in releases v3.7.0 through v4.4.0. The fix adds a bounds check that drops the oversized frame and resets the cursor before the copy.

First published (updated )
Severity
4.6
Divide by Zero
AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

The Zephyr ext2 file system validates the on-disk superblock in ext2verifydisksuperblock() (subsys/fs/ext2/ext2impl.c) before completing a mount. The validator checked the magic number, block size, revision and feature flags, but did not verify that the on-disk fields sblockspergroup and sinodespergroup are non-zero. Both fields are read directly from the image and are later used as divisors during mount-time initialization.

During mount, getngroups() divides and modulos sblockscount by sblockspergroup (reached via ext2fetchblockgroup() from ext2initfs()), and getitableentry() divides (ino - 1) by sinodespergroup when fetching the root inode (both in subsys/fs/ext2/ext2diskops.c). A superblock with either field set to zero therefore causes an integer division by zero during the mount sequence.

An attacker who can present a crafted ext2 image to a device that mounts ext2 — removable media such as an SD card or a USB mass-storage device — can trigger this. On ARMv7-M / ARMv8-M-mainline Cortex-M targets, divide-by-zero trapping is enabled (SCBCCRDIV0TRP), so the division raises a UsageFault that Zephyr treats as a fatal error, producing a denial of service. The impact is limited to availability; the malformed value is consumed only as a divisor.

The fix rejects a zero sblockspergroup or sinodespergroup in the superblock validator, returning -EINVAL so the mount fails before any block-group or inode I/O occurs.

First published (updated )
Severity
7
Race Condition
AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:L

In Zephyr's userspace dynamic-objects subsystem, threadidxalloc() in kernel/userspace/userspace.c allocated a new thread permission index from the global threadidxmap[] bitmap without holding listslock.

On SMP systems, two user-mode threads invoking the kobjectalloc(KOBJTHREAD) syscall concurrently can both observe the same low free bit, perform the same non-atomic RMW to clear it, and return the identical tidx.

The two newly created KOBJTHREAD objects are then assigned the same threadid, so the two user threads alias a single bit position in every kernel object's perms[] bitfield: any subsequent grant of access on a kernel object to one thread is implicitly a grant to the other, defeating userspace ACL isolation. A secondary lost-update window between the unlocked &=~BIT() in alloc and the locked |= BIT() in threadidxfree() can also leak entries from the thread-index pool.

The defect is reachable from any user-mode thread via the unrestricted syscall kobjectalloc and is gated on CONFIGUSERSPACE, CONFIGDYNAMICOBJECTS, and CONFIGSMP. The flaw was introduced when the per-thread permission index was added in 2018 and is present in every release up to and including v4.4.0. Fixed by holding listslock across the bitmap RMW and the permissions clear (and inlining the objlist traversal that previously took the lock itself).

First published (updated )
Severity
7.8
AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H

The userspace verifier zvrfylogfilterset() for the logfilterset syscall in subsys/logging/logmgmt.c performed a signed comparison against the int16t srcid parameter: srcid < (int16t)logsrccntget(domainid). Any negative value for srcid (e.g. -1) trivially satisfied this check and was forwarded into zimpllogfilterset, where it propagated to filterset() and ultimately to getdynamicfilter(), which uses sourceid as an unsigned index into the linker-section array &TYPESECTIONSTART(logdynamic)[sourceid].filters.

After implicit conversion through uint32t, an int16t -1 becomes 0xFFFFFFFF, indexing logdynamic far out of bounds and causing the kernel to perform an OOB read and an OOB read-modify-write (LOGFILTERSLOTGET/SET) against memory adjacent to the logdynamic section.

The written value is a constrained 3-bit log level slot within the targeted 32-bit word, but the target address is attacker-chosen (a small negative offset from logdynamic) and the write occurs in supervisor mode following a syscall from an unprivileged user thread, providing a kernel memory-corruption / privilege-escalation primitive.

The defect is reachable on any build with CONFIGUSERSPACE=y and CONFIGLOGRUNTIMEFILTERING=y. Present from Zephyr v3.3.0 through v4.4.1. The fix replaces the signed bound check with an unsigned comparison: (uint32t)srcid < logsrccntget(domainid), which correctly rejects negative inputs.

First published (updated )
Severity
4.6
AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

In the Synopsys DesignWare I2C driver (drivers/i2c/i2cdw.c) operating in target/slave mode, the rxfull interrupt handler gates the writerequested() callback on dw->state != CMDSEND, and dw->state is only reset to READY on a STOP interrupt. The STARTDET interrupt, whose handler in i2cdwslavereadclearintrbits() would reset the state on every (re)START, was never added to the enabled interrupt mask in i2cdwslaveregister(), so that recovery path was dead code.

As a result, if the STOP interrupt is lost (bus glitch/reset, or a concurrent master driving STOP) or the bus master issues a legal WRITE-repeated-START-WRITE sequence with the same direction, the driver remains in CMDSEND permanently and never invokes writerequested() again for the life of the target.

An I2C master on the same physical bus can deliberately trigger this, causing the I2C target function to malfunction for all subsequent write transactions and desynchronizing consumer framing state (e.g. MCTP-over-I2C), a recoverable-by-reset denial of service of the target peripheral.

The fix unmasks STARTDET so the state is reset at every bus (re)START. Impact is availability-only over a local board-level bus; no memory corruption results in the in-tree consumer, whose per-byte buffer write is independently bounds-checked.

First published (updated )
Severity
6.5
AV:A/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L

Zephyr's Bluetooth Mesh subnet key management leaks one PSA Crypto key slot on every subnet-key teardown. In subsys/bluetooth/mesh/subnet.c, netkeyscreate() imports the Private Beacon Key into a PSA key slot under CONFIGBTMESHPRIVBEACONS (enabled by default), but subnetkeysdestroy() guarded the matching psadestroykey() with CONFIGBTMESHV1d1. That Kconfig symbol was removed when explicit Mesh 1.0.1 support was dropped, so the destroy branch became permanently dead code and the import is never balanced by a destroy.

The imbalanced teardown is reached every time subnet keys are destroyed: deleting a subnet (Config Server NetKey Delete), completing a Key Refresh Procedure (which retires the old key set), and resetting/re-provisioning the node. The over-the-air triggers are processed only under the node's device key, so they are exercisable by the provisioner or network administrator that owns the node, reachable over the Bluetooth Mesh network.

With the default CONFIGMBEDTLSPSAKEYSLOTCOUNT of 16, repeated add/delete or key-refresh cycles exhaust the shared PSA key-slot pool after roughly a dozen rounds. Once exhausted, btmeshprivatebeaconkey() and thus subnet creation fail: the node can no longer add subnets or complete key refresh, and other PSA crypto consumers on the device may be starved, until the device is rebooted. The fix aligns the destroy guard with the import guard (CONFIGBTMESHPRIVBEACONS) so each slot is freed.

First published (updated )
Severity
8.6
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H

The OCPP 1.6 client in subsys/net/lib/ocpp parsed inbound WAMP RPC frames in parserpcmsg() (subsys/net/lib/ocpp/ocppj.c) using a hand-rolled helper, extractstringfield(), that copied the message's uid and action fields with strncpy(outbuf, token + 1, outlen - 1) and then scanned the result with strchr(outbuf, '"'). Because strncpy does not NUL-terminate the destination when the source is at least outlen - 1 (127) bytes long, the subsequent strchr reads past the 128-byte destination buffer into adjacent stack memory; if a " byte is found beyond the buffer, a one-byte out-of-bounds NUL write also occurs. A related defect in extractpayload() runs strchr/strrchr over the receive buffer, which may not be NUL-terminated when a maximal-length frame fills it.

The parsed bytes come directly from the OCPP central-system server over a websocket: the reader thread fills recvbuf via websocketrecvmsg() and calls parserpcmsg() on each inbound DATA frame (subsys/net/lib/ocpp/ocpp.c). A malicious or compromised central server, or an on-path attacker (OCPP is commonly deployed over plain ws://), can send an RPC frame whose uid or action field is 127+ bytes with no closing quote, triggering the out-of-bounds access.

The primary impact is a remotely triggerable denial of service: the unbounded scan can fault on an unmapped page, and the stray NUL write can corrupt adjacent stack state. The over-read data is not reflected to the peer, so disclosure is limited. The feature is EXPERIMENTAL and must be explicitly enabled (CONFIGOCPP). The fix replaces the manual parser with the bounds-respecting jsonmixedarrparse() and copies the extracted uid with an explicitly NUL-terminated buffer, eliminating both over-reads.

First published (updated )
Severity
8.2
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H

The hawkBit device management client in subsys/mgmt/hawkbit accumulates the body of an HTTP response from the update server into a heap buffer in responsejsoncb() (subsys/mgmt/hawkbit/hawkbit.c). The buffer is sized to hold the received body bytes but reserves no space for a terminating NUL. When the full response has arrived, the code writes responsedata[downloadedsize] = '\0' — and whenever the accumulated body length equals the allocation, that terminator lands one byte past the end of the heap object (a heap-based out-of-bounds write, CWE-122 / CWE-787).

The body length and fragmentation are taken directly from the parsed HTTP response (rsp->bodyfragstart / rsp->bodyfraglen) and are fully controlled by the remote hawkBit server, which chooses its own response length. The precise trigger depends on how the buffer grows, and both forms are remotely reachable. Since v4.0.0 the reallocation is sized to exactly downloadedsize + bodylen, so any response body larger than the 1100-byte initial buffer makes the out-of-bounds write deterministic; such response sizes are normal for hawkBit deployment metadata. Before v4.0.0 the buffer grew by doubling and the growth check ((downloadedsize + bodylen) > responsebuffersize) is false at equality, so a response body whose length is exactly the current allocation — 1100 bytes with the default initial buffer — skips the reallocation entirely and writes the terminator at responsedata[1100] of an 1100-byte object. The HTTP length-mismatch check does not catch this, because the declared and received lengths genuinely agree. Either form is reachable by a malicious, compromised, or man-in-the-middle update server (TLS is optional and, when enabled, does not protect against a hostile server), with no authentication of response content and no client-side length cap protecting the write.

The out-of-bounds write is a fixed single NUL byte immediately following the allocation, corrupting adjacent allocator metadata or the next allocation. The practical impact is heap corruption leading to denial of service (fault on a subsequent allocation or free), with the bounded, allocator-dependent possibility of further corruption. The fix sizes the buffer to the body length plus one and copies with memcpy, ensuring the terminator always lands within the allocation.

First published (updated )
Severity
7.1
Use After Free
AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H

The Bluetooth host ATT layer (subsys/bluetooth/host/att.c) associates each in-flight ATT TX buffer with its owning channel via the static txmetadatastorage[] array (data->attchan = chan). When a buffer's last reference is dropped, its net-buf destroy callback defers the completion handling to the system workqueue (atttxdestroy -> atttxdestroyworkhandler -> attonsentcb -> btattsent), where btattsent dereferences the channel and its ATT context (sysslistget(&att->reqs)).

When a peer disconnects while an ATT PDU (a server notification/indication or any response) is still in flight in the controller TX path, L2CAP tears the channel down in l2capchandel(): it runs the disconnected callback and then the released callback (btattreleased), which frees the channel slab slot. Because the in-flight buffer is held by the connection TX path rather than the channel's own queue, its deferred destroy work can run after the channel has been freed. The attonsentcb guard intended to drop the stale callback itself dereferences meta->attchan, which is now a dangling pointer into a freed (and possibly reused) slab slot.

A remote peer with an ATT connection can drive this by disconnecting during routine ATT traffic; no pairing or user interaction is required to reach the ATT bearer. The result is a use-after-free read/write of freed channel memory, reliably crashing the Bluetooth host (denial of service) and, because the channel slab slot may be reused, potentially corrupting live memory.

The fix makes btattreleased() NULL the attchan field of every txmetadatastorage[] entry still referencing the channel before freeing it, so the deferred guard observes a NULL pointer and drops the callback. Teardown and the destroy work both run on the cooperative system workqueue, so the array update is serialized and needs no lock.

First published (updated )
Severity
7.6
Use After Free
AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H

The Zephyr Bluetooth GATT client CCC-write response handler gattwritecccrsp() in subsys/bluetooth/host/gatt.c invoked the application's params->subscribe() callback after it had already called params->notify(conn, params, NULL, 0).

Per the public GATT API, a notify callback with NULL data is the documented signal that the subscription has terminated and the btgattsubscribeparams struct may be freed or reused by the application; calling subscribe() on the struct afterwards is a use-after-free, including an indirect call through the freed params->subscribe function pointer.

The error branch is remotely (adjacent) reachable: a Zephyr device acting as a GATT client that calls btgattsubscribe() can be driven into this ordering when a connected GATT server peer answers the CCC write with an ATT Error Response (the peer-supplied error code flows through atterrorrsp -> atthandlersp into gattwritecccrsp).

For applications that free or recycle subscription parameters in their notification-termination handler, this results in memory corruption, a crash (denial of service), or potentially attacker-influenced control flow. The fix reorders the handler so the subscribe() callback runs before the terminating notify(NULL) in both the error and unsubscribe paths.

First published (updated )
Severity
7.5
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L

Zephyr's IPv6 forwarding path re-sent routed unicast packets without ever decrementing the IPv6 hop limit. Both routing branches of ipv6routepacket() (subsys/net/ip) were affected: the explicit-route path (netroutepacket()) and the on-link cross-interface path (netroutepacketif()). Each set the packet forwarding flag and called netsenddata() with the hop limit untouched and no expiry check.

Per RFC 8200 the hop-limit decrement is the mechanism that bounds packet lifetime and terminates routing loops; without it, a device acting as an IPv6 router relays looping packets indefinitely. An on-path attacker who can induce or exploit a transient L3 loop turns it into a permanent forwarding storm, causing CPU/bandwidth resource exhaustion (availability DoS) on the forwarder and adjacent links; path-discovery and loop diagnostics that rely on hop-limit expiry are also defeated.

Affected configurations. In every affected release the forwarding path is reached via CONFIGNETROUTE (enabled by default when CONFIGNETIPV6NBRCACHE is set), together with CONFIGNETROUTING for cross-interface routing. Note that CONFIGNETIPV6FORWARDING and CONFIGNETIPV4FORWARDING — which appear in the fix and in this advisory's evidence notes — were introduced after v4.4.0, when the routing options were split and renamed; they do not exist in any affected release. When auditing a v4.4.1-or-earlier configuration, look for CONFIGNETROUTE and CONFIGNETROUTING.

IPv4 is not affected in any release. The IPv4 forwarding path (netrouteipv4packet() in routeipv4.c) was added after v4.4.0 and has never shipped in a release. Its TTL decrement and IPv4 header-checksum recomputation landed on main as part of the same fix, so the evidence notes below discuss it, but no released version is reachable by way of IPv4.

Affected releases are v1.8.0 through v4.4.1: v1.8.0 introduced netroutepacket() and v2.2.0 added netroutepacketif(), and neither decremented the hop limit. v4.3.1 carries the explicit-route fix but not the on-link one, so it is affected as well. Fixed on main by 7d8f1afa7345 (explicit-route path) and 589eadc74efa (on-link path).

First published (updated )
Severity
5.4
AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L

The DHCPv4 client helper netdhcpv4msgtypename() in subsys/net/lib/dhcpv4/dhcpv4.c indexes a static 8-element const char name table after a faulty bounds check. The guard used msgtype <= sizeof(name) instead of msgtype <= ARRAYSIZE(name); sizeof returns the byte size of the pointer array (32 on 32-bit, 64 on 64-bit targets) rather than the element count of 8, so message-type values from 9 up to that byte size pass the check and cause name[msgtype - 1] to read past the end of the array.

The msgtype value originates from the DHCP MESSAGE TYPE option, which is read as an unchecked raw byte from a received packet (netpktreadu8) and passed unmodified into the lookup. A DHCP server, or any host able to inject a spoofed DHCP reply onto the client's link, can therefore drive the index out of bounds. The out-of-range slot yields a garbage const char that is then dereferenced by a %s log conversion.

The lookup is reached only from a debug log statement (NETDBG / LOGDBG), so the out-of-bounds read is triggerable only when the DHCPv4 log module is built at DEBUG level (CONFIGNETDHCPV4LOGLEVELDBG), which is not the default configuration. When that condition holds, the result is an out-of-bounds read and a wild-pointer dereference: most likely a crash of the DHCP client (denial of service) and potentially disclosure of an adjacent pointer's contents through the log output. The fix replaces sizeof with ARRAYSIZE, restoring the correct 1..8 acceptance window.

First published (updated )
Severity
6.5
AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Zephyr's Bluetooth host declares a GATT characteristic as two consecutive attributes: a Characteristic Declaration whose permission is hard-coded to BTGATTPERMREAD, and a Characteristic Value attribute that carries the application-specified security permissions (e.g. BTGATTPERMREADENCRYPT / READAUTHEN / READLESC). The public notify and indicate APIs explicitly accept either attribute, and passing the declaration is the documented, common idiom. Before sending each notification or indication, the host re-checks link security with btgattcheckperm() against params->attr in gattnotify(), gattindicate(), and gattnotifymultipleverifyparams() (subsys/bluetooth/host/gatt.c).

When the application passed the Characteristic Declaration attribute, the host correctly redirected the value handle but left params->attr pointing at the declaration, so the security check evaluated the declaration's permissions (no security required) instead of the value's. As a result the encryption/authentication/LESC requirement configured on the characteristic value was skipped. The Notify-Multiple path additionally used a mask that omitted the LE Secure Connections requirement.

A remote peer triggers the disclosure by connecting (optionally without pairing or encryption) and writing the Client Characteristic Configuration descriptor to enable notifications or indications, causing the server to emit the protected value over a link that has not reached the required security level. The impact is information disclosure / access-control bypass for characteristic values the application intended to expose only over a secured link; exposure depends on the application declaring encrypt/authen-required notify/indicate characteristics and on the CCC being writable at a lower security tier. There is no memory-safety or availability impact.

The fix adds btgattattrresolvevalue(), which maps a declaration attribute to the following value attribute before the permission check, and switches the Notify-Multiple path to the full BTGATTPERMREADENCRYPTMASK so the LESC requirement is also enforced.

First published (updated )
Severity
8.1
AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

The IPv6 Neighbor Discovery handlers in subsys/net/ip/ipv6nbr.c (handlerainput, handlensinput, handlenainput) used an incorrect boolean expression that combined the RFC 4861 validity checks with the ICMPv6 code check using the wrong operator precedence: the form was ((length/hop/source/target checks) && (icmphdr->code != 0)). Because every legitimate ND message carries ICMPv6 code 0, an attacker setting code == 0 (the normal value) caused the entire predicate to evaluate false, so the packet was never dropped and all of the other checks were silently skipped. The bypassed checks include the mandatory Hop Limit == 255 verification (which proves an ND packet originated on-link and was not forwarded) and, for Router Advertisements, the requirement that the source be a link-local address, as well as multicast-target sanity checks. As a result, an adjacent on-link attacker — and, because the Hop-Limit-255 guard is bypassed, potentially a remote/off-link attacker whose packets would otherwise be rejected — can have forged Router Advertisement, Neighbor Solicitation, and Neighbor Advertisement messages accepted. A forged RA lets the attacker reconfigure the victim's default router, on-link prefixes (SLAAC), MTU, reachable/retransmit timers, and (with CONFIGNETIPV6RARDNSS) DNS servers, while forged NS/NA enable neighbor-cache poisoning, enabling man-in-the-middle, traffic redirection, and denial of service. The flaw is an input-validation/authentication weakness rather than a memory-safety issue: the underlying packet-parsing primitives (netpktgetdata, netpktread, netpktskip) are independently bounds-safe and the validated length is the true buffer length, so skipping the length check causes no out-of-bounds access. The defect has existed since the logic was introduced in 2018 and shipped in all releases through v4.4.0; it is fixed by splitting the condition so any failing check drops the packet.

First published (updated )
Severity
8.7
AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:H

Zephyr's IP socket recvmsg() implementation (subsys/net/lib/sockets/socketsinet.c, insertpktinfo()) validated the user-supplied ancillary (msgcontrol) buffer using only the payload length (msg->msgcontrollen < pktinfolen) before writing a full control message consisting of an aligned cmsg header plus the payload. Because the check omitted the cmsg header size, a control buffer whose length falls in the under-checked window (e.g. 16-27 bytes for IPv4 IPPKTINFO on a 64-bit target, where a single element actually occupies 28 bytes) passes the guard yet causes a fixed-size out-of-bounds write of up to one cmsg header (~12 bytes) past the end of the buffer.

Under CONFIGUSERSPACE the recvmsg verifier allocates a kernel-heap copy of the control buffer sized to msgcontrollen and runs the implementation against it, so the overflow corrupts kernel heap memory and is triggerable from an unprivileged userspace thread; in supervisor mode it corrupts the caller's buffer.

The path is reachable on a UDP/IP socket with IPPKTINFO/IPV6RECVPKTINFO (or hoplimit/timestamping) enabled when the application calls recvmsg() with an undersized control buffer and a datagram is received; part of the overwritten bytes (the destination IP in ipiaddr) is influenced by the received packet.

The fix makes the capacity check use NETCMSGSPACE(pktinfolen) (aligned header + aligned data) and returns -ENOMEM when the buffer is too small. Affected: v3.6.0 through v4.4.0.

First published (updated )
Severity
7.4
Use After Free
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H

Zephyr's BSD-sockets getaddrinfo() implementation (subsys/net/lib/sockets/getaddrinfo.c) passes a pointer to a stack-allocated state object (struct getaddrinfostate aistate) as the userdata of an asynchronous DNS resolver query. The socket layer waits on a semaphore with a timeout deliberately set slightly longer than the resolver's own per-query timeout. When that semaphore wait nonetheless times out (-EAGAIN) - which can occur when the resolver's timeout work is delayed by workqueue contention, or in the documented multi-retry configuration where CONFIGNETSOCKETSDNSTIMEOUT exceeds CONFIGNETSOCKETSDNSBACKOFFINTERVAL - the pre-fix code retries the query (goto again) without cancelling the previous one and without resetting the semaphore.

The previous query slot remains active in the resolver with its callback and the stack pointer as userdata, and aistate->dnsid is overwritten so the stale query can no longer be cancelled. A subsequent DNS response delivered over UDP and matched by its 16-bit transaction id (in dispatchercb()/dnsread()), or the resolver's own delayed query-timeout work, then invokes dnsresolvecb() against the now out-of-scope stack frame, writing through the stale pointer (state->status, state->idx, state->aiarr[], and ksemgive()).

Because the triggering response is network-delivered and its 16-bit id is spoofable/replayable by an on- or off-path attacker, this is a network-influenceable use-after-return that can corrupt reused stack memory, leading to crashes/denial of service or memory corruption.

The fix cancels the timed-out query by name and type before retrying and resets the local semaphore, eliminating the stale callback path. Affected: Zephyr v4.0.0 through v4.4.0.

First published (updated )
Severity
5.3
AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

The USB CDC-NCM device class (subsys/usb/devicenext/class/usbdcdcncm.c) ignores the return value of usbdepenqueue() in its ethernet transmit callback cdcncmsend(). When the enqueue fails, the function still calls ksemtake(&data->syncsem, KFOREVER), blocking on a completion semaphore that is only ever signaled from the bulk-IN transfer-completion callback. Because nothing was enqueued, that callback never fires and the calling thread — a shared network traffic-class TX thread — deadlocks permanently while holding the interface TX lock, halting transmission until reboot (and leaking the transmit buffer).

The enqueue fails under conditions controlled by the attached USB host: usbdepenqueue() returns -EPERM whenever the bus is suspended (a standard, persistent host operation), and the underlying udcepenqueue() returns -EPERM/-ENODEV on disconnect, bus reset, or endpoint disable. The cdcncmsend() guard only checks the DATAIFACEENABLED and IFACEUP flags, not the suspended state, so a packet transmitted while the host holds the bus suspended reaches the failing enqueue and deadlocks the TX path.

The realistic trigger is a bus suspend that occurs while the exported network interface is active and has traffic to send — host sleep, USB selective/auto-suspend, or hub power management — after which any device-originated packet deadlocks the path, recoverable only by reboot. The impact is a persistent loss of the virtual network connection between the host's NCM interface and the Zephyr device; because the deadlocked thread is a shared traffic-class TX thread, egress on other network interfaces can stall as well. There is no memory corruption or information disclosure.

The defect was introduced with the CDC-NCM driver and shipped in releases through v4.4.0; it is fixed by checking the usbdepenqueue() return value and freeing the buffer before the blocking wait.

First published (updated )
Severity
7.4
AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:L

Zephyr's DNS resolver (subsys/net/lib/dns) parses resource records from DNS responses in dnsunpackanswer(), which validated only the fixed RR header (type, class, TTL, rdlength) and accepted any attacker-declared rdlength, including one extending past the end of the received datagram. The TXT and SRV consumers in dnsvalidaterecord() (resolve.c) then read up to rdlength bytes (clamped only to a record-type maximum such as DNSMAXTEXTSIZE, default 64, not to the packet) from the receive buffer via memcpy without their own bounds check, and pass the result to the application's resolve callback. A malicious or spoofed DNS server, an on-path attacker forging UDP DNS replies, or (with mDNS/LLMNR enabled) any LAN node can craft a truncated TXT or SRV response that causes an out-of-bounds read of adjacent receive-pool memory; the disclosed stale bytes (residual contents of prior DNS packets / uninitialized pool memory) are returned to the application as TXT/SRV record contents, an information leak, and may in some configurations cross the allocation boundary and fault, causing a denial of service. The read is bounded (~64 bytes for TXT, ~6 for SRV) and read-only (no write). The fix rejects any record whose declared rdata extends past dnsmsg->msgsize at the single chokepoint in dnsunpackanswer(). Affected: v4.3.0 and v4.4.0.

First published (updated )
Severity
8.1
Double Free, Use After Free
AV:A/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H

The Zephyr netbuf library (lib/netbuf/buf.c) manipulated both of its reference counts -- the per-header buf->ref and the per-data-block refcount at the start of each variable/heap data allocation -- with plain non-atomic C operators (buf->ref++, if (--buf->ref > 0), if (--(refcount))).

The API is documented as self-synchronizing: callers may share one buffer across threads (e.g. via kfifo) and each holder independently calls netbufunref() with no surrounding lock. Under true concurrency (SMP, or single-core preemption between the non-atomic load and store while another context unrefs the same buffer), two holders can both observe the same prior reference value and both conclude they are the last reference.

For heap/variable-data pools (mempooldataunref/heapdataunref, used by zbus message subscribers, the IP stack RX/TX buffers when CONFIGNETBUFFIXEDDATASIZE=n, capture, wireguard, ISO-TP and usbip) this produces a double kheapfree()/kfree() of the same block -- heap-metadata corruption and a use-after-free on the heap-hardening poison pattern.

For the per-header refcount the buffer is returned to the pool free LIFO twice for any pool type (including fixed-data pools used by Bluetooth and networking), corrupting the free list so a later allocation hands the same buffer to two owners.

The fix converts both refcounts to atomicinc/atomicdec (overlaying buf->ref in an atomict-sized union and changing the data-block refcount from uint8t to atomict).

Impact is gated on genuine concurrency and on an application architecture that shares one buffer among multiple independent unref'ers; the trigger is a refcount/timing race rather than packet content, so an external attacker has at most weak indirect influence over the race window. Affects all Zephyr releases through v4.4.0.

This fix is not being backported to v3.7-branch (LTS). The backport was attempted and closed unmerged (#111181): the v3.7 networking tree has diverged from main, and the new atomic word-packing -- together with the assertions it adds -- turns pre-existing v3.7-only reference-counting defects elsewhere in the stack into hard faults, so landing the change faithfully would mean pulling an open-ended set of additional v3.7-only fixes into an LTS branch. v3.7 remains affected. Applications on v3.7 that share one netbuf across threads should serialize their own netbufunref() calls rather than rely on the documented self-synchronizing behaviour. The fix is on main and has been backported to v4.3-branch (#110852) and v4.4-branch (#110853).

First published (updated )
Severity
5.5
Null Pointer Dereference
AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

The CONFIGUSERSPACE verification handler for the kthreadnamecopy() system call (zvrfykthreadnamecopy() in kernel/thread.c) calls kobjectfind() on the caller-supplied thread pointer and then dereferences the returned struct kobject without checking it for NULL. kobjectfind() returns NULL whenever the supplied pointer is not a registered (static or dynamic) kernel object.

The pre-fix guard tested thread == NULL instead of ko == NULL, so an unprivileged user-mode thread that invokes kthreadnamecopy() with any non-NULL but unregistered pointer (e.g. an arbitrary address) passes the NULL test, after which the verifier reads ko->type through a NULL pointer.

Because the syscall verifier runs in supervisor mode, this NULL dereference is a kernel-mode fault that halts or reboots the system, allowing untrusted user code to crash the kernel across the userspace security boundary (denial of service). The marshaller passes the thread argument to the verifier without any prior KSYSCALLOBJ validation, so the bad pointer reaches the defect directly.

The flaw affects builds with CONFIGUSERSPACE and CONFIGTHREADNAME enabled and has been present since the special-case lookup was introduced around v2.0.0; it is present in v4.4.0 and earlier. The fix changes the guard to check the kobjectfind() return value (ko == NULL) before dereferencing it.

First published (updated )
Severity
5.3
Use After Free
AV:A/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H

Zephyr's native TCP stack iterates the global connection list in nettcpforeach() (subsys/net/ip/tcp.c) using the SYSSLISTFOREACHCONTAINERSAFE macro, which caches a pointer to the next list node. Prior to this fix the function released tcplock while invoking the per-connection callback and re-acquired it afterwards.

During that window a concurrent tcpconnrelease(), running on the dedicated TCP work-queue thread when a connection's reference count drops to zero (e.g. a remote peer closing or resetting the connection), can remove and kmemslabfree() the cached next connection. When the iterator advances it dereferences the freed (and possibly reallocated) slab memory — a use-after-free that can crash the system (denial of service) and, if the slot has been reused, cause the callback to operate on an attacker-influenced object (potential information disclosure or further fault).

nettcpforeach() is reached in production via the net conn network shell command and via nettcpcloseallforiface() on interface-down; the freeing side is driven by ordinary TCP traffic.

The fix moves the connection/context teardown in tcpconnrelease() inside the tcplock critical section and keeps tcplock held across the callback in nettcpforeach(). The defect was introduced with the modern (TCP2) stack in 2020 and affects releases up to and including v4.4.0.

First published (updated )
Severity
4.8
Use After Free
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L

In Zephyr's native IPv4 stack, icmpv4handleechorequest() in subsys/net/ip/icmpv4.c builds an echo-reply packet (reply), hands it to nettrysenddata(), and then, on success, calls netstatsupdateicmpsent(netpktiface(reply)). nettrysenddata() transfers ownership of reply to the TX path (netiftryqueuetx -> netiftx -> L2/driver send, or the asynchronous netiftxthread), which can unref it to refcount 0 and return the struct netpkt to its slab (netpktunref -> kmemslabfree) before the stats line runs. netcore.c documents this exact contract ('the pkt might contain garbage already ... do not use pkt after that call').

The post-send netpktiface(reply) therefore reads reply->iface out of a freed (and possibly already reallocated) netpkt, a use-after-free read; with CONFIGNETSTATISTICSPERINTERFACE the stats macro additionally increments a counter through that value, i.e. a dereference/write through a stale or recycled-slot pointer.

The path is reached unauthenticated by any remote host that pings the device (neticmpv4input -> neticmpcallipv4handlers -> icmpv4handleechorequest) and is gated on CONFIGNETSTATISTICSICMP. Impact is a probabilistic read of recycled packet memory plus a possible wild-pointer write under a timing race, leading most likely to corrupted interface statistics or a remotely triggerable crash (DoS).

The defect was introduced in 2019 (v1.14) and is present through v4.4.0. The companion change in neticmpv4senderror() is not a use-after-free because it reads netpktiface(orig), the caller-owned received packet, which stays alive across the send. The fix caches the interface pointer from the live received packet before sending and uses it for the post-send stats updates.

First published (updated )
Severity
7.8
Integer Overflow
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

On Xtensa SoCs built with CONFIGXTENSAMPU and CONFIGUSERSPACE, archbuffervalidate() in arch/xtensa/core/mpu.c — the architecture hook that verifies a user-mode-supplied buffer is accessible to the calling user thread with the requested permission — defaulted its return value to 0 (access permitted) and only set a denial result inside its per-MPU-region probe loop. When the rounded extent of the buffer wraps the 32-bit address space (size + alignment offset near SIZEMAX, or ROUNDUP(size + offset) overflowing to 0), the loop executes zero iterations and the function returns 0 = permitted without probing any MPU region.

The syscall-layer pre-checks (KSYSCALLMEMORYSIZECHECK / ZDETECTPOINTEROVERFLOW) only catch a raw addr+size wrap and do not cover the ROUNDUP-induced wrap, and the string path (archuserstringnlen -> archbuffervalidate) has no syscall-layer guard at all.

An unprivileged user-mode thread can therefore pass a crafted (addr, size) to any syscall that validates user buffers via kusermodefromcopy/tocopy or kusermodestringcopy and have validation succeed for memory it must not access; the kernel then reads from (disclosure) or, with write=1, writes to (corruption) attacker-chosen kernel or other-partition memory on the thread's behalf, enabling information disclosure, memory corruption, privilege escalation, and denial of service.

Affected from v3.7.0 (when Xtensa MPU userspace support was added) through v4.4.0. The fix changes the default to -EINVAL (deny by default), adds an explicit sizeaddoverflow check, and sets the success value only after the full range has been validated.

First published (updated )
Severity
7.1
AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H

In Zephyr's kernel pipe implementation, the userspace syscall verifier zvrfykpipeinit() in kernel/pipe.c used KSYSCALLOBJ() (which requires the kernel object to already be initialized) instead of KSYSCALLOBJNEVERINIT() (which rejects an already-initialized object). As a result, on CONFIGUSERSPACE builds an unprivileged user thread that has been granted access to a kpipe object can invoke the kpipeinit syscall to re-initialize a pipe that is already in use.

zimplkpipeinit() unconditionally resets the ring buffer, sets pipe->waiting to 0, and re-initializes both wait queues (zwaitqinit on pipe->data and pipe->space) without waking or accounting for threads currently blocked on the pipe. Any thread already pended in kpiperead()/kpipewrite() is left orphaned: still marked pending with pendedon pointing at the cleared wait queue and with stale qnodedlist links into the (now re-initialized) embedded list head.

When such an orphaned waiter is later timed out or woken, the scheduler calls sysdlistremove() on its stale node, writing through dangling prev/next pointers into kernel wait-queue/scheduler structures, causing list corruption (an attacker-driven invalid kernel write), lost wakeups, indefinitely blocked threads, and silent data loss. The flaw lets a deprivileged user thread corrupt the state of a kernel object shared with other threads/partitions.

The fix switches the verifier to KSYSCALLOBJNEVERINIT(), matching the existing kmsgqinit verifier, so a user thread can no longer re-initialize a live pipe. The vulnerable code shipped in v4.1.0 and remained through v4.4.0.

First published (updated )
Severity
9.1
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L

subsys/net/lib/lwm2m/lwm2mpullcontext.c copied the firmware-update Package URI into a fixed static buffer (context.uri, size CONFIGLWM2MSWMGMTPACKAGEURILEN, default 128) with memcpy(context.uri, uri, LWM2MPACKAGEURILEN), copying exactly the destination size with no length validation. The Firmware-Update object stores the server-supplied Package URI (/5/0/1) in a 255-byte buffer, so a LwM2M management server (or an on-path attacker on a session lacking strong DTLS) can WRITE a URI of 128-254 characters; only the first 128 bytes are then copied into context.uri with no NUL terminator. That buffer is subsequently consumed as a C string by httpparserparseurl(context.uri, strlen(context.uri), ...), strlen-based CoAP URI-path/PROXY-URI option appends, and lwm2mparsepeerinfo(), causing an out-of-bounds read of adjacent static memory. The over-read bytes are appended to outbound CoAP requests (information disclosure of adjacent device memory to the server/proxy) and can crash the device (denial of service). The vulnerable copy was introduced by the pull-context refactor (first released in v3.0.0) and is present through v4.4.0; the default-on CONFIGLWM2MFIRMWAREUPDATEPULLSUPPORT path is affected. The fix adds a strlen(uri) >= sizeof(context.uri) check returning -ENOMEM and switches to strcpy(), guaranteeing a bounded, NUL-terminated buffer.

First published (updated )
Severity
5.5
AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

The NXP LPUART serial driver (drivers/serial/uartmcuxlpuart.c), when CONFIGUARTUSERUNTIMECONFIGURE is enabled, called LPUARTDeinit() at the start of mcuxlpuartconfigure(), which disables the LPUART peripheral clocks. The requested configuration is validated only afterwards (in mcuxlpuartconfigurebasic), and unsupported parity/data-bit/stop-bit/flow-control values return -ENOTSUP before the clock is re-enabled.

As a result, a uartconfigure() request with an unsupported configuration left the LPUART in a clock-disabled state; any subsequent access to LPUART registers (pollout/pollin, interrupt handling, or a later reconfigure) faults on the gated peripheral and escalates to a hard fault, crashing the system.

uartconfigure() is a Zephyr syscall whose verifier (zvrfyuartconfigure) only checks that cfg is readable user memory and forwards the caller-supplied configuration unchanged, so an unprivileged userspace thread with access to an LPUART device can deterministically trigger the fault, a persistent system-wide denial of service.

Introduced in v2.5.0 and present in all subsequent releases until this fix, which removes the LPUARTDeinit() call and instead only disables the transmitter/receiver, leaving the clock running.

First published (updated )
Severity
6.5
AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

In Zephyr's Bluetooth Mesh PB-ADV provisioning bearer (subsys/bluetooth/mesh/pbadv.c), provmsgrecv() rescheduled the provisioning protocol watchdog timer unconditionally at the top of the function, before the FCS check and before the ADVLINKINVALID check. Once a provisioning attempt fails, provfailed() sets ADVLINKINVALID and the only recovery path is the protocol timer firing (protocoltimeout -> provlinkclose -> closelink -> resetadvlink and re-enabling of scanning and the unprovisioned device beacon).

A remote, unauthenticated attacker on the BLE advertising channel can first induce a provisioning failure (e.g. with a malformed generic-provisioning PDU) and then transmit any FCS-valid PB-ADV transaction PDU on the same link ID more often than once per protocol timeout (60 s, or 120 s for OOB input/output). Because each such packet reset the timer even on an invalidated link, protocoltimeout never fired, the dead link was never torn down, and the device remained pinned in an un-provisionable state with its unprovisioned beacon disabled and new Link Open requests rejected.

PB-ADV PDUs are processed without authentication and the FCS is a keyless CRC, so no pairing or prior trust is required and the attacker chooses the link ID itself. The impact is a persistent denial of provisioning/re-provisioning service; there is no memory-safety, confidentiality, or integrity impact.

The vulnerable code shipped in releases through v4.4.1. The fix moves the timer reschedule to after the ADVLINKINVALID check (and the FCS check before the reset) so an invalidated link can no longer be kept alive by incoming packets.

First published (updated )
Severity
6.5
AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H

The CONFIGUSERSPACE syscall verifier zvrfykpoll() in kernel/poll.c allocates a kernel-side copy of the user-supplied kpollevent[] via zthreadmalloc() and then validates each event's object handle. Before this fix, validation used KOOPS(KSYSCALLOBJ(...)) inline inside the loop, which kills the calling thread without freeing eventscopy.

A user thread can pass numevents >= 1 with a forged object handle to leak the allocation; because newly spawned user threads inherit the parent's resourcepool (kernel/thread.c), an attacker spawns sacrificial threads to repeat the leak until the shared kernel heap is exhausted. Once depleted, legitimate kernel allocations from that pool (kqueue alloc nodes, kmsgq buffers, future kpoll calls, etc.) fail, causing a system-level denial of service.

The fix replaces each inline KOOPS with a conditional goto oopsfree so the buffer is freed before the thread is killed. Affects Zephyr releases from v1.12.0 (when kpoll was first exposed to user mode) through v4.4.1.

First published (updated )
Severity
8.1
Null Pointer Dereference
AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

The MCTP-over-I2C+GPIO target binding in Zephyr (subsys/pmci/mctp/mctpi2cgpiotarget.c) processes pseudo-register writes from an I2C bus master byte-by-byte in mctpi2cgpiotargetwritereceived() without validating the order or the receive buffer. In the affected versions the MCTPI2CGPIORXMSGADDR (data) handler dereferences and writes through b->rxpkt without checking that the receive buffer was allocated: a controller that selects the data register and writes a byte without first sending the length register (which is what allocates the buffer) causes a write of an attacker-chosen byte through a NULL/unallocated mctppktbuf pointer (i.e. into a small attacker-advanceable offset above address 0), producing memory corruption or a hard fault.

The same handler also performs a write-then-check bounds test, allowing a one-byte heap overflow at data[255] when more than 255 data bytes are sent.

Because the I2C target callback is invoked with raw bytes supplied by whatever device is the bus master and the binding performs no authentication, a malicious or malfunctioning controller on the bus can trigger these without any prior protocol state, leading to memory corruption and/or denial of service on the target device.

The vulnerable code was introduced when the I2C+GPIO target binding was added and shipped in Zephyr v4.3.0 and v4.4.0. The fix defers allocation to the first data byte with a NULL check, treats a missing length as a zero-sized packet rejected by libmctp, and moves the bounds check before the store.

First published (updated )
Severity
5.5
Divide by Zero
AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L

The DesignWare SPI driver (drivers/spi/spidw.c) computed the SPI BAUDR clock divider as info->clockfrequency / config->frequency without validating config->frequency.

spitransceive is a Zephyr syscall and its verify handler (drivers/spi/spihandlers.c) copies the caller-supplied spiconfig from userspace without checking the frequency field, so a userspace thread that has been granted access to a DesignWare SPI device kernel object can pass frequency = 0 and trigger an unsigned integer divide-by-zero in spidwconfigure().

On Cortex-M Mainline (SCB->CCR.DIV0TRP is set in zarmfaultinit()) and on ARC (a dedicated evdivzero vector) this raises a CPU exception, resulting in a kernel fault and local denial of service.

The fix rejects zero frequency and frequencies above clockfrequency / 2 (the DesignWare SSI databook minimum SCKDIV of 2) with -EINVAL. The defect affects all Zephyr releases up to and including v4.4.0; exploitation requires CONFIGUSERSPACE=y and an unprivileged thread already granted SPI driver permission. There is no memory-corruption or information-disclosure impact.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203