From patchwork Wed Mar 18 18:14:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alejandro Colomar X-Patchwork-Id: 59827 Return-Path: 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 032994BC89B0 for ; Wed, 18 Mar 2026 18:16:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 032994BC89B0 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=VRtIq9pu 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 064564BBC0BC for ; Wed, 18 Mar 2026 18:15:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 064564BBC0BC 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 064564BBC0BC 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=1773857703; cv=none; b=ll4OwWL0caI0h5ihv4HZyheOHZ1xCUqQ+egpUTMXaWSbPo9CsvTBwFhdMp6o41ekzrVR0UtRBBjCQs64EWIFzfNF6bsl7AvHDFBi4VJ5q9f5eqNA9Rdypg83/bSHP8nE3buXCw0cRCCemk34VnmsmYvyC2tPu57xu4Ga2QHOPVE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773857703; c=relaxed/simple; bh=fa9vzI9ajldCs/feQEouOU8H280OaEcz0Bhbp7WKpNI=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=EoE7JOXMeXb2P1uyXZOgeHMhRAaxpVOwh3U7cOu5UhTlPNpqJItZFRT0evj4NPLHoCobN8oJeBiysc2E/EqXQe1+ZrSkjUfC+nlehHy0BIDkiWD2yzhwfVCvtumDTxNryDhb/yUG5D6vEUomqoc/F+AYZwGg8/ulynX5xBG39L4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 064564BBC0BC Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sea.source.kernel.org (Postfix) with ESMTP id 0674844243; Wed, 18 Mar 2026 18:15:01 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BEA46C19421; Wed, 18 Mar 2026 18:14:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773857700; bh=fa9vzI9ajldCs/feQEouOU8H280OaEcz0Bhbp7WKpNI=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=VRtIq9pudDVLIkHNnIv0YUtXZ/a/cJQTZ/wpey8amaSf9Ddg78zIbvSimTT+WD74J Dq7nBstfLEoKhoY7l6PUj9pKFb+XOguCGDcngKJkEbnMWxz9jzjRvUnhCHUVIDbvFp 6lhVrOaSr6gOgwud0BJwE3TVlatdhrABcss8xIon2GYALBTc7vG/YsVIM2fyLvkEKR ZgmeLe52Ft0nlrfHOwP6fZ/V4xOKcG54m2nXGHkRJeWxUjI29uiTBEPHvsUZBMzZn8 u/CCNO8VuvnTmMWAFSeGNBeD0PqEHSA07o7kGjMzQfqeNhUHq+dUh2KqdcX3YlAbYy lLyYoKElwgyjA== Date: Wed, 18 Mar 2026 19:14:56 +0100 From: Alejandro Colomar To: libc-alpha@sourceware.org Cc: Alejandro Colomar , Joseph Myers , Paul Eggert , Adhemerval Zanella Netto , Florian Weimer , "Dmitry V. Levin" , Archie Cobbs , Solar Designer , Sam James Subject: [RFC v3 0/2] Add [v]aprintf(3) Message-ID: X-Mailer: git-send-email 2.53.0 References: <916855e5ad90ca3611a235ecbcf08a1359771eb9.1773775432.git.alx@kernel.org> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: 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, RCVD_IN_DNSWL_BLOCKED, 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Hi! I've added docs, some tests, and fortified versions. Doing regression testing shows that the newly added tests are okay, but I somehow broke the Makefiles. $ 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 18:27:58.265965856 +0100 @@ -7,7 +7,7 @@ PASS: check-local-headers PASS: check-wrapper-headers PASS: link-static-libc -PASS: lint-makefiles +FAIL: lint-makefiles Running argp ... PASS: argp/argp-test @@ -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 However, I don't know how I broke 'lint-makefiles'. Where's the log for consulting the details of this? I still need to do the long double variants, as Joseph said. Joseph would you mind pointing me to what I should do about it and how? Also, is there anything else I should do? Of course, I plan to fill the commit messages more. I still haven't started with that part. See the range-diff at the bottom, for the changes in this revision. Have a lovely day! Alex Alejandro Colomar (2): Add [v]aprintf(3) manual/: Prefer aprintf(3) over asprintf(3) debug/Versions | 3 +++ include/stdio.h | 3 +++ libio/Makefile | 4 ++- libio/Versions | 3 +++ libio/bits/stdio-ldbl.h | 2 ++ libio/bits/stdio2-decl.h | 8 ++++++ libio/bits/stdio2.h | 42 +++++++++++++++++++++++++++++ libio/stdio.h | 10 +++++++ libio/tst-aprintf.c | 56 +++++++++++++++++++++++++++++++++++++++ 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 ++++++++++++++++++++++++++++ 16 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 libio/tst-aprintf.c create mode 100644 libio/vaprintf.c create mode 100644 stdio-common/aprintf.c Range-diff against v2: 1: 916855e5 ! 1: f616528f Add [v]aprintf(3) @@ Commit message Signed-off-by: Alejandro Colomar + ## debug/Versions ## +@@ debug/Versions: libc { + GLIBC_2.43 { + __memset_explicit_chk; + } ++ GLIBC_2.44 { ++ __aprintf_chk; __vaprintf_chk; ++ } + GLIBC_PRIVATE { + __fortify_fail; + } + + ## include/stdio.h ## +@@ include/stdio.h: extern const char *__get_errname (int) attribute_hidden; + + 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) + ## libio/Makefile ## @@ libio/Makefile: routines := \ \ @@ libio/Makefile: routines_no_fortify += \ vsnprintf \ vswprintf \ vwprintf \ +@@ libio/Makefile: tests = \ + test-fputs-unbuffered-full \ + test-fputws-unbuffered-full \ + tst-asprintf-null \ ++ tst-aprintf \ + tst-atime \ + tst-bz22415 \ + tst-bz24051 \ + + ## libio/Versions ## +@@ libio/Versions: libc { + # f* + fmemopen; + } ++ GLIBC_2.44 { ++ vaprintf; ++ } + GLIBC_PRIVATE { + # Used by NPTL and librt + __libc_fatal; + + ## libio/bits/stdio-ldbl.h ## +@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR2_DECL (vdprintf_chk) + # 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 (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; ++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) ++ __THROW __attribute__ ((__format__ (__printf__, 2, 0))) ++ __attribute_malloc__; + extern int __obstack_printf_chk (struct obstack *__restrict __obstack, + int __flag, const char *__restrict __format, + ...) + + ## libio/bits/stdio2.h ## +@@ libio/bits/stdio2.h: __NTH (__asprintf (char **__restrict __ptr, const char *__restrict __fmt, + __va_arg_pack ()); + } + ++__fortify_function char * ++__NTH (aprintf (const char *__restrict __fmt, ...)) ++{ ++ return __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ()); ++} ++ ++__fortify_function char * ++__NTH (__aprintf (const char *__restrict __fmt, ...)) ++{ ++ return __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ()); ++} ++ + __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; + } + ++__fortify_function char * ++__NTH (aprintf (const char *__restrict __fmt, ...)) ++{ ++ __gnuc_va_list __fortify_ap; ++ __builtin_va_start (__fortify_ap, __fmt); ++ char * __p = __vaprintf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __fortify_ap); ++ __builtin_va_end (__fortify_ap); ++ return __p; ++} ++ ++__fortify_function char * ++__NTH (__aprintf (const char *__restrict __fmt, ...)) ++{ ++ __gnuc_va_list __fortify_ap; ++ __builtin_va_start (__fortify_ap, __fmt); ++ char *__p = __vaprintf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __fortify_ap); ++ __builtin_va_end (__fortify_ap); ++ return __p; ++} ++ + __fortify_function_error_function __attribute_overloadable__ int + __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *, + __restrict, __obstack), +@@ 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__) ++# define aprintf(...) \ ++ __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__) ++# define __aprintf(...) \ ++ __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__) + # define obstack_printf(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)) ++{ ++ return __vaprintf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __ap); ++} ++ + __fortify_function int + __NTH (obstack_vprintf (struct obstack *__restrict __obstack, + const char *__restrict __fmt, __gnuc_va_list __ap)) ## libio/stdio.h ## -@@ libio/stdio.h: extern int __asprintf (char **__restrict __ptr, - extern int asprintf (char **__restrict __ptr, - const char *__restrict __fmt, ...) +@@ libio/stdio.h: extern int asprintf (char **__restrict __ptr, __THROWNL __attribute__ ((__format__ (__printf__, 2, 3))) __wur; -+ + #endif + ++#ifdef __USE_GNU +/* Write formatted output to a string dynamically allocated with `malloc'. */ +extern char *vaprintf (const char *__restrict __f, __gnuc_va_list __arg) + __THROWNL __attribute__ ((__format__ (__printf__, 1, 0))) @@ libio/stdio.h: extern int __asprintf (char **__restrict __ptr, +extern char *aprintf (const char *__restrict __fmt, ...) + __THROWNL __attribute__ ((__format__ (__printf__, 1, 2))) + __attribute_malloc__; - #endif - ++#endif ++ #ifdef __USE_XOPEN2K8 + /* Write formatted output to a file descriptor. */ + extern int vdprintf (int __fd, const char *__restrict __fmt, + + ## libio/tst-aprintf.c (new) ## +@@ ++/* Test aprintf. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ char *buf; ++ { ++ /* Avoid -Wformat-overflow warning. */ ++ const char *volatile format = "%2000000000d %2000000000d"; ++ buf = aprintf (format, 1, 2); ++ TEST_VERIFY (buf == NULL); ++ } ++ if (errno != ENOMEM) ++ TEST_COMPARE (errno, EOVERFLOW); ++ ++ /* Force ENOMEM in the test below. */ ++ struct rlimit rl; ++ TEST_COMPARE (getrlimit (RLIMIT_AS, &rl), 0); ++ rl.rlim_cur = 10 * 1024 * 1024; ++ TEST_COMPARE (setrlimit (RLIMIT_AS, &rl), 0); ++ ++ buf = aprintf ("%20000000d", 1); ++ TEST_VERIFY (buf == NULL); ++ TEST_COMPARE (errno, ENOMEM); ++ ++ /* Success */ ++ buf = aprintf ("foo %d", 42); ++ TEST_COMPARE_STRING (buf, "foo 42"); ++ free(buf); ++ ++ return 0; ++} ++ ++#include ## libio/vaprintf.c (new) ## @@ @@ libio/vaprintf.c (new) +} +ldbl_weak_alias (__vaprintf, vaprintf) + ## manual/stdio.texi ## +@@ manual/stdio.texi: other systems offer this function as an async-signal-safe alternative to + The functions in this section do formatted output and place the results + in dynamically allocated memory. + ++@deftypefun {char *} aprintf (const char *@var{template}, @dots{}) ++@standards{GNU, stdio.h} ++@safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} ++This function is similar to @code{sprintf}, ++except that it dynamically allocates a string ++(as with @code{malloc}; @pxref{Unconstrained Allocation}) ++to hold the output, ++instead of putting the output in a buffer you allocate in advance. ++A successful call to @code{aprintf} returns ++a pointer to the newly allocated string. ++ ++Here is how to use @code{aprintf} ++to get the same result as the @code{snprintf} example, ++but more easily: ++ ++@smallexample ++/* @r{Construct a message describing the value of a variable} ++ @r{whose name is @var{name} and whose value is @var{value}.} */ ++char * ++make_message (char *name, char *value) ++@{ ++ return aprintf ("value of %s is %s", name, value); ++@} ++@end smallexample ++@end deftypefun ++ + @deftypefun int asprintf (char **@var{ptr}, const char *@var{template}, @dots{}) + @standards{GNU, stdio.h} + @safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} + ## stdio-common/Makefile ## @@ stdio-common/Makefile: routines := \ _itoa \ @@ stdio-common/Makefile: routines := \ fprintf \ printf \ + ## stdio-common/Versions ## +@@ stdio-common/Versions: libc { + __isoc23_vfscanf; + __isoc23_sscanf; + __isoc23_vsscanf; ++ GLIBC_2.44 { ++ aprintf; + } + GLIBC_PRIVATE { + # global variables + ## stdio-common/aprintf.c (new) ## @@ +/* Copyright (C) 2026 Free Software Foundation, Inc. @@ stdio-common/aprintf.c (new) + + return p; +} -+ldbl_hidden_def (__aprintf, aprintf) ++ldbl_hidden_def (___aprintf, __aprintf) + +ldbl_strong_alias (___aprintf, __aprintf) +ldbl_weak_alias (___aprintf, aprintf) -: -------- > 2: 131b1f0c manual/: Prefer aprintf(3) over asprintf(3) base-commit: 9da7ad6d74700811c9b4c82b5f5eb555e39241a7