Skip to content

lib/: Use the comma operator to perform lvalue conversion - #1491

Open
alejandro-colomar wants to merge 5 commits into
shadow-maint:masterfrom
alejandro-colomar:void0
Open

lib/: Use the comma operator to perform lvalue conversion#1491
alejandro-colomar wants to merge 5 commits into
shadow-maint:masterfrom
alejandro-colomar:void0

Conversation

@alejandro-colomar

@alejandro-colomar alejandro-colomar commented Jan 10, 2026

Copy link
Copy Markdown
Collaborator

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 ANSI C89).

By using a simpler feature, we have a smaller risk of running into a compiler bug.

Suggested-by: @uecker
Cc: @chrisbazley
Cc: @kees
Cc: @flatcap


Revisions:

v1b
  • Rebase
$ git rd 
1:  9558736a5b42 = 1:  5732c14f453e lib/: Use the comma operator to perform lvalue conversion
v1c
  • Rebase
$ git rd 
1:  5732c14f = 1:  8956e27a lib/: Use the comma operator to perform lvalue conversion
v1d
  • Rebase
$ git rd 
1:  8956e27a = 1:  5a03c618 lib/: Use the comma operator to perform lvalue conversion
v1e
  • Clarify portability.
$ git rd
1:  5a03c618 ! 1:  b7ab3f97 lib/: Use the comma operator to perform lvalue conversion
    @@ Commit message
     
         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 ANSI C89).
    +    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.
v1f
  • Rebase
$ git rd 
1:  b7ab3f97a0c8 = 1:  617ded6a961c lib/: Use the comma operator to perform lvalue conversion
v2
  • Add rvalue() and ptr_cast(), to encapsulate these operations.
$ git rd 
1:  617ded6a961c = 1:  617ded6a961c lib/: Use the comma operator to perform lvalue conversion
-:  ------------ > 2:  b2f581e3a2de lib/cast.h: rvalue(): Add macro for performing lvalue conversion
-:  ------------ > 3:  ad5d5b419079 lib/: Use rvalue() instead of its pattern
-:  ------------ > 4:  f2878fbaf173 lib/cast.h: ptr_cast(): Add macro
-:  ------------ > 5:  cbd1a16c7541 lib/: Use ptr_cast() instead of its pattern
v2b
  • Rebase
$ git rd -U0
1:  617ded6a961c ! 1:  512aba26e5a2 lib/: Use the comma operator to perform lvalue conversion
    @@ lib/sizeof.h
    -
    - ## lib/string/strerrno.h ##
    -@@
    - 
    - 
    - // strerrno - string errno
    --#define strerrno()  ({(const char *){strerror(errno)};})
    -+#define strerrno()  ((void)0, (const char *){strerror(errno)})
    - 
    - 
    - #endif  // include guard
2:  b2f581e3a2de = 2:  5bc74aa0449d lib/cast.h: rvalue(): Add macro for performing lvalue conversion
3:  ad5d5b419079 ! 3:  ed9069c3cde6 lib/: Use rvalue() instead of its pattern
    @@ lib/sizeof.h
    -
    - ## lib/string/strerrno.h ##
    -@@
    - 
    - 
    - // strerrno - string errno
    --#define strerrno()  ((void)0, (const char *){strerror(errno)})
    -+#define strerrno()  rvalue((const char *){strerror(errno)})
    - 
    - 
    - #endif  // include guard
4:  f2878fbaf173 = 4:  f787e34499fa lib/cast.h: ptr_cast(): Add macro
5:  cbd1a16c7541 ! 5:  04855d90a948 lib/: Use ptr_cast() instead of its pattern
    @@ lib/search/l/lfind.h
    -
    - ## lib/string/strerrno.h ##
    -@@
    - 
    - 
    - // strerrno - string errno
    --#define strerrno()  rvalue((const char *){strerror(errno)})
    -+#define strerrno()  ptr_cast(const char, strerror(errno))
    - 
    - 
v2c
  • Rebase
$ git rd 
1:  512aba26e5a2 = 1:  473f4bfe7f23 lib/: Use the comma operator to perform lvalue conversion
2:  5bc74aa0449d = 2:  1d1201430ee1 lib/cast.h: rvalue(): Add macro for performing lvalue conversion
3:  ed9069c3cde6 = 3:  ffd6f93a1b1d lib/: Use rvalue() instead of its pattern
4:  f787e34499fa = 4:  7a5c6c94ff63 lib/cast.h: ptr_cast(): Add macro
5:  04855d90a948 = 5:  0fec8a9bd8c2 lib/: Use ptr_cast() instead of its pattern
v2d
  • Update includes.
$ git rd -U0
1:  473f4bfe7f23 = 1:  473f4bfe7f23 lib/: Use the comma operator to perform lvalue conversion
2:  1d1201430ee1 = 2:  1d1201430ee1 lib/cast.h: rvalue(): Add macro for performing lvalue conversion
3:  ffd6f93a1b1d ! 3:  f9c915931851 lib/: Use rvalue() instead of its pattern
    @@ lib/alloc/calloc.h
    + #include <stdlib.h>
    + 
    ++#include "cast.h"
    + #include "exit_if_null.h"
    + #include "sizeof.h"
    + 
    + 
    @@ lib/alloc/malloc.h
    + #include <stdlib.h>
    + 
    + #include "attr.h"
    ++#include "cast.h"
    + #include "exit_if_null.h"
    + #include "sizeof.h"
    + 
    @@ lib/sizeof.h
    + #endif
    + #include <sys/types.h>
    + 
    ++#include "cast.h"
    ++
4:  7a5c6c94ff63 = 4:  611f249602be lib/cast.h: ptr_cast(): Add macro
5:  0fec8a9bd8c2 ! 5:  81f34a70a7f9 lib/: Use ptr_cast() instead of its pattern
    @@ lib/alloc/calloc.h
    + #include "cast.h"
    + #include "exit_if_null.h"
    +-#include "sizeof.h"
    + 
    @@ lib/alloc/malloc.h
    + #include <stdlib.h>
    + 
    + #include "attr.h"
    +-#include "cast.h"
    + #include "exit_if_null.h"
    + #include "sizeof.h"

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 <uecker@tugraz.at>
Cc: Christopher Bazley <chris.bazley@arm.com>
Cc: Kees Cook <kees@kernel.org>
Cc: Richard Russon <rich@flatcap.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
This macro takes an lvalue, and performs lvalue conversion, resulting in
an rvalue.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
This helps document why we use '(void)0' with the comma operator.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
This macro takes a pointer --usually, a void pointer--, and converts it
to a pointer to T, implicitly.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
This helps document why we use compound literals.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant