diff mbox series

[RFC] realloc: Make REALLOC_ZERO_BYTES_FREES into a tunable

Message ID 20241019014002.3684656-1-siddhesh@sourceware.org
State New
Headers show
Series [RFC] realloc: Make REALLOC_ZERO_BYTES_FREES into a tunable | expand

Commit Message

Siddhesh Poyarekar Oct. 19, 2024, 1:40 a.m. UTC
Allow users to select this behaviour at runtime, which should allow them
flexibility to have glibc realloc do the same thing that many other
implementations do for realloc (p, 0).  The default behaviour remains
unchanged for now.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---

The have-cake-and-eat-it-too solution.  Lightly tested, mainly intended to get
feedback from folks.

 NEWS                            |  4 +++
 elf/dl-tunables.list            |  6 ++++
 malloc/Makefile                 | 17 ++++++++++--
 malloc/arena.c                  |  3 ++
 malloc/malloc-check.c           |  2 +-
 malloc/malloc-debug.c           |  5 +++-
 malloc/malloc.c                 | 49 +++++++++++++++++----------------
 malloc/mcheck-impl.c            |  9 +++++-
 malloc/tst-realloc-nonfreeing.c | 35 +++++++++++++++++++++++
 manual/memory.texi              | 11 ++++++--
 manual/tunables.texi            |  9 ++++++
 11 files changed, 119 insertions(+), 31 deletions(-)
 create mode 100644 malloc/tst-realloc-nonfreeing.c

Comments

Paul Eggert Oct. 19, 2024, 2:42 a.m. UTC | #1
Thanks for working on this. As often, the trickiest part is naming and 
documentation. Some suggestions:

On 2024-10-18 18:40, Siddhesh Poyarekar wrote:

+* A new tunable glibc.malloc.realloc_zero_bytes_frees ...

The existing macro name REALLOC_ZERO_BYTES_FREES is a misnomer, since it 
doesn't affect whether a successful realloc(P,0) frees P: no matter how 
the tunable is set, the realloc always frees P and using P afterwards is 
undefined behavior. As the tunable affects only whether a successful 
realloc(P,0) returns a null pointer, I suggest a name like 
glibc.malloc.realloc_0_returns_null instead.


> -  if (bytes == 0 && oldmem != NULL)
> +  if (mp_.realloc_zero_bytes_frees && bytes == 0 && oldmem != NULL)

Since the case is so rare I suggest putting 'bytes == 0 && oldmem != 
NULL' first in the condition; that way, the CPU typically might not even 
need to pull mp into cache.


> +   ISO C17 says the realloc call has implementation-defined behavior,
> +   and it might not even free p.  */

The same is true for C23, so how about saying "C23" instead of "C17"?


> -  if (size == 0)
> +  if (realloc_zero_bytes_frees == 1 && size == 0)

Likewise check size before realloc_zero_bytes_frees here.

Also, don't bother checking that realloc_zero_bytes_frees exactly equals 
1, since it is always either 0 or 1; just check that it is nonzero.


> -Otherwise, if @var{newsize} is zero
> -@code{realloc} frees the block and returns @code{NULL}.
> -Otherwise, if @code{realloc} cannot reallocate the requested size
> +
> +Otherwise, if @var{newsize} is zero @code{realloc} will by default free
> +the block and return @code{NULL}.  This behavior can be overridden using
> +the @code{glibc.malloc.realloc_zero_bytes_frees} tunable, where instead
> +@code{realloc} will then free @var{ptr} and attempt to return a zero
> +sized block.
> +
> +Finally, if @code{realloc} cannot reallocate the requested size

Use present rather than future tense for consistency & brevity. Also, 
give a bit more motivation. Perhaps the new paragraph could be something 
like this:

Otherwise, if @var{newsize} is zero a successful @code{realloc} frees 
the block; it then returns returns either @code{NULL} or a non-null 
pointer to a newly allocated size-zero block, depending on the 
@code{glibc.malloc.realloc_0_returns_null} tunable. The @code{NULL} 
return is compatible with @theglibc{} versions 2.6 (2007) through 2.40 
(2024); the non-null return is compatible with older versions of 
@theglibc{} and with most non-GNU implementations. Portable code should 
not assume either behavior.


> +@deftp Tunable glibc.malloc.realloc_zero_bytes_frees
> +
> +By default when the @code{realloc} function is called with the new size as
> +@var{0}, it frees the input pointer and returns @var{NULL}.  Setting the value
> +of this tunable to @var{0} will result in @code{realloc} instead freeing the
> +input pointer and attempting to allocate and return a zero sized block.  Any
> +values other than @var{0} are ignored.

Similarly, here I suggest wording like the following:

@deftp Tunable glibc.malloc.realloc_0_returns_null

If 1, a successful @code{realloc (@var{p}, 0)} returns a null pointer 
when @var{p} is non-null; if 0, the same call instead returns a non-null 
pointer to a zero sized block. Although the current default is 1 for 
compatibility with @theglibc{} versions 2.6 (2007) through 2.40 (2024), 
the default is planned to be changed to 0 in a future version of 
@theglibc{} as this will be more compatible with non-GNU systems and 
will be less likely to break naive programs that assume that when 
@code{realloc} returns a null pointer then @code{realloc} has failed.
Alejandro Colomar Oct. 19, 2024, 3:32 p.m. UTC | #2
Hi Sid, Paul,

Please add some acknowledgement of the reporters and the reports:

Reported-by: Alejandro Colomar <alx@kernel.org>
Reported-by: наб <nabijaczleweli@nabijaczleweli.xyz>
Reported-by: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Link: <https://github.com/shadow-maint/shadow/pull/1095>
Link: <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
Link: <https://inbox.sourceware.org/libc-alpha/t7low35raw7dodsie7umqbnddpm7q2eenkbv5lafesrqrisudn@zqvuq3izem6t/T/#t>

On Fri, Oct 18, 2024 at 07:42:32PM GMT, Paul Eggert wrote:
> Thanks for working on this. As often, the trickiest part is naming and
> documentation. Some suggestions:
> 
> On 2024-10-18 18:40, Siddhesh Poyarekar wrote:
> 
> +* A new tunable glibc.malloc.realloc_zero_bytes_frees ...
> 
> The existing macro name REALLOC_ZERO_BYTES_FREES is a misnomer, since it
> doesn't affect whether a successful realloc(P,0) frees P: no matter how the
> tunable is set, the realloc always frees P and using P afterwards is
> undefined behavior. As the tunable affects only whether a successful
> realloc(P,0) returns a null pointer, I suggest a name like
> glibc.malloc.realloc_0_returns_null instead.

+1

> > -  if (bytes == 0 && oldmem != NULL)
> > +  if (mp_.realloc_zero_bytes_frees && bytes == 0 && oldmem != NULL)
> 
> Since the case is so rare I suggest putting 'bytes == 0 && oldmem != NULL'
> first in the condition; that way, the CPU typically might not even need to
> pull mp into cache.

+1

> > +   ISO C17 says the realloc call has implementation-defined behavior,
> > +   and it might not even free p.  */
> 
> The same is true for C23, so how about saying "C23" instead of "C17"?

No.  C17 had it implementation-defined.  C23 makes it Undefined Behavior.

> > -  if (size == 0)
> > +  if (realloc_zero_bytes_frees == 1 && size == 0)
> 
> Likewise check size before realloc_zero_bytes_frees here.
> 
> Also, don't bother checking that realloc_zero_bytes_frees exactly equals 1,
> since it is always either 0 or 1; just check that it is nonzero.

+1  Just treat it like a boolean:

	if (size == 0 && realloc_0_returns_null)

> > -Otherwise, if @var{newsize} is zero
> > -@code{realloc} frees the block and returns @code{NULL}.
> > -Otherwise, if @code{realloc} cannot reallocate the requested size
> > +
> > +Otherwise, if @var{newsize} is zero @code{realloc} will by default free
> > +the block and return @code{NULL}.  This behavior can be overridden using
> > +the @code{glibc.malloc.realloc_zero_bytes_frees} tunable, where instead
> > +@code{realloc} will then free @var{ptr} and attempt to return a zero
> > +sized block.
> > +
> > +Finally, if @code{realloc} cannot reallocate the requested size
> 
> Use present rather than future tense for consistency & brevity. Also, give a
> bit more motivation. Perhaps the new paragraph could be something like this:
> 
> Otherwise, if @var{newsize} is zero a successful @code{realloc} frees the

s/zero/zero,/

> block; it then returns returns either @code{NULL} or a non-null pointer to a
> newly allocated size-zero block, depending on the
> @code{glibc.malloc.realloc_0_returns_null} tunable. The @code{NULL} return

s/\. /.  /

(Double space after period.)

> is compatible with @theglibc{} versions 2.6 (2007) through 2.40 (2024); the

I got the version incorrectly.  It seems it's much older, and I don't
know exactly when, since a toggle was added, and then it was switched,
so it didn't all happen at once.  I think Sid has a better idea of when
that happened.

> non-null return is compatible with older versions of @theglibc{} and with
> most non-GNU implementations. Portable code should not assume either
> behavior.
> 
> 
> > +@deftp Tunable glibc.malloc.realloc_zero_bytes_frees
> > +
> > +By default when the @code{realloc} function is called with the new size as
> > +@var{0}, it frees the input pointer and returns @var{NULL}.  Setting the value
> > +of this tunable to @var{0} will result in @code{realloc} instead freeing the
> > +input pointer and attempting to allocate and return a zero sized block.  Any
> > +values other than @var{0} are ignored.
> 
> Similarly, here I suggest wording like the following:
> 
> @deftp Tunable glibc.malloc.realloc_0_returns_null
> 
> If 1, a successful @code{realloc (@var{p}, 0)} returns a null pointer when
> @var{p} is non-null; if 0, the same call instead returns a non-null pointer
> to a zero sized block. Although the current default is 1 for compatibility
> with @theglibc{} versions 2.6 (2007) through 2.40 (2024), the default is
> planned to be changed to 0 in a future version of @theglibc{} as this will
> be more compatible with non-GNU systems and will be less likely to break
> naive programs that assume that when @code{realloc} returns a null pointer
> then @code{realloc} has failed.

LGTM.

Thank you for working on this!

Have a lovely day!
Alex
Florian Weimer Oct. 21, 2024, 9:34 a.m. UTC | #3
* Siddhesh Poyarekar:

> Allow users to select this behaviour at runtime, which should allow them
> flexibility to have glibc realloc do the same thing that many other
> implementations do for realloc (p, 0).  The default behaviour remains
> unchanged for now.

What's the point of this tunable?

Applications that depend on the new behavior enabled by the tunable are
just buggy today.  They can't rely on the tunable setting because
tunables are deliberately not part of the glibc interface contract.

Please do not make this change without further discussion.

Thanks,
Florian
Siddhesh Poyarekar Oct. 21, 2024, 12:51 p.m. UTC | #4
On 2024-10-21 05:34, Florian Weimer wrote:
> * Siddhesh Poyarekar:
> 
>> Allow users to select this behaviour at runtime, which should allow them
>> flexibility to have glibc realloc do the same thing that many other
>> implementations do for realloc (p, 0).  The default behaviour remains
>> unchanged for now.
> 
> What's the point of this tunable?

Sorry, I should have referenced the discussion that led to this; it's 
basically a rehash of an old discussion and given that it keeps 
resurfacing, I felt that something needed to be done to move the status 
quo somewhat:

https://inbox.sourceware.org/libc-alpha/t7low35raw7dodsie7umqbnddpm7q2eenkbv5lafesrqrisudn@zqvuq3izem6t/T/#m4367eacdcdcf1633c51bfea8cfe95fcb52dc9d6d

> Applications that depend on the new behavior enabled by the tunable are
> just buggy today.  They can't rely on the tunable setting because
> tunables are deliberately not part of the glibc interface contract.

It's unfortunately not as clear cut since the behaviour is considered 
implementation defined by C17 and earlier and many implementations 
(musl, BSD for example) have had the new behaviour, something glibc 
moved away from in 1999, apparently to comply with C9x.

C23 makes the behaviour with realloc(p, 0) undefined instead of leaving 
it implementation defined, so one stand we could take is to do the same 
and update GNU tools (and the sanitizer, etc.) to promote/enforce that. 
This patch takes the "middle" route, introducing BSD-like behaviour with 
realloc(p, 0).

I'd ideally like to do both, since it solves an immediate problem as 
well as works towards a longer term resolution, eliminating zero-sized 
allocations.

I wonder if a "fortified" realloc (and malloc) would be worthwhile for 
-std=c23 and later, where a call to malloc or realloc with 0 size aborts...

Thanks,
Sid
Sam James Oct. 21, 2024, 1:04 p.m. UTC | #5
Siddhesh Poyarekar <siddhesh@sourceware.org> writes:

> [...]
> I wonder if a "fortified" realloc (and malloc) would be worthwhile for
> -std=c23 and later, where a call to malloc or realloc with 0 size
> aborts...

We should get UBSAN instrumentation done first. I think Bruno filed some
bugs for it already.

It's not a bad idea though. But I admit I don't yet have any sense of
scale of fallout.

sam
Alejandro Colomar Oct. 21, 2024, 1:21 p.m. UTC | #6
Hi Sam, Sid,

On Mon, Oct 21, 2024 at 02:04:23PM GMT, Sam James wrote:
> Siddhesh Poyarekar <siddhesh@sourceware.org> writes:
> 
> > [...]
> > I wonder if a "fortified" realloc (and malloc) would be worthwhile for
> > -std=c23 and later, where a call to malloc or realloc with 0 size
> > aborts...
> 
> We should get UBSAN instrumentation done first. I think Bruno filed some
> bugs for it already.
> 
> It's not a bad idea though. But I admit I don't yet have any sense of
> scale of fallout.

I think it's a bad idea.  It would unnecessarily break stuff randomly.
Why would you force a program to abort when it does perfectly reasonable
stuff?  Allocating 0 bytes is something that fit naturally in a
generic algorithm, for example for storing an arbitrary number of things
where that nuimber may very well be 0, and you don't want to handle 0
specially.

Just fix the bug.  Don't make it worse, please.

Have a lovely day!
Alex

> 
> sam
Alejandro Colomar Oct. 21, 2024, 1:26 p.m. UTC | #7
Hi Sid,

On Mon, Oct 21, 2024 at 08:51:40AM GMT, Siddhesh Poyarekar wrote:
> On 2024-10-21 05:34, Florian Weimer wrote:
> > * Siddhesh Poyarekar:
> > 
> > > Allow users to select this behaviour at runtime, which should allow them
> > > flexibility to have glibc realloc do the same thing that many other
> > > implementations do for realloc (p, 0).  The default behaviour remains
> > > unchanged for now.
> > 
> > What's the point of this tunable?
> 
> Sorry, I should have referenced the discussion that led to this; it's
> basically a rehash of an old discussion and given that it keeps resurfacing,
> I felt that something needed to be done to move the status quo somewhat:
> 
> https://inbox.sourceware.org/libc-alpha/t7low35raw7dodsie7umqbnddpm7q2eenkbv5lafesrqrisudn@zqvuq3izem6t/T/#m4367eacdcdcf1633c51bfea8cfe95fcb52dc9d6d
> 
> > Applications that depend on the new behavior enabled by the tunable are
> > just buggy today.  They can't rely on the tunable setting because
> > tunables are deliberately not part of the glibc interface contract.
> 
> It's unfortunately not as clear cut since the behaviour is considered
> implementation defined by C17 and earlier and many implementations (musl,
> BSD for example) have had the new behaviour, something glibc moved away from

Not new.  Rather old.  The BSDs' behavior is the original from Unix.
It's C89 that innovated, and C17 attempted to revert, but did it in an
inconsistent way that left the language of the standard so broken, that
C23 had to give up and make it undefined.

The BSDs never changed their behavior significantly from Unix V6 and V7.

> in 1999, apparently to comply with C9x.
> 
> C23 makes the behaviour with realloc(p, 0) undefined instead of leaving it
> implementation defined, so one stand we could take is to do the same and
> update GNU tools (and the sanitizer, etc.) to promote/enforce that. This
> patch takes the "middle" route, introducing BSD-like behaviour with
> realloc(p, 0).
> 
> I'd ideally like to do both, since it solves an immediate problem as well as
> works towards a longer term resolution, eliminating zero-sized allocations.

Please do NOT eliminate zero-size allocations.  That's what ANSI C tried
to do in the first place, and they broke realloc(p,0) in their attempt.
We need 0-size allocations.  If you think you don't need them yourself,
don't use them.  I do need them.

Do NOT touch the behavior of malloc(0).
Do NOT touch the behavior of realloc(NULL, 0).
Fix the behavior of realloc(p, 0) to be just what it should have always
been.

Thanks.

Have a lovely day!
Alex

> 
> I wonder if a "fortified" realloc (and malloc) would be worthwhile for
> -std=c23 and later, where a call to malloc or realloc with 0 size aborts...

N. O.

> 
> Thanks,
> Sid
Sam James Oct. 21, 2024, 1:27 p.m. UTC | #8
Alejandro Colomar <alx@kernel.org> writes:

> Hi Sam, Sid,
>
> On Mon, Oct 21, 2024 at 02:04:23PM GMT, Sam James wrote:
>> Siddhesh Poyarekar <siddhesh@sourceware.org> writes:
>> 
>> > [...]
>> > I wonder if a "fortified" realloc (and malloc) would be worthwhile for
>> > -std=c23 and later, where a call to malloc or realloc with 0 size
>> > aborts...
>> 
>> We should get UBSAN instrumentation done first. I think Bruno filed some
>> bugs for it already.
>> 
>> It's not a bad idea though. But I admit I don't yet have any sense of
>> scale of fallout.
>
> I think it's a bad idea.  It would unnecessarily break stuff randomly.
> Why would you force a program to abort when it does perfectly reasonable
> stuff?  Allocating 0 bytes is something that fit naturally in a
> generic algorithm, for example for storing an arbitrary number of things
> where that nuimber may very well be 0, and you don't want to handle 0
> specially.
>

I think a way to easily test at runtime for the behaviour we're
discussing without running everything under Valgrind is useful, using
frameworks (fortify & UBSAN) we're familiar with. Doesn't mean either
would have to be in the usual levels or anything.

> Just fix the bug.  Don't make it worse, please.
>
> Have a lovely day!
> Alex
>
>> 
>> sam
Joseph Myers Oct. 21, 2024, 8:54 p.m. UTC | #9
On Mon, 21 Oct 2024, Alejandro Colomar wrote:

> Not new.  Rather old.  The BSDs' behavior is the original from Unix.
> It's C89 that innovated, and C17 attempted to revert, but did it in an
> inconsistent way that left the language of the standard so broken, that
> C23 had to give up and make it undefined.

C17 made it explicitly obsolescent (see 7.31.12 paragraph 2, "Invoking 
realloc with a size argument equal to zero is an obsolescent feature.").  
C23 following up by removing the obsolescent feature was simply the 
natural followup to that obsolescence (though quicker than the removal of 
obsolescent features often is).  That's nothing to do with "inconsistent", 
"broken" or "give up".
Florian Weimer Oct. 21, 2024, 9:02 p.m. UTC | #10
* Siddhesh Poyarekar:

> On 2024-10-21 05:34, Florian Weimer wrote:
>> * Siddhesh Poyarekar:
>> 
>>> Allow users to select this behaviour at runtime, which should allow them
>>> flexibility to have glibc realloc do the same thing that many other
>>> implementations do for realloc (p, 0).  The default behaviour remains
>>> unchanged for now.
>> What's the point of this tunable?
>
> Sorry, I should have referenced the discussion that led to this; it's
> basically a rehash of an old discussion and given that it keeps
> resurfacing, I felt that something needed to be done to move the
> status quo somewhat:
>
> https://inbox.sourceware.org/libc-alpha/t7low35raw7dodsie7umqbnddpm7q2eenkbv5lafesrqrisudn@zqvuq3izem6t/T/#m4367eacdcdcf1633c51bfea8cfe95fcb52dc9d6d

I was aware of the discussion, but I don't see how a tunable is an
improvement here, sorry.

>> Applications that depend on the new behavior enabled by the tunable are
>> just buggy today.  They can't rely on the tunable setting because
>> tunables are deliberately not part of the glibc interface contract.
>
> It's unfortunately not as clear cut since the behaviour is considered
> implementation defined by C17 and earlier and many implementations
> (musl, BSD for example) have had the new behaviour, something glibc
> moved away from in 1999, apparently to comply with C9x.

I don't see any ambiguity here: if you build for GNU/Linux and the
application is not compatible with the current glibc behavior, this is
an application bug.  As you point out, it's clearly not a conformance
issue anymore.  The fact that the application might possibly work if
compiled for and running on some BSD variant is not particularly
compelling.  There must be dozens of issues where BSD and GNU/Linux
behavior differs (and some with regards to musl, too).

The fundamental problem here is that GCC and other compilers have long
decided to support zero-sized objects, something that is naturally part
of C.  And this eventually led to somewhat conflicting expectations
about malloc and realloc behavior.

Anyway, to make the tunable truly effective, distributions would have to
port system libraries to be compatible with both behaviors.  Replacement
mallocs would also have to react to the glibc tunable, and we currently
do not provide an API for that.  I don't think this is worth the effort.
UBSAN and others flagging this, sure, but porting the system to the new
behavior does not seem like a useful activity.

Thanks,
Florian
Alejandro Colomar Oct. 21, 2024, 9:39 p.m. UTC | #11
Hi Florian,

On Mon, Oct 21, 2024 at 11:02:33PM GMT, Florian Weimer wrote:
> I don't see any ambiguity here: if you build for GNU/Linux and the
> application is not compatible with the current glibc behavior, this is
> an application bug.  As you point out, it's clearly not a conformance
> issue anymore.  The fact that the application might possibly work if
> compiled for and running on some BSD variant is not particularly
> compelling.  There must be dozens of issues where BSD and GNU/Linux
> behavior differs (and some with regards to musl, too).

I'm not sure code written for glibc would have bugs in the BSD.  At
least code that is bug-free in glibc.  Unless the code has strong
guarantees that in all realloc(p,0) calls p is non-NULL, realloc() might
actually return a zero-size region.  Thus, good glibc code must not make
the assumption that it will return NULL.  Which probably means there's a
decent amount of code there that has memory leaks due to this, since I
don't think code is well written in this regard.

alx@debian:~/tmp/gcc/realloc$ cat brain_dmg.c 
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
	void *p, *q;

	errno = 0;

	p = malloc(0);
	// In a sane system, p==NULL is enough of a check.
	if (p == NULL && errno == ENOMEM)  // In glibc we can simplify
		exit(EXIT_FAILURE);
	printf("malloc(0): %p\n", p);
	printf("      errno: %d\n", errno);

	q = NULL;
	for (int i = 0; i < 8; i++) {
		q = realloc(q, 0);
		// In a sane system, p==NULL is enough of a check.
		if (q == NULL && errno == ENOMEM)
			exit(EXIT_FAILURE);
		printf("realloc(q, 0): %p\n", q);
		printf("      errno: %d\n", errno);
	}
	free(q);

	free(p);
}
alx@debian:~/tmp/gcc/realloc$ cc -Wall -Wextra brain_dmg.c 
alx@debian:~/tmp/gcc/realloc$ ./a.out 
malloc(0): 0x55f3e910d2a0
      errno: 0
realloc(q, 0): 0x55f3e910d6d0
      errno: 0
realloc(q, 0): (nil)
      errno: 0
realloc(q, 0): 0x55f3e910d6d0
      errno: 0
realloc(q, 0): (nil)
      errno: 0
realloc(q, 0): 0x55f3e910d6d0
      errno: 0
realloc(q, 0): (nil)
      errno: 0
realloc(q, 0): 0x55f3e910d6d0
      errno: 0
realloc(q, 0): (nil)
      errno: 0


You see the alternating pattern.  If one wants to make sure that the
pointer is freed and nothing is leaked, the only way is free(p).  Or you
could check if the pointer is null before-hand, which would make it
rather useless to call realloc.

I don't think many programmers check errno after checking for NULL to
detect realloc(3) errors (or malloc(3), if they attempt to be portable
to AIX and other such systems).

> The fundamental problem here is that GCC and other compilers have long
> decided to support zero-sized objects, something that is naturally part
> of C.  And this eventually led to somewhat conflicting expectations
> about malloc and realloc behavior.

This is not entirely correct.  This support comes from Unix V6 and V7,
and GCC only continued tradition.  What led to the conflicting
expectations was C89 trying to kill 0-size objects by decree.


Have a lovely night!
Alex
Paul Eggert Oct. 22, 2024, 7:07 a.m. UTC | #12
On 2024-10-21 14:02, Florian Weimer wrote:
> As you point out, it's clearly not a conformance
> issue anymore.

Although that's true for the main point of this discussion, there is 
definitely a conformance issue in shouting distance: when P is non-null, 
glibc's realloc(P, 0) doesn't set errno as POSIX requires.

I'm not planning to file a bug report about this, because in my 
experience apps don't care about this misfeature of POSIX and therefore 
don't care whether glibc conforms.

More generally, in my experience the few apps that call realloc(P, 0) 
and check the return value generally expect the BSD behavior even when 
running on glibc. Yes these apps are "wrong" but this is mainly why 
people are itching to make the proposed change. For this reason I expect 
the change will make glibc-using software a bit more reliable, not a bit 
less. If I didn't think that I wouldn't advocate the change.

The main practical conflict here, as I understand it, is that in 
MS-Windows realloc(P, 0) returns null, whereas in the overwhelming 
Unix/BSD tradition it returns non-null. Evidently the returns-null camp 
won a standards committee battle decades ago, but the returns-nonnull 
camp ignored that part of the standard for decades and finally now, as a 
grudging compromise, C23 says it's undefined.

In this conflict, our natural home is the API of BSD, not of MS-Windows. 
It's easier to port between BSD and GNU than between MS-Windows and GNU, 
and in the long run we'll be better off if we keep it that way. If 
tunables are not the best way to make progress in this area, then it'd 
be helpful to find a better way.
Andreas Schwab Oct. 22, 2024, 8:26 a.m. UTC | #13
On Okt 18 2024, Siddhesh Poyarekar wrote:

> Allow users to select this behaviour at runtime, which should allow them
> flexibility to have glibc realloc do the same thing that many other
> implementations do for realloc (p, 0).  The default behaviour remains
> unchanged for now.

That doesn't make sense.  The required behaviour of realloc (p, 0) is a
static, compile-time property of the program, so it needs to be
controlled at compile time.
Siddhesh Poyarekar Oct. 22, 2024, 10:40 a.m. UTC | #14
On 2024-10-21 17:02, Florian Weimer wrote:
>> https://inbox.sourceware.org/libc-alpha/t7low35raw7dodsie7umqbnddpm7q2eenkbv5lafesrqrisudn@zqvuq3izem6t/T/#m4367eacdcdcf1633c51bfea8cfe95fcb52dc9d6d
> 
> I was aware of the discussion, but I don't see how a tunable is an
> improvement here, sorry.

It's an attempt at a compromise, since the conversation isn't really 
going anywhere otherwise.  It is a net improvement because the glibc 
implementation defined behaviour can then be compliant to the BSD 
behaviour as well.  Of course, there's the argument that C23 makes 
realloc(p, 0) undefined and adding a tunable is just spinning the wheels 
in that context.

>>> Applications that depend on the new behavior enabled by the tunable are
>>> just buggy today.  They can't rely on the tunable setting because
>>> tunables are deliberately not part of the glibc interface contract.
>>
>> It's unfortunately not as clear cut since the behaviour is considered
>> implementation defined by C17 and earlier and many implementations
>> (musl, BSD for example) have had the new behaviour, something glibc
>> moved away from in 1999, apparently to comply with C9x.
> 
> I don't see any ambiguity here: if you build for GNU/Linux and the
> application is not compatible with the current glibc behavior, this is
> an application bug.  As you point out, it's clearly not a conformance
> issue anymore.  The fact that the application might possibly work if
> compiled for and running on some BSD variant is not particularly
> compelling.  There must be dozens of issues where BSD and GNU/Linux
> behavior differs (and some with regards to musl, too).
> 
> The fundamental problem here is that GCC and other compilers have long
> decided to support zero-sized objects, something that is naturally part
> of C.  And this eventually led to somewhat conflicting expectations
> about malloc and realloc behavior.
> 
> Anyway, to make the tunable truly effective, distributions would have to
> port system libraries to be compatible with both behaviors.  Replacement
> mallocs would also have to react to the glibc tunable, and we currently
> do not provide an API for that.  I don't think this is worth the effort.
> UBSAN and others flagging this, sure, but porting the system to the new
> behavior does not seem like a useful activity.

I agree that ubsan, etc. improvements would be more worthwhile.

Thanks,
Sid
Siddhesh Poyarekar Oct. 22, 2024, 10:45 a.m. UTC | #15
On 2024-10-22 04:26, Andreas Schwab wrote:
> On Okt 18 2024, Siddhesh Poyarekar wrote:
> 
>> Allow users to select this behaviour at runtime, which should allow them
>> flexibility to have glibc realloc do the same thing that many other
>> implementations do for realloc (p, 0).  The default behaviour remains
>> unchanged for now.
> 
> That doesn't make sense.  The required behaviour of realloc (p, 0) is a
> static, compile-time property of the program, so it needs to be
> controlled at compile time.

Is there a way to do that without having, e.g., a symbol linked into the 
program that realloc checks to decide which way to go?

Thanks,
Sid
Douglas McIlroy Oct. 22, 2024, 2:09 p.m. UTC | #16
I doubt that a return to the original semantics of realloc(p,0) would
cause harm.

It is hard to imagine a scenario in which one tests for a null return
from realloc(p,0) not because of caution about the definition of
realloc, but  because the prorgram would fail if a zero-length block
were returned.

In the absence of such a threat of failure, programs will not be hurt
by a silent change that returns non-null.

Doug

On Tue, Oct 22, 2024 at 6:45 AM Siddhesh Poyarekar
<siddhesh@sourceware.org> wrote:
>
> On 2024-10-22 04:26, Andreas Schwab wrote:
> > On Okt 18 2024, Siddhesh Poyarekar wrote:
> >
> >> Allow users to select this behaviour at runtime, which should allow them
> >> flexibility to have glibc realloc do the same thing that many other
> >> implementations do for realloc (p, 0).  The default behaviour remains
> >> unchanged for now.
> >
> > That doesn't make sense.  The required behaviour of realloc (p, 0) is a
> > static, compile-time property of the program, so it needs to be
> > controlled at compile time.
>
> Is there a way to do that without having, e.g., a symbol linked into the
> program that realloc checks to decide which way to go?
>
> Thanks,
> Sid
Paul Eggert Oct. 22, 2024, 3:38 p.m. UTC | #17
On 2024-10-22 03:45, Siddhesh Poyarekar wrote:
> Is there a way to do that without having, e.g., a symbol linked into the 
> program that realloc checks to decide which way to go?

We could introduce a compile-time feature test macro, e.g., 
-D_REALLOC_PTR_0_NULL=1 for current behavior, -D_REALLOC_PTR_0_NULL=0 
for BSD behavior. The default can start off being 1, and we can change 
it to 0 later. This approach would overcome Andreas's objection.

I'm not a fan of compile-time feature test macros as we'd be better off 
simply changing the behavior as we did last time without significant ill 
effect. That being said, such a macro would be better than doing 
nothing. Gnulib-using programs could simply set _REALLOC_PTR_0_NULL, as 
they already set _GNU_SOURCE etc.
Siddhesh Poyarekar Oct. 22, 2024, 3:52 p.m. UTC | #18
On 2024-10-22 11:38, Paul Eggert wrote:
> On 2024-10-22 03:45, Siddhesh Poyarekar wrote:
>> Is there a way to do that without having, e.g., a symbol linked into 
>> the program that realloc checks to decide which way to go?
> 
> We could introduce a compile-time feature test macro, e.g., 
> -D_REALLOC_PTR_0_NULL=1 for current behavior, -D_REALLOC_PTR_0_NULL=0 
> for BSD behavior. The default can start off being 1, and we can change 
> it to 0 later. This approach would overcome Andreas's objection.
> 
> I'm not a fan of compile-time feature test macros as we'd be better off 
> simply changing the behavior as we did last time without significant ill 
> effect. That being said, such a macro would be better than doing 
> nothing. Gnulib-using programs could simply set _REALLOC_PTR_0_NULL, as 
> they already set _GNU_SOURCE etc.

That'll still need a symbol in the program at runtime, won't it?  There 
needs to be some way for the realloc (in libc) to decide which path to 
go down.  Or are you suggesting *glibc* build time configuration?

Thanks,
Sid
Paul Eggert Oct. 22, 2024, 3:56 p.m. UTC | #19
On 2024-10-22 08:52, Siddhesh Poyarekar wrote:
> That'll still need a symbol in the program at runtime, won't it?  There 
> needs to be some way for the realloc (in libc) to decide which path to 
> go down.

Yes, it'd need a new runtime symbol, like the other feature-test macros. 
Or rather two symbols, as reallocarray is also affected.
Siddhesh Poyarekar Oct. 22, 2024, 4:01 p.m. UTC | #20
On 2024-10-22 11:56, Paul Eggert wrote:
> On 2024-10-22 08:52, Siddhesh Poyarekar wrote:
>> That'll still need a symbol in the program at runtime, won't it?  
>> There needs to be some way for the realloc (in libc) to decide which 
>> path to go down.
> 
> Yes, it'd need a new runtime symbol, like the other feature-test macros. 
> Or rather two symbols, as reallocarray is also affected.
> 

Yeah, that's the symbol I was looking to avoid.  reallocarray just calls 
realloc, so I don't think it needs to be handled separately and in fact 
shouldn't, because it calls potentially interposed implementations and 
it shouldn't overshadow the interposed implementation's behaviour.

Thanks,
Sid
Joseph Myers Oct. 22, 2024, 4:38 p.m. UTC | #21
On Tue, 22 Oct 2024, Paul Eggert wrote:

> Although that's true for the main point of this discussion, there is
> definitely a conformance issue in shouting distance: when P is non-null,
> glibc's realloc(P, 0) doesn't set errno as POSIX requires.

My understanding of the intent of POSIX (if not the normative wording) is 
given by the APPLICATION USAGE: "The ISO C standard makes it 
implementation-defined whether a call to realloc(p, 0) frees the space 
pointed to by p if it returns a null pointer because memory for the new 
object was not allocated. POSIX.1 instead requires that implementations 
set errno if a null pointer is returned and the space has not been freed, 
and POSIX applications should only free the space if errno was changed.".  
That is, since glibc frees the space when returning a null pointer from 
realloc with size 0, it should not change errno in that case.
Joseph Myers Oct. 22, 2024, 4:52 p.m. UTC | #22
On Tue, 22 Oct 2024, Paul Eggert wrote:

> On 2024-10-22 08:52, Siddhesh Poyarekar wrote:
> > That'll still need a symbol in the program at runtime, won't it?  There
> > needs to be some way for the realloc (in libc) to decide which path to go
> > down.
> 
> Yes, it'd need a new runtime symbol, like the other feature-test macros. Or
> rather two symbols, as reallocarray is also affected.

You'd need two symbols for realloc and two for reallocarray simply 
because, if you add a way to configure this, different parts of a program 
(e.g. shared libraries it uses) might want different semantics, so it 
ought to be compile-time configuration for each object, not link-time 
configuration or glibc-build-time configuration.  (Though I'm not 
convinced of the utility of adding variants for this, as opposed to 
_FORTIFY_SOURCE checks for programs using this undefined behavior, which 
would naturally be per-object based on the _FORTIFY_SOURCE setting when it 
was built.)
Paul Eggert Oct. 22, 2024, 6:59 p.m. UTC | #23
On 2024-10-22 09:52, Joseph Myers wrote:
> it ought to be compile-time configuration for each object, not link-time
> configuration or glibc-build-time configuration

Yes, that's what I was suggesting.


On 2024-10-22 09:01, Siddhesh Poyarekar wrote:

> eallocarray just calls realloc, so I don't think it needs to be handled separately

reallocarray would need separate handling if, as Joseph suggested, 
different compilation units want different semantics.


On 2024-10-22 09:38, Joseph Myers wrote:
> My understanding of the intent of POSIX (if not the normative wording) is 
> given by the APPLICATION USAGE:

Although that's a plausible reading of the intent, POSIX's normative 
wording directly contradicts it. And AIX 7.3 realloc(p,0) (the only one 
I know of besides GNU and MS-Windows that returns a null pointer) 
conforms to the normative wording: it sets errno to EINVAL, which breaks 
the plausible intent entirely.

What a mess, huh? Things would be simpler if we simply reverted Ulrich's 
1999 change and went back to the BSD semantics; this would cure more 
trouble than it'd cause.
Siddhesh Poyarekar Oct. 22, 2024, 7:05 p.m. UTC | #24
On 2024-10-22 14:59, Paul Eggert wrote:
> On 2024-10-22 09:52, Joseph Myers wrote:
>> it ought to be compile-time configuration for each object, not link-time
>> configuration or glibc-build-time configuration
> 
> Yes, that's what I was suggesting.
> 
> 
> On 2024-10-22 09:01, Siddhesh Poyarekar wrote:
> 
>> eallocarray just calls realloc, so I don't think it needs to be 
>> handled separately
> 
> reallocarray would need separate handling if, as Joseph suggested, 
> different compilation units want different semantics.
> 

That's too much machinery for what's essentially undefined behaviour in 
C23 :/

> On 2024-10-22 09:38, Joseph Myers wrote:
>> My understanding of the intent of POSIX (if not the normative wording) 
>> is given by the APPLICATION USAGE:
> 
> Although that's a plausible reading of the intent, POSIX's normative 
> wording directly contradicts it. And AIX 7.3 realloc(p,0) (the only one 
> I know of besides GNU and MS-Windows that returns a null pointer) 
> conforms to the normative wording: it sets errno to EINVAL, which breaks 
> the plausible intent entirely.
> 
> What a mess, huh? Things would be simpler if we simply reverted Ulrich's 
> 1999 change and went back to the BSD semantics; this would cure more 
> trouble than it'd cause.
> 

Tiny nit, it wasn't Ulrich's change:

1999-04-28  Andreas Jaeger  <aj@arthur.rhein-neckar.de>

         * malloc/malloc.c (REALLOC_ZERO_BYTES_FREES): Define it to follow
         ISO C9x and Unix98.
Alejandro Colomar Oct. 24, 2024, 12:50 p.m. UTC | #25
Hi Sid, Paul,

On Tue, Oct 22, 2024 at 03:05:49PM GMT, Siddhesh Poyarekar wrote:
> 
> 
> On 2024-10-22 14:59, Paul Eggert wrote:
> > On 2024-10-22 09:52, Joseph Myers wrote:
> > > it ought to be compile-time configuration for each object, not link-time
> > > configuration or glibc-build-time configuration
> > 
> > Yes, that's what I was suggesting.
> > 
> > 
> > On 2024-10-22 09:01, Siddhesh Poyarekar wrote:
> > 
> > > eallocarray just calls realloc, so I don't think it needs to be
> > > handled separately
> > 
> > reallocarray would need separate handling if, as Joseph suggested,
> > different compilation units want different semantics.
> > 
> 
> That's too much machinery for what's essentially undefined behaviour in C23
> :/

I plan to make it defined again in C2y.  I'll prepare a paper for
standardizing on the BSD/Unix V7 semantics, but I'll do that after glibc
has changed.  Bionic changing would also be an interesting thing.


But yes, I think it's too much machinery.  In reality you don't need to
make anything configurable.  Just changing the behavior tonight wouldn't
break stuff.  At most a few 0B leaks here and there.  Most code will in
fact be more stable, since people are already testing for p==NULL after
realloc, not p==NULL&&errno, so most programs are actually not well
written for glibc (see my example program in the response to Florian).

And it is perfectly possible to write code that works well under both
the new and the old semantics, so it doesn't make sense to provide a
feature test macro.  Just tell people to program for both behaviors, by
testing (p == NULL && errno) for failure.  That errno check will become
dead code under the new behavior, and in a decade when the new behavior
is everywhere it will be possible to remove it.

I think I wouldn't even make a new symbol, since old binaries will
probably benefit from the bugfix and get more stability for free.  But
if you want to be cautiout, a new symbol would be the only thing I'd do.


Have a lovely day!
Alex
Siddhesh Poyarekar Oct. 24, 2024, 12:57 p.m. UTC | #26
On 2024-10-24 08:50, Alejandro Colomar wrote:
> I plan to make it defined again in C2y.  I'll prepare a paper for
> standardizing on the BSD/Unix V7 semantics, but I'll do that after glibc
> has changed.  Bionic changing would also be an interesting thing.
> 
> 
> But yes, I think it's too much machinery.  In reality you don't need to
> make anything configurable.  Just changing the behavior tonight wouldn't
> break stuff.  At most a few 0B leaks here and there.  Most code will in
> fact be more stable, since people are already testing for p==NULL after
> realloc, not p==NULL&&errno, so most programs are actually not well
> written for glibc (see my example program in the response to Florian).

AFAICT, there are strong sustained objections to unconditionally 
changing behaviour, so that is likely not an option anymore.

Sid
Alejandro Colomar Oct. 24, 2024, 3:15 p.m. UTC | #27
Hi Sid,

On Thu, Oct 24, 2024 at 08:57:48AM GMT, Siddhesh Poyarekar wrote:
> On 2024-10-24 08:50, Alejandro Colomar wrote:
> > I plan to make it defined again in C2y.  I'll prepare a paper for
> > standardizing on the BSD/Unix V7 semantics, but I'll do that after glibc
> > has changed.  Bionic changing would also be an interesting thing.
> > 
> > 
> > But yes, I think it's too much machinery.  In reality you don't need to
> > make anything configurable.  Just changing the behavior tonight wouldn't
> > break stuff.  At most a few 0B leaks here and there.  Most code will in
> > fact be more stable, since people are already testing for p==NULL after
> > realloc, not p==NULL&&errno, so most programs are actually not well
> > written for glibc (see my example program in the response to Florian).
> 
> AFAICT, there are strong sustained objections to unconditionally changing
> behaviour, so that is likely not an option anymore.

The strong sustained objections to changing the standard all have one
condition:

	Change the implementations first.

This is for example the case of Robert C. Seacord and JeanHeyd Meneide.
Once we fix the implementations, the standard will be trivial to change.
The only libc implementations that have realloc(p,0) and realloc(NULL,0)
be inconsistent are glibc, Win32, and Bionic, AFAIK.  Then there are
also some interposed libraries that behave like glibc for compatiblity,
but I expect them to follow glibc.  I'm already in talks with Bionic.
Once majority of implementations are fixed, I think we can force Win32
to just follow; in any case, let's ignore Win32 for now.

So, this is still an option.


Regarding the objections to changing glibc, I think it's mainly you.
I have seen a lot of FUD about introducing security vulnerabilities in
the form of double frees, but nobody has shown a single piece of code
that would do that.  So I'd say we're in the process of convincing you
that the change is safe.  It would help if you'd show a minimal
reproducible example program in which you expect to introduce a
vulnerability after this change.

Have a lovely day!
Alex

> 
> Sid
Siddhesh Poyarekar Oct. 24, 2024, 3:24 p.m. UTC | #28
On 2024-10-24 11:15, Alejandro Colomar wrote:
>> AFAICT, there are strong sustained objections to unconditionally changing
>> behaviour, so that is likely not an option anymore.
> 
> The strong sustained objections to changing the standard all have one
> condition:
> 
> 	Change the implementations first.
> 
> This is for example the case of Robert C. Seacord and JeanHeyd Meneide.
> Once we fix the implementations, the standard will be trivial to change.
> The only libc implementations that have realloc(p,0) and realloc(NULL,0)
> be inconsistent are glibc, Win32, and Bionic, AFAIK.  Then there are
> also some interposed libraries that behave like glibc for compatiblity,
> but I expect them to follow glibc.  I'm already in talks with Bionic.
> Once majority of implementations are fixed, I think we can force Win32
> to just follow; in any case, let's ignore Win32 for now.
> 
> So, this is still an option.
> 
> 
> Regarding the objections to changing glibc, I think it's mainly you.

Nope, please read the threads carefully; I actually said that I won't 
sustain an objection if I'm the only one holding that opinion.

Among the glibc maintainers involved in the conversation, only Paul 
aligns with the resolution of changing realloc to behave like BSD.

Sid
Alejandro Colomar Oct. 24, 2024, 3:32 p.m. UTC | #29
Hi Sid,

On Thu, Oct 24, 2024 at 11:24:39AM GMT, Siddhesh Poyarekar wrote:
> > Regarding the objections to changing glibc, I think it's mainly you.
> 
> Nope, please read the threads carefully; I actually said that I won't
> sustain an objection if I'm the only one holding that opinion.

Yup, I know.  That's okay.

> Among the glibc maintainers involved in the conversation, only Paul aligns
> with the resolution of changing realloc to behave like BSD.

I didn't say the others fully support that either.  I just don't see a
strong sustained opposition.  I only see some fear, which is natural.
To remove the fear, we need to make sure what's exactly what might break
and how.  In others, I see doubts about specific proposals, but not
necessarily about the overall change in behavior.


Cheers,
Alex
Florian Weimer Oct. 24, 2024, 3:36 p.m. UTC | #30
* Siddhesh Poyarekar:

> On 2024-10-24 11:15, Alejandro Colomar wrote:
>>> AFAICT, there are strong sustained objections to unconditionally changing
>>> behaviour, so that is likely not an option anymore.
>> The strong sustained objections to changing the standard all have
>> one
>> condition:
>> 	Change the implementations first.
>> This is for example the case of Robert C. Seacord and JeanHeyd
>> Meneide.
>> Once we fix the implementations, the standard will be trivial to change.
>> The only libc implementations that have realloc(p,0) and realloc(NULL,0)
>> be inconsistent are glibc, Win32, and Bionic, AFAIK.  Then there are
>> also some interposed libraries that behave like glibc for compatiblity,
>> but I expect them to follow glibc.  I'm already in talks with Bionic.
>> Once majority of implementations are fixed, I think we can force Win32
>> to just follow; in any case, let's ignore Win32 for now.
>> So, this is still an option.
>> Regarding the objections to changing glibc, I think it's mainly you.
>
> Nope, please read the threads carefully; I actually said that I won't
> sustain an objection if I'm the only one holding that opinion.

I'm still objecting, I don't think this change is valuable.

I'm open to looking at this again once the C standard fully specifies
the behavior of zero-sized objects, the return value of malloc (0), and
so on.  Getting aligned behavior between implementations on these
fundamental interfaces seems quite valuable to programmers.  But
addressing one zero-object-size case and leaving all the others as
unclear as before doesn't seem to be useful, especially given that a
ffuture standard may require different behavior anyway.  A
piece-by-piece transition to the newly required behaviors is also more
onerous on programmers than one well-defined transition in a single
glibc release.

Thanks,
Florian
Joseph Myers Oct. 24, 2024, 3:44 p.m. UTC | #31
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> The strong sustained objections to changing the standard all have one
> condition:
> 
> 	Change the implementations first.

I think a fundamental objection for both standard and implementation 
changes is: we discussed this at length more than once, we settled it 
(first marking the feature obsolescent in C17, then making it undefined 
behavior in C23), there is no significant new evidence that the basis for 
those decisions was wrong or that the decisions have caused problems that 
could justify spending time on revisiting the settled issue so soon after 
settling it.
Alejandro Colomar Oct. 24, 2024, 4:02 p.m. UTC | #32
CC += Martin Uecker

On Thu, Oct 24, 2024 at 05:36:49PM GMT, Florian Weimer wrote:
> * Siddhesh Poyarekar:
> 
> > On 2024-10-24 11:15, Alejandro Colomar wrote:
> >>> AFAICT, there are strong sustained objections to unconditionally changing
> >>> behaviour, so that is likely not an option anymore.
> >> The strong sustained objections to changing the standard all have
> >> one
> >> condition:
> >> 	Change the implementations first.
> >> This is for example the case of Robert C. Seacord and JeanHeyd
> >> Meneide.
> >> Once we fix the implementations, the standard will be trivial to change.
> >> The only libc implementations that have realloc(p,0) and realloc(NULL,0)
> >> be inconsistent are glibc, Win32, and Bionic, AFAIK.  Then there are
> >> also some interposed libraries that behave like glibc for compatiblity,
> >> but I expect them to follow glibc.  I'm already in talks with Bionic.
> >> Once majority of implementations are fixed, I think we can force Win32
> >> to just follow; in any case, let's ignore Win32 for now.
> >> So, this is still an option.
> >> Regarding the objections to changing glibc, I think it's mainly you.
> >
> > Nope, please read the threads carefully; I actually said that I won't
> > sustain an objection if I'm the only one holding that opinion.
> 
> I'm still objecting, I don't think this change is valuable.

Objecting is one thing, which is okay.  But that doesn't mean you won't
change your mind.  Only that we need to talk about it.  Continue reading
below.

> I'm open to looking at this again once the C standard fully specifies
> the behavior of zero-sized objects, the return value of malloc (0), and
> so on.

That's a chicken-and-egg problem.  The standard won't do it if
implementations don't do it first.

>  Getting aligned behavior between implementations on these
> fundamental interfaces seems quite valuable to programmers.

Indeed.

>  But
> addressing one zero-object-size case and leaving all the others as
> unclear as before doesn't seem to be useful, especially given that a
> ffuture standard may require different behavior anyway.

I assume you mean 0-sized arrays as the other zero-sized-object that you
refer to.

Martin and I are working on improving the support for 0-sized arrays in
GCC.  That's a problem for implementing the __countof__ operator to work
on array parameters.  So there's already work to improve the GCC support
on 0-sized arrays before being able to do that.

>  A
> piece-by-piece transition to the newly required behaviors is also more
> onerous on programmers than one well-defined transition in a single
> glibc release.

Here's my plan, step by step:


A.1)  Fix glibc and/or Bionic to have realloc(p,0) be consistent with
      realloc(NULL,0).

A.2)  Ask POSIX and ISO to tighten the specification of realloc(p,0) to
      be consistent with realloc(NULL,0).  This will mean that Win32
      will be forced by decree of ISO to follow the rest.

B.1)  Ask POSIX to tighten the specification of malloc(0) to return
      non-NULL on success.  This will be easy, since the only POSIX
      systems that return NULL from malloc(0) are dying.  The BSDs,
      macOS, and GNU, all of them return non-NULL.

B.2)  Once POSIX has accepted A.2 and B.1, we can ask ISO to tighten
      malloc(0) to return non-NULL.  This might force some embedded
      platforms to conform.  Other dying systems such as AIX will also
      need to either conform, or assume they're dead.  I think all major
      implementations alive already work like that.

C.1)  Make GCC have [n] have a stronger meaning for array parameters.
      That is, not ignore the number of elements of array parameters,
      and instead make it mean the same as [static n], except that [n]
      doesn't require non-null-ness nor require at least 1 element.

C.1)  Add support for array parameters in __countof__ in GCC.
      As a natural consequence, __countof__ will be able to return 0
      for array parameters whose number of elements is 0 at run time.

C.3)  Ask ISO to make [n] be meaningful in array parameters, following
      C.1 from GCC.

C.4)  Add support for array parameters in __countof__ in ISO C,
      following C.2 from GCC.

C.5)  Add official support for 0-sized arrays in ISO.  This would be
      only bureaucracy, since actual support would already have been
      introduced in C3.


But we can't have it all at once, and we can't ask ISO C to innovate on
this, but rather follow implementations that already work.  If there's
not an implementation that works, we can't ask them to come up with a
good one.

How does that sound?

Have a lovely day!
Alex
Alejandro Colomar Oct. 24, 2024, 4:14 p.m. UTC | #33
CC += JeanHeyd

Hi Joseph,

On Thu, Oct 24, 2024 at 03:44:05PM GMT, Joseph Myers wrote:
> On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> 
> > The strong sustained objections to changing the standard all have one
> > condition:
> > 
> > 	Change the implementations first.
> 
> I think a fundamental objection for both standard and implementation 
> changes is: we discussed this at length more than once, we settled it 

You discussed this in a closed committee, without feedback from users.
That's not how it should work.  Also, in those discussions there was FUD
about vulnerabilities (double-free) being a necessary result of this
change.  I haven't been shown a Minimal Reproducible Example of that,
and strongly doubt, just like Doug and Paul, that it is possible.

> (first marking the feature obsolescent in C17, then making it undefined 
> behavior in C23), there is no significant new evidence that the basis for 
> those decisions was wrong

I've requested JeanHeyd to provide one, but you're invited to do so
yourself.  Otherwise, I claim that the decission that WG14 took is based
on FUD, and thus wrong.

Here are the requirements of the program:

It must run on any system of your like (except AIX; that's a broken
one), and behave well.  The same program must have a double free on the
BSD/Unix V7 behavior in a circumstance that wouldn't trigger on the
other system.  Bonus points if the system under which the program works
fine is glibc.  That should be easy if the decission by WG14 was based
on true facts.

> or that the decisions have caused problems that 
> could justify spending time on revisiting the settled issue so soon after 
> settling it.

The internet is on fire about ISO C23 making realloc(p,0) UB.  I don't
know if you've searched about it, but I've personally received messages
from several people despairing about C23, and mentioning that as one of
the most obvious wrongs that ISO has done in C23.


Have a lovely day!
Alex
Joseph Myers Oct. 24, 2024, 4:22 p.m. UTC | #34
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> The internet is on fire about ISO C23 making realloc(p,0) UB.  I don't

This sort of exaggerated claim seems like a great way to lose credibility 
and encourage people to consider all proposals on this topic as noise to 
summarily reject.

I think it would be a bad idea to relitigate the obsolescence and 
subsequent removal of realloc(p, 0), just as it's a bad idea to relitigate 
the choice of name for _Lengthof.
Joseph Myers Oct. 24, 2024, 4:45 p.m. UTC | #35
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> know if you've searched about it, but I've personally received messages
> from several people despairing about C23, and mentioning that as one of
> the most obvious wrongs that ISO has done in C23.

I suggest that people generating noise rather than light are good examples 
of people to ignore.  This really isn't a matter for "despair" or talking 
about "most obvious wrongs" (although such terminology is still to be 
preferred to certain offensive language seen earlier in this discussion).

It's rather a matter for calm, thoughtful, in-depth analysis (thus 
essentially ruling out anything found on short-form social media) that 
recognizes the complicated compromises involved in dealing with 
established bases of code that may expect different semantics, and 
recognizes the tricky issues involved in defining language and library 
semantics as a cohesive whole (in the presence of such bases of 
application code and implementations), and does not attack people or their 
decisions based on hindsight or because you personally disagree with their 
decisions but tries to understand the basis they were or might have been 
working from when coming up with those decisions in the past.

There is a fundamental difference between *disagreeing* (based on the 
information now available with hindsight about what the consequences of 
certain choices turned out to be) with a choice made for C89 to allow a 
particular option for malloc of size 0 based on semantic elegance of not 
having zero-size objects in the specification, and asserting that the 
committee or the choice they made was in some sense objectively wrong.  I 
don't consider the latter to be a reasonable assertion once you make 
proper allowance for the context of trying to define a consistent semantic 
basis for standard C in the first place.  Much the same applies to 
disagreement with other choices made since then.
Alejandro Colomar Oct. 24, 2024, 4:58 p.m. UTC | #36
CC += Lundin

Hi Joseph,

On Thu, Oct 24, 2024 at 04:22:56PM GMT, Joseph Myers wrote:
> On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> 
> > The internet is on fire about ISO C23 making realloc(p,0) UB.  I don't
> 
> This sort of exaggerated claim seems like a great way to lose credibility 

<https://langdev.stackexchange.com/questions/3286/what-was-the-rationale-for-making-reallocptr-0-have-ub-in-c23>
<https://stackoverflow.com/questions/78691087/reallocptr-0-in-c23-now-what>
<https://dl.acm.org/doi/pdf/10.1145/3588242>
<https://software.codidact.com/posts/289414/289415#answer-289415>
<https://software.codidact.com/posts/292694/292695#answer-292695>

> I think it would be a bad idea to relitigate the obsolescence and 
> subsequent removal of realloc(p, 0),

Why do you think banning realloc(p,0) is better than fixing it?  Yes, it
will be hard to fix it, but how is banning it better than fixing it?

Even if you say realloc() cannot be fixed without breaking other stuff,
we'd need realloc2() to be invented and fix this, even if it's just a
thin wrapper that does realloc(p,n?:1).  There's a need for 0-sized
objects.

> just as it's a bad idea to relitigate 
> the choice of name for _Lengthof.

That name was chosen by a small set of programmers, influenced by
programmers who don't even like C as their main language.  I want to
avoid having another mistake from WG14 be a mess for the future.

Why should WG14's preference be more important than GCC's?  Would you
mind surveying GCC and glibc maintainers and contributors on the topic?
Also, I can't even understand how a WG14 member gets offended by me
ignoring the results of his survey presented at his job to programmers
that explicitly say they hope to not have to program ever again in C, as
part of the survey comment.  Their argument is that many other languages
--which don't have the ambiguity problem of null-terminated strings also
using length-- use length for this, so we should follow.

Please consider countof.


Have a lovely day!
Alex
Joseph Myers Oct. 24, 2024, 5:02 p.m. UTC | #37
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> It must run on any system of your like (except AIX; that's a broken
> one), and behave well.  The same program must have a double free on the
> BSD/Unix V7 behavior in a circumstance that wouldn't trigger on the
> other system.  Bonus points if the system under which the program works
> fine is glibc.  That should be easy if the decission by WG14 was based
> on true facts.

A double free comes from a hypothetical change to semantics in one 
direction; leaks from a hypothetical change in a different direction.

The old implementation where a double free doesn't occur would be one that 
only returns NULL from realloc with size 0 when there is an error, and 
doesn't free the old allocation in that case.  (For example, an 
implementation that never returns NULL from realloc with size 0 and a 
non-null pointer argument.)  An application written for that 
interpretation of old versions of the standards might then check for NULL 
return and free the memory itself in that case (and that code might be 
unreachable if the implementation doesn't return NULL).

The new implementation would be one that does return NULL and does free 
the old allocation.

That's why a change in that direction would have caused problems with 
double frees for some applications.

A hypothetical change in semantics in the direction of always returning a 
non-null pointer (and thus freeing the previous allocation) should not 
introduce double frees, but can introduce memory leaks in any application 
that relied on realloc with size 0 being equivalent to free and not doing 
a new allocation even of size 0.

There are at least three options for what hypothetical new semantics might 
be (ignoring further variations such as semantics allowing more than one 
of these options, or semantics involving errno).  Every one might 
introduce either double frees or memory leaks in applications that were 
expecting different semantics.

* Always returns non-null (and frees previous allocation): can introduce 
memory leaks.

* Always returns null, as an error without freeing previous allocation: 
can introduce memory leaks.

* Always returns null and frees previous allocation: can introduce double 
frees in applications that interpreted the null return as indicating an 
error and thus freed the previous allocation themselves.

You can reasonably argue that this renders some choices of new semantics 
better than others because some risks are more serious than others.  I 
think the choice of obsolescence followed by undefined behavior was a 
carefully considered, reasonable conclusion in a situation where every 
choice would have adverse impacts for some (application, implementation) 
pairs, and that there is nothing new sufficient to justify revisiting this 
choice already.
Alejandro Colomar Oct. 24, 2024, 5:07 p.m. UTC | #38
Hi Joseph,

On Thu, Oct 24, 2024 at 04:45:15PM GMT, Joseph Myers wrote:
> On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> 
> > know if you've searched about it, but I've personally received messages
> > from several people despairing about C23, and mentioning that as one of
> > the most obvious wrongs that ISO has done in C23.
> 
> I suggest that people generating noise rather than light are good examples 
> of people to ignore.  This really isn't a matter for "despair" or talking 
> about "most obvious wrongs" (although such terminology is still to be 
> preferred to certain offensive language seen earlier in this discussion).
> 
> It's rather a matter for calm, thoughtful, in-depth analysis (thus 

Agree.  I want to do that.  I'll try to be more measured with my
language.  However, refusing to talk about this saying that this was
discussed in C17, doesn't seem like an in-depth analysis.

Can we please do an in-depth analysis, forgetting any mistakes on either
side that might have been made prior to now?  I'm interested in fixing
this, and am trying to find the best solution.  I'd appreciate some
collaboration.

Some of us think that this is not dangerous, and can be fixed in glibc
without any problems.  I'm, just like everybody, not interested in
making things worse, so it would be great if you could show how
improving the behavior of glibc could make things worse.  Or maybe tell
me what you need so that we can agree to a path to a fix.

> essentially ruling out anything found on short-form social media) that 

I don't use short-form social media.  I was actually referring to
senior engineers that often publish on stackexchange and similar sites.
Engineers that have taught me a lot of what I currently know, and which
have a deep understanding of C.

Have a lovely day!
Alex
Joseph Myers Oct. 24, 2024, 5:18 p.m. UTC | #39
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> CC += Lundin
> 
> Hi Joseph,
> 
> On Thu, Oct 24, 2024 at 04:22:56PM GMT, Joseph Myers wrote:
> > On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> > 
> > > The internet is on fire about ISO C23 making realloc(p,0) UB.  I don't
> > 
> > This sort of exaggerated claim seems like a great way to lose credibility 
> 
> <https://langdev.stackexchange.com/questions/3286/what-was-the-rationale-for-making-reallocptr-0-have-ub-in-c23>

That's someone asking a question and having it answered.  You can expect 
people to ask questions about changes in a new standard version.

> <https://stackoverflow.com/questions/78691087/reallocptr-0-in-c23-now-what>

That's pointing out an editorial issue in non-normative parts of the 
standard (Annex J).  It's true that certain lists there should no longer 
mention realloc now that it's separately undefined behavior to call it 
with size 0.  The fix to that editorial issue would be very 
straightforward: just remove realloc from a few items in Annex J.

> <https://dl.acm.org/doi/pdf/10.1145/3588242>

One person disagreeing with the decision is not "The internet is on fire".

> <https://software.codidact.com/posts/289414/289415#answer-289415>

That's just describing new features in C23.

> <https://software.codidact.com/posts/292694/292695#answer-292695>

That just seems like people discussing size 0 semantics.

All of this just looks like a few discussions of changes in C23.  Nothing 
for which "The internet is on fire" is a good description.

> Why do you think banning realloc(p,0) is better than fixing it?  Yes, it
> will be hard to fix it, but how is banning it better than fixing it?

A fix in any direction would break some (application, implementation) 
pairs that currently work.

> Even if you say realloc() cannot be fixed without breaking other stuff,
> we'd need realloc2() to be invented and fix this, even if it's just a
> thin wrapper that does realloc(p,n?:1).  There's a need for 0-sized
> objects.

New functions are a sensible solution when previous APIs and associated 
implementation divergence make it hard or impossible to use certain 
features safely and portably.  Cf. the discussion we had at WG14 in 
Minneapolis about the problems with struct tm and implementation-defined 
members thereof making it unclear what is / is not a valid way to create 
or modify a struct tm, and how completely new interfaces might be a 
sensible solution there (provided they avoid the problems with struct tmx 
that was first added then removed in C9X drafts).  In the case of realloc, 
however, the only problem is with the size 0 case; all other cases are 
unproblematic.
Joseph Myers Oct. 24, 2024, 5:23 p.m. UTC | #40
On Thu, 24 Oct 2024, Joseph Myers wrote:

> > <https://stackoverflow.com/questions/78691087/reallocptr-0-in-c23-now-what>
> 
> That's pointing out an editorial issue in non-normative parts of the 
> standard (Annex J).  It's true that certain lists there should no longer 
> mention realloc now that it's separately undefined behavior to call it 
> with size 0.  The fix to that editorial issue would be very 
> straightforward: just remove realloc from a few items in Annex J.

(And also add realloc with size 0 to the list of undefined behavior in 
Annex J to match the normative part of the standard, since it seems it 
didn't get added there.)
Alejandro Colomar Oct. 24, 2024, 5:27 p.m. UTC | #41
Hi Joseph,

On Thu, Oct 24, 2024 at 05:02:10PM GMT, Joseph Myers wrote:
> On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> 
> > It must run on any system of your like (except AIX; that's a broken
> > one), and behave well.  The same program must have a double free on the
> > BSD/Unix V7 behavior in a circumstance that wouldn't trigger on the
> > other system.  Bonus points if the system under which the program works
> > fine is glibc.  That should be easy if the decission by WG14 was based
> > on true facts.
> 
> A double free comes from a hypothetical change to semantics in one 
> direction; leaks from a hypothetical change in a different direction.
> 
> The old implementation where a double free doesn't occur would be one that 
> only returns NULL from realloc with size 0 when there is an error, and 
> doesn't free the old allocation in that case.  (For example, an 
> implementation that never returns NULL from realloc with size 0 and a 
> non-null pointer argument.)  An application written for that 
> interpretation of old versions of the standards might then check for NULL 
> return and free the memory itself in that case (and that code might be 
> unreachable if the implementation doesn't return NULL).
> 
> The new implementation would be one that does return NULL and does free 
> the old allocation.
> 
> That's why a change in that direction would have caused problems with 
> double frees for some applications.
> 
> A hypothetical change in semantics in the direction of always returning a 
> non-null pointer (and thus freeing the previous allocation) should not 
> introduce double frees, but can introduce memory leaks in any application 
> that relied on realloc with size 0 being equivalent to free and not doing 
> a new allocation even of size 0.
> 
> There are at least three options for what hypothetical new semantics might 
> be (ignoring further variations such as semantics allowing more than one 
> of these options, or semantics involving errno).  Every one might 
> introduce either double frees or memory leaks in applications that were 
> expecting different semantics.
> 
> * Always returns non-null (and frees previous allocation): can introduce 
> memory leaks.

This is the one we're proposing, and yes, it can potentially introduce
memory leaks.

One case that would obviously produce such leaks is implementing free(3)
as a call to realloc(p,0).  (And I've seen that somewhere in GCC, so
there are indeed things that need to be fixed first.)

However, those uses are actually rare, and we can probably minimize them
by searching all code in Debian that does something like that: a
hardcoded 0 in a call to realloc.  Maybe a diagnostic in GCC could be
added for hardcoded 0's in realloc(3) in -Wall.  I think that would be
part of a good transition plan.

Also, an implementation of free(3) as a call realloc(p,0) is itself
buggy, and potentially leaky, since free(3) accepts NULL, but
realloc(p,0) would leak 0B, so it can't be called with NULL (it's not so
bad, but for something implementing a generic free(3) it would be
obviously wrong).

Then there's run-time 0 values.  In those, I'm not sure what kind of
code could be written that would introduce leaks where they didn't
previously exist.  I can think of the following, which would be even
worse than a forever loop:

	p = NULL;
	while (true) {
		n = ...;
		p = realloc(p, n);
		if (p == NULL)  // n has gone down to 0 (or error).
			break;

		/* do work */
	}

Which would introduce a forever loop (until realloc fails).  However,
that program already relies on the first n not being 0 to not misbehave,
so a forever loop might in fact be better than the current behavior.
This is possibly the most worrying case that I can think of.

> 
> * Always returns null, as an error without freeing previous allocation: 
> can introduce memory leaks.
> 
> * Always returns null and frees previous allocation: can introduce double 
> frees in applications that interpreted the null return as indicating an 
> error and thus freed the previous allocation themselves.
> 
> You can reasonably argue that this renders some choices of new semantics 
> better than others because some risks are more serious than others.  I 
> think the choice of obsolescence followed by undefined behavior was a 
> carefully considered, reasonable conclusion in a situation where every 
> choice would have adverse impacts for some (application, implementation) 
> pairs, and that there is nothing new sufficient to justify revisiting this 
> choice already.

Please let's revisit.

If realloc() cannot be saved, can we please discuss the introduction of
remalloc() or something like that?  I do need the semantics of that
thing.  Calling realloc(3) through a wrapper that does ?:1 is a decent
workaround, but it might hide off-by-one bugs in certain cases, so a
realloc() that works well with 0 would be better.


Cheers,
Alex
Paul Eggert Oct. 24, 2024, 5:28 p.m. UTC | #42
On 2024-10-24 10:02, Joseph Myers wrote:
> the choice of obsolescence followed by undefined behavior was a
> carefully considered, reasonable conclusion in a situation where every
> choice would have adverse impacts for some (application, implementation)
> pairs, and that there is nothing new sufficient to justify revisiting this
> choice already

I assume you are writing about what C23 says, and I agree with that 
assessment. However, the crux of this discussion is elsewhere: it is 
about what future versions of glibc should do and what future C and 
POSIX standards should say. Even if we agree that the current C23 
standard should say realloc(p,0) is undefined and that the current POSIX 
should partially specify its behavior (in a way that current glibc 
doesn't always conform to!), that doesn't mean we can't improve this 
messy situation moving forward.

At bottom this is a disagreement about whether it's worth the trouble to 
clean up this mess in the obvious way. Concerns have been raised that 
having glibc behave like BSD/musl/etc. would adversely affect some 
applications that assume glibc semantics. One specific application has 
been mentioned (systemd) but on further investigation it has no problems.

I'd welcome further examples of significant real-world problems that 
would come from the proposed change. So far we haven't seen any, which 
means that so far all the hobgoblins have been imaginary.
Alejandro Colomar Oct. 24, 2024, 5:33 p.m. UTC | #43
Hi Joseph,

On Thu, Oct 24, 2024 at 05:18:26PM GMT, Joseph Myers wrote:
> > Why do you think banning realloc(p,0) is better than fixing it?  Yes, it
> > will be hard to fix it, but how is banning it better than fixing it?
> 
> A fix in any direction would break some (application, implementation) 
> pairs that currently work.
> 
> > Even if you say realloc() cannot be fixed without breaking other stuff,
> > we'd need realloc2() to be invented and fix this, even if it's just a
> > thin wrapper that does realloc(p,n?:1).  There's a need for 0-sized
> > objects.
> 
> New functions are a sensible solution when previous APIs and associated 
> implementation divergence make it hard or impossible to use certain 
> features safely and portably.  Cf. the discussion we had at WG14 in 
> Minneapolis about the problems with struct tm and implementation-defined 
> members thereof making it unclear what is / is not a valid way to create 
> or modify a struct tm, and how completely new interfaces might be a 
> sensible solution there (provided they avoid the problems with struct tmx 
> that was first added then removed in C9X drafts).  In the case of realloc, 
> however, the only problem is with the size 0 case; all other cases are 
> unproblematic.

Still, programs need that case.

How about this?:

We agree to ban realloc(p,0) temporarily; say until C2y, to give
implementations time to add a fortify assertion that it never happens.
Code needing the workaround ?:1 will either already have done so, or
will do it after a fortified realloc(3) breaks their code.

Then, after C2y is released, we reinstate realloc(p,0), but do it with
semantics compatible with malloc(0) --whatever malloc(0) does on that
system--.  No extant code would break, since they had to migrate away.
In C3x, we reinstate realloc(p,0) to be compatible with realloc(NULL,0)
and malloc(0).  Programs that were using ?:1 can gradually remove it, at
the pace required by their portability needs.

Does that sound like a good compromise?

Cheers,
Alex

> 
> -- 
> Joseph S. Myers
> josmyers@redhat.com
>
Joseph Myers Oct. 24, 2024, 5:40 p.m. UTC | #44
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> We agree to ban realloc(p,0) temporarily; say until C2y, to give
> implementations time to add a fortify assertion that it never happens.
> Code needing the workaround ?:1 will either already have done so, or
> will do it after a fortified realloc(3) breaks their code.

I agree that fortification (when building applications with C23 features 
enabled; hopefully the default with GCC 15 and later if a few remaining 
testsuite issues get resolved) is an implementation change it would make 
sense to explore to detect programs exercising this undefined behavior.

I'm much more doubtful about reinstating defined semantics for realloc 
with size 0 in future.
Paul Eggert Oct. 24, 2024, 5:51 p.m. UTC | #45
On 2024-10-24 08:15, Alejandro Colomar wrote:
> The only libc implementations that have realloc(p,0) and realloc(NULL,0)
> be inconsistent are glibc, Win32, and Bionic, AFAIK.

There's also AIX, which has things almost exactly backwards.

AIX realloc(p,0) normally acts like glibc except it sets errno=EINVAL as 
POSIX requires (glibc does not conform to POSIX here).

However, if you #define _LINUX_SOURCE_COMPAT, AIX realloc(p,0) stops 
acting mostly like glibc: instead, it acts like BSD! It does so via 
<stdlib.h>'s "#define malloc __linux_realloc", where __linux_realloc 
behaves like BSD.

Both AIX behaviors conform to current POSIX, whereas glibc does not conform.

The proposed change to glibc would fix this incompatibility with AIX.
Alejandro Colomar Oct. 24, 2024, 9:04 p.m. UTC | #46
On Thu, Oct 24, 2024 at 10:51:27AM GMT, Paul Eggert wrote:
> On 2024-10-24 08:15, Alejandro Colomar wrote:
> > The only libc implementations that have realloc(p,0) and realloc(NULL,0)
> > be inconsistent are glibc, Win32, and Bionic, AFAIK.
> 
> There's also AIX, which has things almost exactly backwards.

Yep; that's the other one.  Although I think we can ignore it for the
standards.  Does AIX intend to comply with C23 and/or POSIX.1-2024?  How
far from the graveyard is it?

> 
> AIX realloc(p,0) normally acts like glibc except it sets errno=EINVAL as
> POSIX requires (glibc does not conform to POSIX here).
> 
> However, if you #define _LINUX_SOURCE_COMPAT, AIX realloc(p,0) stops acting
> mostly like glibc:

lol

It might come from the times of Linux libc, or before glibc got broken?

> instead, it acts like BSD! It does so via <stdlib.h>'s
> "#define malloc __linux_realloc", where __linux_realloc behaves like BSD.
> 
> Both AIX behaviors conform to current POSIX, whereas glibc does not conform.
> 
> The proposed change to glibc would fix this incompatibility with AIX.

Have a lovely night!
Alex
Alejandro Colomar Oct. 24, 2024, 9:12 p.m. UTC | #47
Hi Joseph,

On Thu, Oct 24, 2024 at 05:40:08PM GMT, Joseph Myers wrote:
> On Thu, 24 Oct 2024, Alejandro Colomar wrote:
> 
> > We agree to ban realloc(p,0) temporarily; say until C2y, to give
> > implementations time to add a fortify assertion that it never happens.
> > Code needing the workaround ?:1 will either already have done so, or
> > will do it after a fortified realloc(3) breaks their code.
> 
> I agree that fortification (when building applications with C23 features 
> enabled; hopefully the default with GCC 15 and later if a few remaining 
> testsuite issues get resolved) is an implementation change it would make 
> sense to explore to detect programs exercising this undefined behavior.
> 
> I'm much more doubtful about reinstating defined semantics for realloc 
> with size 0 in future.

Could you clarify why?

Have a lovely night!
Alex
Alejandro Colomar Oct. 24, 2024, 9:19 p.m. UTC | #48
Hi DJ,

On Thu, Oct 24, 2024 at 02:27:37PM GMT, DJ Delorie wrote:
> Alejandro Colomar <alx@kernel.org> writes:
> > However, refusing to talk about this saying that this was discussed in
> > C17, doesn't seem like an in-depth analysis.
> 
> The request wasn't "don't talk about this".  It was "let's not rehash
> the old discussion."  Focus on today.

That sounds good to me.

> > Can we please do an in-depth analysis,
> 
> Each time someone wants to make a program-facing change in malloc, I say
> the same thing, to wit: glibc's malloc is the system malloc.  It has to
> be very reliable and conservative, and if you want to change it, you
> basically have to test it against *everything* in GNU/Hurd and GNU/Linux
> (I recommend Fedora, because reasons, so that's about 70,000 packages to
> test).
> 
> So far everyone has been arguing... er, "discussing"... based on made up
> scenarios, any of which *could* happen.

Not really *any*.  Some people (e.g., JeanHeyd) said this would
necessarily result in double-frees, which I thin we agree cannot happen
(Joseph just admitted that earlier today).

But yeah, several scenarios which are not good could happen, such as
leaks of 0+16 B, or infinite loops.

>  What we need is for someone to
> FIND OUT which cases actually exist in practice, and what breaking them
> would mean.
> 
> Hard data beats opinion every day.
> 
> I personaly think realloc(...,0) should match malloc(0) when
> appropriate, but I'm against change *in general* if applications could
> unknownly break in the future because of it.  I really don't like
> surprises.

Agreed.

Who is able to test that?


Have a lovely night!
Alex
Joseph Myers Oct. 24, 2024, 9:21 p.m. UTC | #49
On Thu, 24 Oct 2024, Alejandro Colomar wrote:

> > I agree that fortification (when building applications with C23 features 
> > enabled; hopefully the default with GCC 15 and later if a few remaining 
> > testsuite issues get resolved) is an implementation change it would make 
> > sense to explore to detect programs exercising this undefined behavior.
> > 
> > I'm much more doubtful about reinstating defined semantics for realloc 
> > with size 0 in future.
> 
> Could you clarify why?

Once something is in such a mess as realloc with size 0 has been ever 
since there were multiple implementations in use with incompatible 
semantics and multiple standard versions supporting different 
interpretations, resolving things as "don't use this feature" seems 
preferable to me to ending up with an even more complicated resolution 
"don't use in this range of versions, but maybe you can use with this 
newer version".

This admits the possibility of the standard defining some kind of 
erroneous runtime behavior, which conforming applications must not use but 
where there are bounded choices for what an implementation may do 
(terminate the program; free the memory and return either an allocated 
pointer or NULL; fail to free the memory and return NULL).  The ability to 
terminate the program, in particular, would be a useful option if bounding 
the choices like that.
Alejandro Colomar Oct. 24, 2024, 9:48 p.m. UTC | #50
Hi Joseph,

On Thu, Oct 24, 2024 at 09:21:50PM GMT, Joseph Myers wrote:
> > > I'm much more doubtful about reinstating defined semantics for realloc 
> > > with size 0 in future.
> > 
> > Could you clarify why?
> 
> Once something is in such a mess as realloc with size 0 has been ever 
> since there were multiple implementations in use with incompatible 
> semantics and multiple standard versions supporting different 
> interpretations, resolving things as "don't use this feature" seems 
> preferable to me to ending up with an even more complicated resolution 
> "don't use in this range of versions, but maybe you can use with this 
> newer version".

That's why a decade or so is a good thing.  We could wait until the last
system with a bad glibc dies, and then reinstate it with the good
behavior.  Portable programs shouldn't have problems dealing with ranges
of versions; I agree with that.

So, imagine that the last system with the current glibc behavior is EOL
in 2035.  On 2036, we could reinstate realloc() with any behavior we
want.

> This admits the possibility of the standard defining some kind of 
> erroneous runtime behavior, which conforming applications must not use but 
> where there are bounded choices for what an implementation may do 
> (terminate the program; free the memory and return either an allocated 
> pointer or NULL; fail to free the memory and return NULL).

I would reword that to:  "terminate the program; or free the memory and
return the result of malloc(0).  On error, the memory is not freed, NULL
is returned, and errno is set to indicate the error.".

>  The ability to 
> terminate the program, in particular, would be a useful option if bounding 
> the choices like that.

That would be good.


Have a lovely night!
Alex
Paul Eggert Oct. 24, 2024, 10:20 p.m. UTC | #51
On 2024-10-24 14:04, Alejandro Colomar wrote:

> Does AIX intend to comply with C23 and/or POSIX.1-2024?  How
> far from the graveyard is it?

Of all the operating systems in the world, AIX is the most likely to 
conform to POSIX.1-2024 eventually. That is because it's the only OS 
certified to conform to POSIX.1-2017. And AIX is far from the graveyard: 
IBM currently plans to support AIX through at least the year 2035 
<https://www.ibm.com/power>.

On this particular issue, AIX realloc(p,0) obviously conforms to C23, 
where behavior is undefined. And it conforms to POSIX.1-2024, which 
allows either return-non-null behavior or return-null behavior.


>> if you #define _LINUX_SOURCE_COMPAT, AIX realloc(p,0) stops acting
>> mostly like glibc:
> 
> lol
> 
> It might come from the times of Linux libc, or before glibc got broken?

Possibly. Or possibly the AIX maintainers thought that realloc(p,0) 
should be consistent with malloc(0), and so implemented the 
traditional+BSD behavior without thinking about it. We'd likely need 
access to AIX internals to know more.

One other detail: although AIX 7.3 realloc(p,0) conforms to 
POSIX.1-2017+ by setting errno when returning null, AIX 7.1 realloc(p,0) 
did not: it left errno alone, as current glibc does. Presumably this is 
because POSIX didn't require errno to be set until POSIX.1-2017, and 
earlier AIX conformed to earlier POSIX editions.

AIX 7.1 realloc(p,0) therefore was compatible with current glibc if and 
only if you did *not* #define _LINUX_SOURCE_COMPAT, whereas AIX 7.3 
conforms to POSIX.1-2017+ regardless of whether _LINUX_SOURCE_COMPAT is 
defined so it is never compatible with current glibc realloc(p,0).

I use the past tense for AIX 7.1 (2010) because it's no longer 
supported. I lack access to AIX 7.2 (2015), which is still supported and 
which was the first to conform to POSIX.1-2017.
Martin Uecker Oct. 25, 2024, 6:48 a.m. UTC | #52
Am Donnerstag, dem 24.10.2024 um 18:02 +0200 schrieb Alejandro Colomar:
> 
...
> 
> >  But
> > addressing one zero-object-size case and leaving all the others as
> > unclear as before doesn't seem to be useful, especially given that a
> > ffuture standard may require different behavior anyway.
> 
> I assume you mean 0-sized arrays as the other zero-sized-object that you
> refer to.
> 
> Martin and I are working on improving the support for 0-sized arrays in
> GCC.  That's a problem for implementing the __countof__ operator to work
> on array parameters.  So there's already work to improve the GCC support
> on 0-sized arrays before being able to do that.

I think for purposes relevant to this discussion, GCC and clang
already support 0-sized arrays just fine and any standardization
will certainly not break this (modulo corner cases).  I can not
predict how likely standardization of this is, but I would support
this.

(What I am working on is to fix some corner case bugs related
to the internal representation of arrays in GCC. I do not think
this is relevant here.)

...


> 
> C.1)  Make GCC have [n] have a stronger meaning for array parameters.
>       That is, not ignore the number of elements of array parameters,
>       and instead make it mean the same as [static n], except that [n]
>       doesn't require non-null-ness nor require at least 1 element.

This is already the case.  GCC will warn about passing shorter
arrays to function (by default even without -Wall) since GCC 11.

https://godbolt.org/z/bz7fEaE1M


I do not think there were many complaints about this.

Martin
Alejandro Colomar Jan. 8, 2025, 11:50 a.m. UTC | #53
Hi DJ, Paul,

On Thu, Oct 24, 2024 at 11:19:16PM +0200, Alejandro Colomar wrote:
> On Thu, Oct 24, 2024 at 02:27:37PM GMT, DJ Delorie wrote:
> >  What we need is for someone to
> > FIND OUT which cases actually exist in practice, and what breaking them
> > would mean.
> > 
> > Hard data beats opinion every day.
> > 
> > I personaly think realloc(...,0) should match malloc(0) when
> > appropriate, but I'm against change *in general* if applications could
> > unknownly break in the future because of it.  I really don't like
> > surprises.

gnulib did change that back in November, in the realloc-posix module.
Paul, I guess nothing has broken, did it?

> 
> Agreed.
> 
> Who is able to test that?

I'd like to ping about this.  Is anyone able to test such a change in
Fedora?


Have a lovely day!
Alex
Paul Eggert Jan. 8, 2025, 7:45 p.m. UTC | #54
On 2025-01-08 03:50, Alejandro Colomar wrote:

> gnulib did change that back in November, in the realloc-posix module.
> Paul, I guess nothing has broken, did it?

Yes, nothing broke, as far as we know. Not surprising, as it's 
implausible that things would break.
diff mbox series

Patch

diff --git a/NEWS b/NEWS
index 2fe0396b2d..00abfd896e 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,10 @@  Major new features:
 * The iconv program now supports converting files in place.  The program
   automatically uses a temporary file if required.
 
+* A new tunable glibc.malloc.realloc_zero_bytes_frees can be set to 0 to
+  make the realloc function return a zero sized pointer instead of NULL
+  when the requested new size is 0 bytes.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The big-endian ARC port (arceb-linux-gnu) has been removed.
diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 40ac5b3776..5dcf534332 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -82,6 +82,12 @@  glibc {
       type: SIZE_T
       minval: 0
     }
+    realloc_zero_bytes_frees {
+      type: INT_32
+      minval: 0
+      maxval: 1
+      default: 1
+    }
   }
 
   elision {
diff --git a/malloc/Makefile b/malloc/Makefile
index 1630aaf6ac..fcf0806fe1 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -61,6 +61,7 @@  tests := \
   tst-pvalloc \
   tst-pvalloc-fortify \
   tst-realloc \
+  tst-realloc-nonfreeing \
   tst-reallocarray \
   tst-safe-linking \
   tst-tcfree1 tst-tcfree2 tst-tcfree3 \
@@ -136,12 +137,17 @@  tests-exclude-hugetlb1 = \
   tst-mallocfork2 \
   tst-mallocfork3 \
   tst-mallocstate \
+  tst-realloc-nonfreeing \
 # tests-exclude-hugetlb1
+
 # The tst-free-errno relies on the used malloc page size to mmap an
 # overlapping region.
 tests-exclude-hugetlb2 = \
-	$(tests-exclude-hugetlb1) \
-	tst-free-errno
+  $(tests-exclude-hugetlb1) \
+  tst-free-errno \
+  tst-realloc-nonfreeing \
+# tests-exclude-hugetlb2
+
 tests-malloc-hugetlb1 = \
 	$(filter-out $(tests-exclude-hugetlb1), $(tests))
 tests-malloc-hugetlb2 = \
@@ -338,6 +344,13 @@  tst-malloc-usable-ENV = MALLOC_CHECK_=3 \
 tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3 \
 				 LD_PRELOAD=$(objpfx)/libc_malloc_debug.so
 
+tst-realloc-nonfreeing-ENV += \
+  GLIBC_TUNABLES=glibc.malloc.realloc_zero_bytes_frees=0
+tst-realloc-nonfreeing-malloc-check-ENV += \
+  GLIBC_TUNABLES=glibc.malloc.realloc_zero_bytes_frees=0
+tst-realloc-nonfreeing-mcheck-ENV += \
+  GLIBC_TUNABLES=glibc.malloc.realloc_zero_bytes_frees=0
+
 tst-mxfast-ENV = GLIBC_TUNABLES=glibc.malloc.tcache_count=0:glibc.malloc.mxfast=0
 
 CPPFLAGS-malloc-debug.c += -DUSE_TCACHE=0
diff --git a/malloc/arena.c b/malloc/arena.c
index cfb1ff8854..99eb748f9b 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -253,6 +253,7 @@  TUNABLE_CALLBACK_FNDECL (set_tcache_unsorted_limit, size_t)
 #endif
 TUNABLE_CALLBACK_FNDECL (set_mxfast, size_t)
 TUNABLE_CALLBACK_FNDECL (set_hugetlb, size_t)
+TUNABLE_CALLBACK_FNDECL (set_realloc_zero_bytes_frees, int32_t)
 
 #if USE_TCACHE
 static void tcache_key_initialize (void);
@@ -312,6 +313,8 @@  ptmalloc_init (void)
 # endif
   TUNABLE_GET (mxfast, size_t, TUNABLE_CALLBACK (set_mxfast));
   TUNABLE_GET (hugetlb, size_t, TUNABLE_CALLBACK (set_hugetlb));
+  TUNABLE_GET (realloc_zero_bytes_frees, int32_t,
+	       TUNABLE_CALLBACK (set_realloc_zero_bytes_frees));
 
   if (mp_.hp_pagesize > 0)
     {
diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c
index da1158b333..3a4941414b 100644
--- a/malloc/malloc-check.c
+++ b/malloc/malloc-check.c
@@ -257,7 +257,7 @@  realloc_check (void *oldmem, size_t bytes)
   if (oldmem == 0)
     return malloc_check (bytes);
 
-  if (bytes == 0)
+  if (mp_.realloc_zero_bytes_frees == 1 && bytes == 0)
     {
       free_check (oldmem);
       return NULL;
diff --git a/malloc/malloc-debug.c b/malloc/malloc-debug.c
index c9b4166a1e..1fde57722c 100644
--- a/malloc/malloc-debug.c
+++ b/malloc/malloc-debug.c
@@ -73,9 +73,12 @@  __malloc_debug_disable (enum malloc_debug_hooks flag)
   __malloc_debugging_hooks &= ~flag;
 }
 
+/* The ordering matters here because mcheck.c includes elf/dl-tunables.h, which
+   ends up messing with the same include in malloc-check.c.  This probably
+   needs to be fixed at some point.  */
+#include "malloc-check.c"
 #include "mcheck.c"
 #include "mtrace.c"
-#include "malloc-check.c"
 
 #if SHLIB_COMPAT (libc_malloc_debug, GLIBC_2_0, GLIBC_2_24)
 extern void (*__malloc_initialize_hook) (void);
diff --git a/malloc/malloc.c b/malloc/malloc.c
index bcb6e5b83c..be37f2b117 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -177,7 +177,6 @@ 
     USE_PUBLIC_MALLOC_WRAPPERS NOT defined
     USE_MALLOC_LOCK            NOT defined
     MALLOC_DEBUG               NOT defined
-    REALLOC_ZERO_BYTES_FREES   1
     TRIM_FASTBINS              0
 
     Options for customizing MORECORE:
@@ -330,20 +329,6 @@ 
   ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
 #define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)
 
-/*
-  The REALLOC_ZERO_BYTES_FREES macro controls the behavior of realloc (p, 0)
-  when p is nonnull.  If the macro is nonzero, the realloc call returns NULL;
-  otherwise, the call returns what malloc (0) would.  In either case,
-  p is freed.  Glibc uses a nonzero REALLOC_ZERO_BYTES_FREES, which
-  implements common historical practice.
-
-  ISO C17 says the realloc call has implementation-defined behavior,
-  and it might not even free p.
-*/
-
-#ifndef REALLOC_ZERO_BYTES_FREES
-#define REALLOC_ZERO_BYTES_FREES 1
-#endif
 
 /*
   TRIM_FASTBINS controls whether free() of a very small chunk can
@@ -628,9 +613,9 @@  void*  __libc_calloc(size_t, size_t);
   ANSI) and p is NOT freed.
 
   if n is for fewer bytes than already held by p, the newly unused
-  space is lopped off and freed if possible.  Unless the #define
-  REALLOC_ZERO_BYTES_FREES is set, realloc with a size argument of
-  zero (re)allocates a minimum-sized chunk.
+  space is lopped off and freed if possible.  Unless the
+  REALLOC_ZERO_BYTES_FREES tunable is set, realloc with a size argument
+  of zero (re)allocates a minimum-sized chunk.
 
   Large chunks that were internally obtained via mmap will always be
   grown using malloc-copy-free sequences unless the system supports
@@ -1861,6 +1846,7 @@  struct malloc_par
   INTERNAL_SIZE_T mmap_threshold;
   INTERNAL_SIZE_T arena_test;
   INTERNAL_SIZE_T arena_max;
+  int realloc_zero_bytes_frees;
 
   /* Transparent Large Page support.  */
   INTERNAL_SIZE_T thp_pagesize;
@@ -1919,7 +1905,8 @@  static struct malloc_par mp_ =
   .mmap_threshold = DEFAULT_MMAP_THRESHOLD,
   .trim_threshold = DEFAULT_TRIM_THRESHOLD,
 #define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8))
-  .arena_test = NARENAS_FROM_NCORES (1)
+  .arena_test = NARENAS_FROM_NCORES (1),
+  .realloc_zero_bytes_frees = 1
 #if USE_TCACHE
   ,
   .tcache_count = TCACHE_FILL_COUNT,
@@ -3413,12 +3400,11 @@  __libc_realloc (void *oldmem, size_t bytes)
   if (!__malloc_initialized)
     ptmalloc_init ();
 
-#if REALLOC_ZERO_BYTES_FREES
-  if (bytes == 0 && oldmem != NULL)
+  if (mp_.realloc_zero_bytes_frees && bytes == 0 && oldmem != NULL)
     {
-      __libc_free (oldmem); return 0;
+      __libc_free (oldmem);
+      return 0;
     }
-#endif
 
   /* realloc of null is supposed to be same as malloc */
   if (oldmem == 0)
@@ -5554,6 +5540,23 @@  do_set_hugetlb (size_t value)
   return 0;
 }
 
+/* The REALLOC_ZERO_BYTES_FREES tunable controls the behavior of realloc (p, 0)
+   when p is nonnull.  If the macro is nonzero, the realloc call returns NULL;
+   otherwise, the call returns what malloc (0) would.  In either case,
+   p is freed.  Glibc uses a nonzero REALLOC_ZERO_BYTES_FREES by default to
+   remain compatible with older programs.
+
+   ISO C17 says the realloc call has implementation-defined behavior,
+   and it might not even free p.  */
+static __always_inline int
+do_set_realloc_zero_bytes_frees (int32_t value)
+{
+  if (value == 0)
+    mp_.realloc_zero_bytes_frees = 0;
+
+  return 0;
+}
+
 int
 __libc_mallopt (int param_number, int value)
 {
diff --git a/malloc/mcheck-impl.c b/malloc/mcheck-impl.c
index eff4873f62..6d1cd8876e 100644
--- a/malloc/mcheck-impl.c
+++ b/malloc/mcheck-impl.c
@@ -16,6 +16,7 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <elf/dl-tunables.h>
 #include <malloc-internal.h>
 #include <mcheck.h>
 #include <libintl.h>
@@ -31,6 +32,7 @@ 
 
 /* Function to call when something awful happens.  */
 static void (*abortfunc) (enum mcheck_status);
+static int realloc_zero_bytes_frees = 1;
 
 struct hdr
 {
@@ -285,7 +287,7 @@  realloc_mcheck_before (void **ptrp, size_t *sizep, size_t *oldsize,
       return true;
     }
 
-  if (size == 0)
+  if (realloc_zero_bytes_frees == 1 && size == 0)
     {
       __debug_free (ptr);
       *victimp = NULL;
@@ -401,6 +403,11 @@  __mcheck_initialize (void (*func) (enum mcheck_status), bool in_pedantic)
     }
 
   pedantic = in_pedantic;
+
+  if (TUNABLE_GET_FULL (glibc, malloc, realloc_zero_bytes_frees, int32_t, NULL)
+      == 0)
+    realloc_zero_bytes_frees = 0;
+
   return 0;
 }
 
diff --git a/malloc/tst-realloc-nonfreeing.c b/malloc/tst-realloc-nonfreeing.c
new file mode 100644
index 0000000000..eec9fcb767
--- /dev/null
+++ b/malloc/tst-realloc-nonfreeing.c
@@ -0,0 +1,35 @@ 
+/* Smoke test for realloc when glibc.malloc.realloc_zero_bytes_frees tunable
+   is set to zero.
+   Copyright The GNU Toolchain Authors.
+   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 <stdlib.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  char *foo = malloc (42);
+  char *p = realloc (foo, 0);
+
+  TEST_VERIFY (p != NULL);
+  free (p);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/manual/memory.texi b/manual/memory.texi
index 3710d7ec66..5ade11101e 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -834,9 +834,14 @@  contents.
 
 If you pass a null pointer for @var{ptr}, @code{realloc} behaves just
 like @samp{malloc (@var{newsize})}.
-Otherwise, if @var{newsize} is zero
-@code{realloc} frees the block and returns @code{NULL}.
-Otherwise, if @code{realloc} cannot reallocate the requested size
+
+Otherwise, if @var{newsize} is zero @code{realloc} will by default free
+the block and return @code{NULL}.  This behavior can be overridden using
+the @code{glibc.malloc.realloc_zero_bytes_frees} tunable, where instead
+@code{realloc} will then free @var{ptr} and attempt to return a zero
+sized block.
+
+Finally, if @code{realloc} cannot reallocate the requested size
 it returns @code{NULL} and sets @code{errno}; the original block
 is left undisturbed.
 @end deftypefun
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 0b1b2898c0..b76b643313 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -294,6 +294,15 @@  supported ones.  If provided value is invalid, @code{MAP_HUGETLB} will not
 be used.
 @end deftp
 
+@deftp Tunable glibc.malloc.realloc_zero_bytes_frees
+
+By default when the @code{realloc} function is called with the new size as
+@var{0}, it frees the input pointer and returns @var{NULL}.  Setting the value
+of this tunable to @var{0} will result in @code{realloc} instead freeing the
+input pointer and attempting to allocate and return a zero sized block.  Any
+values other than @var{0} are ignored.
+@end deftp
+
 @node Dynamic Linking Tunables
 @section Dynamic Linking Tunables
 @cindex dynamic linking tunables