Avoid multiple evaluations in STRERROR_XYZ() macros (#39794)

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2025-11-20 08:29:32 +01:00
committed by GitHub
6 changed files with 43 additions and 13 deletions

10
src/basic/errno-util.c Normal file
View File

@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "errno-util.h"
const char* strerror_or_eof(int errnum, char *buf, size_t buflen) {
if (errnum != 0)
return strerror_r(ABS(errnum), buf, buflen);
return "Unexpected EOF";
}

View File

@@ -15,10 +15,9 @@
* Note that we use the GNU variant of strerror_r() here. */
#define STRERROR(errnum) strerror_r(ABS(errnum), (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
/* A helper to print an error message or message for functions that return 0 on EOF.
* Note that we can't use ({ … }) to define a temporary variable, so errnum is
* evaluated twice. */
#define STRERROR_OR_EOF(errnum) ((errnum) != 0 ? STRERROR(errnum) : "Unexpected EOF")
/* A helper to print an error message or message for functions that return 0 on EOF. */
const char* strerror_or_eof(int errnum, char *buf, size_t buflen);
#define STRERROR_OR_EOF(errnum) strerror_or_eof(errnum, (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
static inline void _reset_errno_(int *saved_errno) {
if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */

View File

@@ -31,6 +31,7 @@ basic_sources = files(
'env-file.c',
'env-util.c',
'errno-list.c',
'errno-util.c',
'escape.c',
'ether-addr-util.c',
'extract-word.c',

View File

@@ -28,6 +28,26 @@
#include "user-util.h"
#include "utf8.h"
#define DEFINE_STRERROR_ACCOUNT(type) \
const char* strerror_##type( \
int errnum, \
char *buf, \
size_t buflen) { \
\
errnum = ABS(errnum); \
switch (errnum) { \
case ESRCH: \
return "Unknown " STRINGIFY(type); \
case ENOEXEC: \
return "Not a system " STRINGIFY(type); \
default: \
return strerror_r(errnum, buf, buflen); \
} \
}
DEFINE_STRERROR_ACCOUNT(user);
DEFINE_STRERROR_ACCOUNT(group);
bool uid_is_valid(uid_t uid) {
/* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */

View File

@@ -20,11 +20,11 @@
#define MAP_UID_MIN ((uid_t) 60514)
#define MAP_UID_MAX ((uid_t) 60577)
/* A helper to print an error message when user or group resolution fails.
* Note that we can't use ({ … }) to define a temporary variable, so errnum is
* evaluated multiple times. */
#define STRERROR_USER(errnum) ((errnum) == -ESRCH ? "Unknown user" : (errnum) == -ENOEXEC ? "Not a system user" : STRERROR(errnum))
#define STRERROR_GROUP(errnum) ((errnum) == -ESRCH ? "Unknown group" : (errnum) == -ENOEXEC ? "Not a system group" : STRERROR(errnum))
/* A helper to print an error message when user or group resolution fails. */
const char* strerror_user(int errnum, char *buf, size_t buflen);
#define STRERROR_USER(errnum) strerror_user(errnum, (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
const char* strerror_group(int errnum, char *buf, size_t buflen);
#define STRERROR_GROUP(errnum) strerror_group(errnum, (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
static inline bool ERRNO_IS_NEG_BAD_ACCOUNT(intmax_t r) {
return IN_SET(r,

View File

@@ -40,10 +40,10 @@ TEST(STRERROR) {
ASSERT_NOT_NULL(strstr(c, buf));
}
TEST(STRERROR_OR_ELSE) {
log_info("STRERROR_OR_ELSE(0, \"EOF\") → %s", STRERROR_OR_EOF(0));
log_info("STRERROR_OR_ELSE(EPERM, \"EOF\") → %s", STRERROR_OR_EOF(EPERM));
log_info("STRERROR_OR_ELSE(-EPERM, \"EOF\") → %s", STRERROR_OR_EOF(-EPERM));
TEST(STRERROR_OR_EOF) {
log_info("STRERROR_OR_EOF(0, \"EOF\") → %s", STRERROR_OR_EOF(0));
log_info("STRERROR_OR_EOF(EPERM, \"EOF\") → %s", STRERROR_OR_EOF(EPERM));
log_info("STRERROR_OR_EOF(-EPERM, \"EOF\") → %s", STRERROR_OR_EOF(-EPERM));
}
TEST(PROTECT_ERRNO) {