From ba7554682925e76753fd71fe81965526de52e7de Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 27 Mar 2018 21:39:10 -0600 Subject: [PATCH] container_of: Fail compilation when void * is used as the member_ptr This is actually a check_types_match problem, as check_types_match(*(void *)0, (int)0) Will succeed because C allows 'void *' to compare against any other pointer. The solution is the same as typecheck.h uses in the kernel: add another pointer '*' to the type. However, this means check_types_match(*(const int *)0, (int)0) Will start failing as the implicit elimination of const is gone. This is actually used by a few container_of users in ccan, and seems desirable. Use a simple solution to constify both sides before adding the extra pointer '*'. Add tests to show that all of these above scenarios work. Signed-off-by: Jason Gunthorpe --- ccan/check_type/check_type.h | 6 ++++- .../test/compile_fail-bad-void-type.c | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 ccan/container_of/test/compile_fail-bad-void-type.c diff --git a/ccan/check_type/check_type.h b/ccan/check_type/check_type.h index 837aef7b1..9df25a970 100644 --- a/ccan/check_type/check_type.h +++ b/ccan/check_type/check_type.h @@ -49,8 +49,12 @@ #define check_type(expr, type) \ ((typeof(expr) *)0 != (type *)0) -#define check_types_match(expr1, expr2) \ +#define _check_types_match(expr1, expr2) \ ((typeof(expr1) *)0 != (typeof(expr2) *)0) + +#define check_types_match(expr1, expr2) \ + _check_types_match((const typeof(expr1) *)0, \ + (const typeof(expr2) *)0) #else #include /* Without typeof, we can only test the sizes. */ diff --git a/ccan/container_of/test/compile_fail-bad-void-type.c b/ccan/container_of/test/compile_fail-bad-void-type.c new file mode 100644 index 000000000..1faa8955d --- /dev/null +++ b/ccan/container_of/test/compile_fail-bad-void-type.c @@ -0,0 +1,26 @@ +#include +#include + +struct foo { + int *a; + char b; +}; + +int main(void) +{ + struct foo foo = { .a = NULL, .b = 2 }; + void *voidp = &foo.a; + const char *ccharp = &foo.b; + char *charp = &foo.b; + struct foo *p; + +#ifdef FAIL + /* voidp is a void * but b is an int */ + p = container_of(voidp, struct foo, a); +#else + p = voidp; + p = container_of(ccharp, struct foo, b); + p = container_of(charp, struct foo, b); +#endif + return p == NULL; +}