Re: kcsan -Wmaybe-uninitialized warning in ntfs3
Marco Elver <[email protected]> Tue, 21 Apr 2026 11:33:05 +0200
| Newsgroups | dev.linux.lists.ntfs3,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CANpmjNP8aUnZxO6e6ww7Q-8YiJLzww2YAy6Qi9dH5npHtfxSSQ@mail.gmail.com> |
On Tue, 21 Apr 2026 at 09:54, Arnd Bergmann <[email protected]> wrote: > > ntfs3 started enabling -Wmaybe-uninitialized, a warning that is generally > useful but is normally disabled in the kernel because of too many > false positives when used with sanitizers and some other gcc options. > > I ran into one such warning in today's linux-next: > > CC fs/ntfs3/file.o > In file included from /home/arnd/arm-soc/include/asm-generic/rwonce.h:27, > from /home/arnd/arm-soc/arch/arm64/include/asm/rwonce.h:81, > from /home/arnd/arm-soc/include/linux/compiler.h:369, > from /home/arnd/arm-soc/include/linux/array_size.h:5, > from /home/arnd/arm-soc/include/linux/kernel.h:16, > from /home/arnd/arm-soc/include/linux/backing-dev.h:12, > from /home/arnd/arm-soc/fs/ntfs3/file.c:10: > In function 'instrument_copy_from_user_before', > inlined from '_inline_copy_from_user' at /home/arnd/arm-soc/include/linux/uaccess.h:184:2, > inlined from 'copy_from_user' at /home/arnd/arm-soc/include/linux/uaccess.h:221:9, > inlined from 'ntfs_ioctl_fitrim' at /home/arnd/arm-soc/fs/ntfs3/file.c:77:6, > inlined from 'ntfs_ioctl' at /home/arnd/arm-soc/fs/ntfs3/file.c:164:10: > /home/arnd/arm-soc/include/linux/kcsan-checks.h:220:28: error: 'range' may be used uninitialized [-Werror=maybe-uninitialized] > 220 | #define kcsan_check_access __kcsan_check_access > | ^ > /home/arnd/arm-soc/include/linux/kcsan-checks.h:311:9: note: in expansion of macro 'kcsan_check_access' > 311 | kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE) > | ^~~~~~~~~~~~~~~~~~ > /home/arnd/arm-soc/include/linux/instrumented.h:147:9: note: in expansion of macro 'kcsan_check_write' > 147 | kcsan_check_write(to, n); > | ^~~~~~~~~~~~~~~~~ > /home/arnd/arm-soc/include/linux/kcsan-checks.h: In function 'ntfs_ioctl': > /home/arnd/arm-soc/include/linux/kcsan-checks.h:37:6: note: by argument 1 of type 'const volatile void *' to '__kcsan_check_access' declared here > 37 | void __kcsan_check_access(const volatile void *ptr, size_t size, int type); > | ^~~~~~~~~~~~~~~~~~~~ > /home/arnd/arm-soc/fs/ntfs3/file.c:65:29: note: 'range' declared here > 65 | struct fstrim_range range; > | ^~~~~ > > What happens here is that copy_from_user() is used in a very > normal way to copy a user space structure into a previously > uninitialized on-stack buffer. With KCSAN enabled, this passes > the pointer to that buffer into __kcsan_check_access(), which > takes a 'const volatile void *' pointer. Gcc correctly notices > that there is no way to access the data behind this pointer > when it hasn't been initialized yet, as both read and write > would cause undefined behavior. > > I'm not sure what a good solution would be to avoid this, > but I assume this should be fixed in the kcsan instrumentation. > I tried changing that code to pass non-const pointers for > any instrument_write variant, which does avoid the warning, > but also adds a bit of complexity, see below. > > Any other ideas? Indeed, the below is rather complex, and seems unnecessary to me just to suppress this warning which not even the whole kernel enables. How can I reproduce this? GCC version? .config? I can't seem to with an x86 defconfig + CONFIG_NTFS3_FS + KCSAN config + GCC on linux-next (20260420). There's likely a simpler option (attribute or warning suppression around these functions) I want to try. > Arnd > > diff --git a/include/linux/instrumented.h b/include/linux/instrumented.h > index a1b4cf81adc2..d28aa6b0aeb8 100644 > --- a/include/linux/instrumented.h > +++ b/include/linux/instrumented.h > @@ -36,7 +36,7 @@ static __always_inline void instrument_read(const volatile void *v, size_t size) > * Instrument a regular write access. The instrumentation should be inserted > * before the actual write happens. > */ > -static __always_inline void instrument_write(const volatile void *v, size_t size) > +static __always_inline void instrument_write(volatile void *v, size_t size) > { > kasan_check_write(v, size); > kcsan_check_write(v, size); > @@ -50,7 +50,7 @@ static __always_inline void instrument_write(const volatile void *v, size_t size > * Instrument a regular write access. The instrumentation should be inserted > * before the actual write happens. > */ > -static __always_inline void instrument_read_write(const volatile void *v, size_t size) > +static __always_inline void instrument_read_write(volatile void *v, size_t size) > { > kasan_check_write(v, size); > kcsan_check_read_write(v, size); > @@ -92,7 +92,7 @@ static __always_inline void instrument_atomic_read(const volatile void *v, size_ > * Instrument an atomic write access. The instrumentation should be inserted > * before the actual write happens. > */ > -static __always_inline void instrument_atomic_write(const volatile void *v, size_t size) > +static __always_inline void instrument_atomic_write(volatile void *v, size_t size) > { > kasan_check_write(v, size); > kcsan_check_atomic_write(v, size); > @@ -107,7 +107,7 @@ static __always_inline void instrument_atomic_write(const volatile void *v, size > * Instrument an atomic read-write access. The instrumentation should be > * inserted before the actual write happens. > */ > -static __always_inline void instrument_atomic_read_write(const volatile void *v, size_t size) > +static __always_inline void instrument_atomic_read_write(volatile void *v, size_t size) > { > kasan_check_write(v, size); > kcsan_check_atomic_read_write(v, size); > @@ -141,7 +141,7 @@ instrument_copy_to_user(void __user *to, const void *from, unsigned long n) > * variants). The instrumentation should be inserted before the accesses. > */ > static __always_inline void > -instrument_copy_from_user_before(const void *to, const void __user *from, unsigned long n) > +instrument_copy_from_user_before(void *to, const void __user *from, unsigned long n) > { > kasan_check_write(to, n); > kcsan_check_write(to, n); > diff --git a/include/linux/kcsan-checks.h b/include/linux/kcsan-checks.h > index 92f3843d9ebb..e598fd9679a9 100644 > --- a/include/linux/kcsan-checks.h > +++ b/include/linux/kcsan-checks.h > @@ -34,7 +34,8 @@ > * @size: size of access > * @type: access type modifier > */ > -void __kcsan_check_access(const volatile void *ptr, size_t size, int type); > +void __kcsan_check_read_access(const volatile void *ptr, size_t size, int type); > +void __kcsan_check_write_access(volatile void *ptr, size_t size, int type); > > /* > * See definition of __tsan_atomic_signal_fence() in kernel/kcsan/core.c. > @@ -186,8 +187,8 @@ void kcsan_end_scoped_access(struct kcsan_scoped_access *sa); > > #else /* CONFIG_KCSAN */ > > -static inline void __kcsan_check_access(const volatile void *ptr, size_t size, > - int type) { } > +static inline void __kcsan_check_read_access(const volatile void *ptr, size_t size, int type) { } > +static inline void __kcsan_check_write_access(volatile void *ptr, size_t size, int type) { } > > static inline void __kcsan_mb(void) { } > static inline void __kcsan_wmb(void) { } > @@ -217,7 +218,8 @@ static inline void kcsan_end_scoped_access(struct kcsan_scoped_access *sa) { } > * Only calls into the runtime when the particular compilation unit has KCSAN > * instrumentation enabled. May be used in header files. > */ > -#define kcsan_check_access __kcsan_check_access > +#define kcsan_check_write_access __kcsan_check_write_access > +#define kcsan_check_read_access __kcsan_check_read_access > > /* > * Only use these to disable KCSAN for accesses in the current compilation unit; > @@ -226,8 +228,8 @@ static inline void kcsan_end_scoped_access(struct kcsan_scoped_access *sa) { } > #define __kcsan_disable_current kcsan_disable_current > #define __kcsan_enable_current kcsan_enable_current_nowarn > #else /* __SANITIZE_THREAD__ */ > -static inline void kcsan_check_access(const volatile void *ptr, size_t size, > - int type) { } > +static inline void kcsan_check_read_access(const volatile void *ptr, size_t size, int type) { } > +static inline void kcsan_check_write_access(volatile void *ptr, size_t size, int type) { } > static inline void __kcsan_enable_current(void) { } > static inline void __kcsan_disable_current(void) { } > #endif /* __SANITIZE_THREAD__ */ > @@ -273,7 +275,7 @@ static inline void __kcsan_disable_current(void) { } > * @ptr: address of access > * @size: size of access > */ > -#define __kcsan_check_read(ptr, size) __kcsan_check_access(ptr, size, 0) > +#define __kcsan_check_read(ptr, size) __kcsan_check_read_access(ptr, size, 0) > > /** > * __kcsan_check_write - check regular write access for races > @@ -282,7 +284,7 @@ static inline void __kcsan_disable_current(void) { } > * @size: size of access > */ > #define __kcsan_check_write(ptr, size) \ > - __kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE) > + __kcsan_check_write_access(ptr, size, KCSAN_ACCESS_WRITE) > > /** > * __kcsan_check_read_write - check regular read-write access for races > @@ -291,7 +293,7 @@ static inline void __kcsan_disable_current(void) { } > * @size: size of access > */ > #define __kcsan_check_read_write(ptr, size) \ > - __kcsan_check_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE) > + __kcsan_check_read_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE) > > /** > * kcsan_check_read - check regular read access for races > @@ -299,7 +301,7 @@ static inline void __kcsan_disable_current(void) { } > * @ptr: address of access > * @size: size of access > */ > -#define kcsan_check_read(ptr, size) kcsan_check_access(ptr, size, 0) > +#define kcsan_check_read(ptr, size) kcsan_check_read_access(ptr, size, 0) > > /** > * kcsan_check_write - check regular write access for races > @@ -308,7 +310,7 @@ static inline void __kcsan_disable_current(void) { } > * @size: size of access > */ > #define kcsan_check_write(ptr, size) \ > - kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE) > + kcsan_check_write_access(ptr, size, KCSAN_ACCESS_WRITE) > > /** > * kcsan_check_read_write - check regular read-write access for races > @@ -317,7 +319,7 @@ static inline void __kcsan_disable_current(void) { } > * @size: size of access > */ > #define kcsan_check_read_write(ptr, size) \ > - kcsan_check_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE) > + kcsan_check_write_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE) > > /* > * Check for atomic accesses: if atomic accesses are not ignored, this simply > @@ -329,11 +331,11 @@ static inline void __kcsan_disable_current(void) { } > #define kcsan_check_atomic_read_write(...) do { } while (0) > #else > #define kcsan_check_atomic_read(ptr, size) \ > - kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC) > + kcsan_check_read_access(ptr, size, KCSAN_ACCESS_ATOMIC) > #define kcsan_check_atomic_write(ptr, size) \ > - kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE) > + kcsan_check_write_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE) > #define kcsan_check_atomic_read_write(ptr, size) \ > - kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_COMPOUND) > + kcsan_check_write_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_COMPOUND) > #endif > > /** > @@ -368,7 +370,7 @@ static inline void __kcsan_disable_current(void) { } > * @var: variable to assert on > */ > #define ASSERT_EXCLUSIVE_WRITER(var) \ > - __kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT) > + __kcsan_check_write_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT) > > /* > * Helper macros for implementation of for ASSERT_EXCLUSIVE_*_SCOPED(). @id is > @@ -449,7 +451,7 @@ static inline void __kcsan_disable_current(void) { } > * @var: variable to assert on > */ > #define ASSERT_EXCLUSIVE_ACCESS(var) \ > - __kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT) > + __kcsan_check_read_access(&(var), sizeof(var), KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT) > > /** > * ASSERT_EXCLUSIVE_ACCESS_SCOPED - assert no concurrent accesses to @var in scope > @@ -525,7 +527,7 @@ static inline void __kcsan_disable_current(void) { } > #define ASSERT_EXCLUSIVE_BITS(var, mask) \ > do { \ > kcsan_set_access_mask(mask); \ > - __kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT);\ > + __kcsan_check_read_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT);\ > kcsan_set_access_mask(0); \ > kcsan_atomic_next(1); \ > } while (0) > diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c > index 8a7baf4e332e..289634c3de5c 100644 > --- a/kernel/kcsan/core.c > +++ b/kernel/kcsan/core.c > @@ -953,11 +953,17 @@ void kcsan_end_scoped_access(struct kcsan_scoped_access *sa) > } > EXPORT_SYMBOL(kcsan_end_scoped_access); > > -void __kcsan_check_access(const volatile void *ptr, size_t size, int type) > +void __kcsan_check_read_access(const volatile void *ptr, size_t size, int type) > { > check_access(ptr, size, type, _RET_IP_); > } > -EXPORT_SYMBOL(__kcsan_check_access); > +EXPORT_SYMBOL(__kcsan_check_read_access); > + > +void __kcsan_check_write_access(volatile void *ptr, size_t size, int type) > +{ > + check_access(ptr, size, type, _RET_IP_); > +} > +EXPORT_SYMBOL(__kcsan_check_write_access); > > #define DEFINE_MEMORY_BARRIER(name, order_before_cond) \ > void __kcsan_##name(void) \ > diff --git a/kernel/kcsan/debugfs.c b/kernel/kcsan/debugfs.c > index 2af39ba5b70b..665a06bc37a7 100644 > --- a/kernel/kcsan/debugfs.c > +++ b/kernel/kcsan/debugfs.c > @@ -75,7 +75,7 @@ static noinline void microbenchmark(unsigned long iters) > unsigned long addr = iters & ((PAGE_SIZE << 8) - 1); > int type = !(iters & 0x7f) ? KCSAN_ACCESS_ATOMIC : > (!(iters & 0xf) ? KCSAN_ACCESS_WRITE : 0); > - __kcsan_check_access((void *)addr, sizeof(long), type); > + __kcsan_check_write_access((void *)addr, sizeof(long), type); > } > cycles = get_cycles() - cycles; > > diff --git a/mm/slub.c b/mm/slub.c > index 161079ac5ba1..b1dcfc575407 100644 > --- a/mm/slub.c > +++ b/mm/slub.c > @@ -2621,8 +2621,8 @@ bool slab_free_hook(struct kmem_cache *s, void *x, bool init, > > /* Use KCSAN to help debug racy use-after-free. */ > if (!still_accessible) > - __kcsan_check_access(x, s->object_size, > - KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); > + __kcsan_check_write_access(x, s->object_size, > + KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); > > if (kfence_free(x)) > return false; > diff --git a/mm/vma_init.c b/mm/vma_init.c > index 3c0b65950510..248e9042a6af 100644 > --- a/mm/vma_init.c > +++ b/mm/vma_init.c > @@ -125,7 +125,7 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig) > if (!new) > return NULL; > > - ASSERT_EXCLUSIVE_WRITER(orig->vm_flags); > + ASSERT_EXCLUSIVE_WRITER(orig->flags); > ASSERT_EXCLUSIVE_WRITER(orig->vm_file); > vm_area_init_from(orig, new); > > diff --git a/tools/objtool/check.c b/tools/objtool/check.c > index 9b11cf3193b9..7a3650fb10db 100644 > --- a/tools/objtool/check.c > +++ b/tools/objtool/check.c > @@ -1155,7 +1155,8 @@ static const char *uaccess_safe_builtin[] = { > "__asan_report_store8_noabort", > "__asan_report_store16_noabort", > /* KCSAN */ > - "__kcsan_check_access", > + "__kcsan_check_read_access", > + "__kcsan_check_write_access", > "__kcsan_mb", > "__kcsan_wmb", > "__kcsan_rmb",