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

The RTIO submission-queue verifier rtiovrfysqe() in subsys/rtio/rtiosyscalls.c (subsys/rtio/rtiohandlers.c before v4.3.0) accumulated the results of KSYSCALLMEMORY() into its validsqe flag with &=. That macro returns nonzero on failure (it is built on KSYSCALLVERIFYMSG(), which yields !(expr)), so every buffer permission check contributed the opposite of its result. A submission whose tx.buf, rx.buf or txrx.txbuf/txrx.rxbuf pointer was not accessible to the calling thread was accepted, while a legitimate, thread-owned buffer caused the calling thread to be killed with a kernel oops.

The verifier runs on behalf of the rtiosqecopyingethandles() system call (also reached through the inline rtiosqecopyin() wrapper), so it is driven directly by untrusted user-mode input on any image built with CONFIGUSERSPACE. A user thread that has been granted an RTIO context and an rtioiodev object — the intended usage of RTIO userspace support, established via rtioaccessgrant() — can submit an SQE whose buffer pointer and length name arbitrary kernel memory; the object check on sqe->iodev is unaffected and still passes, and the crafted SQE is forwarded to the bus driver unchanged.

Downstream consumers dereference the pointer with supervisor privilege. The generic I2C and I3C RTIO fallback handlers (drivers/i2c/i2crtiodefault.c, drivers/i3c/i3crtiodefault.c) copy sqe.rx.buf/sqe.tx.buf into struct i2cmsg and call i2ctransfer(), and the vendor SPI/I2C RTIO drivers behave equivalently. An RTIOOPRX submission therefore has peripheral data written into kernel memory of the attacker's choosing (arbitrary kernel write), an RTIOOPTX submission clocks kernel memory out over the bus (kernel memory disclosure), and a pointer to unmapped memory faults the kernel. This is a full escape from the user-mode isolation boundary that CONFIGUSERSPACE exists to enforce.

The defect was masked for years because the in-tree user-mode RTIO tests only exercise RTIOOPNOP and mempool-backed reads, neither of which reaches the affected checks. The fix negates each KSYSCALLMEMORY() result and additionally validates the TXRX transmit buffer read-only, matching the SPI, I2C and I3C handlers.

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

The DHCPv4 client in subsys/net/lib/dhcpv4/dhcpv4.c maintained its transaction identifier (iface->config.dhcpv4.xid) as a counter rather than as a per-transaction random value. It was seeded once with sysrand32get() in dhcpv4startinternal(), incremented by one in dhcpv4senddiscover() and dhcpv4senddecline(), and never changed by dhcpv4sendrequest() — so every RENEWING and REBINDING exchange reused, unchanged, the identifier that had already appeared on the wire in the broadcast DHCPDISCOVER at boot, and where the identifier did move it moved predictably by one. RFC 2131 4.1 requires a single identifier per transaction, chosen so that clients are unlikely to collide.

This identifier is the client's only unpredictable reply-validation token. netdhcpv4input() accepts a BOOTP reply on op, a matching xid, and a matching chaddr (the interface MAC, which is public); the UDP callback is registered with a wildcard remote address and port, and no server-identifier or source-address check is applied. An attacker who learns one identifier — for example any host on the same segment that sees the broadcast DHCPDISCOVER, or a DHCP server or relay on a network the device previously joined — therefore retains a valid token for the device's whole uptime, and can predict the identifiers of later exchanges, without having to observe the unicast renew traffic or race the legitimate server.

A forged reply that passes those checks has effect regardless of client state, because dhcpv4parseoptions() applies its side effects before the state machine gate in dhcpv4handlereply(): the router option calls netifipv4setgw() and the DNS option calls dnsresolvereconfigure(). A single accepted datagram can thus repoint the device's default gateway and resolvers at attacker-controlled addresses, redirecting its traffic; during RENEWING or REBINDING a forged DHCPACK re-binds the lease with attacker-chosen parameters and a forged DHCPNAK removes the address and restarts configuration.

The fix draws a fresh sysrand32get() value at each point where a transaction begins — entering SELECTING, RENEWING and REBINDING, and at the INIT/INIT-REBOOT entry taken when an interface with a lease goes down — while REQUESTING and DECLINE keep the identifier of the exchange they belong to, as RFC 2131 requires. As a side effect the client also no longer discards a slow server's offer after retransmitting a discover, and the initial-discover backoff now draws its own randomness instead of deriving it from the identifier.

1 / 2
Source: MITRE
First published (updated )
Severity
5.3
AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L

The device multiplexer pseudo-driver in drivers/misc/devmux/devmux.c exposes two system calls, devmuxselectget() and devmuxselectset(). When CONFIGUSERSPACE is enabled, their user-mode verifiers zvrfydevmuxselectget() and zvrfydevmuxselectset() forwarded directly to the kernel implementations without performing any kernel-object validation — no kobjectaccesscheck() and no driver object check. Compounding this, DEVICEDTINSTDEFINE() registered devmux instances with a NULL driver API pointer, which causes scripts/build/genkobjectlist.py to skip them entirely, so a devmux device had no struct kobject and no permission on it could be granted or withheld in the first place.

Any thread running in user mode can reach the defect: device pointers such as DEVICEDTGET(DTNODELABEL(devmux0)) are compile-time constants available to application code, and the syscall accepted them from a thread that had never been granted access to the device. The kernel implementations do validate the pointer against the static devmux instance table and bounds-check the selection index, so this is not a memory-safety issue; the failure is purely one of authorization. Exposure requires CONFIGUSERSPACE=y together with the experimental CONFIGDEVMUX=y (which depends on CONFIGDEVICEMUTABLE) and a zephyr,devmux devicetree node.

An unprivileged user-mode thread can read and change which downstream device a multiplexer forwards to, bypassing the userspace object-permission model. Because a devmux forwards the data, configuration and API of the selected device — its documented use being zephyr,console and zephyr,shell-uart switching — an attacker can suppress console, log and shell output, redirect that output to a downstream device it is able to observe, or flip the selection underneath another thread that is legitimately using the multiplexer. The impact is bounded to the devicetree-fixed set of downstream devices; no out-of-bounds access or arbitrary pointer dereference is reachable.

The fix declares subsystem struct devmuxdriverapi and attaches a DEVICEAPI(devmux, devmuxapi) instance to each devmux device so that it becomes a tracked KOBJDRIVERDEVMUX kernel object, and adds KOOPS(devmuxinstget(dev) == SIZEMAX) plus KOOPS(kobjectaccesscheck(dev) != 0) to both verifiers.

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

The Bluetooth host's ATT transmit path releases a buffer it does not own when signing fails. chansend() in subsys/bluetooth/host/att.c documents (and every other error exit implements) the contract that on failure the caller retains ownership of the netbuf; the BTATTOPSIGNEDWRITECMD branch instead called netbufunref(buf) when btsmpsign() returned an error and then returned that error to the caller. The callers act on the contract: processqueue() re-queues the buffer with kqueueprepend(), btattchansendrsp() puts it back on chan->txqueue, btattchanreqsend() restores it into the request, and attreqretry() unreferences it — so the already-freed buffer was re-queued, later dereferenced, and eventually freed again.

An ATT Signed Write Command is produced when an application calls btgattwritewithoutresponsecb() with sign set on an unencrypted connection. btsmpsign() fails whenever no local CSRK exists for the peer (-ENOENT), and whether one exists is negotiated with the peer: smpdistributekeys() stores a local CSRK only when the peer's pairing key-distribution field requests the signing key and bonding was agreed. A peer can therefore pair without accepting the CSRK, pair with No Bonding, remain unpaired, or keep the link unencrypted, and the victim's next signed write takes the failing path. Because struct netbuf's node field is simultaneously the linkage word for the ATT TX FIFO, for the deferred-destroy list used by the attpool destroy callback atttxdestroy(), and for the pool free list, the mishandled buffer corrupts these lists and can be re-issued by btattcreatepdu() to a new PDU while the ATT TX path still holds it.

The same branch also saved the netbufsimplestate only after signing, so a buffer restored after a btl2capsendpdu() failure kept its 12-octet signature; a subsequent retry re-signed it and appended a second signature with netbufadd(buf, 12) to a buffer that btattcreatepdu() sized for exactly one, writing past the end of the pooled buffer in builds where the netbuf tailroom assertion is compiled out.

Impact is host-side memory corruption in the Bluetooth stack: list corruption, a pooled ATT buffer with two owners (wrong PDU contents transmitted, or a double free when the queues are drained at disconnect), and a bounded out-of-bounds write on the retry path — typically observed as a crash or assertion failure. Exposure requires the deprecated CONFIGBTSIGNING to be enabled and an application that actually issues signed writes; in that configuration the triggering condition is under the remote peer's control.

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

i2sesp32triggercheck() in drivers/i2s/i2sesp32.c validates the requested direction only for I2SDIRBOTH. The I2SDIRRX and I2SDIRTX branches read devcfg->rx.data->configured / devcfg->tx.data->configured without first checking the stream pointers. The device instantiation macro I2SESP32STREAMINIT() sets both .conf and .data to NULL for a direction the devicetree does not describe, so on an instance that wires only one direction — the normal shape for audio output or a worldsemi,ws2812-i2s LED strip — the other direction dereferences a NULL pointer instead of returning an error.

i2strigger() is a Zephyr syscall, and zvrfyi2strigger() in drivers/i2s/i2shandlers.c validates only the device object and the presence of the trigger API pointer; the dir argument is passed to the driver unvalidated. On a build with CONFIGUSERSPACE enabled, a user-mode thread that has been granted the I2S device can issue a single i2strigger() call naming the unwired direction and cause a load from address 0 in kernel mode. Among the Espressif parts that carry this driver, userspace is available in-tree only on RISC-V SoCs with CONFIGRISCVPMP, and v4.4.0 is the first release where that is buildable: the ESP32-C6 HPCORE selects RISCVPMP when it is not built for MCUboot. ESP32-C5 in v4.4.x carries the same PMP-region and userspace linker support but does not select RISCVPMP by default. Espressif Xtensa targets do not support Zephyr userspace, and in a non-userspace build the bad direction can only come from in-kernel application code.

The impact is limited to availability: the access is a read at offset 0 of the missing stream structure, so there is no attacker-controlled offset, no write primitive and no information disclosure. With the default fatal-error handler the resulting exception halts the system, giving an unprivileged user-mode thread a system-wide denial of service. The fix adds the same pointer check the I2SDIRBOTH branch already performed and returns -ENOSYS for a direction the instance does not implement; the driver's other entry points (i2sesp32configcheck(), i2sesp32configget(), i2sesp32read(), i2sesp32write()) already guarded the pointers, and a static audit found no equivalent unguarded path.

The driver defect is older than the affected range. The unguarded dereference is present from v4.2.0 (reached through i2sesp32triggerstream(), whose if (stream) guard tests the address of a struct member and is never false) and takes its present i2sesp32triggercheck() form in v4.3.0. No in-tree Espressif configuration before v4.4.0 can run a user-mode thread, so in v4.2.x and v4.3.x the direction argument can only come from trusted kernel code. Those releases carry the bug but are not listed as affected; the fix has also been merged to v4.3-branch as hardening.

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

The NXP MCUX TRNG entropy driver in drivers/entropy/entropymcuxtrng.c passed the caller's byte count straight to the vendor SDK routine TRNGGetRandomData(). On i.MX RT5xx and RT6xx parts the SDK compiles its TRNGSWHEALTHTESTS variant, which always copies whole 32-bit words and draws entropy rounded up to a multiple of 128 bytes. Its "caller buffer is full" guard tests dataSize == 0, so a request whose length is not a multiple of four makes dataSize underflow past zero and the SDK keeps writing into the caller's buffer for the entire extraction: a 1-byte request results in 128 bytes written, and any non-word-multiple length overflows by up to 127 bytes.

entropygetentropy() is a syscall, and its verifier in drivers/entropy/entropyhandlers.c validates only the requested length via KSYSCALLMEMORYWRITE(). With CONFIGUSERSPACE enabled, an unprivileged user-mode thread that has merely been granted the entropy device can therefore choose both the destination address and a length such as 1, and cause the kernel to write up to 127 bytes beyond the region it proved it owns. On these Cortex-M33 targets there is no MMU, so user partitions and kernel data share one SRAM and the overflow can land in adjacent kernel state. The same defect is reached from kernel mode by any caller requesting a non-word-multiple length, including getentropy() and, in builds where syscsrandget()/sysrandget() resolve to the hardware generator, sysrand8get() and sysrand16get().

Impact is memory corruption of up to 127 bytes immediately following the supplied buffer — typically the caller's stack in kernel-mode use, or memory outside the caller's partition when driven through the syscall. The overflow offset is fully determined by the requested length and is therefore deterministic, while the written content is uncontrolled TRNG output; the practical consequences range from crashes and unpredictable state corruption to opportunistic escalation when kernel bookkeeping such as object permission bitmaps is overwritten. The affected devices are those where the MCUX SDK enables TRNGSWHEALTHTESTS (MIMXRT595S, MIMXRT555S, MIMXRT533S, MIMXRT685S, MIMXRT633S), on which the TRNG is the zephyr,entropy chosen node; other SoCs using this driver take the SDK path that clamps the copy size and are unaffected.

The fix routes any unaligned prefix and any sub-word tail through a local bounce word and hands the SDK only word-multiple sizes, so the SDK's word-granular writes can no longer pass the end of the caller's buffer.

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

The user-mode syscall verifiers zvrfyfuelgaugegetprops() and zvrfyfuelgaugesetprops() in drivers/fuelgauge/fuelgaugesyscallhandlers.c declared two variable-length arrays, union fuelgaugepropval kvals[len] and fuelgaugepropt kprops[len], sized directly by the caller-supplied len argument. len is an unvalidated sizet taken straight from the syscall ABI, and the VLAs were allocated before any check at all — including before the KSYSCALLDRIVERFUELGAUGE() object-permission check. The subsequent kusermodefromcopy() calls validated only that the user source buffer was readable; the kernel destination was never bounds-checked, since it was sized by the same attacker-chosen len.

Any thread running in user mode with CONFIGUSERSPACE enabled can invoke fuelgaugegetprops() or fuelgaugesetprops() with a large len. This first displaces the supervisor stack pointer by an arbitrary attacker-chosen amount — Zephyr does not build with stack-clash probing, so the displacement itself does not fault, and the nested calls made by the verifier then write frames below the privileged stack. If the caller has been granted access to a fuel-gauge device object, the memcpy inside kusermodefromcopy() additionally writes len sizeof(union fuelgaugepropval) bytes of fully attacker-controlled data starting well below the stack base. CONFIGPRIVILEGEDSTACKSIZE defaults to 1024 bytes, so a len of roughly 170 already exhausts it.

The result is an out-of-bounds write in supervisor mode with attacker-controlled length and, on the permitted path, attacker-controlled content — a break out of the user-mode sandbox into kernel memory, leading to kernel code execution or a system crash. A stack guard region does not contain it, because the copy begins below the guard and walks upward, corrupting unprotected memory before the guard is reached. The fix removes the kernel-side copies entirely and validates the caller's arrays in place with KSYSCALLMEMORYARRAYREAD() / KSYSCALLMEMORYARRAYWRITE(), which also handle the len size multiplication overflow; this is safe because neither fuelgaugepropt nor union fuelgaugepropval contains embedded pointers.

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

The Bluetooth Mesh On-Demand Private Proxy solicitation handler in subsys/bluetooth/mesh/solicitation.c copies a received Solicitation PDU into a fixed 17-byte stack buffer without bounding the source length. In solpdudecrypt(), out is allocated as NETBUFSIMPLE(17) and then filled with netbufsimpleaddmem(out, in->data, in->len); netbufsimpleadd() guards its tailroom only with ASSERTNOMSG, which is compiled out in production builds, so when in->len > 17 the underlying memcpy writes attacker-controlled bytes past the 17-byte stack buffer. The copy occurs before any decryption or authentication, so no key material is required to trigger it.

The oversized length arises because the mesh scan callback in subsys/bluetooth/mesh/adv.c calls netbufsimplerestore() before dispatching to btmeshsolrecv(), leaving buf->len covering the entire remaining advertising payload rather than just the Solicitation Service Data. After the parser locates the Service Data AD and consumes the Identification Type byte, the remaining buf->len is the 17-octet Network PDU plus any trailing advertising bytes, and prior to this fix there was no maximum-length check (only a minimum). An attacker can therefore append extra AD structures or padding after the Solicitation Service Data to make buf->len exceed 17.

btmeshscancb() is registered directly as the BLE scan callback, so buf is raw, unauthenticated advertising data received over the air. Any device in radio range can send a non-connectable advertisement carrying a crafted mesh Proxy Solicitation to a node that has CONFIGBTMESHODPRIVPROXYSRV enabled and is currently eligible to be solicited (GATT proxy disabled, On-Demand Private Proxy enabled), with no pairing, bonding, or provisioning. The result is an attacker-controlled stack overwrite — plausibly leading to remote code execution and at minimum a reliable remote denial of service. The fix trims buf->len to the spec-fixed 17 octets (dropping the PDU if fewer remain) before decryption.

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

The system-call verifier for i3cdoccc() in drivers/i3c/i3chandlers.c validated the outer struct i3ccccpayload, the broadcast ccc.data buffer and the targets.payloads[] array, but did not validate the per-target data buffers those array elements point at. Each struct i3cccctargetpayload carries its own data pointer and datalen, and neither was passed through KSYSCALLMEMORY() before the payload was handed to zimpli3cdoccc() and on to the controller driver. The verifier also operated on the caller's live structure rather than a snapshot, so validated fields could be changed by a second user thread between the check and the driver's use — unlike the sibling zvrfyi3ctransfer(), which has always copied its message array first.

The defect is only present in CONFIGUSERSPACE builds, where drivers/i3c/i3chandlers.c is compiled. An unprivileged user-mode thread that has been granted access to the I3C controller device object — the ordinary way an application lets a user thread talk to I3C peripherals — can issue a direct CCC whose target payload data pointer names an arbitrary kernel address. Controller drivers dereference that pointer directly (for example drivers/i3c/i3cmcux.c, drivers/i3c/i3ccdns.c, drivers/i3c/i3cstm32.c, drivers/i3c/i3cnpcx.c), using rnw to decide direction.

A read CCC therefore causes the kernel-mode driver to write bus-received bytes into an attacker-chosen kernel address for an attacker-chosen length, and a write CCC transmits kernel memory out onto the I3C bus. The result is an out-of-bounds kernel write plus a kernel memory disclosure, i.e. escalation from a user-mode thread to supervisor privilege, defeating the isolation CONFIGUSERSPACE is meant to provide.

The fix introduces copycccanddo(), which snapshots the payload, copies the target array into kernel memory with kusermodeallocfromcopy() (bounding numtargets to fewer than 32), validates each per-target buffer with KSYSCALLMEMORY() according to rnw, and copies the driver-written numxfer and err fields back to the caller.

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

parsewriteop() in subsys/net/lib/lwm2m/lwm2mmessagehandling.c handles inbound CoAP WRITE/CREATE requests that carry a Block1 option. For the first block of a transfer it called initblockctx() and then immediately stored the peer-selected block size with blockctx->ctx.blocksize = blocksize before inspecting the return code. initblockctx() sets the caller's pointer to NULL and returns -ENOMEM when no entry of the static block1contexts[] pool is free or timed out, so that store dereferences a NULL pointer.

The pool holds CONFIGLWM2MNUMBLOCK1CONTEXT entries (default 3) and an entry is only reclaimed once its transfer completes, fails, or ages past 30 seconds. A peer that reaches the client's LwM2M socket can therefore start three block-wise writes on three distinct object paths with the CoAP More bit set and leave them incomplete, then send the first block of a fourth write on a new path to reach the unguarded dereference. Reachability is gated only by the connected UDP socket's source-address filter unless CONFIGLWM2MDTLSSUPPORT is enabled — which has no default — so in a NoSec deployment an on-path or address-spoofing attacker needs no credentials; the same sequence is also reachable from a bootstrap or lower-trust server, and can be hit accidentally by a legitimate server running four concurrent block transfers.

The write targets a fixed low address with a value between 0 and 7, so the consequence is a fatal memory fault (BusFault or corrupted low memory leading to a fault) rather than a usable memory-corruption primitive: the device crashes or resets. Confidentiality and integrity are not affected. The fix moves the store below the guard and validates the context pointer itself instead of the return code, so the context is only touched once it is known to be valid.

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

ieee802154send() in subsys/net/l2/ieee802154/ieee802154.c copies the outgoing packet into a single fixed 125-byte transmit buffer (txframebufpool, sized IEEE802154MTU). In builds with CONFIGNETL2IEEE802154FRAGMENT enabled (the default whenever CONFIGNET6LO is set), the branch taken when 6LoWPAN fragmentation is not required performed an unchecked netbufaddmem(framebuf, pktbuf->data, pktbuf->len). The only guard was ASSERTNOMSG() inside netbufsimpleadd(), which is compiled out without CONFIGASSERT, so an oversized packet silently overran the frame buffer.

The defect is not reachable from the radio: for NETAFINET6 packets ieee8021546loencodepkt() compares the whole packet length against IEEE802154MTU and takes the fragmentation path when it does not fit, so every buffer copied on the unfragmented branch is within bounds. It is reachable through NETAFPACKET sockets bound to an 802.15.4 interface: for NETSOCKRAW the 6LoWPAN block is skipped entirely and for NETSOCKDGRAM it returns early on the address-family test, leaving no length validation anywhere on the transmit path (netcontextsendto() and netiftx() apply none, and pktbufferlength() does not clamp the allocation for this L2).

An application — or, in a CONFIGUSERSPACE build, an unprivileged application thread using the zsocksocket()/zsocksendto() syscalls — can therefore drive a supervisor-mode out-of-bounds write of chosen bytes past the 125-byte pool buffer. With the default CONFIGNETBUFFIXEDDATASIZE of 128 bytes the overrun is bounded to roughly llhdrlen + 3 bytes; with CONFIGNETBUFVARIABLEDATASIZE a single storage buffer can be as large as CONFIGNETPKTBUFTXDATAPOOLSIZE, making the overrun far larger. The consequence is corruption of memory adjacent to the pool, with a crash or further compromise of kernel state as the practical impact.

The fix validates llhdrlen + netpktgetlen(pkt) + authtaglen against IEEE802154MTU before any copy and adds a tailroom-checking copypkttoframe() helper that returns -EMSGSIZE instead of overrunning the buffer. The same change also linearizes the whole netbuf chain into one MAC frame, so packet storage boundaries no longer become frame boundaries on the wire.

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

The userspace verifier zvrfyrtiosqecopyingethandles() in subsys/rtio/rtiosyscalls.c (subsys/rtio/rtiohandlers.c before v4.3.0) validated the RTIO object handle and the sqes input array, but not the handle out-parameter. On the first loop iteration it executed handle = sqe, storing the kernel address of the newly acquired submission-queue entry through a pointer taken verbatim from user mode, with no KSYSCALLMEMORYWRITE check in front of it.

Any user-mode thread that has been granted a struct rtio kernel object can invoke the syscall with an arbitrary address in handle. That is the ordinary way an unprivileged thread uses the RTIO API, for example via sensorreadasyncmempool() or the async ADC helpers, which call rtiosqecopyingethandles() internally. The store happens in supervisor mode before any submission-entry validation, so it fires regardless of whether the SQE contents are subsequently rejected. Only builds with CONFIGUSERSPACE and CONFIGRTIO are affected; without CONFIGUSERSPACE the verifier is not compiled and the caller is already privileged.

The write address is fully attacker-chosen and the written value is a pointer into the caller's own RTIO ring, whose contents the caller controls (the following sqe = sqes[i] copies an attacker-supplied struct rtiosqe into that slot). This yields a write-what-where primitive placing a pointer to attacker-controlled data at any kernel address, sufficient to corrupt kernel function pointers, thread structures, or memory-domain partition tables, and thus to escalate from user mode to kernel mode, defeating the isolation boundary CONFIGUSERSPACE is meant to enforce. At minimum it is a reliable kernel memory-corruption and crash primitive. The reporter reproduced the write on qemux86: a KUSER thread changed a supervisor global from NULL to a live kernel SQE pointer.

The fix adds KSYSCALLMEMORYWRITE(handle, sizeof(handle)) (guarded by the existing optional-NULL semantics) before the loop, so the destination must lie in the calling thread's writable memory domain or the thread is terminated by KOOPS. The neighbouring verifier zvrfyrtiocqegetmempoolbuffer(), which checked its buff/bufflen out-parameters only for read although the implementation writes through them, was hardened separately by bea93400138 ("rtio: syscalls: validate output params as writable"); that residual was materially weaker, since a read check still confines the target to the caller's own memory domain.

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

gptphandlemsg() in subsys/net/l2/ethernet/gptp/gptp.c dereferenced the gPTP header returned by GPTPHDR() and switched on hdr->messagetype without first checking that the received frame carries at least sizeof(struct gptphdr) (34) bytes of payload. The header accessor gptpgethdr() deliberately never fails for a short buffer — it returns pkt->frags->data and leaves validation to its callers — so a truncated frame produced a header pointer covering memory beyond the received data. The per-message-type checks that follow do not compensate: GPTPVALIDLEN() reduces to len > 60 once the Ethernet header has been pulled, which is false for every fixed-size gPTP message, so GPTPCHECKLEN() never rejects a truncated SYNC, FOLLOWUP, PDELAYRESP or SIGNALING message.

The defect is reached by an unauthenticated peer on the same link sending an Ethernet frame with ethertype 0x88F7 to the PTP multicast address on an interface configured as a gPTP port, with CONFIGNETGPTP enabled. Because conformant Ethernet pads frames to 60 bytes, a payload shorter than 34 bytes generally requires a link that can deliver sub-minimum frames — for example the nativesim TAP driver (drivers/ethernet/ethnativetap.c), which forwards whatever length the host device supplies, or a MAC configured to accept undersized frames.

The short packet is retained (netpktref() into rcvdsyncptr, rcvdfollowupptr, rcvdpdelayrespptr or rcvdannounceptr) and later parsed by the media-dependent and media-independent state machines in subsys/net/l2/ethernet/gptp/gptpmd.c and subsys/net/l2/ethernet/gptp/gptpmi.c, which read tens of further bytes and copy some of them (the announce priority vector, hdr->portid) into state that is subsequently transmitted. Under the default fixed-size buffer allocator (CONFIGNETBUFFIXEDDATASIZE, 128-byte fragments) the accesses stay inside the allocated fragment and disclose stale recycled buffer contents; under the experimental CONFIGNETBUFVARIABLEDATASIZE allocator, where fragments are heap-allocated at the exact frame length, they are genuine out-of-bounds reads. There is no write and no availability impact.

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

The HL7800 cellular modem driver's +CGCONTRDP: response handler oncmdatcmdinfoipaddr() in drivers/modem/vendorstandalone/hl7800.c parses the PDP-context dynamic parameters (local address, subnet mask, gateway, and DNS servers) that the cellular network assigns to the device. The response is linearized into a 256-byte stack buffer, after which each address field length is computed from comma/. delimiter positions in the network-supplied data and used directly as the length argument to strncpy() into the fixed 64-byte stack buffer tempaddrstr (and the 16-byte ifacectx.dnsv4string).

Because the field length is derived from attacker-controlled delimiter positions and was not bounded against the destination buffer, a single field can be far larger than 64 bytes. A malicious or impersonated cellular network (for example a rogue base station) can return a crafted +CGCONTRDP response with an overlong address field, causing strncpy() to write past tempaddrstr on the modem worker thread's stack, plus an out-of-bounds NUL write at tempaddrstr[addrlen].

No device-side privileges or user interaction are required: the device itself issues the AT+CGCONTRDP=1 query during normal network attach and parses whatever the network returns. The overflow corrupts adjacent stack memory in supervisor context, yielding at minimum a remotely triggerable crash and potentially control-flow hijacking on targets without stack protection.

The fix bounds every field length against its destination buffer (tempaddrstr and dnsv4string) before each copy, rejecting overlong fields.

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

The user-space system-call verifier zvrfyzlogmsgstaticcreate() in subsys/logging/logmsg.c was a pure pass-through: it forwarded the caller-supplied source, desc, package, and data arguments directly to the kernel-mode implementation zimplzlogmsgstaticcreate() without performing any of the mandatory KSYSCALL checks. Because zlogmsgstaticcreate() is declared syscall, under CONFIGUSERSPACE any unprivileged user-mode thread can invoke it directly with fully attacker-controlled arguments.

The kernel-mode handler dereferences each of these untrusted values: frontendruntimefiltering() reads through the source pointer as a struct logsourcedynamicdata, cbprintfpackagecopy() reads desc.packagelen bytes from the package pointer, and zlogmsgfinalize() performs a memcpy() of desc.datalen bytes from the data pointer. With no verification, a user thread can supply arbitrary kernel addresses and arbitrary lengths, and the kernel will read from them.

The impact is a kernel-mode denial of service (the kernel faults dereferencing an attacker-chosen pointer) and, where a log backend output is observable to the attacker, disclosure of arbitrary kernel memory copied into the emitted log message — a confidentiality breach across the user/kernel boundary that the userspace sandbox is meant to enforce. The reads do not corrupt kernel memory, so there is no out-of-bounds write primitive.

The fix adds the required validation to the verifier: it bounds desc.packagelen against ZLOGMSGMAXPACKAGE, rejects non-NULL/length mismatches, and applies KSYSCALLMEMORYREAD() to package, data, and (when runtime filtering with a frontend is enabled) source, so any out-of-bounds or kernel pointer now raises KOOPS instead of being honored.

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