Re: [PATCH v2] mm: avoid KCSAN false positive in memdesc_nid()

From: Hui Zhu

Date: Wed Jun 24 2026 - 21:33:08 EST


>
> On Tue, 23 Jun 2026 16:44:32 +0800 Hui Zhu <hui.zhu@xxxxxxxxx> wrote:
>
> >
> > From: Hui Zhu <zhuhui@xxxxxxxxxx>
> >
> > KCSAN reports a data race between page_to_nid()/folio_nid() reading
> > page->flags and folio_trylock()/folio_lock() concurrently doing
> > test_and_set_bit_lock(PG_locked, ...) on the same word, e.g.:
> >
> > BUG: KCSAN: data-race in __lruvec_stat_mod_folio / shmem_get_folio_gfp
> >
> > The node id occupies a fixed bit-range of page->flags that is set
> > once at page init and never modified afterwards, so it can never
> > overlap with the low PG_locked/PG_waiters bits touched by the folio
> > lock path.
> >
> > Use ASSERT_EXCLUSIVE_BITS() in memdesc_nid() to scope the exemption
> > to just the node-id bits, consistent with how memdesc_zonenum()
> > already handles the same class of race for the zone-id bits.
> >
> > ...
> >
> > --- a/include/linux/mm.h
> > +++ b/include/linux/mm.h
> > @@ -2290,6 +2290,7 @@ int memdesc_nid(memdesc_flags_t mdf);
> > #else
> > static inline int memdesc_nid(memdesc_flags_t mdf)
> > {
> > + ASSERT_EXCLUSIVE_BITS(mdf.f, NODES_MASK << NODES_PGSHIFT);
> > return (mdf.f >> NODES_PGSHIFT) & NODES_MASK;
> > }
> > #endif
> >
> It seems weird to be doing this against a local variable within a
> random function, seemingly unrelated to the problematic functions which
> you've identified.
>
> Seems that it fooled Sashiko:
> https://sashiko.dev/#/patchset/20260623084432.701120-1-hui.zhu@xxxxxxxxx
>
> I'm wondering what the heck is going on here?
>

Hi Andrew,

Good catch. ASSERT_EXCLUSIVE_BITS(mdf.f, ...) is checking a by-value
copy of the flags word inside memdesc_nid(), not the actual shared
page->flags/folio->flags being modified by folio_trylock(). Whatever
made it appear to suppress the KCSAN report is likely an artifact of
inlining/codegen (kcsan_atomic_next() happening to land on the real
load after inlining), not a principled fix - so Sashiko's pass is
not reassuring here.

I'll move the assertion to where the real dereference happens (at
the page_to_nid()/folio_nid() call sites) instead of inside the
by-value helper. This probably also applies to the existing
memdesc_zonenum() pattern - is that one actually verified to work,
or does it have the same issue?

Best,
Hui