From patchwork Wed Dec 10 00:07:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 888 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325417; bh=8oY13tm1qysJnCbb2QcuNElxpFZyrHGnAHxyfDRPa78=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=FAWMBdWstCiOmb13dRfEabQdZylETCu6iRbv80wpYOHVs3loGwyien+2ndgnsp96o H3mze2fwhE6dfilwdoTEVo9AeoxjA9oDwyc83LXdjq0/Rnt71Z3FpWgRiCOSkNEDVr sLKiyHU2A7rrD8b/s42gKobW/35XX7sjhB8JC2oWVcc2IbSyBY+rcxvGS+wryPkYko dgy33cnrhq5f2wskINQZ3Yfbjn9lgBZi5Suu7FvwxByGpQYo3C3fURh7sXEVGDKgbe WaIfqKWASqrKdhkN1+LHPGvuAHhobcj2FwmVSgOAMxYWtTjb6YRCcdQgp6//s1Qw/D KD5aFlGATQtNQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C0E176895B for ; Tue, 9 Dec 2025 17:10:17 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id VB_roeI6fdxC for ; Tue, 9 Dec 2025 17:10:17 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325417; bh=8oY13tm1qysJnCbb2QcuNElxpFZyrHGnAHxyfDRPa78=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=FAWMBdWstCiOmb13dRfEabQdZylETCu6iRbv80wpYOHVs3loGwyien+2ndgnsp96o H3mze2fwhE6dfilwdoTEVo9AeoxjA9oDwyc83LXdjq0/Rnt71Z3FpWgRiCOSkNEDVr sLKiyHU2A7rrD8b/s42gKobW/35XX7sjhB8JC2oWVcc2IbSyBY+rcxvGS+wryPkYko dgy33cnrhq5f2wskINQZ3Yfbjn9lgBZi5Suu7FvwxByGpQYo3C3fURh7sXEVGDKgbe WaIfqKWASqrKdhkN1+LHPGvuAHhobcj2FwmVSgOAMxYWtTjb6YRCcdQgp6//s1Qw/D KD5aFlGATQtNQ== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id AD61E687A7 for ; Tue, 9 Dec 2025 17:10:17 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325416; bh=XpE5d/E4PWs5f+0hd7Mk0dZGIsrtEJA6UqMDMmOTKrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=R1VMAJUnZOgg97nG2yHu5vI8fT8QwVgqFnRnVFjL41xiIF+XMC+qqksZlDjxJ1Byd 7XRaGIHcoWoHhLK0oY5+OiK46jUKKukfoTXLUFVMEURzUb3dbcXh+Jny6rOMJlcYPw 80RGVBYwzKSwJxG0fEssdDU98jx8UvDs8Vw/KxQX+ooxT4jvZBjYfTuqxCqsIIR2kU P5tMV1ZDeZ7qnON/rZ2mkQfb8paNINH7RD5MPU7OtL6dnruS11Ny+3CcxvkV7i+Myh Axb2DwuIUf5sM+MiNZbdLRYSPj1kBYVxco8puT0sdiUoaJ06Nl5S5hJz5zcFd4C8ue D+5KskkP79Z8w== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5A191687A7; Tue, 9 Dec 2025 17:10:16 -0700 (MST) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id BA7R6Z3U9te2; Tue, 9 Dec 2025 17:10:16 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1765325409; bh=ZYrkMRkcZFGcTNxyOGrhoLRdm4D0GZbnwUE0bFPPjN4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eXAjWxLv10Ocf3igP2sG9hLVELNw73RN01swjRnX6d6NhBtQfccHt4JP7QaFgtdj7 vyRi7DkiQwTl5pDRzDwu37CVgpBxHMxcYWdHWNPBDaL+ICSBc/JHxoLDrFrTozrbsm c8ARftFxl365W6P7EqY5dg6RWwbp51BYiIvH8tElIH7kc94pekXlUsVaQVD9pQUnmB sSVG8tS8kJbozSSi7GwNst9LtAczlGjjz9Zq/jcCpXbufpV3Z6Ry8vWeypR2f08UP/ +cA4e+p6oEE6Mg31qOmePib8hp3HlBGcdC4rzyi+J8XXY6r50IULfzSht5IUfCurwT R+Lq3B7Kk4+rA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 141C86895B; Tue, 9 Dec 2025 17:10:09 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Tue, 9 Dec 2025 17:07:22 -0700 Message-ID: <20251210000737.180797-32-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251210000737.180797-1-sjg@u-boot.org> References: <20251210000737.180797-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: RHMX6DNDUK7OUTVWOMSZ5JIFZTCSGQPN X-Message-ID-Hash: RHMX6DNDUK7OUTVWOMSZ5JIFZTCSGQPN X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 31/35] malloc: Record caller backtrace for each allocation List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass 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 Signed-off-by: Simon Glass --- common/dlmalloc.c | 35 +++++++++++++++++++++++++++++++---- doc/usage/cmd/malloc.rst | 12 ++++++++++++ include/malloc.h | 15 +++++++++++++++ test/lib/backtrace.c | 7 ++++--- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/common/dlmalloc.c b/common/dlmalloc.c index 14515e423cc..c294b355ff2 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -5953,8 +5953,35 @@ size_t dlmalloc_usable_size(const void* mem) { } #if CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) +#include #include "mcheck_core.inc.h" +/* Guard against recursive backtrace calls during malloc */ +static bool in_backtrace __section(".data"); + +/* + * Flag to disable backtrace collection when the stack is known to be corrupt. + * Set via malloc_backtrace_skip() before calling panic(). + */ +static bool mcheck_skip_backtrace __section(".data"); + +void malloc_backtrace_skip(bool skip) +{ + mcheck_skip_backtrace = skip; +} + +static const char *mcheck_caller(void) +{ + const char *caller = NULL; + + if (!in_backtrace && !mcheck_skip_backtrace) { + in_backtrace = true; + caller = backtrace_str(2); + in_backtrace = false; + } + return caller; +} + void *dlmalloc(size_t bytes) { mcheck_pedantic_prehook(); @@ -5963,7 +5990,7 @@ void *dlmalloc(size_t bytes) if (!p) return p; - return mcheck_alloc_posthook(p, bytes, NULL); + return mcheck_alloc_posthook(p, bytes, mcheck_caller()); } void dlfree(void *mem) { dlfree_impl(mcheck_free_prehook(mem)); } @@ -5988,7 +6015,7 @@ void *dlrealloc(void *oldmem, size_t bytes) p = dlrealloc_impl(p, newsz); if (!p) return p; - return mcheck_alloc_noclean_posthook(p, bytes, NULL); + return mcheck_alloc_noclean_posthook(p, bytes, mcheck_caller()); } void *dlmemalign(size_t alignment, size_t bytes) @@ -5999,7 +6026,7 @@ void *dlmemalign(size_t alignment, size_t bytes) if (!p) return p; - return mcheck_memalign_posthook(alignment, p, bytes, NULL); + return mcheck_memalign_posthook(alignment, p, bytes, mcheck_caller()); } /* dlpvalloc, dlvalloc redirect to dlmemalign, so they need no wrapping */ @@ -6013,7 +6040,7 @@ void *dlcalloc(size_t n, size_t elem_size) if (!p) return p; - return mcheck_alloc_noclean_posthook(p, n * elem_size, NULL); + return mcheck_alloc_noclean_posthook(p, n * elem_size, mcheck_caller()); } /* mcheck API */ diff --git a/doc/usage/cmd/malloc.rst b/doc/usage/cmd/malloc.rst index fac9bb29aac..3693034b41e 100644 --- a/doc/usage/cmd/malloc.rst +++ b/doc/usage/cmd/malloc.rst @@ -59,6 +59,18 @@ Example Used: c2ef0 bytes in 931 chunks Free: 5f3f0c0 bytes in 2 chunks + top +With CONFIG_MCHECK_HEAP_PROTECTION enabled, the caller backtrace is shown:: + + => malloc dump + Heap dump: 18a1d000 - 1ea1f000 + Address Size Status + ---------------------------------- + 18a1d000 10 (chunk header) + 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_ + ... + Configuration ------------- diff --git a/include/malloc.h b/include/malloc.h index a4d588936ec..3327bdcb44f 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -712,6 +712,21 @@ void malloc_disable_testing(void); */ void malloc_dump(void); +/** + * malloc_backtrace_skip() - Control backtrace collection in malloc + * + * When the stack is corrupted (e.g., by a stack overflow), collecting + * a backtrace during malloc can crash. Use this function to disable + * backtrace collection before corrupting the stack. + * + * @skip: true to skip backtrace collection, false to enable it + */ +#if CONFIG_IS_ENABLED(MCHECK_HEAP_PROTECTION) +void malloc_backtrace_skip(bool skip); +#else +static inline void malloc_backtrace_skip(bool skip) {} +#endif + /** * mem_malloc_init() - Initialize the malloc() heap * diff --git a/test/lib/backtrace.c b/test/lib/backtrace.c index 3f20b7854bf..155e5b13af8 100644 --- a/test/lib/backtrace.c +++ b/test/lib/backtrace.c @@ -68,16 +68,17 @@ static int lib_test_backtrace_str(struct unit_test_state *uts) line); ut_asserteq_regex(pattern, str); - /* Test backtrace_str() */ + /* Test backtrace_str() - copy result before printf since it may recurse */ line = __LINE__ + 1; cstr = backtrace_str(0); ut_assertnonnull(cstr); + strlcpy(buf, cstr, sizeof(buf)); - printf("backtrace_str: %s\n", cstr); + printf("backtrace_str: %s\n", buf); snprintf(pattern, sizeof(pattern), "lib_test_backtrace_str:%d <-ut_run_test:\\d+ <-ut_run_test_live_flat:\\d+", line); - ut_asserteq_regex(pattern, cstr); + ut_asserteq_regex(pattern, buf); return 0; }