[RFC v1] libio/: Add [v]aprintf()
Solar Designer
solar@openwall.com
Sat Mar 21 01:38:40 GMT 2026
Hi Alejandro,
Thank you for CC'ing me on this, although I don't have a lot to add.
Just a little.
I agree that your proposed aprintf(3) API is better than asprintf(3),
but I am unsure it's better sufficiently to introduce it now that
asprintf(3) is finally in POSIX and finally behaves consistently between
*BSDs and glibc. Sure such consistency should not be relied upon in
portable code, because POSIX does not mandate it and because older
systems exist, which is a problem. Using a new function name like you
propose ensures code would not even build on an older system, which is a
safe solution, but would not be practical for portable code for years.
That said, I agree with your reasoning and I have no objections.
A little more inline:
On Wed, Mar 18, 2026 at 02:17:42AM +0100, Alejandro Colomar wrote:
> On 2026-03-17T16:56:24-0300, Adhemerval Zanella Netto wrote:
> > On 17/03/26 16:40, Alejandro Colomar wrote:
> > > On 2026-03-17T16:26:06-0300, Adhemerval Zanella Netto wrote:
> > >>
> > >>> Projects have come up with APIs for this over time. GNU and the
> > >>> BSDs have it as asprintf(3), but there seems to be consensus
> > >>> that this API isn't very well designed; evidence of this is that
> > >>> the behavior is slightly different in the various
> > >>> implementations, and has changes through history.
> > >>
> > >> Could you add more details of what is wrong with the current asprintf glibc
> > >> implementation, besides the initial designed limitation ('int' return i
> > >> nstead of ssize_t)?
> > >>
> > >> We don't have any bugs against asprintf on glibc bugzilla.
> > >
> > > Here are three issues of asprintf(3) from the top of my head:
> > >
> > > - Quite often, it's used together with strdup(3) in some conditional.
> > > Compare:
> > >
> > > if (cond) {
> > > dup = strdup(s)
> > > if (dup == NULL)
> > > goto fail;
> > > } else {
> > > if (asprintf(&dup, "...", s, ...) < 0)
> > > goto fail;
> > > }
> > > vs
> > > if (cond)
> > > dup = strdup(s);
> > > else
> > > dup = aprintf("...", s, ...);
> > > if (dup == NULL)
> > > goto fail;
> > >
> > > The code using aprintf(3) is much simpler and more readable. This
> > > means it has less chances of having bugs.
> > >
> > > And even in uses not tied to strdup(3), the double-pointer weirdness
> > > make it unnecessarily complex.
Well, another way to write it is:
if (cond)
dup = strdup(s);
else if (asprintf(&dup, "...", s, ...) < 0)
dup = NULL;
if (dup == NULL)
goto fail;
which is closer to your proposed style with aprintf(3), but yes it is
indeed more complicated even if same line count.
> > > - Some BSDs guarantee that asprintf(3) sets the pointer to NULL on
> > > error. When porting BSD code to GNU, that could result in bugs
> > > (reding an uninitialized pointer). I expect this to be unlikely,
> > > because why would one ever use the pointer after the call failed, but
> > > not impossible.
>
> Actually, while working on the documentation of aprintf(3), I was
> git-blame(1)ing the current documentation of asprintf(3), and I found
> that people have actually been bitten by this. It seems to be a real
> issue.
>
> commit cb4692ce1edd5a81c2521de49dfef6125141d1c7
> Author: Florian Weimer <fweimer@redhat.com>
> Date: 2024-12-27 09:17:41 +0100
>
> libio: asprintf should write NULL upon failure
>
> This was suggested most recently by Solar Designer, noting
> that code replacing vsprintf with vasprintf in a security fix
> was subtly wrong:
>
> Re: GStreamer Security Advisory 2024-0003: Orc compiler
> stack-based buffer overflow
> <https://www.openwall.com/lists/oss-security/2024/07/26/2>
>
> Previous libc-alpha discussions:
>
> I: [PATCH] asprintf error handling fix
> <https://inbox.sourceware.org/libc-alpha/20011205185828.GA8376@ldv.office.alt-linux.org/>
>
> asprintf() issue
> <https://inbox.sourceware.org/libc-alpha/CANSoFxt-cdc-+C4u-rTENMtY4X9RpRSuv+axDswSPxbDgag8_Q@mail.gmail.com/>
>
> I don't think we need a compatibility symbol for this. As the
> GStreamer example shows, this change is much more likely to fix bugs
> than cause compatibility issues.
>
> Suggested-by: Dmitry V. Levin <ldv@altlinux.org>
> Suggested-by: Archie Cobbs <archie.cobbs@gmail.com>
> Suggested-by: Solar Designer <solar@openwall.com>
> Reviewed-by: Sam James <sam@gentoo.org>
>
> I've added these people to CC.
Yes. I am really glad this change finally went in. I just wish Ulrich
didn't block it when Dmitry first proposed it back in 2001.
> > > - aprintf(3), by returning the newly allocated pointer, allows using
> > > [[gnu::malloc(free)]], which would improve static analysis, being
> > > able to detect leaks, double-free's, and other related bugs.
> >
> > I would say this the only compelling reason to provide this symbol, since
> > the first is a more a stylist one (and subject to endless discussion), and
> > second I would consider a user error instead of a glibc one.
>
> I tend to agree, but users make mistakes if APIs allow them to, and the
> asprintf(3) API was unnecessarily promoting them. aprintf(3) is free of
> such issues, by design.
I agree.
Searching the web for aprintf, I found your proposal:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3575.txt
but I also found some references to custom functions of this name in
libraries for embedded applications:
"The aprintf functions familly is an asynchronous implementation of the
printf function"
https://wookey-project.github.io/libstd/functions/aprintf.html
"aprintf, afprintf - Cthreads atomic formatted output conversion"
https://sites.cc.gatech.edu/fac/Mustaque.Ahamad/courses/threads_man/aprintf.html
I think this is not a blocker to your use of the name.
Alexander
More information about the Libc-alpha
mailing list