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",