kernel/fork.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-)
When a thread stack is freed via RCU callback,
thread_stack_free_rcu() calls vfree(vm_area->addr). In RCU callback
context (e.g. from rcu_nocb_cb_kthread with BH disabled, or from
RCU_SOFTIRQ), in_interrupt() returns true, so vfree() routes to
vfree_atomic().
vfree_atomic() uses the freed memory as llist storage by calling
llist_add((struct llist_node *)addr, &p->list), which writes 8 bytes
to the address being freed. With KASAN SW_TAGS enabled,
vm_area->addr carries a random tag assigned during allocation by
kasan_unpoison_vmalloc(). If the shadow memory covering this region
has been set to KASAN_TAG_KERNEL (0xFF) — e.g. by
kasan_unpoison_task_stack() using task->stack, which was already
reset to 0xFF by kasan_reset_tag() at allocation time — the shadow
byte (0xFF) no longer matches the pointer tag on vm_area->addr,
and the write in llist_add triggers a KASAN invalid-access report:
==================================================================
BUG: KASAN: invalid-access in vfree_atomic+0x90/0x150
Write of size 8 at addr c2ffffc0a8f70000 by task rcuop/7/75
Pointer tag: [c2], memory tag: [ff]
CPU: 5 UID: 0 PID: 75 Comm: rcuop/7 Tainted: G S W OE
Hardware name: XiaoMi Xring_o1 UDP PHONE (DT)
Call trace:
show_stack+0x18/0x28
__dump_stack+0x28/0x3c
dump_stack_lvl+0xac/0xf0
print_address_description+0x7c/0x25c
print_report+0x70/0x8c
kasan_report+0xdc/0x13c
__hwasan_store8_noabort+0xe8/0xf8
vfree_atomic+0x90/0x150
vfree+0x220/0x298
thread_stack_free_rcu+0x3c/0x4c
rcu_do_batch+0x308/0xaf0
rcu_nocb_cb_kthread+0x33c/0x708
kthread+0x364/0x3cc
ret_from_fork+0x10/0x20
The buggy address belongs to a 8-page vmalloc region starting at
0xc2ffffc0a8f70000 allocated at copy_process+0x1ac/0x12e4
Memory state around the buggy address:
ffffffc0a8f6ff00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
>ffffffc0a8f70000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
^
ffffffc0a8f70100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================
The tag on tsk->stack was already reset to KASAN_TAG_KERNEL (0xFF) by
commit c08e6a1206e6 ("kasan, fork: reset pointer tags of vmapped
stacks"), but vm_area->addr still carries the original random tag.
This is the same class of bug that was fixed for shadow call stacks
in scs_free() by commit 528a4ab45300 ("scs: Release kasan vmalloc
poison in scs_free process").
Fix it by resetting the pointer tag before calling vfree(), so that
vfree_atomic()'s llist_add write uses KASAN_TAG_KERNEL (0xFF), which
makes KASAN bypass tag checks for that write in all modes (SW_TAGS,
HW_TAGS, and Generic).
kasan_unpoison_vmalloc() is not needed alongside kasan_reset_tag():
in HW_TAGS, __kasan_unpoison_vmalloc() is a no-op without
KASAN_VMALLOC_VM_ALLOC (only KASAN_VMALLOC_PROT_NORMAL is passed);
in SW_TAGS and Generic, the 0xFF-tagged pointer already bypasses
shadow checks, so the shadow state is irrelevant.
Fixes: 0f110a9b956c ("kernel/fork: use vfree_atomic() to free thread stack")
Cc: stable@vger.kernel.org
Signed-off-by: sparkhuang <huangshaobo3@xiaomi.com>
---
kernel/fork.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index f0e2e131a..2fd6fd25c 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -280,7 +280,19 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
return;
- vfree(vm_area->addr);
+ /*
+ * Reset the pointer tag before vfree(): in RCU callback context
+ * vfree() routes to vfree_atomic(), which writes to the freed
+ * memory as llist storage. Resetting the tag to KASAN_TAG_KERNEL
+ * (0xFF) makes KASAN bypass tag checks for that write in all modes
+ * (HW_TAGS, SW_TAGS, Generic), avoiding a false tag-mismatch report.
+ *
+ * kasan_unpoison_vmalloc() is not needed here: in HW_TAGS it is a
+ * no-op without KASAN_VMALLOC_VM_ALLOC, and in SW_TAGS/Generic the
+ * 0xFF pointer already bypasses shadow checks. This mirrors the
+ * intent of the fix in scs_free() (commit 528a4ab45300).
+ */
+ vfree(kasan_reset_tag(vm_area->addr));
}
static void thread_stack_delayed_free(struct task_struct *tsk)
--
2.34.1
On 8/6/26 14:30, sparkhuang wrote: > When a thread stack is freed via RCU callback, > thread_stack_free_rcu() calls vfree(vm_area->addr). In RCU callback > context (e.g. from rcu_nocb_cb_kthread with BH disabled, or from > RCU_SOFTIRQ), in_interrupt() returns true, so vfree() routes to > vfree_atomic(). > > vfree_atomic() uses the freed memory as llist storage by calling > llist_add((struct llist_node *)addr, &p->list), which writes 8 bytes > to the address being freed. With KASAN SW_TAGS enabled, > vm_area->addr carries a random tag assigned during allocation by > kasan_unpoison_vmalloc(). If the shadow memory covering this region > has been set to KASAN_TAG_KERNEL (0xFF) — e.g. by > kasan_unpoison_task_stack() using task->stack, which was already > reset to 0xFF by kasan_reset_tag() at allocation time — the shadow > byte (0xFF) no longer matches the pointer tag on vm_area->addr, > and the write in llist_add triggers a KASAN invalid-access report: > > ================================================================== > BUG: KASAN: invalid-access in vfree_atomic+0x90/0x150 > Write of size 8 at addr c2ffffc0a8f70000 by task rcuop/7/75 > Pointer tag: [c2], memory tag: [ff] > > CPU: 5 UID: 0 PID: 75 Comm: rcuop/7 Tainted: G S W OE > Hardware name: XiaoMi Xring_o1 UDP PHONE (DT) > Call trace: > show_stack+0x18/0x28 > __dump_stack+0x28/0x3c > dump_stack_lvl+0xac/0xf0 > print_address_description+0x7c/0x25c > print_report+0x70/0x8c > kasan_report+0xdc/0x13c > __hwasan_store8_noabort+0xe8/0xf8 > vfree_atomic+0x90/0x150 > vfree+0x220/0x298 > thread_stack_free_rcu+0x3c/0x4c > rcu_do_batch+0x308/0xaf0 > rcu_nocb_cb_kthread+0x33c/0x708 > kthread+0x364/0x3cc > ret_from_fork+0x10/0x20 > > The buggy address belongs to a 8-page vmalloc region starting at > 0xc2ffffc0a8f70000 allocated at copy_process+0x1ac/0x12e4 > > Memory state around the buggy address: > ffffffc0a8f6ff00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe > >ffffffc0a8f70000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff > ^ > ffffffc0a8f70100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff > ================================================================== Were you able to reproduce this more than once? Does this relate to CONFIG_KASAN_STACK? Can you share the kernel config? -- Cheers, David
On 2026-08-07 9:27 UTC, David wrote: > Were you able to reproduce this more than once? Still trying to reproduce it. So far it has only occurred this once. > Does this relate to CONFIG_KASAN_STACK? I'm not entirely sure if it's related. The configs relevant to this path are: CONFIG_KASAN_SHADOW_OFFSET=0xefffffc000000000 CONFIG_HAVE_ARCH_KASAN=y CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y CONFIG_HAVE_ARCH_KASAN_HW_TAGS=y CONFIG_HAVE_ARCH_KASAN_VMALLOC=y CONFIG_CC_HAS_KASAN_GENERIC=y CONFIG_CC_HAS_KASAN_SW_TAGS=y CONFIG_KASAN=y CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y # CONFIG_KASAN_GENERIC is not set CONFIG_KASAN_SW_TAGS=y # CONFIG_KASAN_HW_TAGS is not set CONFIG_KASAN_OUTLINE=y # CONFIG_KASAN_INLINE is not set CONFIG_KASAN_STACK=y CONFIG_KASAN_VMALLOC=y # CONFIG_KASAN_KUNIT_TEST is not set # CONFIG_KASAN_EXTRA_INFO is not set > Can you share the kernel config? I'll send it to you separately later. Thanks, Shaobo
On 8/7/26 12:41, sparkhuang wrote: > On 2026-08-07 9:27 UTC, David wrote: >> Were you able to reproduce this more than once? > > Still trying to reproduce it. So far it has only occurred this once. Oh, and if it is about CONFIG_KASAN_STACK, I think you need a very deep stack to set all the tags -- IIUC. Maybe that's what would trigger it easier. -- Cheers, David
On 8/7/26 12:41, sparkhuang wrote: > On 2026-08-07 9:27 UTC, David wrote: >> Were you able to reproduce this more than once? > > Still trying to reproduce it. So far it has only occurred this once. > >> Does this relate to CONFIG_KASAN_STACK? > > I'm not entirely sure if it's related. > The configs relevant to this path are: > CONFIG_KASAN_SHADOW_OFFSET=0xefffffc000000000 > CONFIG_HAVE_ARCH_KASAN=y > CONFIG_HAVE_ARCH_KASAN_SW_TAGS=y > CONFIG_HAVE_ARCH_KASAN_HW_TAGS=y > CONFIG_HAVE_ARCH_KASAN_VMALLOC=y > CONFIG_CC_HAS_KASAN_GENERIC=y > CONFIG_CC_HAS_KASAN_SW_TAGS=y > CONFIG_KASAN=y > CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX=y > # CONFIG_KASAN_GENERIC is not set > CONFIG_KASAN_SW_TAGS=y > # CONFIG_KASAN_HW_TAGS is not set > CONFIG_KASAN_OUTLINE=y > # CONFIG_KASAN_INLINE is not set > CONFIG_KASAN_STACK=y The KASAN_STACK might be the relevant bit. We will get the stack retagged, so when we free the memory, the stored tag and the memory tag will differ. At least that's my understanding from a quick peek :) -- Cheers, David
Hi,
This looks AI-generated but I see no Assisted-by tag, please follow kernel
procedure on this please.
https://docs.kernel.org/process/coding-assistants.html
None of your 4 patches pre-dating the slopularity look anything like this.
Also please use a real name.
On Thu, Aug 06, 2026 at 08:30:20PM +0800, sparkhuang wrote:
> When a thread stack is freed via RCU callback,
> thread_stack_free_rcu() calls vfree(vm_area->addr). In RCU callback
> context (e.g. from rcu_nocb_cb_kthread with BH disabled, or from
> RCU_SOFTIRQ), in_interrupt() returns true, so vfree() routes to
> vfree_atomic().
>
> vfree_atomic() uses the freed memory as llist storage by calling
> llist_add((struct llist_node *)addr, &p->list), which writes 8 bytes
> to the address being freed. With KASAN SW_TAGS enabled,
> vm_area->addr carries a random tag assigned during allocation by
> kasan_unpoison_vmalloc(). If the shadow memory covering this region
> has been set to KASAN_TAG_KERNEL (0xFF) — e.g. by
> kasan_unpoison_task_stack() using task->stack, which was already
> reset to 0xFF by kasan_reset_tag() at allocation time — the shadow
> byte (0xFF) no longer matches the pointer tag on vm_area->addr,
> and the write in llist_add triggers a KASAN invalid-access report:
>
> ==================================================================
> BUG: KASAN: invalid-access in vfree_atomic+0x90/0x150
> Write of size 8 at addr c2ffffc0a8f70000 by task rcuop/7/75
> Pointer tag: [c2], memory tag: [ff]
>
> CPU: 5 UID: 0 PID: 75 Comm: rcuop/7 Tainted: G S W OE
> Hardware name: XiaoMi Xring_o1 UDP PHONE (DT)
> Call trace:
> show_stack+0x18/0x28
> __dump_stack+0x28/0x3c
> dump_stack_lvl+0xac/0xf0
> print_address_description+0x7c/0x25c
> print_report+0x70/0x8c
> kasan_report+0xdc/0x13c
> __hwasan_store8_noabort+0xe8/0xf8
> vfree_atomic+0x90/0x150
> vfree+0x220/0x298
> thread_stack_free_rcu+0x3c/0x4c
> rcu_do_batch+0x308/0xaf0
> rcu_nocb_cb_kthread+0x33c/0x708
> kthread+0x364/0x3cc
> ret_from_fork+0x10/0x20
>
> The buggy address belongs to a 8-page vmalloc region starting at
> 0xc2ffffc0a8f70000 allocated at copy_process+0x1ac/0x12e4
>
> Memory state around the buggy address:
> ffffffc0a8f6ff00: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
> >ffffffc0a8f70000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
> ^
> ffffffc0a8f70100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
> ==================================================================
>
> The tag on tsk->stack was already reset to KASAN_TAG_KERNEL (0xFF) by
> commit c08e6a1206e6 ("kasan, fork: reset pointer tags of vmapped
> stacks"), but vm_area->addr still carries the original random tag.
> This is the same class of bug that was fixed for shadow call stacks
> in scs_free() by commit 528a4ab45300 ("scs: Release kasan vmalloc
> poison in scs_free process").
>
> Fix it by resetting the pointer tag before calling vfree(), so that
> vfree_atomic()'s llist_add write uses KASAN_TAG_KERNEL (0xFF), which
> makes KASAN bypass tag checks for that write in all modes (SW_TAGS,
> HW_TAGS, and Generic).
>
> kasan_unpoison_vmalloc() is not needed alongside kasan_reset_tag():
> in HW_TAGS, __kasan_unpoison_vmalloc() is a no-op without
> KASAN_VMALLOC_VM_ALLOC (only KASAN_VMALLOC_PROT_NORMAL is passed);
> in SW_TAGS and Generic, the 0xFF-tagged pointer already bypasses
> shadow checks, so the shadow state is irrelevant.
Output from a repro that you don't share, brilliant.
>
> Fixes: 0f110a9b956c ("kernel/fork: use vfree_atomic() to free thread stack")
A 2016 Fixes for some KASAN state bug? Really?
> Cc: stable@vger.kernel.org
And of course Cc: stable...
> Signed-off-by: sparkhuang <huangshaobo3@xiaomi.com>
> ---
> kernel/fork.c | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/fork.c b/kernel/fork.c
> index f0e2e131a..2fd6fd25c 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -280,7 +280,19 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
> if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
> return;
>
> - vfree(vm_area->addr);
> + /*
> + * Reset the pointer tag before vfree(): in RCU callback context
> + * vfree() routes to vfree_atomic(), which writes to the freed
> + * memory as llist storage. Resetting the tag to KASAN_TAG_KERNEL
> + * (0xFF) makes KASAN bypass tag checks for that write in all modes
> + * (HW_TAGS, SW_TAGS, Generic), avoiding a false tag-mismatch report.
> + *
> + * kasan_unpoison_vmalloc() is not needed here: in HW_TAGS it is a
> + * no-op without KASAN_VMALLOC_VM_ALLOC, and in SW_TAGS/Generic the
> + * 0xFF pointer already bypasses shadow checks. This mirrors the
> + * intent of the fix in scs_free() (commit 528a4ab45300).
> + */
Nobody in their right mind does a comment like this for a kasan_reset_tag().
> + vfree(kasan_reset_tag(vm_area->addr));
> }
>
> static void thread_stack_delayed_free(struct task_struct *tsk)
> --
> 2.34.1
>
--
Cheers, Lorenzo
Hi Lorenzo,
Thanks for the review. You're right on the process points — I'll fold
those into a v2 (real name, Assisted-by: tag, drop Cc: stable, trim the
comment, correct the Fixes: target). I won't re-spin it just yet though,
for the reason below.
I want to be upfront about where the analysis actually stands, because
I'd rather not push a fix whose rationale I can't fully back up.
The one-line change itself is sound and follows existing precedent:
c08e6a1206e6 already resets tsk->stack to 0xFF, and 528a4ab45300 does
the same in scs_free(). A 0xFF-tagged pointer bypasses the KASAN tag
check, so vfree_atomic()'s llist_add write to the freed region stops
faulting regardless of what the memory tag currently holds. That makes
the reported false positive go away — that part I'm confident in.
What I have *not* nailed down is why "memory tag: [ff]" shows up in the
report in the first place. On the 6.18 android-common tree:
- At allocation __kasan_unpoison_vmalloc() tags vm_area->addr with a
random tag (0xc2 here) and sets the backing memory tag to the same
0xc2, so they match at that point — the report is not from the
alloc path.
- c08e6a1206e6 resets tsk->stack to 0xFF (SP can't be tagged); its
commit message explicitly notes the stack *memory* still gets tagged.
- The only caller of kasan_unpoison_task_stack() — which would write a
0xFF tag into the backing memory — is the idle-task path in
kernel/cpu.c. It is not called for normal task stacks, so my earlier
commit-message attribution to that function was wrong.
- page_kasan_tag_reset() only touches page->flags metadata, not the
physical memory tag, so that's not the source either.
So what flips the memory tag from 0xc2 to 0xFF between allocation and
the RCU callback remains an open question. My suspicion is the async
vfree_atomic / delayed_vfree + page-reuse interplay during the reboot
SIGTERM storm (the trace also shows page flags kasantag=0x55, a third
value inconsistent with both), but I haven't proven a specific path.
Accordingly I don't have a deterministic reproducer. The report is
reproducible only in the sense that it shows up during reboot with heavy
thread churn; I haven't been able to trigger it on demand.
Given that, I'd like to hold v2 until the 0xFF source is understood
rather than ship a commit message that hand-waves the root cause. If you
or the KASAN folks have a view on whether the reset_tag fix is acceptable
as a "stops the false positive, mirrors existing precedent" change
without a fully root-caused explanation — versus waiting — I'd
appreciate the steer.
Best,
Shaobo
I'm sorry that whole reply is more AI slop and I'm not reading it. I have zero interest in chatting to an LLM via a middle man. On Fri, Aug 07, 2026 at 02:53:51PM +0800, sparkhuang wrote: > Hi Lorenzo, > > Thanks for the review. You're right on the process points ??? I'll fold > those into a v2 (real name, Assisted-by: tag, drop Cc: stable, trim the > comment, correct the Fixes: target). I won't re-spin it just yet though, > for the reason below. > > I want to be upfront about where the analysis actually stands, because > I'd rather not push a fix whose rationale I can't fully back up. > > The one-line change itself is sound and follows existing precedent: > c08e6a1206e6 already resets tsk->stack to 0xFF, and 528a4ab45300 does > the same in scs_free(). A 0xFF-tagged pointer bypasses the KASAN tag > check, so vfree_atomic()'s llist_add write to the freed region stops > faulting regardless of what the memory tag currently holds. That makes > the reported false positive go away ??? that part I'm confident in. > > What I have *not* nailed down is why "memory tag: [ff]" shows up in the > report in the first place. On the 6.18 android-common tree: > > - At allocation __kasan_unpoison_vmalloc() tags vm_area->addr with a > random tag (0xc2 here) and sets the backing memory tag to the same > 0xc2, so they match at that point ??? the report is not from the > alloc path. > - c08e6a1206e6 resets tsk->stack to 0xFF (SP can't be tagged); its > commit message explicitly notes the stack *memory* still gets tagged. > - The only caller of kasan_unpoison_task_stack() ??? which would write a > 0xFF tag into the backing memory ??? is the idle-task path in > kernel/cpu.c. It is not called for normal task stacks, so my earlier > commit-message attribution to that function was wrong. > - page_kasan_tag_reset() only touches page->flags metadata, not the > physical memory tag, so that's not the source either. > > So what flips the memory tag from 0xc2 to 0xFF between allocation and > the RCU callback remains an open question. My suspicion is the async > vfree_atomic / delayed_vfree + page-reuse interplay during the reboot > SIGTERM storm (the trace also shows page flags kasantag=0x55, a third > value inconsistent with both), but I haven't proven a specific path. > > Accordingly I don't have a deterministic reproducer. The report is > reproducible only in the sense that it shows up during reboot with heavy > thread churn; I haven't been able to trigger it on demand. > > Given that, I'd like to hold v2 until the 0xFF source is understood > rather than ship a commit message that hand-waves the root cause. If you > or the KASAN folks have a view on whether the reset_tag fix is acceptable > as a "stops the false positive, mirrors existing precedent" change > without a fully root-caused explanation ??? versus waiting ??? I'd > appreciate the steer. > > Best, > Shaobo -- Cheers, Lorenzo
On 8/7/26 09:56, Lorenzo Stoakes (ARM) wrote: > I'm sorry that whole reply is more AI slop and I'm not reading it. > > I have zero interest in chatting to an LLM via a middle man. I think we should start charging a workslop fee of, say 100$ per patch? :) -- Cheers, David
On Fri, Aug 07, 2026 at 09:57:30AM +0200, David Hildenbrand (Arm) wrote: > On 8/7/26 09:56, Lorenzo Stoakes (ARM) wrote: > > I'm sorry that whole reply is more AI slop and I'm not reading it. > > > > I have zero interest in chatting to an LLM via a middle man. > > I think we should start charging a workslop fee of, say 100$ per patch? :) I don't get out of bed for less than $1,000 ;) > > -- > Cheers, > > David -- Cheers, Lorenzo
On 8/7/26 09:58, Lorenzo Stoakes (ARM) wrote: > On Fri, Aug 07, 2026 at 09:57:30AM +0200, David Hildenbrand (Arm) wrote: >> On 8/7/26 09:56, Lorenzo Stoakes (ARM) wrote: >>> I'm sorry that whole reply is more AI slop and I'm not reading it. >>> >>> I have zero interest in chatting to an LLM via a middle man. >> >> I think we should start charging a workslop fee of, say 100$ per patch? :) > > I don't get out of bed for less than $1,000 ;) Oh, sloppers are everywhere, we're going to be rich! :) -- Cheers, David
thread_stack_free_rcu() frees the vmalloc'd thread stack via
vfree(vm_area->addr). In RCU callback context, vfree() routes to
vfree_atomic(), which calls llist_add((struct llist_node *)addr, ...)
and writes 8 bytes to the base of the region being freed.
With KASAN_SW_TAGS, vm_area->addr carries a random tag. If
kasan_unpoison_task_stack_below() has rewritten the shadow covering
[base, sp] to KASAN_TAG_KERNEL (0xff) -- which it does on every CPU
resume for the current task's stack -- the llist_add store checks
shadow[base] (0xff) against the pointer tag (random) and reports an
invalid-access, although writing to the base of a stack queued for
deferred free is legitimate.
Reset the pointer tag to KASAN_TAG_KERNEL before vfree() so that
kasan_check_range() short-circuits the check, the same way the task
accesses its own stack at runtime via sp. The vmalloc lookup is safe:
__find_vmap_area() resets the tag before comparing against va_start.
Fixes: 9f7d416c3612 ("kprobes: Unpoison stack in jprobe_return() for KASAN")
Cc: stable@vger.kernel.org
Assisted-by: zhipuai:glm-5.2
Signed-off-by: Shaobo Huang <huangshaobo3@xiaomi.com>
---
Changes since v1:
- Drop the 12-line comment; keep just the one-line fix.
- Fix the Fixes: tag to point to the commit that introduced
kasan_unpoison_task_stack_below (9f7d416c3612), which added
both the function definition and the _cpu_resume call site, not
the 2016 vfree_atomic commit.
- Add Assisted-by tag per Documentation/process/coding-assistants.rst.
- Use real name (Shaobo Huang) instead of "sparkhuang".
- Trim the commit message; remove the full KASAN dump.
v1:
https://lore.kernel.org/all/20260806123020.90869-1-huangshaobo3@xiaomi.com/
---
kernel/fork.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 45300f59cf2c..9a66b10749de 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -238,7 +238,7 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
return;
- vfree(vm_area->addr);
+ vfree(kasan_reset_tag(vm_area->addr));
}
static void thread_stack_delayed_free(struct task_struct *tsk)
--
2.34.1
Shaobo Huang <huangshaobo3@xiaomi.com> writes:
> thread_stack_free_rcu() frees the vmalloc'd thread stack via
> vfree(vm_area->addr). In RCU callback context, vfree() routes to
> vfree_atomic(), which calls llist_add((struct llist_node *)addr, ...)
> and writes 8 bytes to the base of the region being freed.
>
> With KASAN_SW_TAGS, vm_area->addr carries a random tag. If
> kasan_unpoison_task_stack_below() has rewritten the shadow covering
> [base, sp] to KASAN_TAG_KERNEL (0xff) -- which it does on every CPU
> resume for the current task's stack -- the llist_add store checks
> shadow[base] (0xff) against the pointer tag (random) and reports an
> invalid-access, although writing to the base of a stack queued for
> deferred free is legitimate.
>
> Reset the pointer tag to KASAN_TAG_KERNEL before vfree() so that
> kasan_check_range() short-circuits the check, the same way the task
> accesses its own stack at runtime via sp. The vmalloc lookup is safe:
> __find_vmap_area() resets the tag before comparing against va_start.
>
> Fixes: 9f7d416c3612 ("kprobes: Unpoison stack in jprobe_return() for KASAN")
This commit merely renamed the resume helper without changing what it
unpoisons. Only generic KASAN existed back then, so it cannot have
introduced a tagged-mode problem.
The problem became visible with
449e0b4ed5a1 ("fork: clean-up naming of vm_stack/vm_struct variables
in vmap stacks code")
which changed thread_stack_free_rcu() from vfree(vm_stack) to
vfree(vm_area->addr). vm_stack is derived from tsk->stack, whose tag
alloc_thread_stack_node() resets to 0xff. Since that commit, vfree_atomic()
gets the tagged vm_area->addr instead.
> Cc: stable@vger.kernel.org
> Assisted-by: zhipuai:glm-5.2
> Signed-off-by: Shaobo Huang <huangshaobo3@xiaomi.com>
> ---
> Changes since v1:
> - Drop the 12-line comment; keep just the one-line fix.
> - Fix the Fixes: tag to point to the commit that introduced
> kasan_unpoison_task_stack_below (9f7d416c3612), which added
> both the function definition and the _cpu_resume call site, not
> the 2016 vfree_atomic commit.
> - Add Assisted-by tag per Documentation/process/coding-assistants.rst.
> - Use real name (Shaobo Huang) instead of "sparkhuang".
> - Trim the commit message; remove the full KASAN dump.
> v1:
> https://lore.kernel.org/all/20260806123020.90869-1-huangshaobo3@xiaomi.com/
> ---
> kernel/fork.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 45300f59cf2c..9a66b10749de 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -238,7 +238,7 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
> if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
> return;
>
> - vfree(vm_area->addr);
> + vfree(kasan_reset_tag(vm_area->addr));
Thanks for report the problem and the patch. NAK on the fix though.
It addresses the symptom rather than the cause.
The problem is not that vfree() is called with a tagged pointer. The
problem is the 0xff written into the shadow by
kasan_unpoison_task_stack_below().
That helper is only needed in generic mode. There the compiler expects
the stack shadow to be zero, so the redzones left behind by discarded
frames must be cleared before the stack is reused.
The tagged mode has no such requirement. The compiler fully initializes
the shadow of stack variables on function entry, so stale tags left
below the watermark are harmless.
kasan_unpoison_task_stack_below() should simply be a no-op in tagged
mode. I'll send a patch.
+cc Ulad for vmalloc stuff.
Please don't send a v2 in-reply-to a v1 or any other email.
Send the patch entirely separately.
I really need to write a bot to say this :)...
On Mon, Sep 14, 2026 at 05:33:00PM +0800, Shaobo Huang wrote:
> thread_stack_free_rcu() frees the vmalloc'd thread stack via
> vfree(vm_area->addr). In RCU callback context, vfree() routes to
> vfree_atomic(), which calls llist_add((struct llist_node *)addr, ...)
> and writes 8 bytes to the base of the region being freed.
>
> With KASAN_SW_TAGS, vm_area->addr carries a random tag. If
> kasan_unpoison_task_stack_below() has rewritten the shadow covering
> [base, sp] to KASAN_TAG_KERNEL (0xff) -- which it does on every CPU
> resume for the current task's stack -- the llist_add store checks
> shadow[base] (0xff) against the pointer tag (random) and reports an
> invalid-access, although writing to the base of a stack queued for
> deferred free is legitimate.
>
> Reset the pointer tag to KASAN_TAG_KERNEL before vfree() so that
> kasan_check_range() short-circuits the check, the same way the task
> accesses its own stack at runtime via sp. The vmalloc lookup is safe:
> __find_vmap_area() resets the tag before comparing against va_start.
>
> Fixes: 9f7d416c3612 ("kprobes: Unpoison stack in jprobe_return() for KASAN")
> Cc: stable@vger.kernel.org
> Assisted-by: zhipuai:glm-5.2
Thanks for adding this!
New convention is to say:
Assisted-by: LLM
Rather than to list the agent.
See https://docs.kernel.org/process/coding-assistants.html
> Signed-off-by: Shaobo Huang <huangshaobo3@xiaomi.com>
Looks reasonable to me so, with nits addressed:
Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
But I would like Ulad's input on this from vmalloc side.
> ---
> Changes since v1:
Thanks for adding this!
Nit, but nicer to say 'v2:' here I think.
> - Drop the 12-line comment; keep just the one-line fix.
> - Fix the Fixes: tag to point to the commit that introduced
> kasan_unpoison_task_stack_below (9f7d416c3612), which added
> both the function definition and the _cpu_resume call site, not
> the 2016 vfree_atomic commit.
> - Add Assisted-by tag per Documentation/process/coding-assistants.rst.
> - Use real name (Shaobo Huang) instead of "sparkhuang".
> - Trim the commit message; remove the full KASAN dump.
Please give credit to reviewers for each change, e.g. 'as per XXX' :)
> v1:
> https://lore.kernel.org/all/20260806123020.90869-1-huangshaobo3@xiaomi.com/
> ---
> kernel/fork.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 45300f59cf2c..9a66b10749de 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -238,7 +238,7 @@ static void thread_stack_free_rcu(struct rcu_head *rh)
> if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
> return;
>
> - vfree(vm_area->addr);
> + vfree(kasan_reset_tag(vm_area->addr));
> }
>
> static void thread_stack_delayed_free(struct task_struct *tsk)
> --
> 2.34.1
>
--
Cheers, Lorenzo
On Mon, Sep 14, 2026 at 10:44:26AM +0100, Lorenzo Stoakes (ARM) wrote:
> +cc Ulad for vmalloc stuff.
>
> Please don't send a v2 in-reply-to a v1 or any other email.
>
> Send the patch entirely separately.
>
> I really need to write a bot to say this :)...
>
> On Mon, Sep 14, 2026 at 05:33:00PM +0800, Shaobo Huang wrote:
> > thread_stack_free_rcu() frees the vmalloc'd thread stack via
> > vfree(vm_area->addr). In RCU callback context, vfree() routes to
> > vfree_atomic(), which calls llist_add((struct llist_node *)addr, ...)
> > and writes 8 bytes to the base of the region being freed.
> >
> > With KASAN_SW_TAGS, vm_area->addr carries a random tag. If
> > kasan_unpoison_task_stack_below() has rewritten the shadow covering
> > [base, sp] to KASAN_TAG_KERNEL (0xff) -- which it does on every CPU
> > resume for the current task's stack -- the llist_add store checks
> > shadow[base] (0xff) against the pointer tag (random) and reports an
> > invalid-access, although writing to the base of a stack queued for
> > deferred free is legitimate.
> >
> > Reset the pointer tag to KASAN_TAG_KERNEL before vfree() so that
> > kasan_check_range() short-circuits the check, the same way the task
> > accesses its own stack at runtime via sp. The vmalloc lookup is safe:
> > __find_vmap_area() resets the tag before comparing against va_start.
> >
> > Fixes: 9f7d416c3612 ("kprobes: Unpoison stack in jprobe_return() for KASAN")
> > Cc: stable@vger.kernel.org
> > Assisted-by: zhipuai:glm-5.2
>
> Thanks for adding this!
>
> New convention is to say:
>
> Assisted-by: LLM
>
> Rather than to list the agent.
>
> See https://docs.kernel.org/process/coding-assistants.html
>
> > Signed-off-by: Shaobo Huang <huangshaobo3@xiaomi.com>
>
> Looks reasonable to me so, with nits addressed:
>
> Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
>
> But I would like Ulad's input on this from vmalloc side.
>
Makes sense to me even though we do it in the __find_vmap_area()
but we also use an "addr" in other paths.
LGTM:
Reviewed-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
--
Uladzislau Rezki
© 2016 - 2026 Red Hat, Inc.