From b24b07bf2a36fa71ada47814863c507423509d6f Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sun, 11 Jan 2026 00:15:25 +0100 Subject: [PATCH 1/4] 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 c57f75efb74ccc62c083c7586c34d30f36220d7e Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 12:57:43 +0200 Subject: [PATCH 2/4] 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 261b267b2d1e9eabba3c93f4ba5adaef77132253 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 29 Jul 2026 13:04:58 +0200 Subject: [PATCH 3/4] 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 bdfd180451e549665b79dd7600cfde04366aa410 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Sat, 22 Aug 2026 23:27:13 +0200 Subject: [PATCH 4/4] lib/: Use rvalue() to make macros more robust These macros already perform lvalue conversion thanks to the comma operator used for the _Generic() expressions. However, we may forget about that if we ever remove those _Generic() expressions for some reason, so let's make sure that we perform lvalue conversion on those compound literals regardless of that. Signed-off-by: Alejandro Colomar --- lib/alloc/realloc.h | 3 ++- lib/alloc/reallocf.h | 3 ++- lib/search/l/lfind.h | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/alloc/realloc.h b/lib/alloc/realloc.h index a34e97ac10..448729b382 100644 --- a/lib/alloc/realloc.h +++ b/lib/alloc/realloc.h @@ -10,6 +10,7 @@ #include +#include "cast.h" #include "exit_if_null.h" #include "sizeof.h" @@ -19,7 +20,7 @@ #define realloc_T_(p, n, T) \ ( \ _Generic(p, T *: (void)0), \ - (T *){reallocarray_(p, n, sizeof(T))} \ + rvalue((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..0d9a484f26 100644 --- a/lib/alloc/reallocf.h +++ b/lib/alloc/reallocf.h @@ -12,6 +12,7 @@ #include #include "attr.h" +#include "cast.h" #include "sizeof.h" @@ -20,7 +21,7 @@ #define reallocf_T_(p, n, T) \ ( \ _Generic(p, T *: (void)0), \ - (T *){reallocarrayf_(p, n, sizeof(T))} \ + rvalue((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..73b53ec4aa 100644 --- a/lib/search/l/lfind.h +++ b/lib/search/l/lfind.h @@ -11,6 +11,7 @@ #include #include +#include "cast.h" #include "search/cmp/cmp.h" #include "sizeof.h" @@ -21,7 +22,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)} \ + rvalue((T *){lfind_(k, a, n, sizeof(T), cmp)}) \ ) #define LFIND(T, ...) lfind_T(T, __VA_ARGS__, CMP(T))