From: Ethan Graham <ethangraham@google.com>
Introduce a new helper function, kasan_poison_range(), to encapsulate
the logic for poisoning an arbitrary memory range of a given size, and
expose it publically in <include/linux/kasan.h>.
This is a preparatory change for the upcoming KFuzzTest patches, which
requires the ability to poison the inter-region padding in its input
buffers.
No functional change to any other subsystem is intended by this commit.
Signed-off-by: Ethan Graham <ethangraham@google.com>
---
include/linux/kasan.h | 16 ++++++++++++++++
mm/kasan/shadow.c | 31 +++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 890011071f2b..09baeb6c9f4d 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -102,6 +102,21 @@ static inline bool kasan_has_integrated_init(void)
}
#ifdef CONFIG_KASAN
+
+/**
+ * kasan_poison_range - poison the memory range [start, start + size)
+ *
+ * The exact behavior is subject to alignment with KASAN_GRANULE_SIZE, defined
+ * in <mm/kasan/kasan.h>.
+ *
+ * - If @start is unaligned, the initial partial granule at the beginning
+ * of the range is only poisoned if CONFIG_KASAN_GENERIC is enabled.
+ * - The poisoning of the range only extends up to the last full granule before
+ * the end of the range. Any remaining bytes in a final partial granule are
+ * ignored.
+ */
+void kasan_poison_range(const void *start, size_t size);
+
void __kasan_unpoison_range(const void *addr, size_t size);
static __always_inline void kasan_unpoison_range(const void *addr, size_t size)
{
@@ -402,6 +417,7 @@ static __always_inline bool kasan_check_byte(const void *addr)
#else /* CONFIG_KASAN */
+static inline void kasan_poison_range(const void *start, size_t size) {}
static inline void kasan_unpoison_range(const void *address, size_t size) {}
static inline void kasan_poison_pages(struct page *page, unsigned int order,
bool init) {}
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index d2c70cd2afb1..a1b6bfb35f07 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -147,6 +147,37 @@ void kasan_poison(const void *addr, size_t size, u8 value, bool init)
}
EXPORT_SYMBOL_GPL(kasan_poison);
+void kasan_poison_range(const void *start, size_t size)
+{
+ void *end = (char *)start + size;
+ uintptr_t start_addr = (uintptr_t)start;
+ uintptr_t head_granule_start;
+ uintptr_t poison_body_start;
+ uintptr_t poison_body_end;
+ size_t head_prefix_size;
+ uintptr_t end_addr;
+
+ end_addr = ALIGN_DOWN((uintptr_t)end, KASAN_GRANULE_SIZE);
+ if (start_addr >= end_addr)
+ return;
+
+ head_granule_start = ALIGN_DOWN(start_addr, KASAN_GRANULE_SIZE);
+ head_prefix_size = start_addr - head_granule_start;
+
+ if (IS_ENABLED(CONFIG_KASAN_GENERIC) && head_prefix_size > 0)
+ kasan_poison_last_granule((void *)head_granule_start,
+ head_prefix_size);
+
+ poison_body_start = ALIGN(start_addr, KASAN_GRANULE_SIZE);
+ poison_body_end = ALIGN_DOWN(end_addr, KASAN_GRANULE_SIZE);
+
+ if (poison_body_start < poison_body_end)
+ kasan_poison((void *)poison_body_start,
+ poison_body_end - poison_body_start,
+ KASAN_SLAB_REDZONE, false);
+}
+EXPORT_SYMBOL(kasan_poison_range);
+
#ifdef CONFIG_KASAN_GENERIC
void kasan_poison_last_granule(const void *addr, size_t size)
{
--
2.51.0.318.gd7df087d1a-goog
On Mon, Sep 1, 2025 at 6:43 PM Ethan Graham <ethan.w.s.graham@gmail.com> wrote: > > From: Ethan Graham <ethangraham@google.com> > > Introduce a new helper function, kasan_poison_range(), to encapsulate > the logic for poisoning an arbitrary memory range of a given size, and > expose it publically in <include/linux/kasan.h>. > > This is a preparatory change for the upcoming KFuzzTest patches, which > requires the ability to poison the inter-region padding in its input > buffers. > > No functional change to any other subsystem is intended by this commit. > > Signed-off-by: Ethan Graham <ethangraham@google.com> > --- > include/linux/kasan.h | 16 ++++++++++++++++ > mm/kasan/shadow.c | 31 +++++++++++++++++++++++++++++++ > 2 files changed, 47 insertions(+) > > diff --git a/include/linux/kasan.h b/include/linux/kasan.h > index 890011071f2b..09baeb6c9f4d 100644 > --- a/include/linux/kasan.h > +++ b/include/linux/kasan.h > @@ -102,6 +102,21 @@ static inline bool kasan_has_integrated_init(void) > } > > #ifdef CONFIG_KASAN > + > +/** > + * kasan_poison_range - poison the memory range [start, start + size) > + * > + * The exact behavior is subject to alignment with KASAN_GRANULE_SIZE, defined > + * in <mm/kasan/kasan.h>. > + * > + * - If @start is unaligned, the initial partial granule at the beginning > + * of the range is only poisoned if CONFIG_KASAN_GENERIC is enabled. Nit: for consistency with other functions in this header, can we change @start to @addr? > + * - The poisoning of the range only extends up to the last full granule before > + * the end of the range. Any remaining bytes in a final partial granule are > + * ignored. Maybe we should require that the end of the range is aligned, as we do for e.g. kasan_unpoison()? Are there cases in which we want to call it for non-aligned addresses? > > +void kasan_poison_range(const void *start, size_t size) > +{ > + void *end = (char *)start + size; There's only a single use of `end` below, so maybe drop this variable altogether? > + uintptr_t start_addr = (uintptr_t)start; > + uintptr_t head_granule_start; > + uintptr_t poison_body_start; > + uintptr_t poison_body_end; > + size_t head_prefix_size; > + uintptr_t end_addr; > + > + end_addr = ALIGN_DOWN((uintptr_t)end, KASAN_GRANULE_SIZE); I suggest making it end_addr = ALIGN_DOWN((uintptr_t)start + size, KASAN_GRANULE_SIZE); instead.
On Fri, Sep 5, 2025 at 10:33 AM Alexander Potapenko <glider@google.com> wrote: > > + * - The poisoning of the range only extends up to the last full granule before > > + * the end of the range. Any remaining bytes in a final partial granule are > > + * ignored. > > Maybe we should require that the end of the range is aligned, as we do > for e.g. kasan_unpoison()? > Are there cases in which we want to call it for non-aligned addresses? It's possible in the current KFuzzTest input format. For example you have an 8 byte struct with a pointer to a 35-byte string. This results in a payload: struct [0: 8), padding [8: 16), string: [16: 51), padding: [51: 59). The framework will poison the unaligned region [51, 59). We could enforce that the size of the payload (including all padding) is a multiple of KASAN_GRANULE_SIZE, thus resulting in padding [51, 64) at the end of the payload. It makes encoding a bit more complex, but it may be a good idea to push that complexity up to the user space encoder. What do you think?
On Fri, Sep 5, 2025 at 10:46 AM Ethan Graham <ethan.w.s.graham@gmail.com> wrote: > > On Fri, Sep 5, 2025 at 10:33 AM Alexander Potapenko <glider@google.com> wrote: > > > + * - The poisoning of the range only extends up to the last full granule before > > > + * the end of the range. Any remaining bytes in a final partial granule are > > > + * ignored. > > > > Maybe we should require that the end of the range is aligned, as we do > > for e.g. kasan_unpoison()? > > Are there cases in which we want to call it for non-aligned addresses? > > It's possible in the current KFuzzTest input format. For example you have > an 8 byte struct with a pointer to a 35-byte string. This results in a payload: > struct [0: 8), padding [8: 16), string: [16: 51), padding: [51: 59). The > framework will poison the unaligned region [51, 59). > > We could enforce that the size of the payload (including all padding) is > a multiple of KASAN_GRANULE_SIZE, thus resulting in padding [51, 64) > at the end of the payload. It makes encoding a bit more complex, but it > may be a good idea to push that complexity up to the user space encoder. As discussed offline, it might be good to always require the userspace to align every region on KASAN_GRANULE_SIZE or ARCH_KMALLOC_MINALIGN (whichever is greater). In that case, we won't break any implicit assumptions that the code under test has about the memory buffers passed to it, and we also won't need to care about poisoning a range which has both its ends unaligned. Because that required alignment will likely depend on the arch/kernel config, we can expose it via debugfs to make the userspace tools' life easier.
© 2016 - 2025 Red Hat, Inc.