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 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2026-01-01 11:27:49 -07:00
parent e77755ed85
commit b4f6fdbcd2
5 changed files with 64 additions and 1 deletions

View File

@@ -11,6 +11,7 @@
#include <event.h>
#include <init.h>
#include <log.h>
#include <mcheck.h>
#include <os.h>
#include <pager.h>
#include <sandbox_host.h>
@@ -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];

View File

@@ -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 */

View File

@@ -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 ---------------------------- */

View File

@@ -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 <filename>
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.

View File

@@ -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