From: Jiayuan Chen <jiayuan.chen@linux.dev>
Syzkaller reported a memory out-of-bounds bug [1]. This patch fixes two
issues:
1. In vrealloc the KASAN_VMALLOC_VM_ALLOC flag is missing when
unpoisoning the extended region. This flag is required to correctly
associate the allocation with KASAN's vmalloc tracking.
Note: In contrast, vzalloc (via __vmalloc_node_range_noprof) explicitly
sets KASAN_VMALLOC_VM_ALLOC and calls kasan_unpoison_vmalloc() with it.
vrealloc must behave consistently — especially when reusing existing
vmalloc regions — to ensure KASAN can track allocations correctly.
2. When vrealloc reuses an existing vmalloc region (without allocating
new pages) KASAN generates a new tag, which breaks tag-based memory
access tracking.
Introduce KASAN_VMALLOC_KEEP_TAG, a new KASAN flag that allows reusing
the tag already attached to the pointer, ensuring consistent tag
behavior during reallocation.
Pass KASAN_VMALLOC_KEEP_TAG and KASAN_VMALLOC_VM_ALLOC to the
kasan_unpoison_vmalloc inside vrealloc_node_align_noprof().
[1]: https://syzkaller.appspot.com/bug?extid=997752115a851cb0cf36
Fixes: a0309faf1cb0 ("mm: vmalloc: support more granular vrealloc() sizing")
Reported-by: syzbot+997752115a851cb0cf36@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/68e243a2.050a0220.1696c6.007d.GAE@google.com/T/
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Co-developed-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
---
include/linux/kasan.h | 1 +
mm/kasan/hw_tags.c | 2 +-
mm/kasan/shadow.c | 4 +++-
mm/vmalloc.c | 4 +++-
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index d12e1a5f5a9a..6d7972bb390c 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -28,6 +28,7 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t;
#define KASAN_VMALLOC_INIT ((__force kasan_vmalloc_flags_t)0x01u)
#define KASAN_VMALLOC_VM_ALLOC ((__force kasan_vmalloc_flags_t)0x02u)
#define KASAN_VMALLOC_PROT_NORMAL ((__force kasan_vmalloc_flags_t)0x04u)
+#define KASAN_VMALLOC_KEEP_TAG ((__force kasan_vmalloc_flags_t)0x08u)
#define KASAN_VMALLOC_PAGE_RANGE 0x1 /* Apply exsiting page range */
#define KASAN_VMALLOC_TLB_FLUSH 0x2 /* TLB flush */
diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
index 1c373cc4b3fa..cbef5e450954 100644
--- a/mm/kasan/hw_tags.c
+++ b/mm/kasan/hw_tags.c
@@ -361,7 +361,7 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
return (void *)start;
}
- tag = kasan_random_tag();
+ tag = (flags & KASAN_VMALLOC_KEEP_TAG) ? get_tag(start) : kasan_random_tag();
start = set_tag(start, tag);
/* Unpoison and initialize memory up to size. */
diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
index 5d2a876035d6..5e47ae7fdd59 100644
--- a/mm/kasan/shadow.c
+++ b/mm/kasan/shadow.c
@@ -648,7 +648,9 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
!(flags & KASAN_VMALLOC_PROT_NORMAL))
return (void *)start;
- start = set_tag(start, kasan_random_tag());
+ if (unlikely(!(flags & KASAN_VMALLOC_KEEP_TAG)))
+ start = set_tag(start, kasan_random_tag());
+
kasan_unpoison(start, size, false);
return (void *)start;
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 798b2ed21e46..22a73a087135 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4176,7 +4176,9 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
*/
if (size <= alloced_size) {
kasan_unpoison_vmalloc(p + old_size, size - old_size,
- KASAN_VMALLOC_PROT_NORMAL);
+ KASAN_VMALLOC_PROT_NORMAL |
+ KASAN_VMALLOC_VM_ALLOC |
+ KASAN_VMALLOC_KEEP_TAG);
/*
* No need to zero memory here, as unused memory will have
* already been zeroed at initial allocation time or during
--
2.52.0
On Thu, Dec 4, 2025 at 8:00 PM Maciej Wieczor-Retman
<m.wieczorretman@pm.me> wrote:
>
> From: Jiayuan Chen <jiayuan.chen@linux.dev>
>
> Syzkaller reported a memory out-of-bounds bug [1]. This patch fixes two
> issues:
>
> 1. In vrealloc the KASAN_VMALLOC_VM_ALLOC flag is missing when
> unpoisoning the extended region. This flag is required to correctly
> associate the allocation with KASAN's vmalloc tracking.
>
> Note: In contrast, vzalloc (via __vmalloc_node_range_noprof) explicitly
> sets KASAN_VMALLOC_VM_ALLOC and calls kasan_unpoison_vmalloc() with it.
> vrealloc must behave consistently — especially when reusing existing
> vmalloc regions — to ensure KASAN can track allocations correctly.
>
> 2. When vrealloc reuses an existing vmalloc region (without allocating
> new pages) KASAN generates a new tag, which breaks tag-based memory
> access tracking.
>
> Introduce KASAN_VMALLOC_KEEP_TAG, a new KASAN flag that allows reusing
> the tag already attached to the pointer, ensuring consistent tag
> behavior during reallocation.
>
> Pass KASAN_VMALLOC_KEEP_TAG and KASAN_VMALLOC_VM_ALLOC to the
> kasan_unpoison_vmalloc inside vrealloc_node_align_noprof().
>
> [1]: https://syzkaller.appspot.com/bug?extid=997752115a851cb0cf36
>
> Fixes: a0309faf1cb0 ("mm: vmalloc: support more granular vrealloc() sizing")
> Reported-by: syzbot+997752115a851cb0cf36@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/all/68e243a2.050a0220.1696c6.007d.GAE@google.com/T/
> Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
> Co-developed-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
> Signed-off-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com>
> ---
> include/linux/kasan.h | 1 +
> mm/kasan/hw_tags.c | 2 +-
> mm/kasan/shadow.c | 4 +++-
> mm/vmalloc.c | 4 +++-
> 4 files changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/kasan.h b/include/linux/kasan.h
> index d12e1a5f5a9a..6d7972bb390c 100644
> --- a/include/linux/kasan.h
> +++ b/include/linux/kasan.h
> @@ -28,6 +28,7 @@ typedef unsigned int __bitwise kasan_vmalloc_flags_t;
> #define KASAN_VMALLOC_INIT ((__force kasan_vmalloc_flags_t)0x01u)
> #define KASAN_VMALLOC_VM_ALLOC ((__force kasan_vmalloc_flags_t)0x02u)
> #define KASAN_VMALLOC_PROT_NORMAL ((__force kasan_vmalloc_flags_t)0x04u)
> +#define KASAN_VMALLOC_KEEP_TAG ((__force kasan_vmalloc_flags_t)0x08u)
>
> #define KASAN_VMALLOC_PAGE_RANGE 0x1 /* Apply exsiting page range */
> #define KASAN_VMALLOC_TLB_FLUSH 0x2 /* TLB flush */
> diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c
> index 1c373cc4b3fa..cbef5e450954 100644
> --- a/mm/kasan/hw_tags.c
> +++ b/mm/kasan/hw_tags.c
> @@ -361,7 +361,7 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
> return (void *)start;
> }
>
> - tag = kasan_random_tag();
> + tag = (flags & KASAN_VMALLOC_KEEP_TAG) ? get_tag(start) : kasan_random_tag();
> start = set_tag(start, tag);
>
> /* Unpoison and initialize memory up to size. */
> diff --git a/mm/kasan/shadow.c b/mm/kasan/shadow.c
> index 5d2a876035d6..5e47ae7fdd59 100644
> --- a/mm/kasan/shadow.c
> +++ b/mm/kasan/shadow.c
> @@ -648,7 +648,9 @@ void *__kasan_unpoison_vmalloc(const void *start, unsigned long size,
> !(flags & KASAN_VMALLOC_PROT_NORMAL))
> return (void *)start;
>
> - start = set_tag(start, kasan_random_tag());
> + if (unlikely(!(flags & KASAN_VMALLOC_KEEP_TAG)))
> + start = set_tag(start, kasan_random_tag());
> +
> kasan_unpoison(start, size, false);
> return (void *)start;
> }
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index 798b2ed21e46..22a73a087135 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -4176,7 +4176,9 @@ void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align
> */
> if (size <= alloced_size) {
> kasan_unpoison_vmalloc(p + old_size, size - old_size,
> - KASAN_VMALLOC_PROT_NORMAL);
> + KASAN_VMALLOC_PROT_NORMAL |
> + KASAN_VMALLOC_VM_ALLOC |
> + KASAN_VMALLOC_KEEP_TAG);
> /*
> * No need to zero memory here, as unused memory will have
> * already been zeroed at initial allocation time or during
> --
> 2.52.0
>
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
© 2016 - 2025 Red Hat, Inc.