From b4f6fdbcd20bba9da7d693872e48f4ef21aa6aa1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jan 2026 11:27:49 -0700 Subject: [PATCH] sandbox: Add -M option to disable mcheck at runtime 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 Signed-off-by: Simon Glass --- arch/sandbox/cpu/start.c | 13 +++++++++++++ arch/sandbox/include/asm/state.h | 1 + common/dlmalloc.c | 33 +++++++++++++++++++++++++++++++- doc/arch/sandbox/sandbox.rst | 7 +++++++ include/mcheck.h | 11 +++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 1a5ca97e15e..b9f48376d22 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -523,6 +524,15 @@ static int sandbox_cmdline_cb_noflat(struct sandbox_state *state, } SANDBOX_CMDLINE_OPT_SHORT(noflat, 'F', 0, "Don't run second set of DM tests"); +static int sandbox_cmdline_cb_no_mcheck(struct sandbox_state *state, + const char *arg) +{ + state->disable_mcheck = true; + + return 0; +} +SANDBOX_CMDLINE_OPT_SHORT(no_mcheck, 'M', 0, "Disable mcheck heap protection"); + static int sandbox_cmdline_cb_soft_fail(struct sandbox_state *state, const char *arg) { @@ -674,6 +684,9 @@ int sandbox_init(int argc, char *argv[], struct global_data *data) if (os_parse_args(state, argc, argv)) return 1; + if (state->disable_mcheck) + mcheck_set_disabled(true); + /* Remove old frame*.bmp files if video_frames_dir is set */ if (state->video_frames_dir) { char pattern[256]; diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index ff7493ec5d6..1b6eef4a9c3 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -178,6 +178,7 @@ struct sandbox_state { bool pager_bypass; /* Enable pager-bypass mode */ bool no_term_present; /* Assume no terminal present */ bool quiet_vidconsole; /* Don't use vidconsole for stdout */ + bool disable_mcheck; /* Disable mcheck heap protection */ int video_test; /* ms to wait before next assert */ const char *video_frames_dir; /* Directory to write video frames */ int video_frame_count; /* Number of frames written */ diff --git a/common/dlmalloc.c b/common/dlmalloc.c index 5069a95d9b3..78efdf5fd9a 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -5965,6 +5965,14 @@ static bool in_backtrace __section(".data"); */ static bool mcheck_skip_backtrace __section(".data"); +/* Runtime flag to disable mcheck - allows bypassing heap protection */ +static bool mcheck_disabled __section(".data"); + +void mcheck_set_disabled(bool disabled) +{ + mcheck_disabled = disabled; +} + void malloc_backtrace_skip(bool skip) { mcheck_skip_backtrace = skip; @@ -5993,6 +6001,9 @@ void *dlmalloc(size_t bytes) !(gd->flags & GD_FLG_FULL_MALLOC_INIT)) return malloc_simple(bytes); + if (mcheck_disabled) + return dlmalloc_impl(bytes CALLER_NULL); + mcheck_pedantic_prehook(); size_t fullsz = mcheck_alloc_prehook(bytes); void *p = dlmalloc_impl(fullsz CALLER_NULL); @@ -6009,12 +6020,15 @@ void dlfree(void *mem) dlfree_impl(mem); return; } + if (mcheck_disabled) { + dlfree_impl(mem); + return; + } dlfree_impl(mcheck_free_prehook(mem)); } void *dlrealloc(void *oldmem, size_t bytes) { - mcheck_pedantic_prehook(); #ifdef REALLOC_ZERO_BYTES_FREES if (bytes == 0) { if (oldmem) @@ -6026,6 +6040,10 @@ void *dlrealloc(void *oldmem, size_t bytes) if (oldmem == NULL) return dlmalloc(bytes); + if (mcheck_disabled) + return dlrealloc_impl(oldmem, bytes); + + mcheck_pedantic_prehook(); void *p = mcheck_reallocfree_prehook(oldmem); size_t newsz = mcheck_alloc_prehook(bytes); @@ -6041,6 +6059,9 @@ void *dlmemalign(size_t alignment, size_t bytes) !(gd->flags & GD_FLG_FULL_MALLOC_INIT)) return memalign_simple(alignment, bytes); + if (mcheck_disabled) + return dlmemalign_impl(alignment, bytes); + mcheck_pedantic_prehook(); size_t fullsz = mcheck_memalign_prehook(alignment, bytes); void *p = dlmemalign_impl(alignment, fullsz); @@ -6064,6 +6085,9 @@ void *dlcalloc(size_t n, size_t elem_size) return p; } + if (mcheck_disabled) + return dlcalloc_impl(n, elem_size); + mcheck_pedantic_prehook(); /* NB: no overflow check here */ size_t fullsz = mcheck_alloc_prehook(n * elem_size); @@ -6121,6 +6145,13 @@ void *dlcalloc(size_t n, size_t elem_size) } #endif /* MCHECK_HEAP_PROTECTION */ +#if !CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) +/* Stub when mcheck is not enabled */ +void mcheck_set_disabled(bool disabled) +{ +} +#endif + #endif /* !ONLY_MSPACES */ /* ----------------------------- user mspaces ---------------------------- */ diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 90c3dfa837e..0962aeeb1a0 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -181,6 +181,13 @@ available options. Some of these are described below: all log statements at LOGL_DEBUG and below. The higher the number, the more info is shown. +-M, --no_mcheck + Disable mcheck heap protection at runtime. When enabled, the mcheck 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. + -m, --memory Sets the location of the file which holds sandbox's emulated RAM. This can be read and written across phases, so that sandbox behaves like a normal board. diff --git a/include/mcheck.h b/include/mcheck.h index b170acf6281..cd72edb6ae8 100644 --- a/include/mcheck.h +++ b/include/mcheck.h @@ -51,4 +51,15 @@ enum mcheck_status mprobe(void *__ptr); /* Called during RAM relocation to reset the heap registry */ void mcheck_on_ramrelocation(size_t offset); +/** + * mcheck_set_disabled() - Disable mcheck at runtime + * + * When disabled, mcheck 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. + * + * @disabled: true to disable mcheck, false to enable + */ +void mcheck_set_disabled(bool disabled); + #endif