"auto" was defined as a keyword back in the K&R days, but as a storage type specifier. No one ever used it, since it was and is the default storage type for local variables.
C++11 recycled the keyword to allow a type to be declared based on the type of an initializer. This was finally adopted into standard C in C23.
gcc and clang provide the "__auto_type" alias keyword as an extension for pre-C23, however, there is no reason to pollute the bulk of the source base with this temporary keyword; instead define "auto" as a macro unless the compiler is running in C23+ mode.
This macro is added in <linux/compiler_types.h> because that header is included in some of the tools headers, wheres <linux/compiler.h> is not as it has a bunch of very kernel-specific things in it.
--- arch/nios2/include/asm/uaccess.h | 4 ++-- arch/x86/include/asm/bug.h | 2 +- arch/x86/include/asm/string_64.h | 6 +++--- arch/x86/include/asm/uaccess_64.h | 2 +- fs/proc/inode.c | 16 ++++++++-------- include/linux/cleanup.h | 4 ++-- include/linux/compiler.h | 2 +- include/linux/compiler_types.h | 13 +++++++++++++ include/linux/minmax.h | 6 +++--- tools/testing/selftests/bpf/prog_tests/socket_helpers.h | 9 +++++++-- tools/virtio/linux/compiler.h | 2 +- 11 files changed, 42 insertions(+), 24 deletions(-)
"auto" was defined as a keyword back in the K&R days, but as a storage type specifier. No one ever used it, since it was and is the default storage type for local variables.
C++11 recycled the keyword to allow a type to be declared based on the type of an initializer. This was finally adopted into standard C in C23.
gcc and clang provide the "__auto_type" alias keyword as an extension for pre-C23, however, there is no reason to pollute the bulk of the source base with this temporary keyword; instead define "auto" as a macro unless the compiler is running in C23+ mode.
This macro is added in <linux/compiler_types.h> because that header is included in some of the tools headers, wheres <linux/compiler.h> is not as it has a bunch of very kernel-specific things in it.
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- include/linux/compiler_types.h | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 2b77d12e07b2..c8b1ee37934e 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -13,6 +13,19 @@
#ifndef __ASSEMBLY__
+/* + * C23 introduces "auto" as a standard way to define type-inferred + * variables, but "auto" has been a (useless) keyword even since K&R C, + * so it has always been "namespace reserved." + * + * Until at some future time we require C23 support, we need the gcc + * extension __auto_type, but there is no reason to put that elsewhere + * in the source code. + */ +#if __STDC_VERSION__ < 202311L +# define auto __auto_type +#endif + /* * Skipped when running bindgen due to a libclang issue; * see https://github.com/rust-lang/rust-bindgen/issues/2244.
Replace instances of "__auto_type" with "auto" in include/linux.
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- include/linux/cleanup.h | 4 ++-- include/linux/compiler.h | 2 +- include/linux/minmax.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index 7093e1d08af0..08973560e81d 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -201,8 +201,8 @@
#define __get_and_null(p, nullvalue) \ ({ \ - __auto_type __ptr = &(p); \ - __auto_type __val = *__ptr; \ + auto __ptr = &(p); \ + auto __val = *__ptr; \ *__ptr = nullvalue; \ __val; \ }) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 6f04a1d8c720..f50183b71bb6 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -186,7 +186,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #define data_race(expr) \ ({ \ __kcsan_disable_current(); \ - __auto_type __v = (expr); \ + auto __v = (expr); \ __kcsan_enable_current(); \ __v; \ }) diff --git a/include/linux/minmax.h b/include/linux/minmax.h index eaaf5c008e4d..7c6fba53ea5b 100644 --- a/include/linux/minmax.h +++ b/include/linux/minmax.h @@ -89,7 +89,7 @@ __cmp_once_unique(op, type, x, y, __UNIQUE_ID(x_), __UNIQUE_ID(y_))
#define __careful_cmp_once(op, x, y, ux, uy) ({ \ - __auto_type ux = (x); __auto_type uy = (y); \ + auto ux = (x); auto uy = (y); \ BUILD_BUG_ON_MSG(!__types_ok(ux, uy), \ #op"("#x", "#y") signedness error"); \ __cmp(op, ux, uy); }) @@ -129,7 +129,7 @@ __careful_cmp(max, (x) + 0u + 0ul + 0ull, (y) + 0u + 0ul + 0ull)
#define __careful_op3(op, x, y, z, ux, uy, uz) ({ \ - __auto_type ux = (x); __auto_type uy = (y);__auto_type uz = (z);\ + auto ux = (x); auto uy = (y); auto uz = (z); \ BUILD_BUG_ON_MSG(!__types_ok3(ux, uy, uz), \ #op"3("#x", "#y", "#z") signedness error"); \ __cmp(op, ux, __cmp(op, uy, uz)); }) @@ -203,7 +203,7 @@ * This macro checks @val/@lo/@hi to make sure they have compatible * signedness. */ -#define clamp(val, lo, hi) __careful_clamp(__auto_type, val, lo, hi) +#define clamp(val, lo, hi) __careful_clamp(auto, val, lo, hi)
/** * clamp_t - return a value clamped to a given range using a given type
Replace uses of "__auto_type" in fs/proc/inode.c with "auto".
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- fs/proc/inode.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 3604b616311c..e5b150e70166 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -303,7 +303,7 @@ static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos) { - __auto_type read = pde->proc_ops->proc_read; + auto read = pde->proc_ops->proc_read; if (read) return read(file, buf, count, ppos); return -EIO; @@ -325,7 +325,7 @@ static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count,
static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - __auto_type write = pde->proc_ops->proc_write; + auto write = pde->proc_ops->proc_write; if (write) return write(file, buf, count, ppos); return -EIO; @@ -347,7 +347,7 @@ static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t
static __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts) { - __auto_type poll = pde->proc_ops->proc_poll; + auto poll = pde->proc_ops->proc_poll; if (poll) return poll(file, pts); return DEFAULT_POLLMASK; @@ -369,7 +369,7 @@ static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts)
static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) { - __auto_type ioctl = pde->proc_ops->proc_ioctl; + auto ioctl = pde->proc_ops->proc_ioctl; if (ioctl) return ioctl(file, cmd, arg); return -ENOTTY; @@ -392,7 +392,7 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne #ifdef CONFIG_COMPAT static long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) { - __auto_type compat_ioctl = pde->proc_ops->proc_compat_ioctl; + auto compat_ioctl = pde->proc_ops->proc_compat_ioctl; if (compat_ioctl) return compat_ioctl(file, cmd, arg); return -ENOTTY; @@ -414,7 +414,7 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned
static int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma) { - __auto_type mmap = pde->proc_ops->proc_mmap; + auto mmap = pde->proc_ops->proc_mmap; if (mmap) return mmap(file, vma); return -EIO; @@ -497,7 +497,7 @@ static int proc_reg_open(struct inode *inode, struct file *file) if (!use_pde(pde)) return -ENOENT;
- __auto_type release = pde->proc_ops->proc_release; + auto release = pde->proc_ops->proc_release; if (release) { pdeo = kmem_cache_alloc(pde_opener_cache, GFP_KERNEL); if (!pdeo) { @@ -534,7 +534,7 @@ static int proc_reg_release(struct inode *inode, struct file *file) struct pde_opener *pdeo;
if (pde_is_permanent(pde)) { - __auto_type release = pde->proc_ops->proc_release; + auto release = pde->proc_ops->proc_release; if (release) { return release(inode, file); }
On Fri, Jul 18, 2025 at 02:32:46PM -0700, H. Peter Anvin wrote:
Replace uses of "__auto_type" in fs/proc/inode.c with "auto".
static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos) {
- __auto_type read = pde->proc_ops->proc_read;
- auto read = pde->proc_ops->proc_read;
Thanks! I'd prefer "const auto" but OK.
Reviewed-by: Alexey Dobriyan adobriyan@gmail.com
Replace uses of "__auto_type" in arch/nios2/include/asm/uaccess.h with "auto".
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- arch/nios2/include/asm/uaccess.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index b8299082adbe..fa9b06a7d7c6 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -172,14 +172,14 @@ do { \
#define __put_user(x, ptr) \ ({ \ - __auto_type __pu_ptr = (ptr); \ + auto __pu_ptr = (ptr); \ typeof(*__pu_ptr) __pu_val = (typeof(*__pu_ptr))(x); \ __put_user_common(__pu_val, __pu_ptr); \ })
#define put_user(x, ptr) \ ({ \ - __auto_type __pu_ptr = (ptr); \ + auto __pu_ptr = (ptr); \ typeof(*__pu_ptr) __pu_val = (typeof(*__pu_ptr))(x); \ access_ok(__pu_ptr, sizeof(*__pu_ptr)) ? \ __put_user_common(__pu_val, __pu_ptr) : \
On Fri, 18 Jul 2025 at 14:34, H. Peter Anvin hpa@zytor.com wrote:
__auto_type __pu_ptr = (ptr); \
auto __pu_ptr = (ptr); \ typeof(*__pu_ptr) __pu_val = (typeof(*__pu_ptr))(x); \
Side note: I think some coccinelle (or sed) script that replaces that older form of
typeof(x) Y = (typeof(x))(Z);
or
typeof(Z) Y = Z;
with just
auto Y = Z;
is also worthwhile at some point.
We have more of those, because that's the really traditional gcc way to do things that predates __auto_type.
And the patterns are a bit more complicated, so they need care: not all of the "typeof (x) Z = Y" patterns have the same type in the assignment.
So it's not the universal case, but it's the _common_ case, I think.
For example, it's obviously the case in the above, where we use the exact same "typeof" on both sides, but in other uaccess.h files we also have patterns like
__typeof__(*(ptr)) __x = (x); /* eval x once */ __typeof__(ptr) __ptr = (ptr); /* eval ptr once */
where that *first* case very much needs to use that "__typeof__" model, because 'x' typically does not necessarily have the same type as '*(ptr)' (and we absolutely do not want to use a cast: we want integer types to convert naturally, but we very much want warnings if somebody were to mix types wrong).
But that second case obviously is exactly the "auto type" case, just written using __typeof__.
Linus
On 2025-07-18 14:49, Linus Torvalds wrote:
Side note: I think some coccinelle (or sed) script that replaces that older form of
typeof(x) Y = (typeof(x))(Z);
or
typeof(Z) Y = Z;
with just
auto Y = Z;
is also worthwhile at some point.
We have more of those, because that's the really traditional gcc way to do things that predates __auto_type.
Agreed. And I think that this, indeed is a job more for Coccinelle than for sed, because the patterns can be rather complex and we don't want false positives.
-hpa
On Fri, 18 Jul 2025 at 14:49, Linus Torvalds torvalds@linux-foundation.org wrote:
On Fri, 18 Jul 2025 at 14:34, H. Peter Anvin hpa@zytor.com wrote:
__auto_type __pu_ptr = (ptr); \
auto __pu_ptr = (ptr); \ typeof(*__pu_ptr) __pu_val = (typeof(*__pu_ptr))(x); \
But that second case obviously is exactly the "auto type" case, just written using __typeof__.
Actually, looking at it, I actually think the NIOS2 header is a bit buggy here, exactly because it should *not* have that cast to force the types the same.
It's the exact same situation that on x86 is inside do_put_user_call(), and on x86 uses that
__typeof__(*(ptr)) __x = (x); /* eval x once */
pattern instead: we don't want a cast, because we actually want just the implicit type conversions, and a warning if the types aren't compatible. Writing things to user space is still supposed to catch type safety issues.
So having that '(typeof(*__pu_ptr))' cast of the value of '(x)' is actually wrong, because it will silently (for example) convert a pointer into a 'unsigned long' or vice versa, and __put_user() just shouldn't do that.
If the user access is to a 'unsigned long __user *' location, the kernel shouldn't be writing pointers into it.
Do we care? No. This is obviously nios2-specific, and the x86 version will catch any generic mis-uses where somebody would try to 'put_user()' the wrong type.
And any "auto" conversion wouldn't change the bug anyway. But I thought I'd mention it since it started bothering me and I went and looked closer at that case I quoted.
And while looking at this, I think we have a similar mis-feature / bug on x86 too: the unsafe_put_user() macro does exactly that cast:
#define unsafe_put_user(x, ptr, label) \ __put_user_size((__typeof__(*(ptr)))(x), ..
and I think that cast is wrong.
I wonder if it's actively hiding some issue with unsafe_put_user(), or if I'm just missing something.
Linus
On Fri, 18 Jul 2025 at 15:48, Linus Torvalds torvalds@linux-foundation.org wrote:
And while looking at this, I think we have a similar mis-feature / bug on x86 too: the unsafe_put_user() macro does exactly that cast:
#define unsafe_put_user(x, ptr, label) \ __put_user_size((__typeof__(*(ptr)))(x), ..
and I think that cast is wrong.
I wonder if it's actively hiding some issue with unsafe_put_user(), or if I'm just missing something.
... and I decided to try to look into it by just removing the cast.
And yes indeed, there's a reason for the cast - or at least it's hiding problems:
arch/x86/kernel/signal_64.c:128: unsafe_put_user(fpstate, (unsigned long __user *)&sc->fpstate, Efault);
arch/x86/kernel/signal_64.c:188: unsafe_put_user(ksig->ka.sa.sa_restorer, &frame->pretcode, Efault);
arch/x86/kernel/signal_64.c:332: unsafe_put_user(restorer, (unsigned long __user *)&frame->pretcode, Efault);
The one on line 188 at least makes some sense. The other ones are literally hiding the fact that we explicitly cast things to the wrong pointer.
I suspect it's just very old historical "we have been lazy and mixing 'unsigned long' and 'pointer value'" issues.
Oh well. None of these are actual *bugs*, they are more just ugly. And the cast that is hiding this ugliness might be hiding other things.
Not worth the churn at least late in the release cycle, but one of those "this might be worth cleaning up some day" issues.
Linus
On Fri, 18 Jul 2025 14:49:41 -0700 Linus Torvalds torvalds@linux-foundation.org wrote:
On Fri, 18 Jul 2025 at 14:34, H. Peter Anvin hpa@zytor.com wrote:
__auto_type __pu_ptr = (ptr); \
auto __pu_ptr = (ptr); \ typeof(*__pu_ptr) __pu_val = (typeof(*__pu_ptr))(x); \
You need to align the \ Plausibly possible by post-processing the diffs.
David
On Fri, 18 Jul 2025 14:49:41 -0700 Linus Torvalds torvalds@linux-foundation.org wrote:
...
Side note: I think some coccinelle (or sed) script that replaces that older form of
typeof(x) Y = (typeof(x))(Z);
...
with just
auto Y = Z;
is also worthwhile at some point.
That one needs to keep the typeof() - but the cast might be spurious. It could be either: typeof(x) Y = Z; or: auto Y = (typeof(x))(Z); but the latter could hide compilation errors.
I'm waiting for the next 'duck shoot' (after strings) to be casts.
While casts of 'buffer' to/from 'void *' are fine (and not needed), casts to/from 'integer_type *' are definitely problematic.
And 'random' casts of integer values could easily hide real bugs and most just aren't needed. Although you might want the compiler to make the result of 'u64_var & 0xffu' 'unsigned int'.
David
Replace instances of "__auto_type" with "auto" in:
arch/x86/include/asm/bug.h arch/x86/include/asm/string_64.h arch/x86/include/asm/uaccess_64.h
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- arch/x86/include/asm/bug.h | 2 +- arch/x86/include/asm/string_64.h | 6 +++--- arch/x86/include/asm/uaccess_64.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h index f0e9acf72547..4cce2ce8657b 100644 --- a/arch/x86/include/asm/bug.h +++ b/arch/x86/include/asm/bug.h @@ -94,7 +94,7 @@ do { \ */ #define __WARN_FLAGS(flags) \ do { \ - __auto_type __flags = BUGFLAG_WARNING|(flags); \ + auto __flags = BUGFLAG_WARNING|(flags); \ instrumentation_begin(); \ _BUG_FLAGS(ASM_UD2, __flags, ANNOTATE_REACHABLE(1b)); \ instrumentation_end(); \ diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h index 79e9695dc13e..4635616863f5 100644 --- a/arch/x86/include/asm/string_64.h +++ b/arch/x86/include/asm/string_64.h @@ -31,7 +31,7 @@ KCFI_REFERENCE(__memset); #define __HAVE_ARCH_MEMSET16 static inline void *memset16(uint16_t *s, uint16_t v, size_t n) { - const __auto_type s0 = s; + const auto s0 = s; asm volatile ( "rep stosw" : "+D" (s), "+c" (n) @@ -44,7 +44,7 @@ static inline void *memset16(uint16_t *s, uint16_t v, size_t n) #define __HAVE_ARCH_MEMSET32 static inline void *memset32(uint32_t *s, uint32_t v, size_t n) { - const __auto_type s0 = s; + const auto s0 = s; asm volatile ( "rep stosl" : "+D" (s), "+c" (n) @@ -57,7 +57,7 @@ static inline void *memset32(uint32_t *s, uint32_t v, size_t n) #define __HAVE_ARCH_MEMSET64 static inline void *memset64(uint64_t *s, uint64_t v, size_t n) { - const __auto_type s0 = s; + const auto s0 = s; asm volatile ( "rep stosq" : "+D" (s), "+c" (n) diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index c8a5ae35c871..b0e4533ce625 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -72,7 +72,7 @@ static inline void __user *mask_user_address(const void __user *ptr) return ret; } #define masked_user_access_begin(x) ({ \ - __auto_type __masked_ptr = (x); \ + auto __masked_ptr = (x); \ __masked_ptr = mask_user_address(__masked_ptr); \ __uaccess_begin(); __masked_ptr; })
Replace instances of "__auto_type" with "auto" in:
tools/testing/selftests/bpf/prog_tests/socket_helpers.h
This file does not seem to be including <linux/compiler_types.h> directly or indirectly, so copy the definition but guard it with !defined(auto).
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- tools/testing/selftests/bpf/prog_tests/socket_helpers.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/socket_helpers.h b/tools/testing/selftests/bpf/prog_tests/socket_helpers.h index e02cabcc814e..958b65aa29ea 100644 --- a/tools/testing/selftests/bpf/prog_tests/socket_helpers.h +++ b/tools/testing/selftests/bpf/prog_tests/socket_helpers.h @@ -17,11 +17,16 @@ #define VMADDR_CID_LOCAL 1 #endif
+/* include/linux/compiler_types.h */ +#if __STDC_VERSION__ < 202311L && !defined(auto) +# define auto __auto_type +#endif + /* include/linux/cleanup.h */ #define __get_and_null(p, nullvalue) \ ({ \ - __auto_type __ptr = &(p); \ - __auto_type __val = *__ptr; \ + auto __ptr = &(p); \ + auto __val = *__ptr; \ *__ptr = nullvalue; \ __val; \ })
Replace one instance of "__auto_type" with "auto" in:
tools/virtio/linux/compiler.h
This file *does* include <linux/compiler_types.h> directly, so there is no need to duplicate the definition.
Signed-off-by: H. Peter Anvin (Intel) hpa@zytor.com --- tools/virtio/linux/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/virtio/linux/compiler.h b/tools/virtio/linux/compiler.h index 204ef0e9f542..b6e94c2ebe49 100644 --- a/tools/virtio/linux/compiler.h +++ b/tools/virtio/linux/compiler.h @@ -31,7 +31,7 @@ */ #define data_race(expr) \ ({ \ - __auto_type __v = (expr); \ + auto __v = (expr); \ __v; \ })
linux-kselftest-mirror@lists.linaro.org