From: Muchun Song <songmuchun@bytedance.com>
To: Alexander Potapenko <glider@google.com>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Marco Elver <elver@google.com>,
Dmitry Vyukov <dvyukov@google.com>,
David Hildenbrand <david@kernel.org>,
"Liam R . Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>,
Johannes Weiner <hannes@cmpxchg.org>,
Brendan Jackman <brendan.jackman@linux.dev>,
Zi Yan <ziy@nvidia.com>, Muchun Song <muchun.song@linux.dev>,
Sang-Heon Jeon <ekffu200098@gmail.com>,
Mostafa Saleh <smostafa@google.com>,
Ketan <ketan.kishore@oss.qualcomm.com>,
Usama Arif <usama.arif@bytedance.com>,
kasan-dev@googlegroups.com, linux-kernel@vger.kernel.org,
linux-mm@kvack.org, Muchun Song <songmuchun@bytedance.com>
Subject: [PATCH 1/3] kmsan: encapsulate per-page metadata access
Date: Sat, 14 Nov 2026 18:41:47 +0800 [thread overview]
Message-ID: <20261114104150.58424-2-songmuchun@bytedance.com> (raw)
In-Reply-To: <20261114104150.58424-1-songmuchun@bytedance.com>
KMSAN accesses the shadow and origin page pointers directly through
struct page. This spreads knowledge of the metadata storage across the
shadow implementation and makes changing that storage unnecessarily
invasive.
Introduce helpers that get and set both metadata page pointers together
and convert the existing users to them. Keeping the pair together also
lets callers avoid duplicate lookups once the storage moves out of struct
page.
No functional change is intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
mm/kmsan/shadow.c | 86 +++++++++++++++++++++++++++++++----------------
1 file changed, 57 insertions(+), 29 deletions(-)
diff --git a/mm/kmsan/shadow.c b/mm/kmsan/shadow.c
index 0c88d89bf0d6..8922d1ba9a0a 100644
--- a/mm/kmsan/shadow.c
+++ b/mm/kmsan/shadow.c
@@ -20,29 +20,43 @@
#include "../vmalloc.h"
#include "kmsan.h"
-#define shadow_page_for(page) ((page)->kmsan_shadow)
+struct page_metadata {
+ struct page *shadow;
+ struct page *origin;
+};
-#define origin_page_for(page) ((page)->kmsan_origin)
+static inline struct page_metadata get_page_metadata(struct page *page)
+{
+ return (struct page_metadata) {
+ .shadow = page->kmsan_shadow,
+ .origin = page->kmsan_origin,
+ };
+}
+
+static inline void set_page_metadata(struct page *page, struct page_metadata meta)
+{
+ page->kmsan_shadow = meta.shadow;
+ page->kmsan_origin = meta.origin;
+}
-static void *shadow_ptr_for(struct page *page)
+static inline void *shadow_ptr_for(struct page_metadata meta)
{
- return page_address(shadow_page_for(page));
+ return page_address(meta.shadow);
}
-static void *origin_ptr_for(struct page *page)
+static inline void *origin_ptr_for(struct page_metadata meta)
{
- return page_address(origin_page_for(page));
+ return page_address(meta.origin);
}
-static bool page_has_metadata(struct page *page)
+static inline bool page_has_metadata(struct page_metadata meta)
{
- return shadow_page_for(page) && origin_page_for(page);
+ return meta.shadow && meta.origin;
}
static void set_no_shadow_origin_page(struct page *page)
{
- shadow_page_for(page) = NULL;
- origin_page_for(page) = NULL;
+ set_page_metadata(page, (struct page_metadata) {});
}
/*
@@ -124,6 +138,7 @@ struct shadow_origin_ptr kmsan_get_shadow_origin_ptr(void *address, u64 size,
*/
void *kmsan_get_metadata(void *address, bool is_origin)
{
+ struct page_metadata meta;
u64 addr = (u64)address, off;
struct page *page;
void *ret;
@@ -142,28 +157,36 @@ void *kmsan_get_metadata(void *address, bool is_origin)
page = virt_to_page_or_null(address);
if (!page)
return NULL;
- if (!page_has_metadata(page))
+ meta = get_page_metadata(page);
+ if (!page_has_metadata(meta))
return NULL;
off = offset_in_page(addr);
- return (is_origin ? origin_ptr_for(page) : shadow_ptr_for(page)) + off;
+ return (is_origin ? origin_ptr_for(meta) : shadow_ptr_for(meta)) + off;
}
void kmsan_copy_page_meta(struct page *dst, struct page *src)
{
+ struct page_metadata dst_meta, src_meta;
+
if (!kmsan_enabled || kmsan_in_runtime())
return;
- if (!dst || !page_has_metadata(dst))
+ if (!dst)
return;
- if (!src || !page_has_metadata(src)) {
+ dst_meta = get_page_metadata(dst);
+ if (!page_has_metadata(dst_meta))
+ return;
+ if (src)
+ src_meta = get_page_metadata(src);
+ if (!src || !page_has_metadata(src_meta)) {
kmsan_internal_unpoison_memory(page_address(dst), PAGE_SIZE,
/*checked*/ false);
return;
}
kmsan_enter_runtime();
- __memcpy(shadow_ptr_for(dst), shadow_ptr_for(src), PAGE_SIZE);
- __memcpy(origin_ptr_for(dst), origin_ptr_for(src), PAGE_SIZE);
+ __memcpy(shadow_ptr_for(dst_meta), shadow_ptr_for(src_meta), PAGE_SIZE);
+ __memcpy(origin_ptr_for(dst_meta), origin_ptr_for(src_meta), PAGE_SIZE);
kmsan_leave_runtime();
}
EXPORT_SYMBOL(kmsan_copy_page_meta);
@@ -171,19 +194,18 @@ EXPORT_SYMBOL(kmsan_copy_page_meta);
void kmsan_alloc_page(struct page *page, unsigned int order, gfp_t flags)
{
bool initialized = (flags & __GFP_ZERO) || !kmsan_enabled;
- struct page *shadow, *origin;
+ struct page_metadata meta;
depot_stack_handle_t handle;
int pages = 1 << order;
if (!page)
return;
- shadow = shadow_page_for(page);
- origin = origin_page_for(page);
+ meta = get_page_metadata(page);
if (initialized) {
- __memset(page_address(shadow), 0, PAGE_SIZE * pages);
- __memset(page_address(origin), 0, PAGE_SIZE * pages);
+ __memset(shadow_ptr_for(meta), 0, PAGE_SIZE * pages);
+ __memset(origin_ptr_for(meta), 0, PAGE_SIZE * pages);
return;
}
@@ -191,7 +213,7 @@ void kmsan_alloc_page(struct page *page, unsigned int order, gfp_t flags)
if (kmsan_in_runtime())
return;
- __memset(page_address(shadow), -1, PAGE_SIZE * pages);
+ __memset(shadow_ptr_for(meta), -1, PAGE_SIZE * pages);
kmsan_enter_runtime();
handle = kmsan_save_stack_with_flags(flags, /*extra_bits*/ 0);
kmsan_leave_runtime();
@@ -200,7 +222,7 @@ void kmsan_alloc_page(struct page *page, unsigned int order, gfp_t flags)
* to just fill the origin pages with @handle.
*/
for (int i = 0; i < PAGE_SIZE * pages / sizeof(handle); i++)
- ((depot_stack_handle_t *)page_address(origin))[i] = handle;
+ ((depot_stack_handle_t *)origin_ptr_for(meta))[i] = handle;
}
void kmsan_free_page(struct page *page, unsigned int order)
@@ -238,8 +260,10 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
goto ret;
}
for (int i = 0; i < nr; i++) {
- s_pages[i] = shadow_page_for(pages[i]);
- o_pages[i] = origin_page_for(pages[i]);
+ struct page_metadata meta = get_page_metadata(pages[i]);
+
+ s_pages[i] = meta.shadow;
+ o_pages[i] = meta.origin;
}
prot = PAGE_KERNEL;
@@ -289,10 +313,12 @@ void __init kmsan_init_alloc_meta_for_range(void *start, void *end)
page = virt_to_page_or_null((char *)start + addr);
shadow_p = virt_to_page((char *)shadow + addr);
set_no_shadow_origin_page(shadow_p);
- shadow_page_for(page) = shadow_p;
origin_p = virt_to_page((char *)origin + addr);
set_no_shadow_origin_page(origin_p);
- origin_page_for(page) = origin_p;
+ set_page_metadata(page, (struct page_metadata) {
+ .shadow = shadow_p,
+ .origin = origin_p,
+ });
}
}
@@ -302,7 +328,9 @@ void kmsan_setup_meta(struct page *page, struct page *shadow,
for (int i = 0; i < (1 << order); i++) {
set_no_shadow_origin_page(&shadow[i]);
set_no_shadow_origin_page(&origin[i]);
- shadow_page_for(&page[i]) = &shadow[i];
- origin_page_for(&page[i]) = &origin[i];
+ set_page_metadata(&page[i], (struct page_metadata) {
+ .shadow = &shadow[i],
+ .origin = &origin[i],
+ });
}
}
--
2.54.0
next prev parent reply other threads:[~2026-11-14 10:42 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 10:41 [PATCH 0/3] KMSAN: Move per-page metadata out of struct page Muchun Song
2026-11-14 10:41 ` Muchun Song [this message]
2026-11-14 10:41 ` [PATCH 2/3] mm/page_ext: support early allocation for SPARSEMEM Muchun Song
2026-11-14 10:41 ` [PATCH 3/3] kmsan: fix metadata loss during HugeTLB struct page reinitialization Muchun Song
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=20261114104150.58424-2-songmuchun@bytedance.com \
--to=songmuchun@bytedance.com \
--cc=akpm@linux-foundation.org \
--cc=brendan.jackman@linux.dev \
--cc=david@kernel.org \
--cc=dvyukov@google.com \
--cc=ekffu200098@gmail.com \
--cc=elver@google.com \
--cc=glider@google.com \
--cc=hannes@cmpxchg.org \
--cc=kasan-dev@googlegroups.com \
--cc=ketan.kishore@oss.qualcomm.com \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@suse.com \
--cc=muchun.song@linux.dev \
--cc=rppt@kernel.org \
--cc=smostafa@google.com \
--cc=surenb@google.com \
--cc=usama.arif@bytedance.com \
--cc=vbabka@kernel.org \
--cc=ziy@nvidia.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®