U-Boot Simple Malloc: Budget Cumulative Allocations Even When Callers Free Them
A loop can exhaust U-Boot’s simple allocator while holding only one
temporary buffer at a time. In the v2025.01
SYS_MALLOC_SIMPLE configuration, ordinary
free() does not return that space to the allocation pool.
The useful budget is cumulative allocation consumption within the pool’s
lifetime, including alignment, rather than peak simultaneously live
payload.
This is a configuration-specific allocator contract. Do not
generalize it to every SPL build or to the fully initialized allocator
in U-Boot proper. First identify which implementation the failing code
actually uses.
Follow the compile-time API
mapping
The allocation
header maps malloc to malloc_simple when
the phase-specific SYS_MALLOC_SIMPLE option is enabled.
Without Valgrind support, free() is an empty inline
function in that branch. With Valgrind enabled, the corresponding simple
free reports the lifetime transition to the instrumentation, but does
not restore pool capacity.
The simple
allocator computes an address from gd->malloc_base
and gd->malloc_ptr, applies the requested alignment, and
checks the resulting end against gd->malloc_limit. A
successful allocation advances the pointer, including its bookkeeping
alignment. There is no free-list search or backward movement in this
allocation path.
Consequently, a caller can satisfy its normal ownership discipline
and still consume more of this pool on each iteration. Keeping cleanup
calls remains appropriate for portable ownership handling; assuming
those calls reclaim space in this configuration is the error.
Separate live payload
from consumed space
Consider a hypothetical loop that allocates a temporary buffer,
processes it, frees it and repeats. Its maximum live application payload
can be one buffer. Its simple-pool consumption instead grows with every
successful allocation, plus any alignment gaps.
An error path can have the same effect. If initialization allocates a
workspace and then fails, freeing that workspace does not rewind the
simple allocator. A retry that allocates again starts farther into the
same pool. Review retry counts and partially completed initialization,
not only the successful boot sequence.
Aligned allocation adds another input. memalign_simple()
uses the same allocator with the requested alignment, so the start can
skip bytes before the payload. The post-allocation pointer is also
rounded. An estimate based only on the sum of requested payload lengths
can therefore understate consumption.

Distinguish
the early fallback from the full allocator
The full
allocator implementation also contains an early
SYS_MALLOC_F route. Before full-malloc initialization,
allocation can delegate to the simple allocator, while the matching
early free path returns without normal reclamation.
That is a separate selection mechanism from compiling the whole phase
with SYS_MALLOC_SIMPLE. Record both the resolved
configuration and the initialization point. The same source-level
allocation call can have different behavior before and after the full
allocator becomes available.
Do not infer that a later allocator transition makes an earlier
pointer suitable for arbitrary full-allocator operations. Pointer
ownership and the allocator that created it remain part of the lifecycle
review. This article does not prescribe moving or freeing early
allocations across that boundary.
Design around the actual
pool lifetime
For a proposed qualification, trace requested size, alignment,
success or failure and the pool pointer around a bounded initialization
or retry sequence. Compare cumulative consumption with the configured
pool limit. Separately record peak live application data so the two
metrics cannot be confused.
A reusable caller-owned workspace can avoid repeated allocation when
its lifetime and concurrency permit it. Increasing the pool may instead
be justified by a measured bounded demand. Either decision needs the
actual allocation sequence; a generic “free every buffer” review cannot
establish sufficient capacity here.
No allocator fixture or target boot was executed. The source-level
conclusion is that reclamation assumptions must follow the selected
allocator, and simple-pool capacity must cover the whole allocation
history that occurs before that pool is retired.