From: Zbigniew Jędrzejewski-Szmek Date: Tue, 3 Mar 2026 09:00:29 +0000 (+0100) Subject: basic/stdio-util: introduce asprintf_safe X-Git-Tag: v260-rc3~51^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=52abc9fe9655b21329eda7cf7a7d73fc3a7bdde7;p=thirdparty%2Fsystemd.git basic/stdio-util: introduce asprintf_safe asprintf is nice to use, but the _documented_ error return convention is unclear: > If memory allocation wasn't possible, or some other error occurs, > these functions will return -1, and the contents of strp are undefined. What exactly "undefined" means is up for debate: if it was really undefined, the caller wouldn't be able to meaningfully clean up, because they wouldn't know if strp is a valid pointer. So far we interpreted "undefined" — in some parts of the code base — as "either NULL or a valid pointer that needs to be freed", and — in other parts of the codebase — as "always NULL". I checked glibc and musl, and they both uncoditionally set the output pointer to NULL on failure. There is also no information _why_ asprintf failed. It could be an allocation error or format string error. But we just don't have this information. Let's add a wrapper that either returns a good string or a NULL pointer. Since there's just one failure result, we don't need a separate return value and an output argument and can simplify callers. --- diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md index 767ab6734bb..085c97df5ce 100644 --- a/docs/CODING_STYLE.md +++ b/docs/CODING_STYLE.md @@ -754,9 +754,9 @@ SPDX-License-Identifier: LGPL-2.1-or-later section of the `alloca(3)` man page. - If you want to concatenate two or more strings, consider using `strjoina()` - or `strjoin()` rather than `asprintf()`, as the latter is a lot slower. This - matters particularly in inner loops (but note that `strjoina()` cannot be - used there). + or `strjoin()` rather than `asprintf()` or `asprintf_safe`, as the latter is + a lot slower. This matters particularly in inner loops (but note that + `strjoina()` cannot be used there). ## Runtime Behaviour diff --git a/src/basic/meson.build b/src/basic/meson.build index b8ffa80244d..775dc1fa3d5 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -99,6 +99,7 @@ basic_sources = files( 'sort-util.c', 'stat-util.c', 'static-destruct.c', + 'stdio-util.c', 'strbuf.c', 'string-table.c', 'string-util.c', diff --git a/src/basic/stdio-util.c b/src/basic/stdio-util.c new file mode 100644 index 00000000000..53267f08e23 --- /dev/null +++ b/src/basic/stdio-util.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "stdio-util.h" + +char* asprintf_safe(const char *restrict fmt, ...) { + _cleanup_free_ char *buf = NULL; + va_list ap; + int r; + + va_start(ap, fmt); + r = vasprintf(&buf, fmt, ap); + va_end(ap); + + if (r < 0) + return NULL; + return TAKE_PTR(buf); +} diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h index f8ab0f0012f..f8055b3853b 100644 --- a/src/basic/stdio-util.h +++ b/src/basic/stdio-util.h @@ -21,6 +21,8 @@ static inline char* snprintf_ok(char *buf, size_t len, const char *format, ...) #define xsprintf(buf, fmt, ...) \ assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: buffer too small") +char* asprintf_safe(const char *restrict fmt, ...) _printf_(1, 2); + #define VA_FORMAT_ADVANCE(format, ap) \ do { \ int _argtypes[128]; \