[PATCH] D134461: [Clang] Warn when trying to deferencing void pointers in C
Aaron Ballman via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 26 09:20:42 PDT 2022
aaron.ballman added a comment.
In D134461#3815298 <https://reviews.llvm.org/D134461#3815298>, @nathanchance wrote:
> This warning is quite noisy for the Linux kernel due to a couple of places where a `void *` is dereferenced as part of compile time checking.
Thank you for letting us know!
> Against an x86_64 allmodconfig build (which builds the majority of the code in the kernel), I see:
>
> $ rg -c -- -Wvoid-ptr-dereference build.log
> 1588001
>
> My original commentary is available on our GitHub issue tracker (https://github.com/ClangBuiltLinux/linux/issues/1720) but I will summarize it here:
>
> The first major instance of this warning comes from the `__is_constexpr` macro, which is used a lot in common macros do fancy things at compile time (change that added this: https://git.kernel.org/linus/3c8ba0d61d04ced9f8d9ff93977995a9e4e96e91):
>
> /*
> * This returns a constant expression while determining if an argument is
> * a constant expression, most importantly without evaluating the argument.
> * Glory to Martin Uecker <Martin.Uecker at med.uni-goettingen.de>
> */
> #define __is_constexpr(x) \
> (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
>
> Sample warnings:
>
> In file included from scripts/mod/devicetable-offsets.c:3:
> In file included from ./include/linux/mod_devicetable.h:13:
> In file included from ./include/linux/uuid.h:12:
> In file included from ./include/linux/string.h:253:
> ./include/linux/fortify-string.h:159:10: warning: ISO C does not allow indirection on operand of type 'void *' [-Wvoid-ptr-dereference]
> q_len = strlen(q);
> ^~~~~~~~~
> ./include/linux/fortify-string.h:131:24: note: expanded from macro 'strlen'
> __builtin_choose_expr(__is_constexpr(__builtin_strlen(p)), \
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/const.h:12:25: note: expanded from macro '__is_constexpr'
> (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This one is interesting to me -- I was on the fence about whether we want to diagnose this in an unevaluated context or not. The `clang/test/Analysis/misc-ps.m` test was why I was considering it, and I eventually convinced myself that the dereference there was a harmless UB when we support pointer arithmetic on void as an extension, but UB nonetheless. Now I wonder whether we want different behavior in the `sizeof` case -- it seems odd to me that we warn by default on `sizeof(*vp)` but not `sizeof(void)`: https://godbolt.org/z/PcMrW7b5W so I think we might want to silence `sizeof(*vp)` diagnostics.
WDYT?
> In file included from arch/x86/kernel/asm-offsets.c:9:
> In file included from ./include/linux/crypto.h:20:
> In file included from ./include/linux/slab.h:15:
> In file included from ./include/linux/gfp.h:7:
> In file included from ./include/linux/mmzone.h:8:
> In file included from ./include/linux/spinlock.h:55:
> In file included from ./include/linux/preempt.h:78:
> In file included from ./arch/x86/include/asm/preempt.h:7:
> In file included from ./include/linux/thread_info.h:60:
> In file included from ./arch/x86/include/asm/thread_info.h:53:
> In file included from ./arch/x86/include/asm/cpufeature.h:5:
> In file included from ./arch/x86/include/asm/processor.h:22:
> In file included from ./arch/x86/include/asm/msr.h:11:
> In file included from ./arch/x86/include/asm/cpumask.h:5:
> In file included from ./include/linux/cpumask.h:12:
> In file included from ./include/linux/bitmap.h:9:
> ./include/linux/find.h:40:17: warning: ISO C does not allow indirection on operand of type 'void *' [-Wvoid-ptr-dereference]
>
> val = *addr & GENMASK(size - 1, offset);
> ^~~~~~~~~~~~~~~~~~~~~~~~~
>
> ./include/linux/bits.h:38:3: note: expanded from macro 'GENMASK'
>
> (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
> ^~~~~~~~~~~~~~~~~~~~~~~~~
>
> ./include/linux/bits.h:25:3: note: expanded from macro 'GENMASK_INPUT_CHECK'
>
> __is_constexpr((l) > (h)), (l) > (h), 0)))
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> ./include/linux/const.h:12:25: note: expanded from macro '__is_constexpr'
>
> (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
> ^
>
> ./include/linux/build_bug.h:16:62: note: expanded from macro 'BUILD_BUG_ON_ZERO'
> #define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))
>
> ^
>
> The second major instance is the type checking in `container_of` when ptr is a `void *` (change that added this: https://git.kernel.org/linus/c7acec713d14c6ce8a20154f9dfda258d6bcad3b):
>
> /**
>
> - container_of - cast a member of a structure out to the containing structure
> - @ptr: the pointer to the member.
> - @type: the type of the container struct this is embedded in.
> - @member: the name of the member within the struct. * */
>
> #define container_of(ptr, type, member) ({ \
> void *__mptr = (void *)(ptr); \
> static_assert(__same_type(*(ptr), ((type *)0)->member) || \
>
> __same_type(*(ptr), void), \
> "pointer type mismatch in container_of()"); \
>
> ((type *)(__mptr - offsetof(type, member))); })
>
>
>
> #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
>
>
This is another interesting case involving an unevaluated operand. `typeof` doesn't evaluate its argument and so it seems somewhat defensible to allow `typeof(*vp)` for generic programming cases; it should just yield `void` as a type.
> Sample warning:
>
> In file included from sound/soc/sof/core.c:14:
> In file included from ./include/sound/sof.h:14:
> ./include/linux/pci.h:600:9: warning: ISO C does not allow indirection on operand of type 'void *' [-Wvoid-ptr-dereference]
> return container_of(priv, struct pci_host_bridge, private);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/container_of.h:20:21: note: expanded from macro 'container_of'
> __same_type(*(ptr), void), \
> ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/compiler_types.h:295:63: note: expanded from macro '__same_type'
> #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
> ^
> ./include/linux/build_bug.h:77:50: note: expanded from macro 'static_assert'
> #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
> ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
> ./include/linux/build_bug.h:78:56: note: expanded from macro '__static_assert'
> #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
> ^~~~
>
> I am aware these are quite specialized cases so warning is likely appropriate. If so, we can just disable this warning for the kernel but I figured it was worth a comment in case the diagnostic can be improved in any way.
All this said, I think in both cases we want `-pedantic` to warn on these situations even if we would silence by default. e.g.,
void func(void *vp) {
sizeof(void); // No warning by default, does get pedantic warning
sizeof(*vp); // No warning by default, does get pedantic warning
sizeof(*(vp))`; // No warning by default, does get pedantic warning
void inner(typeof(*vp)); // No warning by default, does get pedantic warning
(void)*vp; // Warning by default
}
What do folks think of that idea?
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D134461/new/
https://reviews.llvm.org/D134461
More information about the cfe-commits
mailing list