Search Results (263 CVEs found)

CVE Vendors Products Updated CVSS v3.1
CVE-2026-19575 1 Zephyrproject 1 Zephyr 2026-10-09 7.8 High
The user-mode verification handler for the device_deinit() system call, z_vrfy_device_deinit() in kernel/device.c, validated its dev argument with K_SYSCALL_OBJ_INIT(dev, K_OBJ_ANY). k_object_validate() short-circuits its type comparison when the requested type is K_OBJ_ANY, so the check reduced to "this pointer is the base address of some kernel object the calling thread has been granted" — the object's actual type was never compared, and K_SYSCALL_OBJ_INIT also skips the initialization-state check. The sibling handlers z_vrfy_device_init() and z_vrfy_device_is_ready() already used K_OBJ_DRIVER_ANY and were unaffected. A thread running in user mode can therefore pass any kernel object it holds permission on — most usefully a thread stack object obtained from the k_thread_stack_alloc() syscall or a statically defined K_THREAD_STACK it was granted in order to spawn a child user thread — whose backing memory is writable from user mode. z_impl_device_deinit() then interprets those attacker-written bytes as a struct device: it dereferences the state pointer read out of the object, calls the function pointer read out of ops.deinit, and on success writes through state again. The result is an indirect call to an arbitrary address executed in supervisor mode, plus an arbitrary kernel read and a single-byte kernel write. Exploitation gives a local unprivileged thread full kernel code execution, defeating the CONFIG_USERSPACE isolation boundary entirely; a less precise attempt yields a supervisor-mode fault and a system crash. The defect is only reachable in builds that enable both CONFIG_USERSPACE and CONFIG_DEVICE_DEINIT_SUPPORT — with de-initialization support disabled, z_impl_device_deinit() returns -ENOTSUP without ever dereferencing the pointer. In v4.2.x and v4.3.x, CONFIG_DEVICE_DEINIT_SUPPORT defaulted to y, so every CONFIG_USERSPACE build of those releases is exposed unless the option was explicitly turned off. From v4.4.0 the option is opt-in (no default, and not selected by any in-tree subsystem), so a v4.4.x build is exposed only if it enables the option explicitly. The v4.2 line is no longer maintained and receives no backport. The fix changes the object check to K_OBJ_DRIVER_ANY, which constrains the argument to the build-generated driver object type range (K_OBJ_DRIVER_FIRST..K_OBJ_DRIVER_LAST) — the real struct device instances placed by the linker — so the state and ops.deinit fields are once again kernel-controlled.
CVE-2026-19574 1 Zephyrproject 1 Zephyr 2026-10-09 7 High
The ARM64 MMU back-end allocated address space identifiers (ASIDs) for memory domains with a bare round-robin counter in arch_mem_domain_init() (arch/arm64/core/mmu.c). VM_ASID_BITS is 8, so only 255 ASIDs exist; once the counter wrapped, arch_mem_domain_init() could hand an ASID to a new domain while a still-live domain held the same one. Domain-private mappings are installed non-global (MT_NG), so the ASID is the only tag separating one domain's cached translations from another's in the TLB. The context-switch path in z_arm64_swap_ptables() only flushes the TLB when the outgoing and incoming domains carry the same ASID, which does not cover a duplicate reached through a third domain: for domains A and C sharing an ASID and an unrelated domain B, the schedule A -> B -> C never takes the flush branch, so the ASID-tagged entries A populated remain resident while C runs. Under SMP two live domains sharing an ASID can additionally be resident on two CPUs at once, which the architecture does not allow for distinct translation-table sets. Triggering the wrap requires a CONFIG_USERSPACE application on ARM64 that creates more than 255 memory domains over its lifetime; k_mem_domain_init() and k_mem_domain_deinit() are supervisor-only APIs and are not exposed as syscalls, so an unprivileged thread cannot drive the counter directly. Once two live domains alias, however, a user-mode thread in one domain can read and write memory belonging to the other domain's partitions and thread stacks with that domain's permissions, defeating the memory-domain isolation boundary. The fix scans the live domain_list before assigning an ASID, advances the round-robin counter past ASIDs already in use, and returns -ENOMEM when all are taken, so domain creation fails closed instead of silently aliasing.
CVE-2026-19571 1 Zephyrproject 1 Zephyr 2026-10-09 6.7 Medium
The ITE IT8xxx2 SHI host-command backend (subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_ite.c) copied the 8-byte host-command request header from the SPI Rx FIFO directly into the shared receive buffer data->in_msg and only afterwards checked the protocol version and the derived packet length. The interrupt handler also accepted a chip-select assertion and an Rx-valid-length (RVLI) interrupt in any driver state other than SHI_STATE_DISABLED, so a new header could be parsed while the host-command thread was still processing the previous request out of the very same buffer. The host processor is the SPI controller and drives both chip select and the clock. After sending a well-formed request it can immediately de-assert chip select — which returns the driver to the ready state and re-enables the FIFO — and start a second transaction carrying a header with data_len = 0xFFFF. Those eight bytes are written into in_msg before the oversized length is rejected, so they land in a buffer whose contents verify_rx() in subsys/mgmt/ec_host_cmd/ec_host_cmd_handler.c has already validated. If this lands in the window before the host-command thread executes args.input_buf_size = rx_header->data_len, the framework hands the registered command handler a 65535-byte input length over a 256-byte buffer. The result is an out-of-bounds read of up to roughly 64 KiB beyond the request buffer: command handlers that copy or echo input_buf_size bytes disclose adjacent embedded-controller memory back to the host or overflow the response buffer, and a read past the end of SRAM faults the controller. The same race also allows cmd_id and cmd_ver to be swapped after checksum verification and after handler lookup. Exploitation requires the ability to drive the inter-processor SHI bus (a compromised host OS or physical access to the SPI lines) and winning a timing race, which the SPI controller can retry indefinitely. The fix parses the header into a local struct ec_host_cmd_request_header and copies it into in_msg only after the length has been bounded by sizeof(data->in_msg), and ignores chip-select and RVLI interrupts outside SHI_STATE_READY_TO_RECV/SHI_STATE_RECEIVING. A residual, bounded race remains: an end-of-transaction interrupt still resets the state to ready while the host-command thread owns the buffer, so a valid second request can still overwrite the in-flight request's contents, unlike the NPCX backend which parks in SHI_STATE_CNL_RESP_NOT_RDY while the buffer is in use.
CVE-2026-19570 1 Zephyrproject 1 Zephyr 2026-10-09 8.8 High
The LE Audio Broadcast Sink in subsys/bluetooth/audio/bap_broadcast_sink.c copies subgroup metadata from a received Basic Audio Announcement (BASE) into the static Broadcast Audio Scan Service parameter structure mod_src_param without any bounds check. In base_subgroup_meta_cb() the destination element was selected as mod_src_param.subgroups[mod_src_param.num_subgroups] with no test against ARRAY_SIZE(mod_src_param.subgroups) (sized by CONFIG_BT_BAP_BASS_MAX_SUBGROUPS, default 1), and the metadata was copied with memcpy() using the raw on-air length returned by bt_bap_base_get_subgroup_codec_meta() into a metadata array sized by CONFIG_BT_AUDIO_CODEC_CFG_MAX_METADATA_SIZE (default 4). The BASE validator bt_bap_base_get_base_from_ad() only checks structural consistency and permits up to ~24 subgroups and metadata LTVs of ~240 octets. The defect is reached from the periodic advertising receive callback: pa_recv() → bt_data_parse() → pa_decode_base() → update_recv_state_base() → bt_bap_base_foreach_subgroup() → base_subgroup_meta_cb(). Every broadcast sink registers a scan-delegator receive state at creation (bt_bap_broadcast_sink_create() calls broadcast_sink_add_src()), and CONFIG_BT_BAP_BROADCAST_SINK depends on CONFIG_BT_BAP_SCAN_DELEGATOR, so the path is active in every broadcast-sink build once the device is periodic-advertising-synced. An attacker in radio range who operates a broadcast source the device syncs to — or who impersonates the advertiser address and SID of one already in use, periodic advertising data being unauthenticated — can change the BASE at will; each new BASE is re-parsed. A crafted BASE therefore writes attacker-chosen bytes past the end of a fixed static object in .bss: up to roughly 236 bytes for an oversized metadata LTV, plus whole struct bt_bap_bass_subgroup records for each subgroup beyond CONFIG_BT_BAP_BASS_MAX_SUBGROUPS. This is memory corruption of adjacent Bluetooth-audio state reachable with no pairing, bonding or GATT connection, with a potential for remote code execution in the Bluetooth RX thread; in addition, the unvalidated metadata_len is forwarded to bt_bap_scan_delegator_mod_src(), which neither clamps it nor rejects it, leading to a further copy into the receive state and to out-of-bounds memory being disclosed in the BASS receive-state notification sent to a connected Broadcast Assistant. The fix rejects a BASE carrying more subgroups than the receive state can hold (discarding the update entirely) and omits metadata that does not fit rather than copying it, and additionally honours the previously-ignored error return of the subgroup decode pass.
CVE-2026-19569 1 Zephyrproject 1 Zephyr 2026-10-09 8.8 High
dynamic_object_create() in kernel/userspace/userspace.c computed the backing allocation for a dynamically allocated kernel object as obj_size_get(otype) + size, and for thread stack elements as STACK_ELEMENT_DATA_SIZE(size) (a round-up plus fixed overhead), without checking either expression for unsigned wrap-around. A size close to SIZE_MAX makes the computed total wrap to a very small value, so the heap chunk handed out is a few bytes while the object descriptor is still tagged with the full requested type and registered in the kernel object table. The size argument reaches that arithmetic directly from user mode. k_object_alloc_size() is declared __syscall in include/zephyr/sys/kobject.h, its verifier z_vrfy_k_object_alloc_size() in kernel/userspace/userspace_handler.c is a bare pass-through, and z_object_alloc() only range-checks otype — nothing bounds size. The stack-element branch is additionally reachable through the k_thread_stack_alloc() syscall via kernel/dynamic.c. Because subsequent kernel-object validation checks only the object's type and initialization state, the undersized handle passes K_SYSCALL_OBJ_INIT()/K_SYSCALL_OBJ_NEVER_INIT(), and the matching init syscall (for example k_mutex_init(), k_sem_init(), or k_thread_create()) then writes a complete object over the truncated allocation. An unprivileged user-mode thread can therefore trigger a supervisor-mode out-of-bounds write into the kernel resource-pool heap, of a size and content it substantially controls, corrupting sys_heap chunk metadata and adjacent kernel objects. Under CONFIG_GEN_PRIV_STACKS the thread-stack branch additionally stores an attacker-influenced wild pointer as a user thread's privileged stack base. The practical result is escape from the CONFIG_USERSPACE sandbox — kernel-level code execution or at minimum kernel memory corruption and system compromise. Exploitation requires CONFIG_USERSPACE together with CONFIG_DYNAMIC_OBJECTS (also selected by CONFIG_DYNAMIC_THREAD under userspace), and a calling thread with an assigned resource pool. The fix rejects both overflowing computations and frees the partially built descriptor.
CVE-2026-19186 1 Zephyrproject 1 Zephyr 2026-10-07 8.1 High
ieee802154_decipher_data_frame() in subsys/net/l2/ieee802154/ieee802154_frame.c computed payload_len = net_pkt_get_len(pkt) - ll_hdr_len - authtag_len without first checking that the received frame is at least ll_hdr_len + authtag_len bytes long. All three variables are uint8_t, so a frame whose payload is shorter than the configured authentication tag makes the subtraction wrap around to a large value (up to 255). The wrapped length is passed unchanged to ieee802154_decrypt_auth() and on to the CCM operation as cipher_pkt.in_len/out_buf_max, with apkt->tag pointing at frame + ll_hdr_len + payload_len. Because the receive buffer is allocated to the exact length of the frame received from the radio driver, the crypto layer then reads several hundred bytes past the end of the packet buffer and writes the same number of decrypted bytes back over it in place. The frame's authentication tag is only verified after this processing has taken place, so no key material, association or prior authentication is needed — a single crafted short frame from any device in radio range is sufficient. Frame validation in ieee802154_validate_frame() does not prevent it: a data frame is accepted with a one-byte payload. The result is an out-of-bounds read and an out-of-bounds write of up to roughly 240 bytes into the adjacent network-buffer pool, corrupting other packets or allocator metadata and typically faulting the target. The out-of-bounds content is not attacker-chosen (it is ciphertext XOR keystream over out-of-bounds memory) and the frame is dropped when tag verification fails, so the primary impact is memory corruption and denial of service rather than information disclosure. Exposure is limited to configurations that enable the experimental CONFIG_NET_L2_IEEE802154_SECURITY option, select a crypto device via CONFIG_NET_L2_IEEE802154_SECURITY_CRYPTO_DEV_NAME, and have established a security session with a level other than IEEE802154_SECURITY_LEVEL_NONE; with security disabled or at level NONE the tag length is zero and no underflow occurs. The fix rejects frames shorter than ll_hdr_len + authtag_len before the subtraction, and adds the matching guard on the transmit side in ieee802154_create_data_frame().
CVE-2026-15894 1 Zephyrproject 1 Zephyr 2026-10-07 8.8 High
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 sol_pdu_decrypt(), out is allocated as NET_BUF_SIMPLE(17) and then filled with net_buf_simple_add_mem(out, in->data, in->len); net_buf_simple_add() guards its tailroom only with __ASSERT_NO_MSG, 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 net_buf_simple_restore() before dispatching to bt_mesh_sol_recv(), 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. bt_mesh_scan_cb() 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 CONFIG_BT_MESH_OD_PRIV_PROXY_SRV 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.
CVE-2026-19185 1 Zephyrproject 1 Zephyr 2026-10-05 7.8 High
The system-call verifier for i3c_do_ccc() in drivers/i3c/i3c_handlers.c validated the outer struct i3c_ccc_payload, 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 i3c_ccc_target_payload carries its own data pointer and data_len, and neither was passed through K_SYSCALL_MEMORY() before the payload was handed to z_impl_i3c_do_ccc() 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 z_vrfy_i3c_transfer(), which has always copied its message array first. The defect is only present in CONFIG_USERSPACE builds, where drivers/i3c/i3c_handlers.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/i3c_mcux.c, drivers/i3c/i3c_cdns.c, drivers/i3c/i3c_stm32.c, drivers/i3c/i3c_npcx.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 CONFIG_USERSPACE is meant to provide. The fix introduces copy_ccc_and_do(), which snapshots the payload, copies the target array into kernel memory with k_usermode_alloc_from_copy() (bounding num_targets to fewer than 32), validates each per-target buffer with K_SYSCALL_MEMORY() according to rnw, and copies the driver-written num_xfer and err fields back to the caller.
CVE-2026-19184 1 Zephyrproject 1 Zephyr 2026-10-05 8.4 High
The NXP GAU ADC driver (drivers/adc/adc_mcux_gau_adc.c) validated the caller-supplied sequence->buffer_size, which is expressed in bytes, against the number of active channels, which is a sample count. It then stored that byte count directly in data->results_length and used it in mcux_gau_adc_read_samples() as the number of uint16_t slots available. Because each conversion result occupies sizeof(uint16_t) bytes, a buffer that was accepted as "large enough" could be written with up to twice its size in bytes, so every sample past the buffer's midpoint was written out of bounds. adc_read() and adc_read_async() are Zephyr system calls. The syscall verifier in drivers/adc/adc_handlers.c only confirms that the caller owns buffer_size writable bytes (K_SYSCALL_MEMORY_WRITE); deciding whether that size is sufficient for the requested channels and extra_samplings is delegated entirely to the driver. On a build with CONFIG_USERSPACE=y, a user-mode thread that has been granted the ADC device object could therefore submit a deliberately half-sized buffer and cause the driver's work-queue handler — which runs in supervisor mode, outside the caller's MPU restrictions — to write ADC conversion results past the end of that buffer, at an address and for a length of the caller's choosing. The overrun is bounded by the requested sequence: with sequence->options->extra_samplings set, the sampling loop walks the buffer pointer forward across every sampling, so the total overrun can reach the full size of the supplied buffer (kilobytes for a large extra_samplings). The written words are 16-bit ADC conversion results, so the content is only partially attacker-influenced (via the selected analog input, gain and resolution), but the destination and length are fully controlled — sufficient for kernel memory corruption, a crash, or a userspace-to-kernel privilege escalation. Builds without CONFIG_USERSPACE, or on SoCs other than NXP RW61x with the GAU ADC node enabled, are not exposed to the privilege boundary; there the same defect only causes a silent overflow when the application itself passes an undersized buffer. The fix replaces the ad-hoc check with the shared adc_sequence_validate_buffer() helper (validating against num_channels * sizeof(uint16_t)), stores buffer_size / sizeof(uint16_t) in results_length, and corrects the loop bound to a post-decrement so exactly the available number of slots may be written.
CVE-2026-17053 1 Zephyrproject 1 Zephyr 2026-10-01 4.4 Medium
The SMBus driver API exposed smbus_smbalert_remove_cb() and smbus_host_notify_remove_cb() as Zephyr syscalls. Their verifiers in drivers/smbus/smbus_handlers.c validated only the dev argument with K_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS) and forwarded the caller-supplied struct smbus_callback *cb pointer into kernel-mode driver code without any K_SYSCALL_MEMORY_READ/K_SYSCALL_MEMORY_WRITE validation. A companion change in 2023 had already removed the matching smbus_smbalert_set_cb() / smbus_host_notify_set_cb() syscalls for this reason, but the two removal syscalls were left exposed. On a build with CONFIG_USERSPACE=y, CONFIG_SMBUS=y and a driver implementing the callback operations (drivers/smbus/intel_pch_smbus.c with CONFIG_SMBUS_INTEL_PCH_SMBALERT/CONFIG_SMBUS_INTEL_PCH_HOST_NOTIFY, or drivers/smbus/smbus_stm32.c with CONFIG_SMBUS_STM32_SMBALERT), any user-mode thread that has been granted the SMBus device object can invoke these syscalls with an arbitrary pointer. The value reaches smbus_callback_remove() in drivers/smbus/smbus_utils.h, which uses it as a node identity against the kernel's sys_slist_t of registered callbacks. The consequence is that an unprivileged thread can unregister an SMBALERT or Host Notify callback that a supervisor-mode component registered, silently disabling alert handling for the rest of the system; because Zephyr images have fixed symbol addresses and the syscall returns 0 on a hit versus -ENOENT on a miss, the target address is both derivable and searchable. In builds with CONFIG_ASSERT=y the __ASSERT(callback->handler, ...) check additionally dereferences the caller-supplied address in supervisor mode, so a bogus pointer raises a kernel-mode fault and a fatal system error, and the fault/no-fault outcome discloses which addresses are mapped. The fix removes both syscall entry points, demoting the two functions to ordinary static inline calls so that callback list manipulation is available only to supervisor-mode code. There is no impact on builds without CONFIG_USERSPACE, and no impact on configurations that do not enable an SMBus driver with SMBALERT or Host Notify support.
CVE-2026-18413 1 Zephyrproject 1 Zephyr 2026-09-30 7.8 High
The ADC API requires each driver to reject a sampling sequence whose destination buffer is too small: the buffer_size field of struct adc_sequence in include/zephyr/drivers/adc.h documents that "the driver must ensure that samples are not written beyond the limit and it must return an error if the buffer turns out to be not large enough". The NXP MCUX LPADC driver did not honour that contract. mcux_lpadc_start_read() in drivers/adc/adc_mcux_lpadc.c performed no buffer-size check at all before assigning data->buffer = sequence->buffer. Each completed conversion then stores one 16-bit sample per enabled channel per sampling round through an unbounded *data->buffer++: in mcux_lpadc_isr() for interrupt-driven builds, and in mcux_lpadc_dma_callback() for DMA-driven builds on releases that have the DMA path. A sequence selecting two channels with a two-byte buffer, for example, has its second sample written past the end of the buffer. On a build with CONFIG_USERSPACE, adc_read() and adc_read_async() are system calls. The handler in drivers/adc/adc_handlers.c copies the sequence in from user memory, verifies only that [buffer, buffer + buffer_size) is writable by the calling thread, and rejects a user-supplied options->callback; it deliberately leaves the size arithmetic to the driver. A user-mode thread that has been granted access to an LPADC device object therefore fully controls channels, buffer, buffer_size and options->extra_samplings, and can request far more samples than its buffer can hold: up to channels * 65536 samples into a two-byte buffer, since the sample pointer is only rewound on a repeat sampling, never on the extra samplings of a sequence. The resulting stores are performed by the driver in kernel mode (in the ADC interrupt handler or the DMA completion callback), where the MPU does not restrict the thread's memory domain, so the write walks linearly out of the user partition and into adjacent memory such as other partitions, kernel data or thread stacks. The impact is kernel-memory corruption of attacker-chosen length at an attacker-chosen offset, a plausible privilege-escalation and denial-of-service primitive from an unprivileged user-mode thread. Builds without CONFIG_USERSPACE are affected only as a caller-side robustness defect, since the application itself supplies the buffer. The fix calls the new shared helper adc_sequence_validate_buffer() in drivers/adc/adc_common.c from mcux_lpadc_start_read(). The helper computes active_channels sizeof(uint16_t) (1 + extra_samplings) and returns -ENOMEM before any sampling is started.
CVE-2026-16513 1 Zephyrproject 1 Zephyr 2026-09-30 7.8 High
The userspace verifier z_vrfy_rtio_sqe_copy_in_get_handles() in subsys/rtio/rtio_syscalls.c (subsys/rtio/rtio_handlers.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 K_SYSCALL_MEMORY_WRITE 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 sensor_read_async_mempool() or the async ADC helpers, which call rtio_sqe_copy_in_get_handles() 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 CONFIG_USERSPACE and CONFIG_RTIO are affected; without CONFIG_USERSPACE 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 rtio_sqe 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 CONFIG_USERSPACE is meant to enforce. At minimum it is a reliable kernel memory-corruption and crash primitive. The reporter reproduced the write on qemu_x86: a K_USER thread changed a supervisor global from NULL to a live kernel SQE pointer. The fix adds K_SYSCALL_MEMORY_WRITE(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 K_OOPS. The neighbouring verifier z_vrfy_rtio_cqe_get_mempool_buffer(), which checked its buff/buff_len 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.
CVE-2026-18416 1 Zephyrproject 1 Zephyr 2026-09-30 3.7 Low
The CoAP link-format helper match_path_uri() in subsys/net/lib/coap/coap_link_format.c compares a registered resource path against the URI carried in a Uri-Query href= option. That URI is not NUL terminated, but the inner character loop advanced its index k once per path character without ever testing it against the option length len. When a registered path segment is longer than the supplied URI and the URI is a prefix of it, the loop reads uri[len] and beyond, past the end of the option value. The path is reached from coap_well_known_core_get_len() and coap_well_known_core_get() via match_queries_resource(), i.e. by any unauthenticated GET /.well-known/core?href=/<prefix> request to a device that serves /.well-known/core (for the CoAP server subsystem, CONFIG_COAP_SERVER_WELL_KNOWN_CORE, default y) and has at least one resource that declares struct coap_core_metadata attributes. The over-read does not reach the receive buffer. The well-known-core builders parse the query into a stack-local struct coap_option, whose value is a fixed array (value[12], or CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE bytes) that the option bytes are copied into, so uri points into that copy. Reading past len therefore reads the unused, uninitialized tail of the array and, when the option fills it, the bytes just past it in the same stack frame. (In the ZoAP library of v1.8.0 to v1.9.x the option value was instead a pointer into the received packet, and the over-read ran past the option inside the packet buffer.) The impact is bounded. The number of bytes read past the end is limited by the length of the resource path segment, and each additional byte is only read if it happens to equal the next path character, so in practice the over-read is one byte. It also cannot influence the response: returning a match requires the final compared index to be len - 1 or len, both in bounds, so out-of-bounds bytes only ever steer the loop to the next candidate resource. The consequence is undefined behaviour, not information disclosure and not a matching error. The fix adds a k >= len guard at the top of the inner loop, so every uri[k] dereference is within the option value while still allowing a trailing * wildcard to match a longer path.
CVE-2026-18415 1 Zephyrproject 1 Zephyr 2026-09-30 6.3 Medium
ieee802154_send() in subsys/net/l2/ieee802154/ieee802154.c copies the outgoing packet into a single fixed 125-byte transmit buffer (tx_frame_buf_pool, sized IEEE802154_MTU). In builds with CONFIG_NET_L2_IEEE802154_FRAGMENT enabled (the default whenever CONFIG_NET_6LO is set), the branch taken when 6LoWPAN fragmentation is not required performed an unchecked net_buf_add_mem(frame_buf, pkt_buf->data, pkt_buf->len). The only guard was __ASSERT_NO_MSG() inside net_buf_simple_add(), which is compiled out without CONFIG_ASSERT, so an oversized packet silently overran the frame buffer. The defect is not reachable from the radio: for NET_AF_INET6 packets ieee802154_6lo_encode_pkt() compares the whole packet length against IEEE802154_MTU 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 NET_AF_PACKET sockets bound to an 802.15.4 interface: for NET_SOCK_RAW the 6LoWPAN block is skipped entirely and for NET_SOCK_DGRAM it returns early on the address-family test, leaving no length validation anywhere on the transmit path (net_context_sendto() and net_if_tx() apply none, and pkt_buffer_length() does not clamp the allocation for this L2). An application — or, in a CONFIG_USERSPACE build, an unprivileged application thread using the zsock_socket()/zsock_sendto() syscalls — can therefore drive a supervisor-mode out-of-bounds write of chosen bytes past the 125-byte pool buffer. With the default CONFIG_NET_BUF_FIXED_DATA_SIZE of 128 bytes the overrun is bounded to roughly ll_hdr_len + 3 bytes; with CONFIG_NET_BUF_VARIABLE_DATA_SIZE a single storage buffer can be as large as CONFIG_NET_PKT_BUF_TX_DATA_POOL_SIZE, 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 ll_hdr_len + net_pkt_get_len(pkt) + authtag_len against IEEE802154_MTU before any copy and adds a tailroom-checking copy_pkt_to_frame() helper that returns -EMSGSIZE instead of overrunning the buffer. The same change also linearizes the whole net_buf chain into one MAC frame, so packet storage boundaries no longer become frame boundaries on the wire.
CVE-2026-18414 1 Zephyrproject 1 Zephyr 2026-09-30 7.8 High
The ADC API requires each driver to reject a sampling sequence whose destination buffer is too small: the buffer_size field of struct adc_sequence in include/zephyr/drivers/adc.h documents that "the driver must ensure that samples are not written beyond the limit and it must return an error if the buffer turns out to be not large enough". The ADI MAX32 driver did not honour that contract. start_read() in drivers/adc/adc_max32.c compared buffer_size, a byte count, against a sample count ((1 + extra_samplings) channels), ignoring sizeof(uint16_t), so it accepted a buffer half the required size. The samples are then stored through the uint16_t data->buffer by Wrap_MXC_ADC_GetData(), which writes two bytes per sample and advances the pointer by one uint16_t: in adc_max32_start_channel() for synchronous reads, and in adc_max32_isr() for asynchronous ones. A sequence selecting two channels with a two-byte buffer, for example, passes the check and has its second sample written past the end of the buffer. On a build with CONFIG_USERSPACE, adc_read() and adc_read_async() are system calls. The handler in drivers/adc/adc_handlers.c copies the sequence in from user memory, verifies only that [buffer, buffer + buffer_size) is writable by the calling thread, and rejects a user-supplied options->callback; it deliberately leaves the size arithmetic to the driver. A user-mode thread that has been granted access to a MAX32 ADC device object therefore fully controls channels, buffer, buffer_size and options->extra_samplings, and can make the driver write twice as many bytes as its buffer holds. Because the check scales with extra_samplings, the overrun equals the length of the buffer itself, up to channels * 65536 bytes past its end, since the sample pointer is only rewound on a repeat sampling, never on the extra samplings of a sequence. The resulting stores are performed by the driver in kernel mode (in the system call itself, the ADC context timer, or the ADC interrupt handler for asynchronous reads), where the MPU does not restrict the thread's memory domain, so the write walks linearly out of the user partition and into adjacent memory such as other partitions, kernel data or thread stacks. The impact is kernel-memory corruption of attacker-chosen length at an attacker-chosen offset, a plausible privilege-escalation and denial-of-service primitive from an unprivileged user-mode thread. Builds without CONFIG_USERSPACE are affected only as a caller-side robustness defect, since the application itself supplies the buffer. The fix replaces that check in start_read() with a call to the new shared helper adc_sequence_validate_buffer() in drivers/adc/adc_common.c, passing sizeof(uint16_t) as the sample size. The helper computes active_channels sizeof(uint16_t) (1 + extra_samplings) and returns -ENOMEM before any sampling is started.
CVE-2026-18747 1 Zephyrproject 1 Zephyr 2026-09-30 6.8 Medium
The MCUmgr SMP-over-console transport decodes a base64 frame, reads a 16-bit packet length from it, verifies a CRC and then unconditionally strips the trailing CRC with rx_ctxt->nb->len -= 2U; in mcumgr_serial_process_frag() (subsys/mgmt/mcumgr/transport/src/serial_util.c). mcumgr_serial_extract_len() accepted any declared length, including 0 and 1, and a packet declaring length 0 passes the checksum test for free because crc16_itu_t() over zero bytes returns the zero seed. Since net_buf::len is a uint16_t, the subtraction underflows and the buffer is handed to SMP claiming roughly 65 KB of payload while its data area is only CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE bytes (default 384). The trigger is a single unauthenticated 7-byte line on the management console — the 0x06 0x09 packet marker followed by the base64 group AAA= and a newline — delivered to any transport built on this helper: CONFIG_MCUMGR_TRANSPORT_UART (smp_uart.c) or CONFIG_MCUMGR_TRANSPORT_SHELL (smp_shell.c), both of which select MCUMGR_TRANSPORT_SERIAL_HAS_SMP_OVER_CONSOLE. No prior session state, fragmentation or credentials are required to trigger the underflow, and the malformed frame is mishandled before any command handler or command-level access control runs. The attacker only needs write access to that console, which on many boards is a USB CDC-ACM port rather than a bare UART header. With the inflated length, smp_process_request_packet() in subsys/mgmt/mcumgr/smp/src/smp.c loses its bound: cbor_nb_reader_init() gives the CBOR decoder a ~65 KB window into a 384-byte buffer, and each request header's nh_len is checked only against the inflated length. On its own the 7-byte frame re-parses whatever stale bytes the reused pool buffer still holds, typically a replay of the previously received request followed by a parse error, without leaving the buffer. Because the transport is unauthenticated, though, the attacker also controls the frames sent before the trigger, and can stage buffer contents so that a request succeeds with an nh_len larger than the buffer; net_buf_pull(), guarded only by __ASSERT_NO_MSG, then moves the parse cursor out of bounds and the loop reads further headers and CBOR from adjacent memory. The consequence is an out-of-bounds read that can fault the MCUmgr thread (denial of service); memory disclosure is also possible, since the default-enabled os echo handler (CONFIG_MCUMGR_GRP_OS_ECHO) decodes its string inside that window and copies it into its response. There is no integrity gain beyond what the unauthenticated transport already permits. The fix rejects any declared packet length of two bytes or fewer in mcumgr_serial_extract_len(), so the CRC-strip subtraction can no longer underflow. The identical pattern remains in the test-only loopback transport subsys/mgmt/mcumgr/transport/src/smp_dummy.c (CONFIG_MCUMGR_TRANSPORT_DUMMY), which has no external input path and therefore carries no practical exposure.
CVE-2026-18417 1 Zephyrproject 1 Zephyr 2026-09-29 6.5 Medium
The native BSD-socket layer recorded a pending asynchronous socket error by type-punning it into struct net_context's void user_data field (ctx->user_data = INT_TO_POINTER(-status) in zsock_accepted_cb(), zsock_received_cb(), zsock_connected_cb() and zsock_close_ctx() in subsys/net/lib/sockets/sockets_inet.c), reading it back with POINTER_TO_INT(). That same field is owned by the network stack for listening TCP contexts: net_tcp_accept() stores the parent context pointer there and the TCP core passes it back to the registered accept callback. A failed accept therefore left a small integer (an errno value) where the stack expected a struct net_context . When the network interface carrying a listening TCP socket goes down, close_tcp_conn() in subsys/net/ip/tcp.c invokes the accept callback with -ENETDOWN and the context's user_data. In v4.3.0 the callback was not disarmed afterwards, so a second interface-down event forwarded the previously stored errno to zsock_accepted_cb(), which dereferenced it as the parent context and performed several stores through it (sock_set_error()'s read-modify-write of socket_data, k_fifo_cancel_wait(&parent->recv_q)) — the crash described in the fix's commit message. v4.3.1 and v4.4.x carry a later change clearing conn->accept_cb after the error callback (269cb8823d3 on the v4.3 branch, 913fae5169425550f2364655298fceb79b320066 on main), which closes that repeat path; on those releases the poisoned cookie remains reachable only by a narrower race, a handshake completing alongside the interface-down still passing the stale cookie to k_fifo_put(&parent->accept_q, ...), and by getsockopt(SO_ERROR), which reads the field back unconditionally. On v4.3.0 an application that keeps a listening TCP socket open across repeated link-down events is sufficient to reach the defect; the triggering condition is a network-interface state change, not attacker-supplied packet data, so the practical attacker is one able to force the link down repeatedly (for example an adjacent attacker disrupting a wireless link) or one with local/physical access. Because both the faulting address and the stored data are fixed small constants derived from the errno value, the outcome is a wild-pointer access leading to a kernel fatal error — a denial of service (device crash or reset) rather than an attacker-directed memory corruption. The fix stores the pending error in a dedicated net_context.sock_error field and converts every producer and consumer to sock_set_error()/sock_get_error(), leaving user_data untouched. As a side effect it also stops getsockopt(SO_ERROR) — which is evaluated unconditionally — from returning the kernel address held in user_data to a userspace application.
CVE-2026-18746 1 Zephyrproject 1 Zephyr 2026-09-29 5.9 Medium
parse_write_op() in subsys/net/lib/lwm2m/lwm2m_message_handling.c handles inbound CoAP WRITE/CREATE requests that carry a Block1 option. For the first block of a transfer it called init_block_ctx() and then immediately stored the peer-selected block size with block_ctx->ctx.block_size = block_size before inspecting the return code. init_block_ctx() sets the caller's pointer to NULL and returns -ENOMEM when no entry of the static block1_contexts[] pool is free or timed out, so that store dereferences a NULL pointer. The pool holds CONFIG_LWM2M_NUM_BLOCK1_CONTEXT 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 CONFIG_LWM2M_DTLS_SUPPORT 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.
CVE-2026-17054 1 Zephyrproject 1 Zephyr 2026-09-22 5.3 Medium
The Espressif ESP-hosted Wi-Fi driver (drivers/wifi/esp_hosted/) parses frames received over SPI from the ESP co-processor in esp_hosted_event_task(). For control frames it took the 16-bit TLV field data_length straight off the wire and passed it to pb_istream_from_buffer(frame.data_value, frame.data_length) without checking it against the frame length or the receive buffer. frame.data_value sits 26 bytes into a 3188-byte stack object, so a data_length of up to 0xFFFF makes pb_decode() read up to roughly 62 KB past the end of that object. Only the first fragment of a fragmented control response carries a TLV header; the pre-fix driver performed half-duplex SPI transactions and silently discarded any frame the co-processor queued while the host was transmitting (esp_hosted_hal_spi_transfer() aliased the RX buffer onto the TX buffer). When the discarded frame is the first fragment of a fragmented response, the driver treats the next fragment as a new frame — its per-fragment header and checksum are genuine, so both validation steps pass — and reads the TLV header out of raw protobuf continuation bytes. Those bytes come from control responses whose size and content an adjacent, unauthenticated attacker can influence, notably the AP scan list, which grows with the number and SSID length of access points in radio range. The impact is denial of service rather than disclosure. Reading past the end of the RAM region faults the device, and CONFIG_NANOPB_ENABLE_MALLOC is selected by the driver, so garbage length prefixes read out of bounds also drive heap allocations. The out-of-bounds bytes themselves do not reach the application: pb_decode() is started mid-stream on raw protobuf continuation bytes and so almost always fails outright, and anything that did decode would still have to pass esp_hosted_response(), which requires an exact msg_id match against the pending request, and then esp_hosted_ctrl_response(), which requires a success resp — an attacker influences the size and content of legitimate control responses, not the structure decoded out of misaligned bytes. Two related defects in the same receive path make the denial of service permanent: the fragment reassembly guard was sized with ESP_FRAME_SIZE instead of ESP_FRAME_MAX_PAYLOAD and, when tripped, returned from the sole RX thread instead of dropping the frame, and unhandled control events were queued with k_msgq_put(..., K_FOREVER) on an eight-entry queue that nothing drains, blocking that same thread. The driver has no watchdog or restart path, so either condition ends all Wi-Fi reception until the device is rebooted.
CVE-2026-15890 1 Zephyrproject 1 Zephyr 2026-09-22 5.3 Medium
The default AEAD nonce provider for the PSA Internal Trusted Storage transform module, secure_storage_its_transform_aead_get_nonce() in subsys/secure_storage/src/its/transform/aead_get.c, stores its nonce counter in unsynchronized function-local static variables (s_nonce and s_nonce_initialized). Every ITS write obtains its AES-GCM or ChaCha20-Poly1305 nonce here via secure_storage_its_transform_to_store(). Because the function held no lock, two threads calling it concurrently race on the shared statics: the initialization path (psa_generate_random() followed by memcpy()) and the non-atomic increment-then-copy path can each hand the same nonce value to two distinct encryption operations, and can lose increments so the counter repeats values it was designed never to repeat. The ITS layer (secure_storage_its_set() in subsys/secure_storage/src/its/implementation.c) performs no serialization of its own, so concurrent same-UID writes reach the racy provider directly. Reusing a nonce with the same key under AES-GCM or ChaCha20-Poly1305 is a catastrophic AEAD failure: it leaks the XOR of the two plaintexts (ITS routinely stores secrets, including PSA persistent keys) and, for GCM, exposes the authentication key, enabling forgery of stored entries. Because the AEAD key is derived per entry UID, the security-relevant collision is two concurrent writes to the same UID both receiving the same nonce; an adversary able to read the raw backing storage can then exploit the reuse. Both ITS store back-ends shipped with Zephyr, zms.c and the settings/NVS back-end in settings.c, are log-structured flash stores with deferred garbage collection, so an entry superseded by a rewrite remains physically present in the partition until its sector is reclaimed. Two same-UID writes that race therefore leave both ciphertexts readable in the raw image at once, which is the condition the nonce reuse needs to be exploitable. The trigger remains narrow: both built-in key providers (DEVICE_ID_HASH and ENTRY_UID_HASH) salt the derived key with the entry UID, so reuse across different UIDs is harmless, and the exposure requires an application that writes the same UID concurrently from two threads. The fix serializes the provider with a K_MUTEX_DEFINE(s_nonce_mutex) held for the duration of nonce generation.