2020-11-09 13:23:58 +09:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2015-10-27 01:26:31 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <printf.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include "macro.h"
|
|
|
|
|
|
2022-08-12 13:17:24 +02:00
|
|
|
_printf_(3, 4)
|
2024-04-28 15:59:36 +08:00
|
|
|
static inline char* snprintf_ok(char *buf, size_t len, const char *format, ...) {
|
2022-08-12 13:17:24 +02:00
|
|
|
va_list ap;
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
|
r = vsnprintf(buf, len, format, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return r >= 0 && (size_t) r < len ? buf : NULL;
|
|
|
|
|
}
|
2015-10-27 01:26:31 +01:00
|
|
|
|
udev/net-id: check all snprintf return values
gcc-8 throws an error if it knows snprintf might truncate output and the
return value is ignored:
../src/udev/udev-builtin-net_id.c: In function 'dev_pci_slot':
../src/udev/udev-builtin-net_id.c:297:47: error: '%s' directive output may be truncated writing up to 255 bytes into a region of size between 0 and 4095 [-Werror=format-truncation=]
snprintf(str, sizeof str, "%s/%s/address", slots, dent->d_name);
^~
../src/udev/udev-builtin-net_id.c:297:17: note: 'snprintf' output between 10 and 4360 bytes into a destination of size 4096
snprintf(str, sizeof str, "%s/%s/address", slots, dent->d_name);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
Let's check all return values. This actually makes the code better, because there's
no point in trying to open a file when the name has been truncated, etc.
2018-02-23 11:12:19 +01:00
|
|
|
#define xsprintf(buf, fmt, ...) \
|
2022-10-07 15:26:36 +02:00
|
|
|
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
|
2015-10-27 01:26:31 +01:00
|
|
|
|
|
|
|
|
#define VA_FORMAT_ADVANCE(format, ap) \
|
|
|
|
|
do { \
|
|
|
|
|
int _argtypes[128]; \
|
|
|
|
|
size_t _i, _k; \
|
2018-11-04 21:43:08 +01:00
|
|
|
/* See https://github.com/google/sanitizers/issues/992 */ \
|
|
|
|
|
if (HAS_FEATURE_MEMORY_SANITIZER) \
|
2022-08-12 13:54:49 +02:00
|
|
|
memset(_argtypes, 0, sizeof(_argtypes)); \
|
2015-10-27 01:26:31 +01:00
|
|
|
_k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
|
|
|
|
|
assert(_k < ELEMENTSOF(_argtypes)); \
|
|
|
|
|
for (_i = 0; _i < _k; _i++) { \
|
|
|
|
|
if (_argtypes[_i] & PA_FLAG_PTR) { \
|
|
|
|
|
(void) va_arg(ap, void*); \
|
|
|
|
|
continue; \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
switch (_argtypes[_i]) { \
|
|
|
|
|
case PA_INT: \
|
|
|
|
|
case PA_INT|PA_FLAG_SHORT: \
|
|
|
|
|
case PA_CHAR: \
|
|
|
|
|
(void) va_arg(ap, int); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_INT|PA_FLAG_LONG: \
|
|
|
|
|
(void) va_arg(ap, long int); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_INT|PA_FLAG_LONG_LONG: \
|
|
|
|
|
(void) va_arg(ap, long long int); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_WCHAR: \
|
|
|
|
|
(void) va_arg(ap, wchar_t); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_WSTRING: \
|
|
|
|
|
case PA_STRING: \
|
|
|
|
|
case PA_POINTER: \
|
|
|
|
|
(void) va_arg(ap, void*); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_FLOAT: \
|
|
|
|
|
case PA_DOUBLE: \
|
|
|
|
|
(void) va_arg(ap, double); \
|
|
|
|
|
break; \
|
|
|
|
|
case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
|
|
|
|
|
(void) va_arg(ap, long double); \
|
|
|
|
|
break; \
|
|
|
|
|
default: \
|
Drop the text argument from assert_not_reached()
In general we almost never hit those asserts in production code, so users see
them very rarely, if ever. But either way, we just need something that users
can pass to the developers.
We have quite a few of those asserts, and some have fairly nice messages, but
many are like "WTF?" or "???" or "unexpected something". The error that is
printed includes the file location, and function name. In almost all functions
there's at most one assert, so the function name alone is enough to identify
the failure for a developer. So we don't get much extra from the message, and
we might just as well drop them.
Dropping them makes our code a tiny bit smaller, and most importantly, improves
development experience by making it easy to insert such an assert in the code
without thinking how to phrase the argument.
2021-07-27 12:27:28 +02:00
|
|
|
assert_not_reached(); \
|
2015-10-27 01:26:31 +01:00
|
|
|
} \
|
|
|
|
|
} \
|
2016-02-23 09:52:52 -08:00
|
|
|
} while (false)
|