| Message ID | cover.1773870057.git.alx@kernel.org (mailing list archive) |
|---|---|
| Headers |
Return-Path: <libc-alpha-bounces~patchwork=sourceware.org@sourceware.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 41C254BCA42D for <patchwork@sourceware.org>; Wed, 18 Mar 2026 21:48:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 41C254BCA42D Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20201202 header.b=ESzRYxTg X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from sea.source.kernel.org (sea.source.kernel.org [IPv6:2600:3c0a:e001:78e:0:1991:8:25]) by sourceware.org (Postfix) with ESMTPS id 8E92C4BC898B for <libc-alpha@sourceware.org>; Wed, 18 Mar 2026 21:47:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8E92C4BC898B Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=kernel.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kernel.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8E92C4BC898B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2600:3c0a:e001:78e:0:1991:8:25 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773870472; cv=none; b=aMXPCjCGI6FxM0s+oQCo2RA+7azL2ORG3R77i1CRuVjPcaOyRc/jPD3mr/yaeaFn+hswXRpBQ0Mfv5DIWT5dlFDkb2Yn1RLT84IBCMIKJ/7zEMrFPFSmR8TGe8cWR4yRUTN2LtmEiPVWfKuvuf7IdsU2kFFiVzKFJYg2l+F17n0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773870472; c=relaxed/simple; bh=hLsvSkoq0zeOvNPN/baXeHVS+j52btp8Nw+bO/Unq8s=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=Ihfq4Q+omFYuR5hb3rJB/+tXzQx1sTkAarpKFApLrnDsbxYilMbkMEZhpKtwJMFsM5IRnjz7ariFdueutbo6xs7wPmgRHf43FBWQ9uBpCpWo+ZlcwapLjOgJcX38tSSQo7wTbaGrORWWPz4+y1SF6jYCy4amLPV3wKzqFTczAYI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8E92C4BC898B Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sea.source.kernel.org (Postfix) with ESMTP id D26FF444B4; Wed, 18 Mar 2026 21:47:51 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 81D37C19421; Wed, 18 Mar 2026 21:47:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773870471; bh=hLsvSkoq0zeOvNPN/baXeHVS+j52btp8Nw+bO/Unq8s=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=ESzRYxTgSvYNISP7pcmzB+TzAjx8AcJ73abML65zQI1WjXcw6qZQMySm+BKlcmM7H enrmRBvVcLziAwgZOFMq8omdW8oW1ErVLnPhnLWEkQ+0q8MYanaiSkmqhxu4H//BQJ hh8gG2jv9ZUHx84err+9yT9Aj+OHoiAUGNhDNAEfN6Gge/JqJiJlheoPgWaxXc824L 5LDXqtv/BEyS7cPiog2HHFNIQZ3FYcYTwWzaGqW6LqEVIq9tpTRx9rLMUmI8SPhOKV cYETmt+aVuJymKzaHEm/ogzDlRNOgwRyeqHOqbOaCqkCReGZ8jgmRf5Qx3vLeB7ICJ qYsXX62xHpm+A== Date: Wed, 18 Mar 2026 22:47:47 +0100 From: Alejandro Colomar <alx@kernel.org> To: libc-alpha@sourceware.org Cc: Alejandro Colomar <alx@kernel.org>, Joseph Myers <josmyers@redhat.com>, Paul Eggert <eggert@cs.ucla.edu>, Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>, Florian Weimer <fweimer@redhat.com>, "Dmitry V. Levin" <ldv@altlinux.org>, Archie Cobbs <archie.cobbs@gmail.com>, Solar Designer <solar@openwall.com>, Sam James <sam@gentoo.org> Subject: [RFC v4 0/2] Add [v]aprintf(3) Message-ID: <cover.1773870057.git.alx@kernel.org> X-Mailer: git-send-email 2.53.0 References: <abSVWeS3nHmkZSKn@devuan> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <abSVWeS3nHmkZSKn@devuan> X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list <libc-alpha.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/libc-alpha/> List-Post: <mailto:libc-alpha@sourceware.org> List-Help: <mailto:libc-alpha-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=subscribe> Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org |
| Series |
Add [v]aprintf(3)
|
|
Message
Alejandro Colomar
March 18, 2026, 9:47 p.m. UTC
Hi!
In v4:
- I've written the code to resemble [v]asprintf(3), as Joseph
suggested.
- I fixed the 'lint-makefiles' issues, which I think were because
I wasn't using alphabetic order.
- I've also sorted [v]aprintf(3) before [v]asprintf(3) in source code.
- I've written commit messages.
See range-diff at the bottom.
Regression testing now says ok.
$ diff -u ../.tmp.master/tests.sum tests.sum
--- ../.tmp.master/tests.sum 2026-03-18 18:52:49.914978699 +0100
+++ tests.sum 2026-03-18 22:36:56.877761477 +0100
@@ -1936,6 +1936,7 @@
PASS: libio/test-fputs-unbuffered-full
PASS: libio/test-fputws-unbuffered-full
PASS: libio/test-freopen
+PASS: libio/tst-aprintf
PASS: libio/tst-asprintf-null
PASS: libio/tst-atime
PASS: libio/tst-bz22415
Things I haven't done:
- I still didn't write tests for the sysdeps _chk implementations, nor
the Versions files under sysdeps/. I'm aware I need to do this.
- I didn't update '.abilist' files. Is that something I should do?
Is there anything else I'm missing?
Have a lovely night!
Alex
Alejandro Colomar (2):
Add [v]aprintf(3)
manual/: Prefer aprintf(3) over asprintf(3)
debug/Makefile | 4 ++
debug/Versions | 3 +
debug/aprintf_chk.c | 51 +++++++++++++++++
debug/vaprintf_chk.c | 39 +++++++++++++
include/stdio.h | 8 +++
libio/Makefile | 4 +-
libio/Versions | 3 +
libio/bits/stdio-ldbl.h | 5 ++
libio/bits/stdio2-decl.h | 8 +++
libio/bits/stdio2.h | 42 ++++++++++++++
libio/stdio.h | 13 +++++
libio/tst-aprintf.c | 57 +++++++++++++++++++
libio/vaprintf.c | 39 +++++++++++++
manual/examples/rprintf.c | 4 +-
manual/stdio.texi | 30 +++++++++-
manual/string.texi | 2 +-
stdio-common/Makefile | 2 +
stdio-common/Versions | 2 +
stdio-common/aprintf.c | 40 +++++++++++++
sysdeps/ieee754/ldbl-128ibm-compat/Makefile | 8 ++-
.../ldbl-128ibm-compat/ieee128-aprintf.c | 36 ++++++++++++
.../ldbl-128ibm-compat/ieee128-aprintf_chk.c | 40 +++++++++++++
.../ldbl-128ibm-compat/ieee128-vaprintf.c | 29 ++++++++++
.../ldbl-128ibm-compat/ieee128-vaprintf_chk.c | 31 ++++++++++
sysdeps/ieee754/ldbl-opt/Makefile | 6 ++
sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c | 17 ++++++
sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c | 15 +++++
sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c | 9 +++
sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c | 8 +++
29 files changed, 548 insertions(+), 7 deletions(-)
create mode 100644 debug/aprintf_chk.c
create mode 100644 debug/vaprintf_chk.c
create mode 100644 libio/tst-aprintf.c
create mode 100644 libio/vaprintf.c
create mode 100644 stdio-common/aprintf.c
create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf.c
create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf_chk.c
create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf.c
create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf_chk.c
create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c
create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c
create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c
create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c
Range-diff against v3:
1: f616528f ! 1: f927e1d9 Add [v]aprintf(3)
@@ Metadata
## Commit message ##
Add [v]aprintf(3)
+ This function is fundamentally the same as asprintf(3) --which was
+ standardized in POSIX.1-2024--.
+
+ However, it has a key difference, which makes it a much better design.
+ Instead of returning the newly allocated pointer through a char**
+ parameter, it returns the pointer (like strdup(3)). The API is as
+ simple as it can be:
+
+ char *aprintf(const char *restrict fmt, ...);
+ char *vaprintf(const char *restrict fmt, va_list ap);
+
+ This difference has several benefits:
+
+ - One less parameter. This means less source code for using this API,
+ which implicitly means less chances of making mistakes. It is also
+ less noise while reading code, which increases readability.
+
+ Especially, we get rid of a pointer-to-pointer parameter, which are
+ especially weird.
+
+ - The function has an prototype that resembles strdup(3). This
+ functionality is commonly paired with strdup(3) calls, because
+ they're very related. By having a similar prototype, the code is
+ more naturally paired with
+
+ Compare:
+
+ if (cond) {
+ p = strdup("foo");
+ if (p == NULL)
+ goto fail;
+ } else {
+ if (asprintf(&p, "foo %d", 42) < 0)
+ goto fail;
+ }
+ vs
+ p = cond ? strdup("foo") : aprintf("foo %d", 42);
+ if (p == NULL)
+ goto fail;
+
+ - We don't need to fail with EOVERFLOW. This initial implementation
+ still fails with EOVERFLOW, because it would require a lot of work
+ implementing it in a way that doesn't have such a failure point, but
+ the API has no inherent reasons to fail with EOVERFLOW.
+
+ - This API returns a pointer, which makes it possible to use
+ __attribute__((__malloc__(free))). This would allow static analysers
+ to know that these APIs allocate memory, which would prevent memory
+ leaks, use-after-free errors, and other common issues of handling
+ memory.
+
+ The name is similar to asprintf(3), removing the 's'. This is the same
+ name that gnulib has used for their implementation. It also matches the
+ name of the n3750 (alx-0007) proposal for ISO C2y.
+
+ The C Committee was worried that such a non-reserved name might be
+ unlikely to be viable, as some existing code already uses it. However:
+
+ - Most code using this name, uses it with the same exact semantics.
+ In those cases, it's not a problem.
+
+ - In the remaining cases, the prototype is different, so they'll
+ eventually get a compiler error for the clash. This is entirely
+ safe. And by hiding the name under _GNU_SOURCE, we make that clash
+ less likely. Once/if this is eventually standard, I expect people
+ will adapt their code, which is entirely reasonable. Still, then the
+ APIs will be hidden under _ISOC2Y_SOURCE et al., so this will only
+ affect users when they opt-in to such a version.
+
+ Link: <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3750.txt>
+ Cc: Joseph Myers <josmyers@redhat.com>
+ Cc: Paul Eggert <eggert@cs.ucla.edu>
+ Cc: Bruno Haible <bruno@clisp.org>
+ Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
+ Cc: Florian Weimer <fweimer@redhat.com>
+ Cc: "Dmitry V. Levin" <ldv@altlinux.org>
+ Cc: Archie Cobbs <archie.cobbs@gmail.com>
+ Cc: Solar Designer <solar@openwall.com>
+ Cc: Sam James <sam@gentoo.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
+ ## debug/Makefile ##
+@@ debug/Makefile: headers := execinfo.h
+ routines = \
+ $(static-only-routines) \
+ ____longjmp_chk \
++ aprintf_chk \
+ asprintf_chk \
+ backtrace \
+ backtracesyms \
+@@ debug/Makefile: routines = \
+ strncpy_chk \
+ swprintf_chk \
+ ttyname_r_chk \
++ vaprintf_chk \
+ vasprintf_chk \
+ vdprintf_chk \
+ vfprintf_chk \
+@@ debug/Makefile: CFLAGS-sprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-snprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-vsprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-vsnprintf_chk.c += $(libio-mtsafe)
++CFLAGS-aprintf_chk.c += $(libio-mtsafe)
++CFLAGS-vaprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-asprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-vasprintf_chk.c += $(libio-mtsafe)
+ CFLAGS-obprintf_chk.c += $(libio-mtsafe)
+
## debug/Versions ##
@@ debug/Versions: libc {
GLIBC_2.43 {
@@ debug/Versions: libc {
__fortify_fail;
}
+ ## debug/aprintf_chk.c (new) ##
+@@
++/* Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>. */
++
++#include <stdarg.h>
++#include <libio/libioP.h>
++
++
++/* Write formatted output from FORMAT to a string allocated with malloc. */
++char *
++___aprintf_chk (int flag, const char *fmt, ...)
++{
++ /* For flag > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n
++ can only come from read-only format strings. */
++ unsigned int mode = (flag > 0) ? PRINTF_FORTIFY : 0;
++ va_list ap;
++ char *p;
++
++ va_start (ap, fmt);
++ if (__vasprintf_internal (&p, fmt, ap, mode) < 0)
++ p = NULL;
++ va_end (ap);
++
++ return p;
++}
++#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
++/* This is needed since <bits/stdio-lbdl.h> is included in this case, leading to
++ * multiple asm redirection of the same symbol
++ */
++ldbl_hidden_def (___aprintf_chk, __aprintf_chk)
++ldbl_strong_alias (___aprintf_chk, __aprintf_chk)
++#else
++/* On some systems introduction of ldbl_* macros lead to ABI breakage due to the
++ * long_double_symbol aliasing.
++ */
++strong_alias (___aprintf_chk, __aprintf_chk)
++libc_hidden_def (__aprintf_chk)
++#endif
+
+ ## debug/vaprintf_chk.c (new) ##
+@@
++/* Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>.
++
++ As a special exception, if you link the code in this file with
++ files compiled with a GNU compiler to produce an executable,
++ that does not cause the resulting executable to be covered by
++ the GNU Lesser General Public License. This exception does not
++ however invalidate any other reasons why the executable file
++ might be covered by the GNU Lesser General Public License.
++ This exception applies to code released by its copyright holders
++ in files containing the exception. */
++
++#include <libio/libioP.h>
++
++char *
++__vaprintf_chk (int flag, const char *fmt, va_list ap)
++{
++ /* For flag > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n
++ can only come from read-only format strings. */
++ unsigned int mode = (flag > 0) ? PRINTF_FORTIFY : 0;
++ char *p;
++
++ return __vasprintf_internal (&p, fmt, ap, mode) < 0 ? NULL : p;
++}
++libc_hidden_def (__vaprintf_chk)
+
## include/stdio.h ##
-@@ include/stdio.h: extern const char *__get_errname (int) attribute_hidden;
+@@ include/stdio.h: extern int __vfprintf_chk (FILE *, int, const char *, __gnuc_va_list);
+ stdio_hidden_ldbl_proto (__, vfprintf_chk)
+ extern char *__fgets_unlocked_chk (char *buf, size_t size, int n, FILE *fp);
+ extern char *__fgets_chk (char *buf, size_t size, int n, FILE *fp);
++extern char *__aprintf_chk (int, const char *, ...)
++ __THROW __attribute_malloc__;
++extern char *__vaprintf_chk (int, const char *, __gnuc_va_list)
++ __THROW __attribute_malloc__;
++stdio_hidden_ldbl_proto (__, vaprintf_chk)
+ extern int __asprintf_chk (char **, int, const char *, ...) __THROW;
+ extern int __vasprintf_chk (char **, int, const char *, __gnuc_va_list) __THROW;
+ stdio_hidden_ldbl_proto (__, vasprintf_chk)
+@@ include/stdio.h: extern const size_t _sys_errlist_internal_len attribute_hidden;
+ extern const char *__get_errlist (int) attribute_hidden;
+ extern const char *__get_errname (int) attribute_hidden;
++libc_hidden_ldbl_proto (__aprintf)
libc_hidden_ldbl_proto (__asprintf)
-+extern __typeof (aprintf) __aprintf;
-+libc_hidden_ldbl_proto (__aprintf)
-+
# if IS_IN (libc)
- extern FILE *_IO_new_fopen (const char*, const char*);
- # define fopen(fname, mode) _IO_new_fopen (fname, mode)
+@@ include/stdio.h: rtld_hidden_proto (__libc_fatal)
+ libc_hidden_proto (__fgets_unlocked_chk)
+
+ #if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1
++libc_hidden_ldbl_proto (__aprintf_chk)
+ libc_hidden_ldbl_proto (__asprintf_chk)
+ #else
++libc_hidden_proto (__aprintf_chk)
+ libc_hidden_proto (__asprintf_chk)
+ #endif
+
## libio/Makefile ##
@@ libio/Makefile: routines := \
@@ libio/Makefile: routines := \
\
__fbufsize __freading __fwriting __freadable __fwritable __flbf \
@@ libio/Makefile: routines_no_fortify += \
+ iofgetws_u \
iovdprintf \
swprintf \
- vasprintf \
+ vaprintf \
+ vasprintf \
vsnprintf \
vswprintf \
- vwprintf \
@@ libio/Makefile: tests = \
+ test-fmemopen \
test-fputs-unbuffered-full \
test-fputws-unbuffered-full \
- tst-asprintf-null \
+ tst-aprintf \
+ tst-asprintf-null \
tst-atime \
tst-bz22415 \
- tst-bz24051 \
## libio/Versions ##
@@ libio/Versions: libc {
@@ libio/Versions: libc {
__libc_fatal;
## libio/bits/stdio-ldbl.h ##
-@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR2_DECL (vdprintf_chk)
+@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR_DECL (dprintf)
+ #endif
+
+ #ifdef __USE_GNU
++__LDBL_REDIR_DECL (vaprintf)
++__LDBL_REDIR2_DECL (aprintf)
++__LDBL_REDIR_DECL (aprintf)
+ __LDBL_REDIR_DECL (vasprintf)
+ __LDBL_REDIR2_DECL (asprintf)
+ __LDBL_REDIR_DECL (asprintf)
+@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR2_DECL (dprintf_chk)
+ __LDBL_REDIR2_DECL (vdprintf_chk)
+ # endif
# ifdef __USE_GNU
- __LDBL_REDIR2_DECL (asprintf_chk)
- __LDBL_REDIR2_DECL (vasprintf_chk)
+__LDBL_REDIR2_DECL (aprintf_chk)
+__LDBL_REDIR2_DECL (vaprintf_chk)
+ __LDBL_REDIR2_DECL (asprintf_chk)
+ __LDBL_REDIR2_DECL (vasprintf_chk)
__LDBL_REDIR2_DECL (obstack_printf_chk)
- __LDBL_REDIR2_DECL (obstack_vprintf_chk)
- # endif
## libio/bits/stdio2-decl.h ##
-@@ libio/bits/stdio2-decl.h: extern int __asprintf_chk (char **__restrict __ptr, int __flag,
- extern int __vasprintf_chk (char **__restrict __ptr, int __flag,
- const char *__restrict __fmt, __gnuc_va_list __arg)
- __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur;
+@@ libio/bits/stdio2-decl.h: extern int __vdprintf_chk (int __fd, int __flag,
+
+ # ifdef __USE_GNU
+
+extern char *__aprintf_chk (int __flag,
+ const char *__restrict __fmt, ...)
+ __THROW __attribute__ ((__format__ (__printf__, 2, 3)))
+ __attribute_malloc__;
+extern char *__vaprintf_chk (int __flag,
-+ const char *__restrict __fmt, __gnuc_va_list __arg)
++ const char *__restrict __fmt, __gnuc_va_list __ap)
+ __THROW __attribute__ ((__format__ (__printf__, 2, 0)))
+ __attribute_malloc__;
- extern int __obstack_printf_chk (struct obstack *__restrict __obstack,
- int __flag, const char *__restrict __format,
- ...)
+ extern int __asprintf_chk (char **__restrict __ptr, int __flag,
+ const char *__restrict __fmt, ...)
+ __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur;
## libio/bits/stdio2.h ##
-@@ libio/bits/stdio2.h: __NTH (__asprintf (char **__restrict __ptr, const char *__restrict __fmt,
- __va_arg_pack ());
- }
+@@ libio/bits/stdio2.h: vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __ap)
+ # ifdef __USE_GNU
+ # ifdef __va_arg_pack
+__fortify_function char *
+__NTH (aprintf (const char *__restrict __fmt, ...))
+{
@@ libio/bits/stdio2.h: __NTH (__asprintf (char **__restrict __ptr, const char *__r
+}
+
__fortify_function int
- __NTH (obstack_printf (struct obstack *__restrict __obstack,
- const char *__restrict __fmt, ...))
-@@ libio/bits/stdio2.h: __NTH (__asprintf (__fortify_clang_overload_arg (char **, __restrict, __ptr),
- return __r;
+ __NTH (asprintf (char **__restrict __ptr, const char *__restrict __fmt, ...))
+ {
+@@ libio/bits/stdio2.h: __NTH (obstack_printf (struct obstack *__restrict __obstack,
+ __va_arg_pack ());
}
-
+ # elif __fortify_use_clang
+__fortify_function char *
+__NTH (aprintf (const char *__restrict __fmt, ...))
+{
@@ libio/bits/stdio2.h: __NTH (__asprintf (__fortify_clang_overload_arg (char **, _
+}
+
__fortify_function_error_function __attribute_overloadable__ int
- __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *,
- __restrict, __obstack),
+ __NTH (asprintf (__fortify_clang_overload_arg (char **, __restrict, __ptr),
+ const char *__restrict __fmt, ...))
@@ libio/bits/stdio2.h: __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *,
- __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
- # define __asprintf(ptr, ...) \
- __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
+ return __r;
+ }
+ # elif !defined __cplusplus
+# define aprintf(...) \
+ __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
+# define __aprintf(...) \
+ __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
- # define obstack_printf(obstack, ...) \
+ # define asprintf(ptr, ...) \
+ __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
+ # define __asprintf(ptr, ...) \
+@@ libio/bits/stdio2.h: __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *,
__obstack_printf_chk (obstack, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__)
# endif
-@@ libio/bits/stdio2.h: __NTH (vasprintf (char **__restrict __ptr, const char *__restrict __fmt,
- return __vasprintf_chk (__ptr, __USE_FORTIFY_LEVEL - 1, __fmt, __ap);
- }
+__fortify_function char *
+__NTH (vaprintf (const char *__restrict __fmt, __gnuc_va_list __ap))
@@ libio/bits/stdio2.h: __NTH (vasprintf (char **__restrict __ptr, const char *__re
+}
+
__fortify_function int
- __NTH (obstack_vprintf (struct obstack *__restrict __obstack,
- const char *__restrict __fmt, __gnuc_va_list __ap))
+ __NTH (vasprintf (char **__restrict __ptr, const char *__restrict __fmt,
+ __gnuc_va_list __ap))
## libio/stdio.h ##
@@ libio/stdio.h: extern int asprintf (char **__restrict __ptr,
@@ libio/stdio.h: extern int asprintf (char **__restrict __ptr,
+#ifdef __USE_GNU
+/* Write formatted output to a string dynamically allocated with `malloc'. */
-+extern char *vaprintf (const char *__restrict __f, __gnuc_va_list __arg)
++extern char *vaprintf (const char *__restrict __fmt, __gnuc_va_list __ap)
+ __THROWNL __attribute__ ((__format__ (__printf__, 1, 0)))
+ __attribute_malloc__;
++extern char *__aprintf (const char *__restrict __fmt, ...)
++ __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
++ __attribute_malloc__;
+extern char *aprintf (const char *__restrict __fmt, ...)
+ __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
+ __attribute_malloc__;
@@ libio/tst-aprintf.c (new)
+do_test (void)
+{
+ char *buf;
++
++ /* Success */
++ buf = aprintf ("foo %d", 42);
++ TEST_COMPARE_STRING (buf, "foo 42");
++ free(buf);
++
+ {
+ /* Avoid -Wformat-overflow warning. */
+ const char *volatile format = "%2000000000d %2000000000d";
@@ libio/tst-aprintf.c (new)
+ TEST_VERIFY (buf == NULL);
+ TEST_COMPARE (errno, ENOMEM);
+
-+ /* Success */
-+ buf = aprintf ("foo %d", 42);
-+ TEST_COMPARE_STRING (buf, "foo 42");
-+ free(buf);
-+
+ return 0;
+}
+
@@ libio/vaprintf.c (new)
+#include <stdio.h>
+
+char *
-+__vaprintf (const char *fmt, va_list args)
++__vaprintf (const char *fmt, va_list ap)
+{
+ char *p;
+
-+ return __vasprintf_internal (&p, fmt, args, 0) < 0 ? NULL : p;
++ return __vasprintf_internal (&p, fmt, ap, 0) < 0 ? NULL : p;
+}
+ldbl_weak_alias (__vaprintf, vaprintf)
@@ manual/stdio.texi: other systems offer this function as an async-signal-safe alt
## stdio-common/Makefile ##
@@ stdio-common/Makefile: routines := \
+ _fitoa_word \
_itoa \
_itowa \
- asprintf \
+ aprintf \
+ asprintf \
ctermid \
cuserid \
- dprintf \
@@ stdio-common/Makefile: routines := \
+
# Exclude fortified routines from being built with _FORTIFY_SOURCE
routines_no_fortify += \
- asprintf \
+ aprintf \
+ asprintf \
dprintf \
fprintf \
- printf \
## stdio-common/Versions ##
@@ stdio-common/Versions: libc {
@@ stdio-common/aprintf.c (new)
+
+ldbl_strong_alias (___aprintf, __aprintf)
+ldbl_weak_alias (___aprintf, aprintf)
+
+ ## sysdeps/ieee754/ldbl-128ibm-compat/Makefile ##
+@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: ldbl-extra-routines += fwscanf \
+ swscanf \
+ swprintf \
+ wscanf \
++ vaprintf \
+ vasprintf \
+ vdprintf \
+ vscanf \
+@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: endif
+
+ ifeq ($(subdir),stdio-common)
+ ldbl-extra-routines += printf_size \
++ aprintf \
+ asprintf \
+ dprintf \
+ fprintf \
+@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: $(objpfx)test-printf-size-ibm128.out: \
+ endif
+
+ ifeq ($(subdir),debug)
+-ldbl-extra-routines += asprintf_chk \
++ldbl-extra-routines += aprintf_chk \
++ asprintf_chk \
+ dprintf_chk \
+ fprintf_chk \
+ printf_chk \
+ snprintf_chk \
+ sprintf_chk \
++ vaprintf_chk \
+ vasprintf_chk \
+ vdprintf_chk \
+ vfprintf_chk \
+@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: endif
+ routines += $(foreach r,$(ldbl-extra-routines),ieee128-$(r))
+
+ routines_no_fortify += \
++ ieee128-aprintf \
+ ieee128-asprintf \
+ ieee128-dprintf \
+ ieee128-fprintf \
+@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: routines_no_fortify += \
+ ieee128-sprintf \
+ ieee128-swprintf \
+ ieee128-syslog \
++ ieee128-vaprintf \
+ ieee128-vasprintf \
+ ieee128-vdprintf \
+ ieee128-vfprintf \
+
+ ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf.c (new) ##
+@@
++/* Wrapper for aprintf. IEEE128 version.
++ Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>. */
++
++#include <stdarg.h>
++#include <libio/libioP.h>
++
++extern char *
++___ieee128___aprintf (const char *fmt, ...)
++{
++ va_list ap;
++ char *p;
++
++ va_start (ap, fmt);
++ if (__vasprintf_internal (&p, fmt, ap, PRINTF_LDBL_USES_FLOAT128) < 0)
++ p = NULL;
++ va_end (ap);
++
++ return p;
++}
++hidden_def (___ieee128___aprintf)
++strong_alias (___ieee128___aprintf, __aprintfieee128)
+
+ ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf_chk.c (new) ##
+@@
++/* Wrapper for __aprintf_chk. IEEE128 version.
++ Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>. */
++
++#include <stdarg.h>
++#include <libio/libioP.h>
++
++extern char *
++___ieee128___aprintf_chk (int flag, const char *fmt, ...)
++{
++ va_list ap;
++ char *p;
++
++ unsigned int mode = PRINTF_LDBL_USES_FLOAT128;
++ if (flag > 0)
++ mode |= PRINTF_FORTIFY;
++
++ va_start (ap, fmt);
++ if (__vasprintf_internal (&p, fmt, ap, mode) < 0)
++ p = NULL;
++ va_end (ap);
++
++ return p;
++}
++hidden_def (___ieee128___aprintf_chk)
++strong_alias (___ieee128___aprintf_chk, __aprintf_chkieee128)
+
+ ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf.c (new) ##
+@@
++/* Wrapper for vaprintf. IEEE128 version.
++ Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>. */
++
++#include <libio/libioP.h>
++
++extern char *
++___ieee128_vaprintf (const char *fmt, va_list ap)
++{
++ char *p;
++ if (__vasprintf_internal (&p, fmt, ap, PRINTF_LDBL_USES_FLOAT128) < 0)
++ p = NULL;
++ return p;
++}
++strong_alias (___ieee128_vaprintf, __vaprintfieee128)
+
+ ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf_chk.c (new) ##
+@@
++/* Wrapper for __vaprintf_chk. IEEE128 version.
++ Copyright (C) 2026 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ The GNU C Library is free software; you can redistribute it and/or
++ modify it under the terms of the GNU Lesser General Public
++ License as published by the Free Software Foundation; either
++ version 2.1 of the License, or (at your option) any later version.
++
++ The GNU C Library is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public
++ License along with the GNU C Library; if not, see
++ <https://www.gnu.org/licenses/>. */
++
++#include <libio/libioP.h>
++
++extern char *
++___ieee128___vaprintf_chk (int flag, const char *fmt, va_list ap)
++{
++ char *p;
++ unsigned int mode = PRINTF_LDBL_USES_FLOAT128;
++ if (flag > 0)
++ mode |= PRINTF_FORTIFY;
++
++ return __vasprintf_internal (&p, fmt, ap, mode) < 0 ? NULL : p;
++}
++strong_alias (___ieee128___vaprintf_chk, __vaprintf_chkieee128)
+
+ ## sysdeps/ieee754/ldbl-opt/Makefile ##
+@@ sysdeps/ieee754/ldbl-opt/Makefile: libnldbl-calls = \
+ acos \
+ acosh \
+ acospi \
++ aprintf \
++ aprintf_chk \
+ asin \
+ asinh \
+ asinpi \
+@@ sysdeps/ieee754/ldbl-opt/Makefile: libnldbl-calls = \
+ trunc \
+ ufromfp \
+ ufromfpx \
++ vaprintf \
++ vaprintf_chk \
+ vasprintf \
+ vasprintf_chk \
+ vdprintf \
+@@ sysdeps/ieee754/ldbl-opt/Makefile: CFLAGS-tst-nldbl-wscanf-binary-gnu89.c += -mlong-double-64 -std=gnu89 \
+ endif
+
+ routines_no_fortify += \
++ nldbl-aprintf \
+ nldbl-asprintf \
+ nldbl-dprintf \
+ nldbl-fprintf \
+@@ sysdeps/ieee754/ldbl-opt/Makefile: routines_no_fortify += \
+ nldbl-sprintf \
+ nldbl-swprintf \
+ nldbl-syslog \
++ nldbl-vaprintf \
+ nldbl-vasprintf \
+ nldbl-vdprintf \
+ nldbl-vfprintf \
+
+ ## sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c (new) ##
+@@
++#include "nldbl-compat.h"
++
++attribute_hidden
++char *
++__aprintf (const char *fmt, ...)
++{
++ va_list ap;
++ char *p;
++
++ va_start (ap, fmt);
++ p = __nldbl_vaprintf (fmt, ap);
++ va_end (ap);
++
++ return p;
++}
++extern __typeof (__aprintf) aprintf attribute_hidden;
++weak_alias (__aprintf, aprintf)
+
+ ## sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c (new) ##
+@@
++#include "nldbl-compat.h"
++
++attribute_hidden
++char *
++__aprintf_chk (int flag, const char *fmt, ...)
++{
++ va_list ap;
++ char *p;
++
++ va_start (ap, fmt);
++ p = __nldbl___vaprintf_chk (flag, fmt, ap);
++ va_end (ap);
++
++ return p;
++}
+
+ ## sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c (new) ##
+@@
++#include "nldbl-compat.h"
++
++attribute_hidden
++weak_function
++char *
++vaprintf (const char *fmt, va_list ap)
++{
++ return __nldbl_vaprintf (fmt, ap);
++}
+
+ ## sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c (new) ##
+@@
++#include "nldbl-compat.h"
++
++attribute_hidden
++char *
++__vaprintf_chk (int flag, const char *fmt, va_list ap)
++{
++ return __nldbl___vaprintf_chk (flag, fmt, ap);
++}
2: 131b1f0c ! 2: f1dca5c0 manual/: Prefer aprintf(3) over asprintf(3)
@@ Metadata
## Commit message ##
manual/: Prefer aprintf(3) over asprintf(3)
+ While it's less portable at the moment, it's a better API (simpler,
+ safer, and less error-prone).
+
+ The C Committee has expressed strong support for this API, intending to
+ standardize it. Thus, it should be more portable in the long term.
+
Signed-off-by: Alejandro Colomar <alx@kernel.org>
## manual/examples/rprintf.c ##
base-commit: 9da7ad6d74700811c9b4c82b5f5eb555e39241a7
Comments
On Wed, 18 Mar 2026, Alejandro Colomar wrote: > Things I haven't done: > > - I still didn't write tests for the sysdeps _chk implementations, nor > the Versions files under sysdeps/. I'm aware I need to do this. > - I didn't update '.abilist' files. Is that something I should do? You need to update Versions files for all exported functions, including those for long double variants; otherwise they won't be exported and won't be usable. You need to update .abilist files for all exported functions (appropriately for each glibc ABI); otherwise the ABI tests will fail.
Hi Joseph, On 2026-03-20T16:50:22+0000, Joseph Myers wrote: > On Wed, 18 Mar 2026, Alejandro Colomar wrote: > > > Things I haven't done: > > > > - I still didn't write tests for the sysdeps _chk implementations, nor > > the Versions files under sysdeps/. I'm aware I need to do this. > > - I didn't update '.abilist' files. Is that something I should do? > > You need to update Versions files for all exported functions, including > those for long double variants; otherwise they won't be exported and won't > be usable. You need to update .abilist files for all exported functions > (appropriately for each glibc ABI); otherwise the ABI tests will fail. Thanks! I'll try to imitate asprintf(3) in that regard too. Since I don't know much about ABIs, I might not understand some of what I do, but I hope it's more or less correct. I'll probably have it in a few days. Have a lovely night! Alex