Add malloc_dump_to_file() and malloc_log_to_file() functions to write
heap dumps and malloc traffic logs to host files. This is useful for
debugging memory leaks in sandbox by allowing comparison of before/after
heap states with external tools like diff.
These functions are only available in sandbox builds since they use
host-filesystem access.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Backtrace collection is relatively expensive and can significantly slow
down malloc()-heavy code when mcheck is enabled.
Add a new CONFIG_MCHECK_BACKTRACE option (default y) to allow disabling
backtrace collection while keeping the other mcheck features (canaries,
double-free detection, etc.) enabled. This allows using mcheck with less
overhead when caller information is not needed.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add malloc_log_dump() to print all recorded malloc/free/realloc calls
with their addresses, sizes, and caller information. This provides a
way to inspect the log after recording.
The dump shows a summary line with entry counts, followed by a table
with sequence number, operation type, pointer, size, and caller for
each recorded operation.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a malloc()-traffic log that records all malloc()-related calls with
their addresses, sizes, and caller information. This is useful for
debugging allocation patterns and finding the source of allocations that
lack caller info in heap dumps.
Each entry stores:
- Operation type (alloc/free/realloc/memalign)
- Pointer address
- Size (and old size for realloc)
- Full caller backtrace string
On sandbox, the log buffer is allocated from host memory using
os_malloc(), so it does not affect U-Boot's heap. The size is
controlled by CONFIG_MCHECK_LOG_SIZE (default 512K entries).
If the log fills up, it wraps around (circular buffer) and a warning
is shown when dumping to indicate how many entries were lost.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Replace direct printf calls in malloc_dump() with an output callback
function. This introduces dump_out_fn type and dump_to_console() helper,
with malloc_dump_impl() taking the callback and context pointer.
This allows the same implementation logic to be reused for different
output destinations such as writing to a file.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When mcheck is enabled, malloc_usable_size() returns incorrect results
because mem2chunk() is called on the offset user pointer rather than the
actual chunk.
The pointer returned to the user is offset by the mcheck header, but
malloc_usable_size() is unaware of this. Add a wrapper that returns the
user-requested size stored in the mcheck header. This fixes test
failures when CONFIG_MCHECK_CALLER_LEN is set to larger values.
Also add a wrapper for the case where MALLOC_DEBUG is enabled without
MCHECK_HEAP_PROTECTION, since MALLOC_DEBUG makes
dlmalloc_usable_size_impl() static but no public dlmalloc_usable_size
exists outside the mcheck block.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a command-line option (-M or --no_mcheck) to disable mcheck heap
protection at runtime. When mcheck is disabled, the wrapper functions
pass through directly to the underlying allocator without adding
headers or checking for corruption.
This is useful for debugging when mcheck interferes with test results,
such as when memory-leak detection reports false positives due to
accumulated allocations from other tests.
Changes:
- Add disable_mcheck flag to sandbox_state
- Add mcheck_set_disabled() function to mcheck API
- Modify dlmalloc wrappers to bypass mcheck when disabled
- Add stub for when MCHECK_HEAP_PROTECTION is not enabled
- Document the new option in sandbox.rst
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When mcheck heap-protection is enabled, each allocation adds quite a
bit of overhead for headers and canaries. While this is needed for the
full allocator, it serves no purpose for pre-relocation allocations,
since:
1. Simple malloc is a bump allocator that cannot free memory
3. Mcheck's corruption-detection provides no benefit for non-freeable
memory
Since the pre-relocation heap space is limited (typically <16KB), this
overhead can exhaust the heap, causing boot failures.
Fix this by bypassing mcheck hooks in dlmalloc(), dlfree(),
dlmemalign() and dlcalloc() when called before relocation, directly
calling the simple malloc functions instead.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The mcheck heap protection stores a caller string in each allocation
header for debugging purposes. The length is hard-coded to 48 bytes in
mcheck_core.inc.h
Add a CONFIG_MCHECK_CALLER_LEN Kconfig option to make this configurable,
allowing users to adjust the trade-off between the amount of debugging
context and the memory overhead per allocation.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
If neither CONFIG_BLOBLIST_FIXED NOR CONFIG_BLOBLIST_ALLOC is set,
currently CONFIG_BLOBLIST_SIZE_RELOC defaults to 0 except if
* CONFIG_ARM=y && CONFIG_EFI_LOADER=y && GENERATE_ACPI_TABLE=y.
A size of zero never makes sense for a bloblist.
When using QFW we need more than 64 KiB to host the ACPI table.
In this case CONFIG_BLOBLIST_ALLOC is used.
Set a reasonable default.
Remove the CONFIG_BLOBLIST_SIZE_RELOC in ARM QEMU defconfigs which are
not compatible with ACPI tables passed from QEMU.
Reported-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
Fixes: 6f9b015c13 ("common: Enable BLOBLIST_TABLES on arm")
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
(cherry picked from commit 559f11e66c)
Patch keeps the access to dtb_dt_embedded() within fdtdec API,
by means of new API function introduction. This new function is a
common place for updating appropriate global_data fields for
OF_EMBED case.
Also, the consequence of the patch is movement of '___dtb_dt_*begin'
symbols' declaration from header file, because nobody used symbols
outside the lib/fdtdec.c.
Signed-off-by: Evgeny Bachinin <EABachinin@salutedevices.com>
Suggested-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
(cherry picked from commit 623f5cf517)
The FIT name in RISC-V Falcon mode should be different from that in
normal boot mode; it is called linux.itb. If the setting is missing
in common/spl, the normal boot file name will be used.
Signed-off-by: Randolph <randolph@andestech.com>
(cherry picked from commit e59241f8b1)
Rather than doing autoprobe within the driver model code, move it out to
the board-init code. This makes it clear that it is a separate step from
binding devices.
For now this is always done twice, before and after relocation, but we
should discuss whether it might be possible to drop the post-relocation
probe.
For boards with SPL, the autoprobe is still done there as well.
Note that with this change, autoprobe happens after the
EVT_DM_POST_INIT_R/F events are sent, rather than before.
Link: https://lore.kernel.org/u-boot/20240626235717.272219-1-marex@denx.de/
Signed-off-by: Simon Glass <sjg@chromium.org>
(cherry picked from commit 6995f2c8be)
This is enabled by all boards, so drop the condition.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Tom Rini <trini@konsulko.com>
(cherry picked from commit 70c79dc88f)
Patch resolves two kind of bugs, one of which is vulnerability related
to KASLR.
=== Issue briefly ===
Working with FDT (via non-relocated gd::fdt_blob) from inside bootm
command may lead to the reading the garbage instead of FDT nodes. And
this can result in various side-effects depending on DTS nodes, being
parsed during bootm.
But below is my specific story how I faced with this issue due to
MESON_RNG probing failure.
=== Bugs description ===
1) Bug is revealed on:
* configuration below
* U-boot 2024.10 - f919c3a889 ("Prepare v2024.10")
It seems, the following patch is a trigger:
ea955eea4f ("fdt: automatically add /chosen/kaslr-seed if DM_RNG is enabled")
Generally, CONFIG_OF_EMBED=y & CONFIG_RNG_MESON=y are the most
valuable ones for reproducing the issue.
```
CONFIG_ARCH_FIXUP_FDT_MEMORY=y
CONFIG_CMD_FDT=y
CONFIG_DEFAULT_FDT_FILE=""
CONFIG_FDT_64BIT=y
CONFIG_OF_BOARD_SETUP=y
CONFIG_OF_CONTROL=y
CONFIG_OF_EMBED=y
CONFIG_OF_LIBFDT_ASSUME_MASK=0x0
CONFIG_OF_LIBFDT_OVERLAY=y
CONFIG_OF_LIBFDT=y
CONFIG_OF_LIST="meson-axg-our-device-name"
CONFIG_OF_REAL=y
CONFIG_OF_TRANSLATE=y
CONFIG_SUPPORT_OF_CONTROL=y
CONFIG_SYS_FDT_PAD=0x3000
CONFIG_TOOLS_OF_LIBFDT=y
CONFIG_DM_RNG=y
CONFIG_RNG_MESON=y
```
2) Due to CONFIG_OF_EMBED, the DTS is embedded into U-boot ELF and
accessible via __dtb_dt_begin symbol.
On early boot stage (board_f.c) the fdtdec_setup() is called only
once before U-boot's relocation into top of RAM. fdtdec_setup()
initializes gd::fdt_blob for FDTSRC_EMBED case:
```
gd->fdt_blob = dtb_dt_embedded();
gd->fdt_src = FDTSRC_EMBED;
```
3) Then reloc_fdt() is called in board_f.c
But due to CONFIG_OF_EMBED=y the reloc_fdt() does not update
gd::fdt_blob value (strictly speaking, it is impossible for
CONFIG_OF_EMBED=y, because U-boot ELF has not been relocated yet
at this moment).
As a result after relocation we get fdt_blob, pointing to DTS address
before relocation:
```
# bdinfo
<...>
relocaddr = 0x000000000fedf000
reloc off = 0x000000000eedf000
<...>
fdt_blob = 0x010ce6c0 << points to __dtb_dt_begin before relocation
new_fdt = 0x0000000000000000 << empty erroneously
fdt_size = 0x0000000000000000 << zero erroneously
```
4) During bootm command (according to our ITS-config file) the Linux
is loaded into 0x01080000 (which is very close to fdt_blob addr
0x010ce6c0).
```
## Loading kernel from FIT Image at 04000000 ...
Trying 'kernel' kernel subimage
<...>
Load Address: 0x01080000
```
So Linux image overwrites the gd::fdt_blob memory location
in RAM (0x010ce6c0).
5) Issue:
Hence any manipulation with DTS (say, via FDT API) inside
implementation of bootm command leads to accessing the fdt_blob area
with garbage, that can lead to two situations:
5.1) Abort.
Call to fdt_off_dt_struct() from fdt_next_tag() :: fdt_offset_ptr()::
fdt_offset_ptr_() returns with garbage, that leads to tagp value
being out of RAM top addr (256 Mb in our board), causing the abort:
```
Boot cmd: bootm 0x4000000#boot_evt1
bootm_run_states()
<...>
image_setup_libfdt()
fdt_chosen()
fdt_kaslrseed()
uclass_get_device()
uclass_get_device_tail()
device_probe()
device_of_to_plat()
meson_rng_of_to_plat()
clk_get_by_name_optional()
clk_get_by_name()
clk_get_by_name_nodev()
ofnode_stringlist_search()
fdt_stringlist_search()
fdt_getprop()
fdt_get_property_namelen_()
fdt_first_property_offset()
fdt_check_node_offset_()
fdt_next_tag():
```
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
```
fdt_next_tag() tagp:0x22890766
fdt_next_tag() ram_top:0x10000000 (tagp OUT of RAM)
"Synchronous Abort" handler, esr 0x96000010, far 0x22890766
elr: 000000000108be24 lr : 000000000108be24 (reloc)
elr: 000000000ff6fe24 lr : 000000000ff6fe24
x0 : 0000000000000041 x1 : 0000000000000000
x2 : 000000000ff3b57c x3 : 0000000000000012
x4 : 000000000ded2ad5 x5 : 0000000000000020
x6 : 00000000ffffffe8 x7 : 000000000ded2f40
x8 : 00000000ffffffd8 x9 : 000000000000000d
x10: 0000000000000006 x11: 000000000001869f
x12: 000000000fffffff x13: 000000000fffffff
x14: 0000000000000000 x15: 000000000ded2abb
x16: 000000000ff3b080 x17: 0000000000000001
x18: 000000000ded3dc0 x19: 0000000022890766
x20: 00000000010cb0f0 x21: 00000000000015e4
x22: 000000000ff8f4d8 x23: 000000000000000b
x24: 000000000ded2fbc x25: 000000000ffe2000
x22: 000000000ff8f4d8 x23: 000000000000000b
x24: 000000000ded2fbc x25: 000000000ffe2000
x26: 000000000ffe2000 x27: 000000000000000b
x28: 000000000ff9cf2d x29: 000000000ded2f40
Code: aa1603e1 91197484 52801742 94004de8 (b9400276)
```
5.2) Vulnerability situation "KASLR is disabled".
Almost the same as in (5.1), but 2 situations happen (depending on
the value of garbage):
* call to fdt_offset_ptr_() :: fdt_off_dt_struct(fdt)
returns not so big garbage, leading to tagp, being inside RAM.
* or calculations of absoffset inside fdt_offset_ptr() leads to
failure of the one of if() conditions with NULL as retval.
Result is fdt_next_tag() interprets the tagp as FDT_END. And we are
returning from our callstack via functions' error paths, leading to
"No RNG device" and "KASLR disabled due to lack of seed":
```
fdt_kaslrseed()
uclass_get_device()
<...>
device_probe()
device_of_to_plat()
meson_rng_of_to_plat()
clk_get_by_name()
clk_get_by_name_nodev()
<...>
fdt_stringlist_search()
fdt_getprop()
fdt_get_property_namelen_()
fdt_first_property_offset()
fdt_check_node_offset_()
fdt_next_tag():
```
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
```
fdt_next_tag() tagp:0000000001890677
fdt_next_tag() ram_top:0x10000000 (tagp is inside RAM)
uclass_get_device_tail():486 device_probe() ret:-22
No RNG device
Starting kernel ...
[ 0.000000] Linux version 6.9.12
[ 0.000000] KASLR disabled due to lack of seed
```
Signed-off-by: Evgeny Bachinin <EABachinin@salutedevices.com>
(cherry picked from commit acab6e78ac)
On several RISC-V boards we have seen that 1 MiB is a insufficient value
for CONFIG_SPL_SYS_MALLOC_SIZE.
For instance qemu-riscv32_spl_defconfig fails booting because u-boot.itb
exceeds 1 MiB.
8 MiB is a reasonable value that allows adding FPGA blobs or splash images
to main U-boot.
Reported-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Leo Yu-Chi Liang <ycliang@andestech.com>
(cherry picked from commit 8b410cab51)
Dropped modifications to all files except common/spl/Kconfig:
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Use a static bool flag to ensure the overflow warning is printed only
once, avoiding repeated messages when the registry is full.
Make sure to set the flag before calling printf(), which can itself do
allocations with sandbox / Truetype.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When CONFIG_MCHECK_HEAP_PROTECTION is enabled, show the original
caller info for freed chunks. This helps identify what code allocated
memory that is now free.
The mcheck header's canary and caller fields survive after free since
dlmalloc only overwrites the first 16 bytes (size, aln_skip) with its
free list pointers (fd, bk). We detect freed headers by looking for
the MAGICFREE canary pattern.
For small chunks or coalesced chunks where the header is not preserved,
the caller info is simply omitted.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When the stack is corrupted (e.g., by the stack protector test),
collecting a backtrace during malloc causes a crash because the
backtrace code walks the invalid stack frames.
Update __stack_chk_fail() to set the flag before calling panic()
Also update stackprot_test() to set the flag before intentionally
corrupting the stack. This is needed because of the printf() in the
test: on sandbox printf() results in truetype allocations due to the
console output.
These fixes allow the stack protector test to pass with mcheck enabled.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When CONFIG_MCHECK_HEAP_PROTECTION is enabled, use backtrace_str() to
capture the caller information for each malloc/calloc/realloc/memalign
call. This information is stored in the mcheck header and can be viewed
with 'malloc dump'.
Add a flag to disable the backtrace when the stack is corrupted, since
the backtrace code tries to walks the invalid stack frames and will
crash.
Note: A few allocations made during libbacktrace initialisation may
not have caller info since they occur during the first backtrace call.
Example output showing caller info:
18a1d010 90 used log_init:453 <-board_init_r:774
18a1d0a0 6060 used membuf_new:420 <-console_record
18a3b840 90 used of_alias_scan:911 <-board_init_
Fix up the backtrace test to avoid recursion.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
For memalign allocations, the mcheck header is placed at an offset from
the chunk start to maintain alignment. The current assumption is that
the header is always at the start of the chunk, but this is not true for
memalign allocations.
Add find_mcheck_hdr_in_chunk() which looks up the header in the mcheck
registry and validates:
- The header falls within the chunk's memory range
- The aln_skip field is consistent with the header position
- The canary is MAGICWORD (active), not MAGICFREE (freed)
This ensures malloc_dump correctly displays caller info for all
allocations including those made via memalign.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new 'malloc dump' command that walks the dlmalloc heap from start
to end, printing each chunk's address, size (in hex), and status
(used/free/top). This is useful for debugging memory allocation issues.
When CONFIG_MCHECK_HEAP_PROTECTION is enabled, the caller string is
also shown if available.
Example output:
Heap dump: 18a1d000 - 1ea1f000
Address Size Status
----------------------------------
18a1d000 10 (chunk header)
18a1d010 90 used
18adfc30 60 <free>
18adff90 5f3f030 top
1ea1f000 end
----------------------------------
Used: c2ef0 bytes in 931 chunks
Free: 5f3f0c0 bytes in 2 chunks + top
Expand the console-record size to handle this command.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When CONFIG_MALLOC_DEBUG is enabled, add an optional caller string
parameter to dlmalloc_impl(). This allows tracking where allocations
originate from when debugging memory issues.
The CALLER_PARAM, CALLER_ARG, and CALLER_NULL macros hide the parameter
when MALLOC_DEBUG is not enabled, keeping the non-debug code path clean.
When MALLOC_DEBUG is enabled (with or without MCHECK), the _impl
functions must be separate from the public API functions since they
have different signatures. Add simple pass-through wrappers for this
case.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a field to store the caller for each allocation, using the backtrace
information. This can be useful when debugging heap corruption.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Pre-relocation heap allocations are expected to be discarded during
relocation - this is normal behavior, not a warning condition.
Remove the verbose messages that were printed for each chunk.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The mcheck registry tracks concurrent allocations for pedantic checking.
The current size of 6608 entries is insufficient when running the full
pytest suite, causing registry overflow warnings.
Increase REGISTRY_SZ to 12000 to handle the full test suite.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Fix dlposix_memalign(), dlvalloc(), and dlpvalloc() to call dlmemalign()
instead of dlmemalign_impl() or internal_memalign() directly. This
ensures these functions go through the mcheck wrappers when
CONFIG_MCHECK_HEAP_PROTECTION is enabled.
Without this fix, internal_memalign is undefined when mcheck is enabled,
causing a build error.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
With mcheck enabled, internal_malloc and internal_free go through mcheck
wrappers which expect user pointers (after mcheck header), not raw
allocator pointers. Also, dlmemalign_impl() needs a properly aligned base
pointer for mcheck to place its header correctly.
Use _impl functions directly for internal_malloc/internal_free. Call
internal_memalign() for alignments greater than MALLOC_ALIGNMENT.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Move the CONFIG_MCHECK_HEAP_PROTECTION block after the standard
includes so that size_t and string functions are available for
the inline MALLOC_ZERO and MALLOC_COPY functions.
Add the <string.h> and <linux/types.h> includes needed for mcheck.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add CONFIG_MCHECK_HEAP_PROTECTION option to enable mcheck heap
protection. Convert all uses of MCHECK_HEAP_PROTECTION to use the
CONFIG_ -prefixed version to work with Kconfig.
Disable this option when tracing is enabled, since the mcheck hooks (mcheck_pedantic_prehook(), etc.) interfere with function tracing.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add counters to track the number of calls to malloc(), free(), and
realloc(). These are displayed by the 'malloc info' command and
accessible via malloc_get_info().
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When debugging or running unit tests it is helpful to have information
available from the malloc subsystem. Enable these features in those
cases.
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new CONFIG_MALLOC_DEBUG option to control malloc() debugging
features. This replaces the direct UNIT_TEST check and allows enabling
malloc debugging independently of whether UNIT_TEST is enabled.
The option defaults to y when UNIT_TEST is enabled, preserving existing
behaviour. Disable for RISC-V since it seems to have a size limitation.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add struct malloc_info and malloc_get_info() function to
programmatically access the memory-allocation stats that malloc_stats()
prints.
The struct contains the size of the malloc() poll and the number of
bytes in use.
Add a static inline stub to return an error when DEBUG is not defined.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When CONFIG_MCHECK_HEAP_PROTECTION is enabled, dlmemalign_impl() calls
dlmalloc_impl() directly since the mcheck wrapper handles alignment.
This leaves internal_memalign() unused, causing a compiler warning.
Guard internal_memalign() with a preprocessor check so it's only
compiled when needed (mcheck disabled or MSPACES enabled).
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The mcheck wrapper for realloc() unconditionally frees memory and
returns NULL when size is 0. This differs from dlmalloc's default
behaviour which returns a minimum-sized allocation unless
REALLOC_ZERO_BYTES_FREES is defined.
Make the mcheck wrapper respect the same REALLOC_ZERO_BYTES_FREES
setting for consistent behavior with or without mcheck enabled.
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Include <mcheck.h> and remove the local declaration of
mcheck_on_ramrelocation() to use the proper header file.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When the console-record buffer overflows, show both the current buffer
size and the size needed. This helps the user know what value to set
for CONFIG_CONSOLE_RECORD_OUT_SIZE.
Add a console_out_ovf field to global_data to track the number of bytes
that could not be written due to overflow.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add doc/develop/malloc.rst documenting U-Boot's dynamic memory
allocation implementation:
- Overview of pre/post-relocation malloc phases
- dlmalloc 2.8.6 version and features
- Data structure sizes (~500 bytes vs 1032 bytes in 2.6.6)
- Configuration options for code-size optimization
- Debugging features (mcheck, valgrind, malloc testing)
- API reference
Also add an introductory comment to dlmalloc.c summarising the
U-Boot configuration.
Series-to: concept
Series-cc: heinrich
Cover-letter:
malloc: Import dlmalloc 2.8.6
This series imports dlmalloc 2.8.6 from Doug Lea, replacing the old
version 2.6.6 that U-Boot has been using since 2002.
The new version provides:
- Better memory efficiency with improved binning algorithms
- More robust overflow checking via MAX_REQUEST
- Somewhat cleaner codebase
All U-Boot-specific modifications from the historical commits have been
ported to the new implementation, including:
- Pre-relocation malloc via malloc_simple
- Valgrind annotations
- Malloc testing infrastructure
- mcheck heap protection support
- Sandbox USE_DL_PREFIX support
The approach here is to leave the upstream code unchanged, so much as
possible, clearly marking U-Boot-specific changes with an #ifdef
Unfortunately the code size is not great out-of-the-box, so the final
part of the series includes some options to remove in-place realloc(),
provide a simplified init, remove the tree stucture for large blocks
and a few other things.
With these adjustments the new version is about 1K less code on Thumb2
(firefly-rk3288).
The new free() algorithm is more sophisticated but also larger. If
needed we might be able to shrink by a few hundred bytes. Of course SPL
doesn't normally use free() so the benefit might be minimal.
Another point worth mentioning is that the pre-inited av_[] array has
been replaced replaced with a BSS _sm_ struct which reduces the image
size by about 1.5K. One patch adjusts some imx8mp boards to deal with
the larger BSS.
Some code-size stats:
$ buildman -b mala imx8mp_venice firefly-rk3288 firefly-rk3399 -sS --step 0
Summary of 2 commits for 3 boards (3 threads, 11 jobs per thread)
01: backtrace: Strip the source tree prefix from filenames
aarch64: w+ imx8mp_venice firefly-rk3399
40: doc: Add malloc documentation
aarch64: (for 2/2 boards) all -3904.0 bss +864.0 data -2076.0
spl/u-boot-spl:all -654.0 spl/u-boot-spl:bss +316.0
spl/u-boot-spl:data -1034.0 spl/u-boot-spl:text +64.0 text -2692.0
arm: (for 1/1 boards) all -1436.0 data -1040.0 text -396.0
For the new malloc.h I have avoided including string.h so have added
that to various places that need it.
The existing common/dlmalloc.src file is left alone.
In order to bring this in without losing functionality, I went through
the patches applied to the original implementation over time. Where
these commits were added, they are added as a cherry-pick, with the
original commit hash.
Here is a list of what was done with each U-Boot commit on top of the
new common/dlmalloc.c and include/malloc.h:
1. 217c9dad82 2002-10-25 Initial revision
- Ignored
2. 5b1d713721 2002-11-03 Initial revision
- Ignored
3. 8bde7f776c 2003-06-27 * Code cleanup:
- Ignored as we don't really want to change the style
4. d87080b721 2006-03-31 GCC-4.x fixes: clean up global data pointer initialization for all boards.
- Global data is not needed at this point
5. 81673e9ae1 2008-05-13 Make sure common.h is the first include.
- common.h has been removed
6. f2302d4430 2008-08-06 Fix merge problems
- no merge problems to fix with the new code
7. 60a3f404ac 2009-06-13 malloc.h: protect it against multiple include
- Already covered: new malloc.h has MALLOC_280_H include guards
8. 5e93bd1c9a 2009-08-21 Consolidate arch-specific sbrk() implementations
- Already covered: sbrk() and mem_malloc_init() in separate commit
9. d4e8ada0f6 2009-08-21 Consolidate arch-specific mem_malloc_init() implementations
- Already covered: mem_malloc_init() in separate commit
10. 521af04d85 2009-09-21 Conditionally perform common relocation fixups
- Not needed: Manual relocation removed in 4babaa0c28
11. b4feeb4e8a 2009-11-24 i386: Fix malloc initialization
- Already covered: mem_malloc_init() is common, no arch-specific guards
12. 2740544881 2010-01-15 malloc: return NULL if not initialized yet
- Done: Add check in dlmalloc() to return NULL if not initialized
13. ae30b8c200 2010-04-06 malloc: sbrk() should return MORECORE_FAILURE instead of NULL on failure
- Already covered: sbrk() returns MFAIL on failure
14. ea882baf9c 2010-06-20 New implementation for internal handling of environment variables.
- Not needed: Just changes #if 0 to #ifdef DEBUG for old stats code
15. 1ba91ba233 2010-10-14 dlmalloc.c: Fix gcc alias warning
- Not needed: New dlmalloc has no strict-aliasing warnings
16. 2e5167ccad 2010-10-28 Replace CONFIG_RELOC_FIXUP_WORKS by CONFIG_NEEDS_MANUAL_RELOC
- Not needed: Manual relocation removed in 4babaa0c28
17. 6163f5b4c8 2010-11-15 malloc: Fix issue with calloc memory possibly being non-zero
- Already covered: sbrk() clears memory on negative increment
18. 21726a7afc 2011-06-29 Add assert() for debug assertions
- Not needed: New dlmalloc uses U-Boot's global assert()
19. ea95cb7331 2011-09-10 utx8245: fix build breakage due to assert()
- Not needed: New dlmalloc has different debug check functions
20. 213adf6dff 2012-03-29 Malloc: Fix -Wundef warnings
- Not needed: New malloc.h doesn't have these #if issues
21. 93691842e8 2012-09-04 Fix strict-aliasing warning in dlmalloc
- Not needed: New dlmalloc has no malloc_bin_reloc()
22. 00d0d2ad4e 2012-06-03 malloc: remove extern declarations of malloc_bin_reloc() in board.c files
- Not needed: New dlmalloc has no malloc_bin_reloc()
23. 199adb601f 2012-10-29 common/misc: sparse fixes
- Not needed: New dlmalloc uses proper NULL
24. 7b395232da 2013-01-21 malloc: make malloc_bin_reloc static
- Not needed: New dlmalloc has no malloc_bin_reloc()
25. 472d546054 2013-04-01 Consolidate bool type
- Not needed: Just a comment change (True -> true)
26. d93041a4ca 2014-07-10 Remove form-feeds from dlmalloc.c
- Not needed: New dlmalloc doesn't have form-feeds
27. d59476b644 2014-07-10 Add a simple malloc() implementation for pre-relocation
- Done (updated): Redirect to malloc_simple before GD_FLG_FULL_MALLOC_INIT
28. 6d7601e744 2014-07-10 sandbox: Always enable malloc debug
- Done (updated): Combined with #64, use 'DEBUG 1' for new dlmalloc
29. 854d2b9753 2014-10-29 dlmalloc: ensure gd is set for early alloc
- Not needed: Reverted by #38
30. 868de51dde 2014-08-26 malloc: Output region when debugging
- Already covered: debug() message in mem_malloc_init()
31. c9356be307 2014-11-10 dm: Split the simple malloc() implementation into its own file
- Already covered: Redirect to malloc_simple.c via GD_FLG_FULL_MALLOC_INIT
32. 0aa8a4ad99 2015-03-04 dlmalloc: do memset in malloc init as new default config
- Already covered: SYS_MALLOC_CLEAR_ON_INIT at line 6396
33. fb5cf7f16b 2015-02-27 Move initf_malloc() to a common place
- Already covered: initf_malloc() at line 6357
34. 1eb0c03c21 2015-09-13 malloc_simple: Add Kconfig option for using only malloc_simple in the SPL
- Not needed: Changes to Kconfig/malloc_simple.c, not dlmalloc.c
35. 4f144a4164 2016-01-25 malloc: work around some memalign fragmentation issues
- Done (updated): Ported to internal_memalign() at line 4955
36. ee05fedc6c 2016-02-04 malloc: solve dead code issue in memalign()
- Not needed: New dlmalloc 2.8.6 has rewritten internal_memalign()
37. 2f0bcd4de1 2016-03-05 malloc: use hidden visibility
- Done (updated): Use DLMALLOC_EXPORT at line 546
38. deff6fb3a7 2016-03-05 malloc: remove !gd handling
- Not needed: Reverts #29, we don't add gd check
39. 4eece2602b 2016-04-21 common/dlmalloc.c: Delete content that was moved to malloc.h
- Not needed: New dlmalloc doesn't have #if 0 code
40. 034eda867f 2016-04-25 malloc: improve memalign fragmentation fix
- Done (updated): Combined with #35 in memalign workaround port
41. 4e33316f65 2017-05-25 malloc: Turn on DEBUG when enabling unit tests
- Already covered: Combined with #28, #63 at line 555
42. f1896c45cb 2017-07-24 spl: make SPL and normal u-boot stage use independent SYS_MALLOC_F_LEN
- Already covered: Use CONFIG_IS_ENABLED and CONFIG_VAL at line 6410
43. a874cac3b4 2017-11-10 malloc: don't compare pointers to 0
- Not needed: New dlmalloc uses proper NULL comparisons
44. ee038c58d5 2018-05-18 malloc: Use malloc simple before malloc is fully initialized in memalign()
- Already covered: memalign_simple redirect at line 5367
45. 7cbd2d2e32 2018-11-18 malloc_simple: Add logging of allocations
- Not needed: Changes to malloc_simple.c, not dlmalloc.c
46. 4c6be01c27 2019-03-27 malloc: Fix memalign not honoring alignment prior to full malloc init
- Already covered: Uses memalign_simple at line 5367
47. bb71a2d9dc 2019-10-25 dlmalloc: calloc: fix zeroing early allocations
- Done (updated): Port to dlcalloc() at line 4857
48. cfda60f99a 2020-02-03 sandbox: Use a prefix for all allocation functions
- Done: USE_DL_PREFIX and reverse mappings in malloc.h
49. be621c11b9 2020-04-15 dlmalloc: remove unit test support in SPL
- Already covered: CONFIG_IS_ENABLED(UNIT_TEST) at line 554
50. 9297e366d6 2020-04-29 malloc: dlmalloc: add an ability for the malloc to be re-init/init multiple times
- Not needed: No boards use CONFIG_SYS_MALLOC_DEFAULT_TO_INIT
51. f7ae49fc4f 2020-05-10 common: Drop log.h from common header
- Already covered: Includes log.h at line 559
52. 401d1c4f5d 2020-10-30 common: Drop asm/global_data.h from common header
- Already covered: Includes asm/global_data.h at line 557
53. c6bf4f3898 2021-02-10 malloc: adjust memcpy() and memset() definitions.
- Not needed: New malloc.h doesn't declare memset/memcpy
54. c197f6e279 2021-03-15 malloc: Export malloc_simple_info()
- Not needed: Only changes malloc.h, not dlmalloc.c
55. 5ad9220bf7 2021-05-29 malloc: add SPDX license identifiers
- Not needed: New dlmalloc has MIT-0 license from upstream
56. bdaeea1b68 2022-03-23 malloc: Annotate allocator for valgrind
- Done (updated): Valgrind annotations in dlmalloc(), dlfree(), dlrealloc()
57. 62d638386c 2022-09-06 test: Support testing malloc() failures
- Done: malloc_testing/malloc_max_allocs in dlmalloc()
58. f88d48cc74 2023-02-27 dlmalloc: Fix a warning with clang-15
- Done: Add (void) to dlmalloc_stats() function definition
59. c9db9a2ef5 2023-08-25 dlmalloc: Add support for SPL_SYS_MALLOC_CLEAR_ON_INIT
- Already covered: Uses CONFIG_IS_ENABLED() in mem_malloc_init() from #32
60. 6a595c2f67 2023-09-06 common: malloc: Remove unused NEEDS_MANUAL_RELOC code bits
- Not needed: NEEDS_MANUAL_RELOC has been removed
61. ac897385bb 2023-10-02 Merge branch 'next'
- Not needed: Merge commit, no dlmalloc changes
62. 3d6d507514 2023-09-26 spl: Use SYS_MALLOC_F instead of SYS_MALLOC_F_LEN
- Already covered: Uses CONFIG_IS_ENABLED(SYS_MALLOC_F) throughout
63. 1786861415 2023-10-07 malloc: Enable assertions if UNIT_TEST is enabled
- Done (updated): Combined with #28, use 'DEBUG 1' for new dlmalloc
64. c82ff48115 2024-03-31 mcheck: prepare +1 tier for mcheck-wrappers, in dl-*alloc commands
- Done (updated): Added STATIC_IF_MCHECK and *_impl macros for dlmalloc 2.8.6
65. dfba071ddc 2024-03-31 mcheck: Use memset/memcpy instead of MALLOC_ZERO/MALLOC_COPY for mcheck.
- Done: Undef and redefine MALLOC_ZERO/MALLOC_COPY when mcheck enabled
66. 151493a875 2024-03-31 mcheck: integrate mcheck into dlmalloc.c
- Done: Added mcheck wrapper functions for dlmalloc, dlfree, dlrealloc, dlmemalign, dlcalloc
67. ae838768d7 2024-03-31 mcheck: support memalign
- Done: Implemented dlmemalign wrapper with mcheck hooks
68. 18c1bfafe0 2024-03-31 mcheck: add pedantic mode support
- Done: Added mcheck_pedantic_prehook() calls and mcheck_pedantic()/mcheck_check_all() API
69. a79fc7a79c 2024-04-27 common: Remove <common.h> and add needed includes
- Not needed: common.h has been removed
70. d678a59d2d 2024-05-18 Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
- Not needed: common.h has been removed
71. 03de305ec4 2024-05-20 Restore patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet"
- Not needed: common.h has been removed
72. 910cef3d2f 2024-07-13 common: Remove duplicate newlines
- Not needed: New dlmalloc has its own formatting from upstream
73. 6627fbba20 2024-07-23 include: Remove duplicate newlines
- Not needed: New malloc.h has its own formatting from upstream
74. 04894f5ad5 2024-07-30 malloc: Support testing with realloc()
- Done: Combined with #57, malloc_testing check in dlrealloc()
75. 8642b2178d 2024-08-02 dlmalloc: Fix integer overflow in request2size()
- Not needed: New dlmalloc 2.8.6 uses MAX_REQUEST for robust overflow checks
76. 0a10b49206 2024-08-02 dlmalloc: Fix integer overflow in sbrk()
- Already covered: sbrk() checks bounds before memset in U-Boot section
77. 9b9368b5c4 2024-08-02 dlmalloc: Make sure allocation size is within malloc area
- Not needed: New dlmalloc 2.8.6 uses MAX_REQUEST for robust overflow checks
78. 41fecdc94e 2024-10-21 common: Tidy up how malloc() is inited
- Already covered: mem_malloc_init() uses map_sysmem in U-Boot section
79. 22f87ef530 2025-08-17 malloc: Avoid defining calloc()
- Done: Added SYS_MALLOC_SIMPLE section to malloc.h with calloc redirect
END
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add CONFIG_SYS_MALLOC_LEGACY to select the current allocator and adjust
the header-file and Makefile rule to use the new dlmalloc
implementation.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
The insert_small_chunk() and unlink_first_small_chunk() macros are
inlined at multiple places in the code.
Provide an option to convert these to functions, so the compiler can try
to reduce code size.
Add braces to the insert_chunk macro.
This reduces code size imx8mp_venice SPL by about 208 bytes
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new SIMPLE_MEMALIGN to remove the fallback-retry logic from
memalign(), to reduce code sizein SPL.
The fallback mechanism attempts multiple allocation strategies:
1. Over-allocate to guarantee finding aligned space
2. If that fails, allocate exact size and check if aligned
3. If not aligned, free and retry with calculated extra space
While this fallback is useful in low-memory situations, SPL typically
has predictable memory usage and sufficient heap space for the initial
over-allocation to succeed. The fallback adds code complexity without
obvious practical benefit.
This reduces code size on imx8mp_venice (for example) SPL by 74 bytes.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new NO_TREE_BINS option to disable binary-tree bins for large
allocations (>= 256 bytes). While this is an invasive changes, it saves
about 1.25K of code size on arm64 (as well as 250 bytes of data). When
enabled, all large chunks use a simple doubly-linked list instead of
tree bins, trading O(log n) performance for smaller code size.
The tradeoff here is that large allocations use O(n) search instead of
O(log n) and fragmentation coud also become worse. So performance will
suffer when there are lots of large allocations and frees are done. This
is rare in SPL.
Implementation:
- Add a dedicated mchunkptr largebin field to malloc_state
- Replace treebins[NTREEBINS] array with a single linked-list pointer
- Implement simplified insert/unlink operations using largebin list
- Update allocation functions (tmalloc_small/large) for linear search
- Update heap checking functions (do_check_treebin, bin_find) to handle
linked list traversal instead of tree traversal
It is enabled by CONFIG_SYS_MALLOC_SMALL, i.e. by default in SPL.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add a new NO_REALLOC_IN_PLACE option that disables in-place realloc
optimization. When enabled via CONFIG_SYS_MALLOC_SMALL, realloc() always
allocates new memory, copies data, and frees the old block instead of
trying to extend the existing allocation.
This saves about 500 bytes by eliminating try_realloc_chunk() and
mmap_resize() functions.
When unit tests are enabled, the extra realloc logic is included.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
When building boards that use CONFIG_SPL_SYS_MALLOC_SIMPLE (like
qemu-x86_64), we need to avoid a conflict between the stub free()
function defined by malloc and the real free() defined by dlmalloc.c
Fix this by define COMPILING_DLMALLOC in dlmalloc.c before including
malloc.h and adding a guard to the latter.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Move the malloc state initialisation from lazy init in sys_alloc() to
explicit init in mem_malloc_init(). This allows is_initialized() to
always return true for U-Boot, eliminating runtime checks.
The initialisation sets up:
- least_addr, seg.base, seg.size, seg.sflags
- magic, release_checks, mflags
- bins (via init_bins)
- top chunk (via init_top)
- footprint tracking
Add a simplified sys_alloc() for small builds. Since the
heap is pre-allocated with fixed size, sys_alloc() only needs to extend
via sbrk() if space remains: no mmap, no multiple segments, no comple
merging. The helper functions mmap_alloc(), mmap_resize(),
prepend_alloc() and add_segment() are thus compiled out for non-sandbox
builds.
This is controlled by a new SIMPLE_SYSALLOC option, which is the
default. Sandbox retains full functionality for testing.
With this, the new dlmalloc is approximately at parity with the old one,
e.g. about 400 bytes less code on Thumb2 (firefly-rk3288).
There is a strange case here with a small number of boards which set up
the full malloc() when CONFIG_SYS_MALLOC_SIMPLE is enabled. This cannot
work.
With CONFIG_SPL_SYS_MALLOC_SIMPLE, all malloc()/free()/realloc() calls
are redirected to simple implementations via macros in thhe malloc.h
header. In this case, mem_malloc_init() doesn't need to init the dlmalloc
state structure (gm) since it will never be used.
Initing _gm_ pulls in the entire malloc_state BSS structure (~472
bytes) plus initialisation code (~128 bytes), adding ~600 bytes to SPL
on boards that use full malloc (K3 platforms with CONFIG_K3_LOAD_SYSFW).
Skip the _gm_ init when SYS_MALLOC_SIMPLE is enabled.
These boards call mem_malloc_init() even though it will have no effect:
$ ./tools/qconfig.py -f CONFIG_K3_LOAD_SYSFW SPL_SYS_MALLOC_SIMPLE -l
am62ax_evm_r5
am62px_evm_r5
am62x_beagleplay_r5
am62x_evm_r5
am62x_evm_r5_ethboot
am62x_lpsk_r5
am64x_evm_r5
am68_sk_r5
am69_sk_r5
j721s2_evm_r5
j722s_evm_r5
j784s4_evm_r5
phycore_am62x_r5
phycore_am62x_r5_usbdfu
phycore_am64x_r5
verdin-am62_r5
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
Add mcheck_pedantic_prehook() calls to dlmalloc, dlrealloc,
dlmemalign, and dlcalloc wrapper functions. Also add the
mcheck_pedantic() and mcheck_check_all() API functions.
The pedantic mode is runtime controlled, so the registry hooks
are called on every allocation operation.
Changes from original commit:
- Uses dl* function names instead of mALLOc style names
Signed-off-by: Eugene Uriev <eugeneuriev@gmail.com>
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
(cherry picked from commit 18c1bfafe0)
Implement the dlmemalign wrapper function for mcheck heap protection.
Uses mcheck_memalign_prehook() and mcheck_memalign_posthook() to
properly handle aligned allocations.
Changes from original commit:
- Uses dlmemalign/dlmemalign_impl instead of mEMALIGn/mEMALIGn_impl
Signed-off-by: Eugene Uriev <eugeneuriev@gmail.com>
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
(cherry picked from commit ae838768d7)