From 473f4bfe7f23effc6a9482bc18c0831eb5ba423e Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sun, 11 Jan 2026 00:15:25 +0100 Subject: [PATCH 1/5] lib/: Use the comma operator to perform lvalue conversion Compound literals are lvalues, and thus somewhat dangerous. Their address can be taken, and they can be assigned to. We were using statement expressions to perform lvalue conversion on compound literals, transforming them to rvalues, and thus removing their dangers. However, statement expressions are non-standard, and quite complex within the compiler, so it would be interesting to use simpler compiler features to achieve the same. The comma operator also performs lvalue conversion, and we can use a dummy (void)0 expression to introduce it. This is significantly simpler, and is more portable than the statement expression: it is valid all the way back to C99 (the comma operator and the (void)0 expression are portable to C89, but the compound literal is from C99). By using a simpler feature, we have a smaller risk of running into a compiler bug. Suggested-by: Martin Uecker Cc: Christopher Bazley Cc: Kees Cook Cc: Richard Russon Signed-off-by: Alejandro Colomar --- lib/alloc/calloc.h | 7 ++++--- lib/alloc/malloc.h | 7 ++++--- lib/alloc/realloc.h | 8 ++++---- lib/alloc/reallocf.h | 8 ++++---- lib/search/l/lfind.h | 10 +++++----- lib/sizeof.h | 2 +- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/alloc/calloc.h b/lib/alloc/calloc.h index db10a685bb..ba4e35485c 100644 --- a/lib/alloc/calloc.h +++ b/lib/alloc/calloc.h @@ -17,9 +17,10 @@ // calloc_T - calloc type-safe #define calloc_T(n, T) calloc_T_(n, typeas(T)) #define calloc_T_(n, T) \ -({ \ - (T *){calloc(n, sizeof(T))}; \ -}) +( \ + (void)0, \ + (T *){calloc(n, sizeof(T))} \ +) // xcalloc_T - exit-on-error calloc type-safe diff --git a/lib/alloc/malloc.h b/lib/alloc/malloc.h index 1e3a69291a..9cf23a618b 100644 --- a/lib/alloc/malloc.h +++ b/lib/alloc/malloc.h @@ -18,9 +18,10 @@ // malloc_T - malloc type-safe #define malloc_T(n, T) malloc_T_(n, typeas(T)) #define malloc_T_(n, T) \ -({ \ - (T *){mallocarray(n, sizeof(T))}; \ -}) +( \ + (void)0, \ + (T *){mallocarray(n, sizeof(T))} \ +) // xmalloc_T - exit-on-error malloc type-safe diff --git a/lib/alloc/realloc.h b/lib/alloc/realloc.h index ac9f046ec6..a34e97ac10 100644 --- a/lib/alloc/realloc.h +++ b/lib/alloc/realloc.h @@ -17,10 +17,10 @@ // realloc_T - realloc type-safe #define realloc_T(p, n, T) realloc_T_(p, n, typeas(T)) #define realloc_T_(p, n, T) \ -({ \ - _Generic(p, T *: (void)0); \ - (T *){reallocarray_(p, n, sizeof(T))}; \ -}) +( \ + _Generic(p, T *: (void)0), \ + (T *){reallocarray_(p, n, sizeof(T))} \ +) #define reallocarray_(p, n, size) reallocarray(p, (n) ?: 1, (size) ?: 1) diff --git a/lib/alloc/reallocf.h b/lib/alloc/reallocf.h index c3522829d6..f2271ea3c0 100644 --- a/lib/alloc/reallocf.h +++ b/lib/alloc/reallocf.h @@ -18,10 +18,10 @@ // reallocf_T - realloc free-on-error type-safe #define reallocf_T(p, n, T) reallocf_T_(p, n, typeas(T)) #define reallocf_T_(p, n, T) \ -({ \ - _Generic(p, T *: (void)0); \ - (T *){reallocarrayf_(p, n, sizeof(T))}; \ -}) +( \ + _Generic(p, T *: (void)0), \ + (T *){reallocarrayf_(p, n, sizeof(T))} \ +) #define reallocarrayf_(p, n, size) reallocarrayf(p, (n) ?: 1, (size) ?: 1) diff --git a/lib/search/l/lfind.h b/lib/search/l/lfind.h index 7bbd16e0e3..54aa62d3cb 100644 --- a/lib/search/l/lfind.h +++ b/lib/search/l/lfind.h @@ -18,11 +18,11 @@ // lfind_T - linear find type-safe #define lfind_T(T, ...) lfind_T_(typeas(T), __VA_ARGS__) #define lfind_T_(T, k, a, n, cmp) \ -({ \ - _Generic(k, T *: (void)0, const T *: (void)0); \ - _Generic(a, T *: (void)0, const T *: (void)0); \ - (T *){lfind_(k, a, n, sizeof(T), cmp)}; \ -}) +( \ + _Generic(k, T *: (void)0, const T *: (void)0), \ + _Generic(a, T *: (void)0, const T *: (void)0), \ + (T *){lfind_(k, a, n, sizeof(T), cmp)} \ +) #define LFIND(T, ...) lfind_T(T, __VA_ARGS__, CMP(T)) diff --git a/lib/sizeof.h b/lib/sizeof.h index 1fc38873dd..e17fac5c5c 100644 --- a/lib/sizeof.h +++ b/lib/sizeof.h @@ -17,7 +17,7 @@ #define typeas(T) typeof((T){0}) -#define ssizeof(x) ({(ssize_t){sizeof(x)};}) +#define ssizeof(x) ((void)0, (ssize_t){sizeof(x)}) #define memberof(T, member) ((T){}.member) #define WIDTHOF(x) (sizeof(x) * CHAR_BIT) From 1d1201430ee14a5941c79592bdacce39756f6797 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 12:57:43 +0200 Subject: [PATCH 2/5] lib/cast.h: rvalue(): Add macro for performing lvalue conversion This macro takes an lvalue, and performs lvalue conversion, resulting in an rvalue. Signed-off-by: Alejandro Colomar --- lib/cast.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cast.h b/lib/cast.h index e8e42e1a0b..e96c4a4159 100644 --- a/lib/cast.h +++ b/lib/cast.h @@ -11,5 +11,7 @@ #define const_cast(T, p) _Generic(p, const T: (T) (p)) +#define rvalue(lv) ((void)0, (lv)) + #endif // include guard From f9c91593185147bf2b02c355d2ba887c106fd9be Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 13:04:58 +0200 Subject: [PATCH 3/5] lib/: Use rvalue() instead of its pattern This helps document why we use '(void)0' with the comma operator. Signed-off-by: Alejandro Colomar --- lib/alloc/calloc.h | 7 ++----- lib/alloc/malloc.h | 7 ++----- lib/sizeof.h | 4 +++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/alloc/calloc.h b/lib/alloc/calloc.h index ba4e35485c..9b32c5ef61 100644 --- a/lib/alloc/calloc.h +++ b/lib/alloc/calloc.h @@ -10,17 +10,14 @@ #include +#include "cast.h" #include "exit_if_null.h" #include "sizeof.h" // calloc_T - calloc type-safe #define calloc_T(n, T) calloc_T_(n, typeas(T)) -#define calloc_T_(n, T) \ -( \ - (void)0, \ - (T *){calloc(n, sizeof(T))} \ -) +#define calloc_T_(n, T) rvalue((T *){calloc(n, sizeof(T))}) // xcalloc_T - exit-on-error calloc type-safe diff --git a/lib/alloc/malloc.h b/lib/alloc/malloc.h index 9cf23a618b..53602ea732 100644 --- a/lib/alloc/malloc.h +++ b/lib/alloc/malloc.h @@ -11,17 +11,14 @@ #include #include "attr.h" +#include "cast.h" #include "exit_if_null.h" #include "sizeof.h" // malloc_T - malloc type-safe #define malloc_T(n, T) malloc_T_(n, typeas(T)) -#define malloc_T_(n, T) \ -( \ - (void)0, \ - (T *){mallocarray(n, sizeof(T))} \ -) +#define malloc_T_(n, T) rvalue((T *){mallocarray(n, sizeof(T))}) // xmalloc_T - exit-on-error malloc type-safe diff --git a/lib/sizeof.h b/lib/sizeof.h index e17fac5c5c..e9aa343a0b 100644 --- a/lib/sizeof.h +++ b/lib/sizeof.h @@ -14,10 +14,12 @@ #endif #include +#include "cast.h" + #define typeas(T) typeof((T){0}) -#define ssizeof(x) ((void)0, (ssize_t){sizeof(x)}) +#define ssizeof(x) rvalue((ssize_t){sizeof(x)}) #define memberof(T, member) ((T){}.member) #define WIDTHOF(x) (sizeof(x) * CHAR_BIT) From 611f249602bed328f1d56d0e47512043e10c43bc Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 13:08:21 +0200 Subject: [PATCH 4/5] lib/cast.h: ptr_cast(): Add macro This macro takes a pointer --usually, a void pointer--, and converts it to a pointer to T, implicitly. Signed-off-by: Alejandro Colomar --- lib/cast.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cast.h b/lib/cast.h index e96c4a4159..6678220e04 100644 --- a/lib/cast.h +++ b/lib/cast.h @@ -10,6 +10,7 @@ #define const_cast(T, p) _Generic(p, const T: (T) (p)) +#define ptr_cast(T, p) rvalue((typeas(T) *){(p)}) #define rvalue(lv) ((void)0, (lv)) From 81f34a70a7f94b99998069228ce51e13f5192d70 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 13:16:24 +0200 Subject: [PATCH 5/5] lib/: Use ptr_cast() instead of its pattern This helps document why we use compound literals. Signed-off-by: Alejandro Colomar --- lib/alloc/calloc.h | 4 +--- lib/alloc/malloc.h | 4 +--- lib/alloc/realloc.h | 2 +- lib/alloc/reallocf.h | 2 +- lib/search/l/lfind.h | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/alloc/calloc.h b/lib/alloc/calloc.h index 9b32c5ef61..a9b64a2a29 100644 --- a/lib/alloc/calloc.h +++ b/lib/alloc/calloc.h @@ -12,12 +12,10 @@ #include "cast.h" #include "exit_if_null.h" -#include "sizeof.h" // calloc_T - calloc type-safe -#define calloc_T(n, T) calloc_T_(n, typeas(T)) -#define calloc_T_(n, T) rvalue((T *){calloc(n, sizeof(T))}) +#define calloc_T(n, T) ptr_cast(T, calloc(n, sizeof(T))) // xcalloc_T - exit-on-error calloc type-safe diff --git a/lib/alloc/malloc.h b/lib/alloc/malloc.h index 53602ea732..70a7f522bd 100644 --- a/lib/alloc/malloc.h +++ b/lib/alloc/malloc.h @@ -11,14 +11,12 @@ #include #include "attr.h" -#include "cast.h" #include "exit_if_null.h" #include "sizeof.h" // malloc_T - malloc type-safe -#define malloc_T(n, T) malloc_T_(n, typeas(T)) -#define malloc_T_(n, T) rvalue((T *){mallocarray(n, sizeof(T))}) +#define malloc_T(n, T) ptr_cast(T, mallocarray(n, sizeof(T))) // xmalloc_T - exit-on-error malloc type-safe diff --git a/lib/alloc/realloc.h b/lib/alloc/realloc.h index a34e97ac10..d52d152472 100644 --- a/lib/alloc/realloc.h +++ b/lib/alloc/realloc.h @@ -19,7 +19,7 @@ #define realloc_T_(p, n, T) \ ( \ _Generic(p, T *: (void)0), \ - (T *){reallocarray_(p, n, sizeof(T))} \ + ptr_cast(T, reallocarray_(p, n, sizeof(T))) \ ) #define reallocarray_(p, n, size) reallocarray(p, (n) ?: 1, (size) ?: 1) diff --git a/lib/alloc/reallocf.h b/lib/alloc/reallocf.h index f2271ea3c0..717987e521 100644 --- a/lib/alloc/reallocf.h +++ b/lib/alloc/reallocf.h @@ -20,7 +20,7 @@ #define reallocf_T_(p, n, T) \ ( \ _Generic(p, T *: (void)0), \ - (T *){reallocarrayf_(p, n, sizeof(T))} \ + ptr_cast(T, reallocarrayf_(p, n, sizeof(T))) \ ) #define reallocarrayf_(p, n, size) reallocarrayf(p, (n) ?: 1, (size) ?: 1) diff --git a/lib/search/l/lfind.h b/lib/search/l/lfind.h index 54aa62d3cb..6c0945b19d 100644 --- a/lib/search/l/lfind.h +++ b/lib/search/l/lfind.h @@ -21,7 +21,7 @@ ( \ _Generic(k, T *: (void)0, const T *: (void)0), \ _Generic(a, T *: (void)0, const T *: (void)0), \ - (T *){lfind_(k, a, n, sizeof(T), cmp)} \ + ptr_cast(T, lfind_(k, a, n, sizeof(T), cmp)) \ ) #define LFIND(T, ...) lfind_T(T, __VA_ARGS__, CMP(T))