[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