Commit graph

2,134 commits

Author SHA1 Message Date
Nicolas Pitre
90d1963745 sys: util: move lowercase min/max/clamp to a new minmax.h
Since commit 37717b229f ("sys: util: rename Z_MIN Z_MAX Z_CLAMP to min
max and clamp"), <zephyr/sys/util.h> unconditionally defines function-
like macros named `min`, `max`, and `clamp` in the global namespace (in
C mode). util.h gets pulled in transitively by very broad headers,
including the POSIX layer's <pthread.h>, so any third-party C code that
uses these names as ordinary identifiers (e.g. XNNPACK's static `clamp`
helper and its public `clamp` struct field) fails to build as soon as
<pthread.h> is included.

Following the approach used by Linux, move the lowercase `min`, `max`,
`min3`, `max3`, and `clamp` macros (and their helpers) into a new
<zephyr/sys/minmax.h> header that has to be included explicitly by
source files that want them. util.h keeps the uppercase MIN/MAX/CLAMP,
so most code is unaffected; only the (much smaller) set of files that
actually use the lowercase variants needs to pick up the new include.

Fixes #107853.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-05-19 17:49:24 -04:00
Pieter De Gendt
7d963833f3 lib: uuid: Add uuid_is_nil
Add helper function to check whether a given UUID is the Nil/empty UUID.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
2026-05-18 15:21:10 +01:00
Henrik Brix Andersen
3244a59bbf Revert "os: boot_banner: remove selecting EARLY_CONSOLE"
This reverts commit 19d4060532.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
2026-05-14 19:41:20 +02:00
Fin Maaß
19d4060532 os: boot_banner: remove selecting EARLY_CONSOLE
the boot banner is printed at the end of
the init process with
``SYS_INIT(boot_banner, APPLICATION, 0);``
we don't need the early console for it.
When CONFIG_EARLY_CONSOLE is enabled,
the console will init with PRE_KERNEL_1, otherwise
with POST_KERNEL. Both are before APPLICATION.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
2026-05-14 15:15:28 +02:00
Diego Solano
63fe39ee5d cbprintf: guard append_string against NULL string pointers
When cbvprintf_package() packages a %s argument, append_string()
eventually calls strlen(str) on the pointer. If the caller passed
NULL to %s, strlen() dereferences address 0. On MMU-less targets
this is undefined behavior; on TF-M targets the SPU fields a read
at address 0 as SECURE_FAULT, which aborts the whole image.

Passing NULL to %s is a caller bug. However, deferred/packaged
logging captures the argument now and dereferences it later,
disconnecting the crash site from the offending call site and
making triage significantly harder. Substitute "(null)" in place
of a NULL string, matching the behavior of glibc's printf family,
so the buggy caller shows up in the log output rather than the
log infrastructure crashing.

Signed-off-by: Diego Solano <diegosolano@gmail.com>
2026-05-13 13:41:39 +01:00
Tomi Fontanilles
e26b5545bb lib: uuid: fix prerequisites for CONFIG_UUID_V5
It should not depend on CONFIG_MBEDTLS nor CONFIG_MBEDTLS_PSA_CRYPTO_C
as a PSA Crypto provider other than Mbed TLS may be enabled.
In fact, it doesn't even need to depend on CONFIG_PSA_CRYPTO
because CONFIG_PSA_WANT_ALG_SHA_1 is already guarded behind
CONFIG_PSA_CRYPTO_CLIENT.

At the same time, replace all the `depends on UUID` by a single if
which is the standard way to do. Also turn CONFIG_UUID into a menuconfig
instead of creating a menu manually.

Signed-off-by: Tomi Fontanilles <tomi.fontanilles@nordicsemi.no>
2026-05-11 10:56:56 +02:00
Jamie McCrae
a036c6ab5b lib: Add support for dts RAM configuration
Allows using the chosen SRAM node for RAM configuration without
using Kconfig values

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2026-05-11 08:45:38 +02:00
Robert Lubos
b3d4e29e08 net: sockets: Remove deprecated CONFIG_NET_SOCKETS_POLL_MAX
The CONFIG_NET_SOCKETS_POLL_MAX Kconfig option was deprecated in
Zephyr 4.0.0, remove it and any leftover in-tree option use.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2026-05-08 16:02:17 +01:00
Egill Sigurdur
16ac4a5b78 kernel: mem_slab: add K_MEM_SLAB_DEFINE_TYPE for automatic alignment
Introduces K_MEM_SLAB_DEFINE_TYPE() and K_MEM_SLAB_DEFINE_STATIC_TYPE()
helpers to allow the user to declare slabs for types without having to
manually ensure the alignment is correct.

Manual slab alignment was very error-prone and this change fixes several
instances of misalignment that would be trapped by the undefined
behavior sanitizer when running on 64-bit targets.

Signed-off-by: Egill Sigurdur <egill@egill.xyz>
2026-05-07 18:09:41 -05:00
Tomi Fontanilles
7d65b380e2 modules: mbedtls: link to mbedTLS only when CONFIG_MBEDTLS_BUILTIN
393350fd65 made it so that the `mbedTLS`
library is only created when `CONFIG_MBEDTLS_BUILTIN`.

Before this commit, users of Mbed TLS did the following:
`zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS)`

If the `mbedTLS` CMake library doesn't exist but is still linked to
(as is the case when `CONFIG_MBEDTLS && !CONFIG_MBEDTLS_BUILTIN`),
the linker command is populated with `-lmbedTLS` which makes the build
fail because there is no `libmbedTLS.a` in the build.

Make it so that users of Mbed TLS only link to the `mbedTLS` CMake
library when the builtin version is used.

Signed-off-by: Tomi Fontanilles <tomi.fontanilles@nordicsemi.no>
2026-05-07 09:11:36 +02:00
Daniel Leung
c91e5e1390 kernel: move atomic_c.c to lib/os
This moves the atomic_c.c from kernel to lib/os as atomic
functions are not exactly kernel features.

This also moves all the atomic kconfigs from kernel to lib/os
as the atomic headers are already under include/zephyr/sys/.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2026-05-01 11:17:27 -05:00
Daniel Leung
b45c82b69b libc: common: rename _k_neg_eagain to _errno_neg_eagain
Since errno is no longer grouped with kernel, we should not be
using kernel prefix.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2026-05-01 11:16:31 -05:00
Daniel Leung
cc61366283 kernel: move errno from kernel to lib/libc/common
errno is not exactly a kernel functionality but more of C
library feature. So move errno from kernel into lib/libc/common.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2026-05-01 11:16:31 -05:00
Pieter De Gendt
15eab8a440 lib: uuid: Add uuid_cmp
Add helper function to compare two UUIDs.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
2026-04-30 14:03:16 -04:00
Nicolas Pitre
9bb2878319 net_buf: make reference counts atomic
The two reference counts in the net_buf library -- the per-header
`buf->ref` and the per-data-block `*ref_count` byte at the start of
each variable-data allocation -- were manipulated with plain non-atomic
C operators (`++`, `--`, `if (--rc)`, `if (!rc)`).

The documented contract says otherwise. The Network Buffers chapter of
the Zephyr docs (`doc/services/net_buf/index.rst`) states:

    "The buffers have native support for being passed through k_fifo
     kernel objects. Use k_fifo_put and k_fifo_get to pass buffer from
     one thread to another."

    "The reference count can be incremented with net_buf_ref() or
     decremented with net_buf_unref(). When the count drops to zero the
     buffer is automatically placed back to the free buffers pool."

There is no requirement for callers to hold a higher-level lock around
ref/unref. The API is documented as self-synchronizing, and existing
users (notably zbus's msg-subscriber path) rely on exactly that:
a producer clones a buffer N times and hands the clones off to N
subscriber threads via their FIFOs, after which the N+1 holders
independently call `net_buf_unref()` with no surrounding lock.

With non-atomic decrement-and-test, two CPUs can concurrently observe
the same prior value (e.g. 1), both decrement, and both conclude they
were the last reference. Concrete failure modes:

  * `mem_pool_data_unref`: both CPUs call `k_heap_free(pool, ref_count)`
    on the same block. `k_heap_free` is internally serialized, so the
    duplicate free typically corrupts heap metadata silently.

  * `heap_data_unref`: both CPUs call `k_free(ref_count)` on the same
    block. `k_free` reads the owning `struct k_heap *` from the 8 bytes
    immediately preceding `ref_count`. The first call frees the block
    and the heap-hardening fill replaces those 8 bytes with the poison
    pattern (0xcfdfdfdfdfdfdfcf). The second call then dereferences a
    poisoned pointer and faults inside `k_spin_lock` (translation
    fault on the bogus heap address).

  * `net_buf_unref`: two CPUs racing the per-header decrement-and-test
    can both decide "I am the last reference," both proceed to
    `net_buf_destroy()`, and the buffer is returned to the pool's LIFO
    twice -- silently corrupting the free list.

Fix: use atomic operations on both reference counts.

The per-data-block refcount changes from `uint8_t` to `atomic_t`. This
fits inside the existing `GET_ALIGN(pool)` reservation (>= sizeof(void
*)) at no memory cost.

The per-header `buf->ref` is overlaid in a union with three small
adjacent uint8_t fields (`flags`, `pool_id`, `user_data_size`) and an
`atomic_t ref_word` view of the same storage:

    union {
        atomic_t ref_word;
        struct {
            uint8_t ref;
            uint8_t flags;
            uint8_t pool_id;
            uint8_t user_data_size;
        };
    };

(Byte order conditional on endianness so `ref` is always the LSB of
`ref_word`; on big-endian 64-bit, the byte struct is shifted by 4
bytes of padding for the same reason.)

Net_buf internals issue `atomic_inc(&buf->ref_word)` /
`atomic_dec(&buf->ref_word)` and narrow the returned word value to
`uint8_t` to extract the ref byte. Because the ref count is bounded
to 254 (already implicit in its uint8_t domain), atomic_inc/dec
adjusts only the LSB; the other three bytes are untouched. Plain
uint8_t reads of `buf->ref` from non-atomic call sites continue to
work, so the change is transparent to the dozens of consumers that
read it for diagnostics.

`flags`, `pool_id` and `user_data_size` are written exactly once at
allocation time on a single thread (or, for `flags`, from a context
that owns the buf exclusively such as bt_buf_make_view on a fresh
view), so there are no concurrent byte writes that could conflict
with the atomic word update. struct net_buf does not grow on either
32-bit or 64-bit: on 32-bit the four bytes are exactly `sizeof(long)`,
on 64-bit they fit in alignment padding the next field already
required.

A BUILD_ASSERT in lib/net_buf/buf.c documents the
`atomic_t == long` assumption that the conditional padding relies on.

In `net_buf_unref`, the per-header refcount and the fields needed for
the debug log (`buf->pool_id`) are captured into local variables
*before* the atomic decrement -- once the reference is dropped, another
CPU may immediately free the buffer, so the buffer must not be read
again. The post-decrement diagnostic log uses the value returned by
`atomic_dec` rather than re-reading `buf->ref`. The `pool->avail_count`
sanity check uses the value returned by `atomic_inc` to avoid a
follow-up `atomic_get` of memory another CPU may have changed.

`net_pkt_frag_unref()` previously had the racy
`if (frag->ref == 1U) alloc_del(); net_buf_unref();` pattern; it is
restructured to do the atomic decrement here and slot the tracker call
in atomically with the "I'm the last reference" decision, with
`net_pkt_frag_del()` routed through it.

This bug had been latent. On real SMP hardware the race window is very
small and the typical net_buf consumers (Bluetooth, networking) tend
to use fixed-data pools (`fixed_data_unref` is a no-op). The race
manifests reliably under FVP, where the FastModel's quantum-based
execution model can schedule N threads to all reach the unref point in
the same simulated moment. We discovered it through the zbus
`msg_subscriber_dynamic_isolated` sample, which exchanges shared data
buffers among 16+ subscribers running on 4 SMP cores.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-04-30 07:52:34 +02:00
Daniel Leung
db7a5e80a4 kernel: move bootargs out of kernel into lib/os.
This moves boot arguments from kernel into the lib/os.
This is not strictly a kernel function so this change provides
a separation between core kernel functionalities and others.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2026-04-29 06:23:14 -05:00
Daniel Leung
9fe4cc20a2 kernel: move boot banner into lib/os
Boot banner is not exactly a kernel feature. It is more like
an OS feature so moving it into lib/os.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
2026-04-29 06:23:14 -05:00
Anas Nashif
26967bb3a6 arch: common: move lib/acpi into arch/common
acpi is not really library code based on the new definition of what
should go into lib/. Move acpi into arch/common/ as it is cross arch
feature.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-29 06:16:43 -05:00
Anas Nashif
9b1c89a601 kernel: move gen_offset.h to arch
gen_offset.h is an architecture-specific header, not a kernel one.
Move it under the arch tree where it belongs.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-24 15:39:20 -04:00
Pieter De Gendt
63a422c537 lib: cobs: Make CONFIG_NET_BUF optional
COBS streaming works without net_buf instances, optionally enable the
encode/decode helper functions, and remove the select.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
2026-04-23 07:09:08 -04:00
Parthiban Nallathambi
d17595dea7 lib: libc: thrd: fix undefined behavior in thrd_create return value
The C11 thrd_create() was casting the user's thrd_start_t (returns int)
to a pthread function pointer (returns void *) and passing it directly
to pthread_create(). This is undefined behavior per C99 6.3.2.3 and
breaks on architectures with split data/address return registers.

On TriCore, int returns in d2 (data register) while void * returns in
a2 (address register). The cast caused pthread internals to read the
return value from a2 (garbage) instead of d2, making thrd_join() always
return wrong results.

Use the already-defined struct thrd_trampoline_arg with a proper
trampoline function that calls the user function as int-returning and
converts the result via INT_TO_POINTER. A semaphore ensures the
parent's stack-allocated trampoline arg stays alive until the child
has copied it.

Fixes: tests/lib/c_lib/thrd failures on all TriCore targets.

Signed-off-by: Parthiban Nallathambi <parthiban@linumiz.com>
2026-04-21 18:41:52 -04:00
Shuai Ma
f3d287cace lib: cbprintf: fix C23 incompatible function pointer types
The cbprintf_cb typedef used an empty parameter list () which in C23
is equivalent to (void), making it incompatible with any callback
function that takes parameters.

Fix by giving cbprintf_cb a proper prototype (int c, void *ctx),
removing the now-redundant cbprintf_cb_local typedef, and adding
explicit (cbprintf_cb) casts at call sites where the callback has a
different signature (fputc, sprintf_out).

Signed-off-by: Shuai Ma <Shuai.MA@cn.bosch.com>
2026-04-17 10:40:05 +02:00
Anas Nashif
243012c33c kernel: move thread_entry from lib/os to kernel
Not really library code, this a core component that is part of the core
os/kernel.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-14 22:31:16 -04:00
Anas Nashif
c60e0e9436 kernel: move userspace sem into kernel/sys
This is a kernel permitive for use with userspace, so move it under
kernel.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-14 22:31:16 -04:00
Anas Nashif
b572cb23fc kernel: userspace: move mutex/user_work to userspace
Move userspace code out of lib/os into userspace folder under kernel.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-14 22:31:16 -04:00
Christoph Busold
bd2d8a1d52 lib: heap: Make randomized canaries a dedicated option
This makes the dependency on a random number source explicit for
this option.

Signed-off-by: Christoph Busold <cbusold@qti.qualcomm.com>
2026-04-14 22:14:04 -04:00
Christoph Busold
b7b0403a26 lib: heap: Make canaries random and reduce their size
Randomize the base canary value at heap creation in order to make
it unpredictable to attackers and reduce their size to 4 bytes,
which should still give enough entropy but reduce overhead
especially on 32-bit targets.

This adds a dependency for heap hardening levels with canaries to
a random number generator.

Signed-off-by: Christoph Busold <cbusold@qti.qualcomm.com>
2026-04-14 22:14:04 -04:00
Alex Rodriguez
151ab82e0e lib: utils: json: Add support for polymorphic fields
Allow multiple descriptors with  the same field name to support
polymorphic JSON fields such as id: string | integer.

The parser now tries all descriptors matching a field name and only
returns an error if all the attempts fail. This preserves existing
error behavior for non-polymorphic fields.

Signed-off-by: Alex Rodriguez <alejandro.rodriguezlimon@nxp.com>
2026-04-14 22:07:30 -04:00
Anas Nashif
b474cfc2b6 posix: move posix to subsys/portability
Posix is not a library per the definition of libraries in Zephyr. It
always has been a portability layer and shall reside with other
portability layers under subsys/portability.

Addresses #99250

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-04-14 22:03:46 -04:00
Benjamin Cabé
044ee12b69 Revert "libc: use picolibc module with openrisc [REVERT ME]"
This reverts commit 1113abfb66.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
2026-03-31 13:56:17 -05:00
Ivan Iushkov
b9c4d724b4 lib: net_buf: inline several simple functions
This commit aims to slightly improve performance of
Zephyr-based applications by inlining often-used functions.

net_buf is used by different subsystems and applications so
performance of this library affects performance of many other
modules and it is important to keep its implementation efficient
and robust.

The following functions were moved from buf_simple.c to net_buf.h:
- net_buf_simple_headroom() - it was already used by some of inlined
functions which made inlining less efficient
- net_buf_simple_tailroom() and net_buf_simple_max_len() that do
very basic size calculations

Signed-off-by: Ivan Iushkov <ivan.iushkov@nordicsemi.no>
2026-03-31 10:27:20 -05:00
Andrew Gillan
466c04862e lib: json: Configurable float and double precision encoding
Created Kconfig options to set the precison used for encoding
float and double values

Signed-off-by: Andrew Gillan <andrew.gillan@gmail.com>
2026-03-26 07:19:43 -04:00
Anas Nashif
1113abfb66 libc: use picolibc module with openrisc [REVERT ME]
Temporary fix while we wait for new SDK release.

See https://github.com/zephyrproject-rtos/sdk-ng/pull/1106

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
2026-03-21 07:50:57 -05:00
Tim Pambor
e8c0418e22 lib: json: support encoding null values
Add support for encoding null values in json. This is useful to be
able to encode optional values as null when they are not set.

Signed-off-by: Tim Pambor <tim.pambor@codewrights.de>
2026-03-20 12:35:15 -05:00
James Roy
ecda2300fa sys: Add Disjoint-set data structure
Add a set of Disjoint-set functions (`find`, `union`)
to handle queries between sets.

Signed-off-by: James Roy <rruuaanng@outlook.com>
2026-03-20 09:52:24 -05:00
Vladislav Kulikov
d7e892ea93 lib: smf: Fix Kconfig style and conventions
- Change `config SMF` to `menuconfig SMF` since it serves as a parent
  for child config options under `if SMF`.
- Update help text descriptions to follow Zephyr wording conventions,
  replacing descriptions with clearer explanations.

Signed-off-by: Vladislav Kulikov <vlad_kulikov_c@pm.me>
2026-03-19 17:00:25 +01:00
Anas Nashif
180f667812 cmake: toolchain: combine host variants
Combine toolchains provided by the host into one variant. This includes
both gcc and llvm installation typically coming from the distribution
(on Linux).

Both gcc and llvm are now part of the 'host' variant, the default is the
gnu compiler, so setting

	ZEPHYR_TOOLCHAIN_VARIANT=host

Will select the gnu compiler. To select llvm or any other compuler
provided in this variant, use the follwoing format:

	ZEPHYR_TOOLCHAIN_VARIANT=<variant>/<compiler>

The following will select llvm:

	ZEPHYR_TOOLCHAIN_VARIANT=host/llvm

Although gnu is the default, it can also be selected using the above
syntax:

	ZEPHYR_TOOLCHAIN_VARIANT=host/gnu

This commit removes the llvm variant for now, it should be deperecated
in another commit to follow.

Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2026-03-17 15:43:52 -04:00
Stephanos Ioannidis
c4b76b8fa0 lib: cpp: Enable POSIX and GNU C extensions for libc++
LLVM C++ Standard Library aka. libc++ makes use of POSIX and GNU C
extensions in its headers.

While far from ideal, there is no other option but to globally enable these
extensions for C++ source files in order to ensure that libc++ headers can
be used.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2026-03-17 15:43:52 -04:00
Stephanos Ioannidis
75e4503f17 lib: cpp: Allow selecting libc++ with picolibc
LLVM C++ Standard Library aka. libc++ may be used with Picolibc -- for
example, LLVM Embedded Toolchain for Arm ships with Picolibc and libc++
compiled for Picolibc, and so does Zephyr SDK LLVM toolchain.

Ideally, we would a flag like `TOOLCHAIN_HAS_LIBCXX_PICOLIBC` indicating
that toolchain has libc++ compiled specifically for Picolibc; but, we do
not want to overcomplicate the toolchain feature flags at this time without
proper underlying infrastructure to handle it.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2026-03-17 15:43:52 -04:00
Stephanos Ioannidis
7550cc1ae4 cmake: toolchain: Introduce TOOLCHAIN_HAS_LIBCXX
This commit introduces `TOOLCHAIN_HAS_LIBCXX` CMake variable, which is set
to `y` when LLVM C++ Standard Library aka. libc++ is available.

This helps filter libc++-specific Kconfig and tests in a more refined
manner.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2026-03-17 15:43:52 -04:00
Stephanos Ioannidis
d5f8b3032d cmake: toolchain: Introduce TOOLCHAIN_HAS_GLIBCXX
This commit introduces `TOOLCHAIN_HAS_GLIBCXX` CMake variable, which is
set to `y` when GNU C++ Standard Library aka. libstdc++ is available.

This helps filter libstdc++-specific Kconfig and tests in a more refined
manner.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
2026-03-17 15:43:52 -04:00
Glenn Andrews
4cf56ae059 Lib: SMF: Add instrumentation for SMF
Add instrumentation hooks for transitions and actions, as well as
error states. This will allow us to track the state of the state
machine during testing, and also provide a way to debug issues
that may arise during execution.

Signed-off-by: Glenn Andrews <andrewsglenn@meta.com>
2026-03-13 16:29:57 +01:00
Nicolas Pitre
30c07c0ff7 lib: heap: validate canaries on free chunk removal
At FULL hardening, trailer canaries on used chunks implicitly guard
adjacent free chunk headers: a sequential buffer overflow must corrupt
the used chunk's trailer before reaching the next header.  However,
when a free neighbor's metadata is needed for merging during free(),
the neighbor's header could already have been corrupted by an overflow
from its left used chunk that hasn't been freed yet.  For example:

  [hdr_U1] [data_U1] [trailer_U1] [hdr_F] [...] [hdr_U2] [trailer_U2]

If data_U1 overflows past trailer_U1 and corrupts hdr_F, freeing U2
would use hdr_F's corrupted size and free-list pointers for merging,
potentially leading to heap structure corruption or arbitrary writes.

Additionally, a corrupted LEFT_SIZE in a used chunk being freed can
point to a fake header crafted inside the left neighbor's data area:

  [hdr_U1] [data_U1 ..fake_hdr.. trailer_U1'] [hdr_U2'] [data_U2] ...
                       |           corrupted   LEFT_SIZE points
                       |           by overflow  to fake_hdr
                       +<--------------------------+

A determined attacker can make fake_hdr's size field self-consistent
with the corrupted LEFT_SIZE so that the structural round-trip checks
pass.  If fake_hdr is marked "used", free_chunk() skips the left
merge and the corruption goes undetected.  If marked "free", it
triggers a bogus merge with attacker-controlled free-list pointers.

Both cases are caught by verifying the left used neighbor's canary:
any overflow from the left must pass through trailer_U1 to reach
hdr_U2's LEFT_SIZE field, so the corrupted canary acts as a tripwire
regardless of whether the resulting fake header is marked used or free.

Address this by introducing free_chunk_check() which validates a free
chunk's structural integrity before trusting its header fields.  It
consolidates the existing MODERATE-level structural checks (chunk
linkage, used-bit consistency) with a new FULL-level canary
verification of the left used neighbor.  Factoring these checks into
a dedicated function lets callers specify @left_trusted according to
context (e.g. the chunk being freed is the left neighbor of the right
merge candidate, so its canary need not be rechecked).

In inplace_realloc()'s shrink path, the freed suffix's left neighbor
is always the chunk being reallocated (used, just validated).  This
path inlines only the right merge to avoid the unnecessary left canary
check.

The check is called before every free list removal: in free_chunk()
for left/right merge candidates, in alloc_chunk() when pulling from
a bucket, and in inplace_realloc() before consuming the right
neighbor.

Also give chunk0 a canary trailer so that the left-neighbor canary
check works uniformly for the first free chunk in the heap.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
b846fd8b22 lib: heap: introduce SYS_HEAP_HARDENING tiered Kconfig
Replace the standalone SYS_HEAP_CANARIES bool with a tiered
SYS_HEAP_HARDENING choice controlling runtime validation:

  NONE (0)      - no checks
  BASIC (1)     - double-free and overflow detection in free/realloc
  MODERATE (2)  - free list and neighbor consistency checks
  FULL (3)      - trailer canary on every allocation
  EXTREME (4)   - exhaustive heap validation on every operation

Default is MODERATE when ASSERT is enabled, BASIC otherwise.

Hardening checks are independent of CONFIG_ASSERT: they use
LOG_ERR + k_panic() instead of __ASSERT so the configured level
is always honored regardless of assertion settings.

Also adds heap logging via LOG_MODULE_REGISTER. This paves the way for
eventual permanent debugging instrumentation.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
0a4b0a573b lib: heap: extract z_heap_full_check() from sys_heap_validate()
Factor out the core heap validation logic into z_heap_full_check()
which takes a struct z_heap pointer directly. This allows internal
callers (e.g. alloc_chunk) to validate the heap without needing
the public struct sys_heap wrapper.

sys_heap_validate() becomes a thin wrapper that calls
z_heap_full_check() and then validates runtime stats.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
c3c404af96 lib: heap: add CONFIG_SYS_HEAP_CANARIES for corruption detection
Add optional canary values at the end of each heap allocation to detect
memory corruption. The canary is validated when memory is freed, catching
buffer overflows (writing past allocation) and double-free errors.

The canary is computed from the chunk address and size, XORed with a
magic value. On free, it is checked and then poisoned to detect
double-free attempts.

The canary is stored as trailer data at the end of the chunk rather than
in the header to avoid complicating aligned allocation processing, and
because buffer overflows are most likely to overwrite past the buffer end
anyway.

This adds 8 bytes of memory overhead per allocation and a canary
computation on alloc and validation on free. It is useful for hardening
against memory corruption as well as for chasing bugs during
development. The trailer structure can be readily extended to carry
additional per-allocation metadata if so desired.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
c776a6c91c lib: heap: rename solo_free_header() to undersized_chunk()
The function checks if a chunk is too small to be added to the free list.
The new name better reflects its purpose and the comparison now uses
min_chunk_size() for clarity.

Such chunks are not added to the free list because they would be too
small to be allocatable, and they might be too small to store the free
list pointers. It happens that min_chunk_size() is always >= the free
pointer storage size.

The big_heap() condition short-circuits the comparison with a build-time
constant when undersized chunks cannot occur.

A following commit will rely on this to exclude chunks that are smaller
than min_chunk_size() which will grow to account for trailer data.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
b57ab90fdb lib: heap: rework chunksz_to_bytes into chunk_usable_bytes
Replace chunksz_to_bytes() which took a chunk size with
chunk_usable_bytes() which takes a chunk_id directly. This eliminates
the redundant pattern of chunksz_to_bytes(h, chunk_size(h, c)) and makes
the API clearer by returning actual usable bytes (excluding the header).

Add mem_align_gap() helper to compute alignment padding between the
start of usable chunk memory and the actual memory pointer, using
efficient bit masking. This simplifies sys_heap_usable_size() and
inplace_realloc(). Use these helpers to make runtime stats reporting
reflect actual usable chunk memory (excluding chunk headers), and heap
listener notifications also account for alignment gaps.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Nicolas Pitre
dee349919b lib: heap: compute heap sizing constants from actual struct layouts
Z_HEAP_MIN_SIZE and Z_HEAP_MIN_SIZE_FOR were defined in kernel.h as
hardcoded magic numbers gated by a growing tower of #ifdefs — one
per Kconfig option that happened to affect the heap struct layout.
Every internal change required manually recomputing the constants,
duplicating layout knowledge across files, and praying nobody forgot
to update the #ifdef matrix.  This is fragile and unscalable: adding
a single new heap feature (e.g. a chunk canary trailer) would add yet
another dimension to the combinatorial explosion.

Replace this with build-time computation from the actual C structures.
A new lib/heap/heap_constants.c uses GEN_ABSOLUTE_SYM to emit the
correct values into a generated heap_constants.h header via the
zephyr_constants_library() infrastructure.  Z_HEAP_MIN_SIZE is
derived through an iterative fixed-point expansion (3 rounds, always
convergent) that mirrors the runtime logic in sys_heap_init().

Big vs small heap determination uses CONFIG_SYS_HEAP_SMALL_ONLY,
CONFIG_SYS_HEAP_BIG_ONLY, and sizeof(void *), mirroring the
big_heap_chunks() logic in heap.h.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
2026-03-11 20:52:01 -04:00
Roman Bakshansky
5c8e33bed1 lib: os: cbprintf: fix -wsign-compare warning
Replace ternary operator with if-else to avoid mixing signed and unsigned
types in the conditional expression. This eliminates the compiler warning
while preserving the original logic.

Fixes #104581

Signed-off-by: Roman Bakshansky <bakshansky@protonmail.com>
2026-03-11 20:48:34 -04:00