[RFC,v1] libio/: Add [v]aprintf()
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
warning
|
Skipped because it is an RFC
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
warning
|
Skipped because it is an RFC
|
Commit Message
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
Hi Paul, Joseph,
Since nobody replied, I guess people are not strongly opposed to it, but
they probably want to see a patch before spending time with this.
Thus, I've written a patch (or a draft of a patch).
This is the first time I add a function to glibc, and I find it quite
difficult, so I'll need some help. I've added the most obvious pieces:
the prototypes and the implementation. However, this doesn't work at
all, so I guess I'm missing stuff. Would you mind helping me figure out
what I'm missing (or what's wrong)?
I tried building this simple test program, but I get linker errors:
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
char *p;
p = aprintf("foo %d", 42);
if (p == NULL)
exit(1);
puts(p);
}
Have a lovely night!
Alex
libio/stdio.h | 8 ++++++++
libio/vaprintf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
create mode 100644 libio/vaprintf.c
Range-diff against v0:
-: -------- > 1: e5defcbd libio/: Add [v]aprintf()
Comments
On 2026-03-17T19:59:05+0100, Alejandro Colomar wrote:
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>
> Hi Paul, Joseph,
>
> Since nobody replied, I guess people are not strongly opposed to it, but
> they probably want to see a patch before spending time with this.
>
> Thus, I've written a patch (or a draft of a patch).
>
> This is the first time I add a function to glibc, and I find it quite
> difficult, so I'll need some help. I've added the most obvious pieces:
> the prototypes and the implementation. However, this doesn't work at
> all, so I guess I'm missing stuff. Would you mind helping me figure out
> what I'm missing (or what's wrong)?
>
> I tried building this simple test program, but I get linker errors:
>
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main(void)
> {
> char *p;
>
> p = aprintf("foo %d", 42);
> if (p == NULL)
> exit(1);
> puts(p);
> }
>
>
> Have a lovely night!
> Alex
>
>
> libio/stdio.h | 8 ++++++++
> libio/vaprintf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 61 insertions(+)
> create mode 100644 libio/vaprintf.c
>
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 3bf6a1f6..3db363f1 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -410,6 +410,14 @@ extern int __asprintf (char **__restrict __ptr,
> extern int asprintf (char **__restrict __ptr,
> const char *__restrict __fmt, ...)
> __THROWNL __attribute__ ((__format__ (__printf__, 2, 3))) __wur;
> +
> +/* 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)))
> + __attribute_malloc__;
> +extern char *aprintf (const char *__restrict __fmt, ...)
> + __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
> + __attribute_malloc__;
> #endif
>
> #ifdef __USE_XOPEN2K8
> diff --git a/libio/vaprintf.c b/libio/vaprintf.c
> new file mode 100644
> index 00000000..0ba147a0
> --- /dev/null
> +++ b/libio/vaprintf.c
> @@ -0,0 +1,53 @@
> +/* 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 "libioP.h"
> +#include <stdarg.h>
> +#include <stddef.h>
> +#include <stdio.h>
> +
> +char **
> +__vaprintf (const char *fmt, va_list args)
> +{
> + char *p;
> +
> + return (__vasprintf_internal (&p, fmt, args, 0) < 0 ? NULL : p;
Ohh, this has a spurious parenthesis, which shows that it's not even
building this file. I'll need to add something to the Makefiles.
Cheers,
Alex
> +}
> +ldbl_weak_alias (__vaprintf, vaprintf)
> +
> +char **
> +__aprintf (const char *fmt, ...)
> +{
> + char *p;
> + va_list ap;
> +
> + va_start(ap, fmt);
> + p = __vaprintf(fmt, ap);
> + va_end(ap);
> +
> + return p;
> +}
> +ldbl_weak_alias (__aprintf, aprintf)
>
> Range-diff against v0:
> -: -------- > 1: e5defcbd libio/: Add [v]aprintf()
> --
> 2.53.0
>
> 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.
On 17/03/26 15:59, Alejandro Colomar wrote:
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>
> Hi Paul, Joseph,
>
> Since nobody replied, I guess people are not strongly opposed to it, but
> they probably want to see a patch before spending time with this.
>
> Thus, I've written a patch (or a draft of a patch).
>
> This is the first time I add a function to glibc, and I find it quite
> difficult, so I'll need some help. I've added the most obvious pieces:
> the prototypes and the implementation. However, this doesn't work at
> all, so I guess I'm missing stuff. Would you mind helping me figure out
> what I'm missing (or what's wrong)?
>
> I tried building this simple test program, but I get linker errors:
You need to export the symbol by adding a version tag and its name on
stdio-common/Versions.
I don't have a strong opinion about this, albeit since gnulib is already
providing and that committee has shown interest I would way for C standard
inclusion.
>
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main(void)
> {
> char *p;
>
> p = aprintf("foo %d", 42);
> if (p == NULL)
> exit(1);
> puts(p);
> }
>
>
> Have a lovely night!
> Alex
>
>
> libio/stdio.h | 8 ++++++++
> libio/vaprintf.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 61 insertions(+)
> create mode 100644 libio/vaprintf.c
>
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 3bf6a1f6..3db363f1 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -410,6 +410,14 @@ extern int __asprintf (char **__restrict __ptr,
> extern int asprintf (char **__restrict __ptr,
> const char *__restrict __fmt, ...)
> __THROWNL __attribute__ ((__format__ (__printf__, 2, 3))) __wur;
> +
> +/* 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)))
> + __attribute_malloc__;
> +extern char *aprintf (const char *__restrict __fmt, ...)
> + __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
> + __attribute_malloc__;
> #endif
>
> #ifdef __USE_XOPEN2K8
> diff --git a/libio/vaprintf.c b/libio/vaprintf.c
> new file mode 100644
> index 00000000..0ba147a0
> --- /dev/null
> +++ b/libio/vaprintf.c
> @@ -0,0 +1,53 @@
> +/* 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 "libioP.h"
> +#include <stdarg.h>
> +#include <stddef.h>
> +#include <stdio.h>
> +
> +char **
> +__vaprintf (const char *fmt, va_list args)
> +{
> + char *p;
> +
> + return (__vasprintf_internal (&p, fmt, args, 0) < 0 ? NULL : p;
> +}
> +ldbl_weak_alias (__vaprintf, vaprintf)
> +
> +char **
> +__aprintf (const char *fmt, ...)
> +{
> + char *p;
> + va_list ap;
> +
> + va_start(ap, fmt);
> + p = __vaprintf(fmt, ap);
> + va_end(ap);
> +
> + return p;
> +}
> +ldbl_weak_alias (__aprintf, aprintf)
>
> Range-diff against v0:
> -: -------- > 1: e5defcbd libio/: Add [v]aprintf()
Hi Adhemerval,
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.
- 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.
- 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.
> On 17/03/26 15:59, Alejandro Colomar wrote:
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
> > ---
> >
> > Hi Paul, Joseph,
> >
> > Since nobody replied, I guess people are not strongly opposed to it, but
> > they probably want to see a patch before spending time with this.
> >
> > Thus, I've written a patch (or a draft of a patch).
> >
> > This is the first time I add a function to glibc, and I find it quite
> > difficult, so I'll need some help. I've added the most obvious pieces:
> > the prototypes and the implementation. However, this doesn't work at
> > all, so I guess I'm missing stuff. Would you mind helping me figure out
> > what I'm missing (or what's wrong)?
> >
> > I tried building this simple test program, but I get linker errors:
>
> You need to export the symbol by adding a version tag and its name on
> stdio-common/Versions.
Thanks! I'll try.
> I don't have a strong opinion about this, albeit since gnulib is already
> providing and that committee has shown interest I would way for C standard
> inclusion.
I don't think we want that. The committee is bad at choosing names, and
would probably end up choosing a bad name. Now that we're in time to
pick a name before the committee decides, we should do it, and then tell
the committee about it so that it uses the same name.
In fact, gnulib added it as aprintf(3) precisely for that reason.
Have a lovely night!
Alex
On 17/03/26 16:40, Alejandro Colomar wrote:
> Hi Adhemerval,
>
> 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.
>
> - 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.
>
> - 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.
>
>> On 17/03/26 15:59, Alejandro Colomar wrote:
>>> Signed-off-by: Alejandro Colomar <alx@kernel.org>
>>> ---
>>>
>>> Hi Paul, Joseph,
>>>
>>> Since nobody replied, I guess people are not strongly opposed to it, but
>>> they probably want to see a patch before spending time with this.
>>>
>>> Thus, I've written a patch (or a draft of a patch).
>>>
>>> This is the first time I add a function to glibc, and I find it quite
>>> difficult, so I'll need some help. I've added the most obvious pieces:
>>> the prototypes and the implementation. However, this doesn't work at
>>> all, so I guess I'm missing stuff. Would you mind helping me figure out
>>> what I'm missing (or what's wrong)?
>>>
>>> I tried building this simple test program, but I get linker errors:
>>
>> You need to export the symbol by adding a version tag and its name on
>> stdio-common/Versions.
>
> Thanks! I'll try.
>
>> I don't have a strong opinion about this, albeit since gnulib is already
>> providing and that committee has shown interest I would way for C standard
>> inclusion.
>
> I don't think we want that. The committee is bad at choosing names, and
> would probably end up choosing a bad name. Now that we're in time to
> pick a name before the committee decides, we should do it, and then tell
> the committee about it so that it uses the same name.
>
> In fact, gnulib added it as aprintf(3) precisely for that reason.
I would refrain to make this kind of judgement ("bad at choosing names") because
it only adds contention on discussing a technical inclusion.
Before adding this it would be helpful to hear other maintainers, but I inclined
to consider this addition.
Hi Adhemerval,
On 2026-03-17T16:56:24-0300, Adhemerval Zanella Netto wrote:
> > - 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,
I agree this is the most objective reason.
> >> I don't have a strong opinion about this, albeit since gnulib is already
> >> providing and that committee has shown interest I would way for C standard
> >> inclusion.
> >
> > I don't think we want that. The committee is bad at choosing names, and
> > would probably end up choosing a bad name. Now that we're in time to
> > pick a name before the committee decides, we should do it, and then tell
> > the committee about it so that it uses the same name.
> >
> > In fact, gnulib added it as aprintf(3) precisely for that reason.
>
> I would refrain to make this kind of judgement ("bad at choosing
> names") because it only adds contention on discussing a technical
> inclusion.
Okay.
> Before adding this it would be helpful to hear other maintainers, but
> I inclined to consider this addition.
Sure; let's hear them.
Thanks!
Cheers,
Alex
On Tue, 17 Mar 2026, Alejandro Colomar wrote:
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 3bf6a1f6..3db363f1 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -410,6 +410,14 @@ extern int __asprintf (char **__restrict __ptr,
> extern int asprintf (char **__restrict __ptr,
> const char *__restrict __fmt, ...)
> __THROWNL __attribute__ ((__format__ (__printf__, 2, 3))) __wur;
> +
> +/* 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)))
> + __attribute_malloc__;
> +extern char *aprintf (const char *__restrict __fmt, ...)
> + __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
> + __attribute_malloc__;
> #endif
Extensions should be conditioned on __USE_GNU, or __USE_MISC given a good
enough argument to enable them by default. They don't belong in a defined
(__USE_MISC) || __GLIBC_USE (LIB_EXT2) section because they aren't in TR
24731-2, which is what __GLIBC_USE (LIB_EXT2) is for.
Hi Joseph,
On 2026-03-17T20:40:09+0000, Joseph Myers wrote:
> Extensions should be conditioned on __USE_GNU, or __USE_MISC given a good
> enough argument to enable them by default. They don't belong in a defined
> (__USE_MISC) || __GLIBC_USE (LIB_EXT2) section because they aren't in TR
> 24731-2, which is what __GLIBC_USE (LIB_EXT2) is for.
Thanks! I didn't understand that that LIB_EXT2 was for. I guess that
given the name aprintf() is somewhat widely used, __USE_GNU would be
more appropriate.
Have a lovely night!
Alex
Hi Adhemerval,
On 2026-03-17T16:56:24-0300, Adhemerval Zanella Netto wrote:
>
>
> On 17/03/26 16:40, Alejandro Colomar wrote:
> > Hi Adhemerval,
> >
> > 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.
> >
> > - 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.
> >
> > - 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.
Have a lovely night!
Alex
>
> >
> >> On 17/03/26 15:59, Alejandro Colomar wrote:
> >>> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> >>> ---
> >>>
> >>> Hi Paul, Joseph,
> >>>
> >>> Since nobody replied, I guess people are not strongly opposed to it, but
> >>> they probably want to see a patch before spending time with this.
> >>>
> >>> Thus, I've written a patch (or a draft of a patch).
> >>>
> >>> This is the first time I add a function to glibc, and I find it quite
> >>> difficult, so I'll need some help. I've added the most obvious pieces:
> >>> the prototypes and the implementation. However, this doesn't work at
> >>> all, so I guess I'm missing stuff. Would you mind helping me figure out
> >>> what I'm missing (or what's wrong)?
> >>>
> >>> I tried building this simple test program, but I get linker errors:
> >>
> >> You need to export the symbol by adding a version tag and its name on
> >> stdio-common/Versions.
> >
> > Thanks! I'll try.
> >
> >> I don't have a strong opinion about this, albeit since gnulib is already
> >> providing and that committee has shown interest I would way for C standard
> >> inclusion.
> >
> > I don't think we want that. The committee is bad at choosing names, and
> > would probably end up choosing a bad name. Now that we're in time to
> > pick a name before the committee decides, we should do it, and then tell
> > the committee about it so that it uses the same name.
> >
> > In fact, gnulib added it as aprintf(3) precisely for that reason.
>
> I would refrain to make this kind of judgement ("bad at choosing names") because
> it only adds contention on discussing a technical inclusion.
>
> Before adding this it would be helpful to hear other maintainers, but I inclined
> to consider this addition.
>
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
@@ -410,6 +410,14 @@ extern int __asprintf (char **__restrict __ptr,
extern int asprintf (char **__restrict __ptr,
const char *__restrict __fmt, ...)
__THROWNL __attribute__ ((__format__ (__printf__, 2, 3))) __wur;
+
+/* 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)))
+ __attribute_malloc__;
+extern char *aprintf (const char *__restrict __fmt, ...)
+ __THROWNL __attribute__ ((__format__ (__printf__, 1, 2)))
+ __attribute_malloc__;
#endif
#ifdef __USE_XOPEN2K8
new file mode 100644
@@ -0,0 +1,53 @@
+/* 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 "libioP.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+
+char **
+__vaprintf (const char *fmt, va_list args)
+{
+ char *p;
+
+ return (__vasprintf_internal (&p, fmt, args, 0) < 0 ? NULL : p;
+}
+ldbl_weak_alias (__vaprintf, vaprintf)
+
+char **
+__aprintf (const char *fmt, ...)
+{
+ char *p;
+ va_list ap;
+
+ va_start(ap, fmt);
+ p = __vaprintf(fmt, ap);
+ va_end(ap);
+
+ return p;
+}
+ldbl_weak_alias (__aprintf, aprintf)