From: Matt Fleming <matt@readmodwrite.com>
To: Andrew Morton <akpm@linux-foundation.org>,
Marco Elver <elver@google.com>
Cc: Alexander Potapenko <glider@google.com>,
Andrey Konovalov <andreyknvl@gmail.com>,
Dmitry Vyukov <dvyukov@google.com>,
Oscar Salvador <osalvador@suse.de>,
Vlastimil Babka <vbabka@kernel.org>,
Caleb Kan <ckan@cloudflare.com>,
Caleb Kan <calebkan1106@gmail.com>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
kasan-dev@googlegroups.com, kernel-team@cloudflare.com,
Matt Fleming <mfleming@cloudflare.com>
Subject: [PATCH v3 07/10] stackdepot: Remove stack_depot_fetch()
Date: Fri, 13 Nov 2026 18:56:59 +0000 [thread overview]
Message-ID: <20261113185702.1332171-8-matt@readmodwrite.com> (raw)
In-Reply-To: <20261113185702.1332171-1-matt@readmodwrite.com>
From: Matt Fleming <mfleming@cloudflare.com>
All callers now use stack_depot_fetch_into(), stack_depot_print() or
stack_depot_snprint(). stack_depot_fetch() hands out a pointer into
depot storage, which leaks how frames are stored internally by
stackdepot. This will be undesirable when compressed frame support
lands in a later patch.
Add a KUnit test that stack_depot_snprint() output and return value
match stack_trace_snprint(), including truncation.
Signed-off-by: Matt Fleming <mfleming@cloudflare.com>
---
include/linux/stackdepot.h | 11 ------
lib/stackdepot.c | 68 ++++++++++++++++--------------------
lib/tests/stackdepot_kunit.c | 36 +++++++++++++++++++
3 files changed, 66 insertions(+), 49 deletions(-)
diff --git a/include/linux/stackdepot.h b/include/linux/stackdepot.h
index 734529767c8a..dcc2e1d21f92 100644
--- a/include/linux/stackdepot.h
+++ b/include/linux/stackdepot.h
@@ -188,17 +188,6 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries,
*/
struct stack_record *__stack_depot_get_stack_record(depot_stack_handle_t handle);
-/**
- * stack_depot_fetch - Fetch a stack trace from stack depot
- *
- * @handle: Stack depot handle returned from stack_depot_save()
- * @entries: Pointer to store the address of the stack trace
- *
- * Return: Number of frames for the fetched stack
- */
-unsigned int stack_depot_fetch(depot_stack_handle_t handle,
- unsigned long **entries);
-
/**
* stack_depot_fetch_into - Fetch a stack trace into caller-owned storage
*
diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index 556f3ecdcc8f..8f1aaa0c6d88 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -757,33 +757,14 @@ struct stack_record *__stack_depot_get_stack_record(depot_stack_handle_t handle)
return depot_fetch_stack(handle);
}
-unsigned int stack_depot_fetch(depot_stack_handle_t handle,
- unsigned long **entries)
+/* Copy the first @nr_entries frames of @stack into @entries. */
+static void depot_copy_entries(const struct stack_record *stack,
+ unsigned long *entries, unsigned int nr_entries)
{
- struct stack_record *stack;
-
- *entries = NULL;
- /*
- * Let KMSAN know *entries is initialized. This shall prevent false
- * positive reports if instrumented code accesses it.
- */
- kmsan_unpoison_memory(entries, sizeof(*entries));
-
- if (!handle || stack_depot_disabled)
- return 0;
-
- stack = depot_fetch_stack(handle);
- /*
- * Should never be NULL, otherwise this is a use-after-put (or just a
- * corrupt handle).
- */
- if (WARN(!stack, "corrupt handle or use after stack_depot_put()"))
- return 0;
-
- *entries = stack->entries;
- return stack->size;
+ memcpy(entries, stack->entries, nr_entries * sizeof(*entries));
+ /* stackdepot.c is not instrumented; tell KMSAN the copy is initialized. */
+ kmsan_unpoison_memory(entries, nr_entries * sizeof(*entries));
}
-EXPORT_SYMBOL_GPL(stack_depot_fetch);
unsigned int stack_depot_fetch_into(depot_stack_handle_t handle,
unsigned long *entries,
@@ -808,12 +789,27 @@ unsigned int stack_depot_fetch_into(depot_stack_handle_t handle,
if (nr_entries > max_entries)
return 0;
- memcpy(entries, stack->entries, nr_entries * sizeof(*entries));
- kmsan_unpoison_memory(entries, nr_entries * sizeof(*entries));
+ depot_copy_entries(stack, entries, nr_entries);
return nr_entries;
}
EXPORT_SYMBOL_GPL(stack_depot_fetch_into);
+static struct stack_record *depot_print_fetch(depot_stack_handle_t handle)
+{
+ struct stack_record *stack;
+
+ if (!handle || stack_depot_disabled)
+ return NULL;
+
+ stack = depot_fetch_stack(handle);
+ /*
+ * Should never be NULL, otherwise this is a use-after-put (or just a
+ * corrupt handle).
+ */
+ WARN(!stack, "corrupt handle or use after stack_depot_put()");
+ return stack;
+}
+
void stack_depot_put(depot_stack_handle_t handle)
{
struct stack_record *stack;
@@ -834,26 +830,22 @@ void stack_depot_put(depot_stack_handle_t handle)
}
EXPORT_SYMBOL_GPL(stack_depot_put);
-void stack_depot_print(depot_stack_handle_t stack)
+void stack_depot_print(depot_stack_handle_t handle)
{
- unsigned long *entries;
- unsigned int nr_entries;
+ struct stack_record *stack = depot_print_fetch(handle);
- nr_entries = stack_depot_fetch(stack, &entries);
- if (nr_entries > 0)
- stack_trace_print(entries, nr_entries, 0);
+ if (stack)
+ stack_trace_print(stack->entries, stack->size, 0);
}
EXPORT_SYMBOL_GPL(stack_depot_print);
int stack_depot_snprint(depot_stack_handle_t handle, char *buf, size_t size,
int spaces)
{
- unsigned long *entries;
- unsigned int nr_entries;
+ struct stack_record *stack = depot_print_fetch(handle);
- nr_entries = stack_depot_fetch(handle, &entries);
- return nr_entries ? stack_trace_snprint(buf, size, entries, nr_entries,
- spaces) : 0;
+ return stack ? stack_trace_snprint(buf, size, stack->entries,
+ stack->size, spaces) : 0;
}
EXPORT_SYMBOL_GPL(stack_depot_snprint);
diff --git a/lib/tests/stackdepot_kunit.c b/lib/tests/stackdepot_kunit.c
index b0c44c096976..ae4f7661a48f 100644
--- a/lib/tests/stackdepot_kunit.c
+++ b/lib/tests/stackdepot_kunit.c
@@ -3,6 +3,8 @@
#include <kunit/test.h>
#include <linux/array_size.h>
#include <linux/gfp.h>
+#include <linux/minmax.h>
+#include <linux/stacktrace.h>
#include <linux/stackdepot.h>
#include <linux/string.h>
@@ -71,9 +73,43 @@ static void stackdepot_fetch_into_rejects_missing_or_short_stack(struct kunit *t
KUNIT_EXPECT_MEMEQ(test, fetched, expected, sizeof(expected));
}
+/* stack_depot_snprint() must match stack_trace_snprint(), with truncation. */
+static void stackdepot_snprint_matches_stack_trace(struct kunit *test)
+{
+ const unsigned int nr = min(40, CONFIG_STACKDEPOT_MAX_FRAMES);
+ const size_t size = 4096;
+ unsigned long entries[40];
+ depot_stack_handle_t handle;
+ char *got, *want;
+ unsigned int i;
+
+ for (i = 0; i < nr; i++)
+ entries[i] = 0x301000UL + 0x1000UL * i;
+ got = kunit_kzalloc(test, size, GFP_KERNEL);
+ want = kunit_kzalloc(test, size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, got);
+ KUNIT_ASSERT_NOT_NULL(test, want);
+
+ KUNIT_ASSERT_EQ(test, stack_depot_init(), 0);
+ handle = stack_depot_save(entries, nr, GFP_KERNEL);
+ KUNIT_ASSERT_NE(test, handle, (depot_stack_handle_t)0);
+
+ KUNIT_EXPECT_EQ(test, stack_depot_snprint(handle, got, size, 2),
+ stack_trace_snprint(want, size, entries, nr, 2));
+ KUNIT_EXPECT_STREQ(test, got, want);
+
+ /* Truncate part-way through the trace. */
+ memset(got, 0, size);
+ memset(want, 0, size);
+ KUNIT_EXPECT_EQ(test, stack_depot_snprint(handle, got, 700, 0),
+ stack_trace_snprint(want, 700, entries, nr, 0));
+ KUNIT_EXPECT_STREQ(test, got, want);
+}
+
static struct kunit_case stackdepot_test_cases[] = {
KUNIT_CASE(stackdepot_fetch_into_roundtrip),
KUNIT_CASE(stackdepot_fetch_into_rejects_missing_or_short_stack),
+ KUNIT_CASE(stackdepot_snprint_matches_stack_trace),
{}
};
--
2.43.0
next prev parent reply other threads:[~2026-11-13 18:57 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 18:56 [PATCH v3 00/10] stackdepot: store compressed frames in stack records Matt Fleming
2026-11-13 18:56 ` [PATCH v3 01/10] stackdepot: add caller-owned stack trace fetching Matt Fleming
2026-11-13 18:56 ` [PATCH v3 02/10] mm/kmemleak: use stack_depot_fetch_into() for leak reports Matt Fleming
2026-11-13 18:56 ` [PATCH v3 03/10] kmsan: use stack_depot_fetch_into() for origin reports Matt Fleming
2026-11-13 18:56 ` [PATCH v3 04/10] mm/slub: use stack_depot_fetch_into() for owner tracking Matt Fleming
2026-11-13 18:56 ` [PATCH v3 05/10] drm/locking: print deadlock backtraces with stack_depot_snprint() Matt Fleming
2026-11-13 18:56 ` [PATCH v3 06/10] mm/page_owner: Read stack traces with stack_depot_fetch_into() Matt Fleming
2026-11-13 18:56 ` Matt Fleming [this message]
2026-11-13 18:57 ` [PATCH v3 08/10] stackdepot: add architecture hooks for compact frame storage Matt Fleming
2026-11-13 18:57 ` [PATCH v3 09/10] stackdepot: Store compressed frames in stack records Matt Fleming
2026-11-13 18:57 ` [PATCH v3 10/10] scripts/gdb: Decode compressed stack depot frames Matt Fleming
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113185702.1332171-8-matt@readmodwrite.com \
--to=matt@readmodwrite.com \
--cc=akpm@linux-foundation.org \
--cc=andreyknvl@gmail.com \
--cc=calebkan1106@gmail.com \
--cc=ckan@cloudflare.com \
--cc=dvyukov@google.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=kasan-dev@googlegroups.com \
--cc=kernel-team@cloudflare.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mfleming@cloudflare.com \
--cc=osalvador@suse.de \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.