Re: kcsan -Wmaybe-uninitialized warning in ntfs3

Marco Elver
2026-04-21T13:18:46Z

On Tue, Apr 21, 2026 at 02:28PM +0200, Arnd Bergmann wrote:
> On Tue, Apr 21, 2026, at 13:35, Marco Elver wrote:
> > On Tue, 21 Apr 2026 at 12:21, Arnd Bergmann <arnd@arndb.de> wrote:
> >>
> >> I ran into this during randconfig testing, I have attached a
> >> reproducer .config here, but have not tried to narrow down the
> >> configuration options that are required for triggering it.
> >
> > The attached .config is a KASAN config, still can't reproduce.
>
> Odd, this is what I see in the attached file:

Mea culpa - I added one '.' too many and ended up copying some old
config.

Anyway, I can reproduce this now.

There appear to be 2 options to fix, see patch below:

1. __kcsan_nodiag wrapper to disable warnings for the kcsan_check
helpers. Tested and seems to work in this case.

2. __attribute__((access(none, 1))) per https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html:
"The access attribute enables the detection of invalid or unsafe
accesses by functions or their callers, as well as write-only
accesses to objects that are never read from. Such accesses may be
diagnosed by warnings such as -Wstringop-overflow, -Wuninitialized,
-Wunused, and others."

Option #2 seems a lot simpler. While KCSAN does read the accessed
memory, for the purpose of diagnostics hiding this fact from the
compiler is perfectly fine as it's not part of "normal" kernel code.

Preferences?

------ >8 ------

diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index b041639ab406..088412189f16 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -73,6 +73,7 @@ static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
if (!bdev_max_discard_sectors(dev))
return -EOPNOTSUPP;

+ kcsan_check_write(&range, sizeof(range));
user_range = (struct fstrim_range __user *)arg;
if (copy_from_user(&range, user_range, sizeof(range)))
return -EFAULT;
diff --git a/include/linux/kcsan-checks.h b/include/linux/kcsan-checks.h
index 92f3843d9ebb..7304512cf2a2 100644
--- a/include/linux/kcsan-checks.h
+++ b/include/linux/kcsan-checks.h
@@ -27,6 +27,14 @@
* to validate access to an address. Never use these in header files!
*/
#ifdef CONFIG_KCSAN
+#define __kcsan_nodiag(...) \
+({ \
+ __diag_push(); \
+ __diag_ignore_all("-Wmaybe-uninitialized", "") \
+ __VA_ARGS__; \
+ __diag_pop() \
+})
+
/**
* __kcsan_check_access - check generic access for races
*
@@ -34,7 +42,7 @@
* @size: size of access
* @type: access type modifier
*/
-void __kcsan_check_access(const volatile void *ptr, size_t size, int type);
+void __kcsan_check_access(const volatile void *ptr, size_t size, int type)/* __attribute__((access (none, 1)))*/;

/*
* See definition of __tsan_atomic_signal_fence() in kernel/kcsan/core.c.
@@ -186,6 +194,8 @@ void kcsan_end_scoped_access(struct kcsan_scoped_access *sa);

#else /* CONFIG_KCSAN */

+#define __kcsan_nodiag(...) ({ __VA_ARGS__; })
+
static inline void __kcsan_check_access(const volatile void *ptr, size_t size,
int type) { }

@@ -273,7 +283,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_nodiag(__kcsan_check_access(ptr, size, 0))

/**
* __kcsan_check_write - check regular write access for races
@@ -282,7 +292,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_nodiag(__kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE))

/**
* __kcsan_check_read_write - check regular read-write access for races
@@ -291,7 +301,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_nodiag(__kcsan_check_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE))

/**
* kcsan_check_read - check regular read access for races
@@ -299,7 +309,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_nodiag(kcsan_check_access(ptr, size, 0))

/**
* kcsan_check_write - check regular write access for races
@@ -308,7 +318,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_nodiag(kcsan_check_access(ptr, size, KCSAN_ACCESS_WRITE))

/**
* kcsan_check_read_write - check regular read-write access for races
@@ -317,7 +327,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_nodiag(kcsan_check_access(ptr, size, KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE))

/*
* Check for atomic accesses: if atomic accesses are not ignored, this simply
@@ -329,11 +339,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_nodiag(kcsan_check_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_nodiag(kcsan_check_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_nodiag(kcsan_check_access(ptr, size, KCSAN_ACCESS_ATOMIC | KCSAN_ACCESS_WRITE | KCSAN_ACCESS_COMPOUND))
#endif

/**
@@ -368,7 +378,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_nodiag(__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT))

/*
* Helper macros for implementation of for ASSERT_EXCLUSIVE_*_SCOPED(). @id is
@@ -380,9 +390,9 @@ static inline void __kcsan_disable_current(void) { }
struct kcsan_scoped_access __kcsan_scoped_name(id, _) \
__kcsan_cleanup_scoped; \
struct kcsan_scoped_access *__kcsan_scoped_name(id, _dummy_p) \
- __maybe_unused = kcsan_begin_scoped_access( \
+ __maybe_unused = __kcsan_nodiag(kcsan_begin_scoped_access( \
&(var), sizeof(var), KCSAN_ACCESS_SCOPED | (type), \
- &__kcsan_scoped_name(id, _))
+ &__kcsan_scoped_name(id, _)))

/**
* ASSERT_EXCLUSIVE_WRITER_SCOPED - assert no concurrent writes to @var in scope
@@ -449,7 +459,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_nodiag(__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT))

/**
* ASSERT_EXCLUSIVE_ACCESS_SCOPED - assert no concurrent accesses to @var in scope
@@ -525,7 +535,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_nodiag(__kcsan_check_access(&(var), sizeof(var), KCSAN_ACCESS_ASSERT));\
kcsan_set_access_mask(0); \
kcsan_atomic_next(1); \
} while (0)