-
Notifications
You must be signed in to change notification settings - Fork 254
lib/string/: strerrno(): Use statement expression to perform lvalue conversion #1390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…onversion Compound literals are lvalues. This means it's possible to take their address. That is, it would be possible (albeit nonsensical) to do &strerrno(); It is also possible to assign to them (albeit also nonsensical): strerrno() = NULL; The statement expression performs lvalue conversion, which turns the lvalue into an "rvalue", as expected, and disallows all those issues. Signed-off-by: Alejandro Colomar <alx@kernel.org>
79de0c8 to
885f435
Compare
|
What is the concern here? |
Maybe this example shows the concern: alx@devuan:~/tmp$ cat strerrno.c
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define strerrno1() ((const char *){strerror(errno)})
#define strerrno2() ({(const char *){strerror(errno)};})
int
main(void)
{
if (strerrno1() = NULL) // Oops, I meant == NULL
return 1;
if (strerrno2() = NULL) // Oops, I meant == NULL
return 2;
printf("%s\n", &strerrno1());
printf("%s\n", &strerrno2());
}alx@devuan:~/tmp$ gcc strerrno.c
strerrno.c: In function ‘main’:
strerrno.c:15:25: error: lvalue required as left operand of assignment
15 | if (strerrno2() = NULL) // Oops, I meant == NULL
| ^
strerrno.c:19:24: error: lvalue required as unary ‘&’ operand
19 | printf("%s\n", &strerrno2());
| ^With the first implementation, those trivial bugs are not caught by the compiler. With the second implementation (the one used in this PR), the compiler is able to diagnose them. |
|
A cast (alternative 3) would also serve the same purpose, but casts have their own problems (they silence almost every diagnostic), so I don't want to use them if we can avoid them. alx@devuan:~/tmp$ cat strerrno.c
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define strerrno1() ((const char *){strerror(errno)})
#define strerrno2() ({(const char *){strerror(errno)};})
#define strerrno3() ((const char *) strerror(errno))
int
main(void)
{
if (strerrno1() = NULL) // Oops, I meant == NULL
return 1;
if (strerrno2() = NULL) // Oops, I meant == NULL
return 2;
if (strerrno3() = NULL) // Oops, I meant == NULL
return 3;
printf("%s\n", &strerrno1());
printf("%s\n", &strerrno2());
printf("%s\n", &strerrno3());
}alx@devuan:~/tmp$ gcc strerrno.c
strerrno.c: In function ‘main’:
strerrno.c:15:25: error: lvalue required as left operand of assignment
15 | if (strerrno2() = NULL) // Oops, I meant == NULL
| ^
strerrno.c:17:25: error: lvalue required as left operand of assignment
17 | if (strerrno3() = NULL) // Oops, I meant == NULL
| ^
strerrno.c:21:24: error: lvalue required as unary ‘&’ operand
21 | printf("%s\n", &strerrno2());
| ^
strerrno.c:22:24: error: lvalue required as unary ‘&’ operand
22 | printf("%s\n", &strerrno3());
| ^ |
|
Ok, that's what I thought, but that's purely defensive against our own future Still might be worth doing, as I think there are no side effects to worry about. |
Right. There's nothing exploitable. It's purely defensive against our own future silly mistakes. :)
You're welcome! |
Revisions:
v2