Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <5q3avwhziavvc5qgny5wbmof2hk2bf6cl2t5l5lmksuyeftewf@hfp4ifxz56qk>
Date: Fri, 20 Jun 2025 02:22:49 +0200
From: Alejandro Colomar <alx@...nel.org>
To: Rich Felker <dalias@...c.org>
Cc: linux-man@...r.kernel.org, musl@...ts.openwall.com, 
	libc-alpha@...rceware.org, Paul Eggert <eggert@...ucla.edu>, Bruno Haible <bruno@...sp.org>, 
	bug-gnulib@....org
Subject: Re: [v2] malloc.3: Clarify realloc(3) standards conformance

Hi Rich,

On Thu, Jun 19, 2025 at 11:32:09AM -0400, Rich Felker wrote:
> On Thu, Jun 19, 2025 at 03:57:47PM +0200, Alejandro Colomar wrote:
> > Hi,
> > 
> > Here's a revision of this change, addressing some concerns.  I'm only
> > showing the formatted changes, since the patch itself is unimportant.
> > 
> > 
> > Have a lovely day!
> > Alex
> > 
> > ---
> > $ MANWIDTH=72 diffman-git HEAD
> > --- HEAD^:man/man3/malloc.3
> > +++ HEAD:man/man3/malloc.3
> > @@ -126,15 +126,32 @@
> >         │ realloc()                          │               │         │
> >         └────────────────────────────────────┴───────────────┴─────────┘
> >  
> > +VERSIONS
> > +       The behavior of realloc(p, 0) in glibc doesn’t conform to any of
> > +       C99, C11, POSIX.1‐2001, POSIX.1‐2008, POSIX.1‐2017, or
> > +       POSIX.1‐2024.  The C17 specification was changed to make it con‐
> > +       forming, but that specification was broken —it is impossible to
> > +       write code that works portably—, and C23 changed it again to
> > +       make this undefined behavior, acknowledging that the C17 speci‐
> > +       fication was broad enough that undefined behavior wasn’t worse
> > +       than that.
> 
> This is still full of your polemics. The word "broken" generally
> belongs in personal blog posts, not a manual that's supposed to be
> documenting the facts of an interface.

Hmmm, agree.  I've changed the wording:

@@ -252,8 +265,10 @@ .SH VERSIONS
 POSIX.1-2017,
 or POSIX.1-2024.
 The C17 specification was changed to make it conforming,
-but that specification was broken
-\[em]it is impossible to write code that works portably\[em],
+but that specification made it
+impossible to write code that
+reliably determines if the input pointer is freed after
+.IR realloc(p,\~0) ,
 and C23 changed it again to make this undefined behavior,
 acknowledging that the C17 specification was broad enough that
 undefined behavior wasn't worse than that.


> In fact it is very possible to
> write code which works portably: by refraining from passing 0.

But that entire paragraph is talking about the impossibility to call
realloc(p,0) portably.  Of course if you call realloc(p,n?:1) it works,
because you've avoided the case altogether.

> Regardless of what action is taken here on the standards or
> documentation, that's already been necessary for a long time, and will
> continue to be necessary for a long time, because of the existence of
> implementations on which passing 0 has inconsistent results.
> 
> I would suggest something more like:
> 
>       The behavior of realloc(p, 0) in glibc doesn’t conform to any of
>       C99, C11, POSIX.1‐2001, POSIX.1‐2008, POSIX.1‐2017, or
>       POSIX.1‐2024. C11 was amended in 2017 to allow the glibc
>       behavior [insert description of exactly how that was done, I
>       forget] and C23 followed up by making the behavior explicitly
>       undefined.
> 
> In particular, this text is purely matters of fact, no statement of
> your or my preferred future outcome or disagreement with what
> happened.
> 
> I would also move it to CONFORMANCE rather than VERSIONS since
> VERSIONS is normally about differences between versions of the
> implementation being described, not conformance requirement
> differences between versions of the standard.

Agree.  I've moved it to STANDARDS.

> > +BUGS
> > +       Programmers would naturally expect that realloc(p, size) is con‐
> > +       sistent with free(p) and malloc(size).  This is not explicitly
> > +       required by POSIX.1‐2024 or C11, but all conforming implementa‐
> > +       tions are consistent with that.
> 
> This has not historically been a conformance requirement and it is not
> one now. Because the behavior is undefined, arbitrarily-inconsistent
> behavior is conforming.

This section is about bugs, not about standards.  The expectation of
programmers is what matters.  Since programmers do

	new = realloc(old, size);
	if (new == NULL)
		goto fail;

the glibc implementation is causing bugs in user programs, and thus has
a bogus implementation.  Especially, when it deviates from common
historical implementations, which supported the idiom shown above.

This is enough to claim it's a bug in glibc.

> It's possible to read this as not stating a conformance requirement,
> just a matter of fact that all implementations which conform(ed to
> past versions of the standard) happened to also be consistent here.

Yes.

> But in that case I would very much prefer if you make it clear by just
> saying that they're consistent on [some explicit list or description
> of the class of implementations you've reviewed to have this
> property].

I don't fully understand this suggestion.  Please clarify.

> 
> > +       The glibc implementation of realloc() is not consistent with
> > +       that, and as a consequence, it is dangerous to call
> > +       realloc(p, 0) in glibc.
> 
> It's not dangerous if you know what it's doing. Rather it's
> non-portable. It does something predictable that you can use safely,
> but the way you use it safely is different from other, more consistent
> implementations in a way that can be a footgun.

It is dangerous, because programmers don't expect the glibc behavior.

Please show me a piece of code calling realloc(3) from glibc that does
something like this:

	new = realloc(old, size);
	if (new == NULL) {
		if (errno == ENOMEM)
			free(old);
		goto fail;
	}

	free(new);

It is only unnecessary if you know for sure 'size' won't ever be 0, but
I bet there's code out there where size might be 0 and they're not doing
this.  If they're freeing 'old' on NULL without checking for ENOMEM,
they're causing a double-free.  If they're using old on NULL without
checking for ENOMEM, they're causing a use-after-free.

That *is* dangerous.

> 
> > +       A trivial workaround for glibc is calling it as
> > +       realloc(p, size?size:1).
> 
> It should probably be noted that use of such a workaround sacrifices
> the ability to diagnose logic errors (via sanitizers, valgrind, etc.)
> where 1 byte is written to allocated memory that was not intended to
> have any accessible bytes of storage.

I would note it for n+1, but since n?n:1 would only hide them for the
case where size is 0, I find it less problematic.  I think adding such
text might put off programmers from using this, which would be
counterproductive.  So, I hope they realize about it without writing it.


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es/>

Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.