* BUG: realloc(p,0) should be consistent with malloc(0)
@ 2025-06-16 11:55 Alejandro Colomar
2025-06-16 12:24 ` [musl] " Rich Felker
` (3 more replies)
0 siblings, 4 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 11:55 UTC (permalink / raw)
To: libc-alpha
Cc: наб,
Paul Eggert, Robert Seacord, musl, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 2331 bytes --]
Hi!
For context, the old discussion was in this thread:
<https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
Also for context, here's the excellent research by наб about malloc(0)
and realloc(p, 0) in historic UNIX systems and their descendents:
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
We discussed last year about realloc(p, 0) being problematic currently
in glibc. Ideally, realloc(p, n) should be consistent with malloc(n)
in that:
- It is equivalent to free(p) and malloc(n), regardless of the value of
n, including when it is 0, and regardless of p, including when it is
NULL.
- Except that of course, if there's enough contiguous space, it doesn't
free/malloc, and taht if malloc(n) fails, it doesn't free(p).
This congruency existed in every UNIX system and their descendents,
including the historic BSDs. It wasn't until new systems were written
artificially by the letter of the standard, without regards to
self-consistency, when this congruency was broken. I believe glibc was
the first one to do that, and I don't know/remember if any other systems
followed glibc.
Paul Eggert and I are convinced that changing the implementation to
conform to this behavior consistent with malloc(0) would be harmless.
As such, we modified gnulib's realloc-posix module to conform to that.
This was done in
d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
After more than half a year of changing the behavior in gnulib, there
have been no dramatic consequences. As expected, no fallout at all.
Now, can we please make the same change in glibc?
I've CCed musl and Elliott (Bionic), because I don't remember what's the
behavior in their libraries. Is it already self-consistent in musl and
Bionic? Or do they need a similar fix?
glibc (and possibly libraries that attempt glibc compatibility) is the
only libc implementation that is not self consistent, and by which ISO C
has UB in realloc(p, 0). We expect that when glibc is fixed,
realloc(p, 0) can be allowed again in ISO C, and can be specified to be
consistent with malloc(0), thus removing a case of UB.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 11:55 BUG: realloc(p,0) should be consistent with malloc(0) Alejandro Colomar
@ 2025-06-16 12:24 ` Rich Felker
2025-06-16 12:43 ` Alejandro Colomar
2025-06-16 17:35 ` Paul Eggert
2025-06-16 16:40 ` enh
` (2 subsequent siblings)
3 siblings, 2 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-16 12:24 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, наб,
Paul Eggert, Robert Seacord, musl, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
On Mon, Jun 16, 2025 at 01:55:26PM +0200, Alejandro Colomar wrote:
> Hi!
>
> For context, the old discussion was in this thread:
> <https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
>
> Also for context, here's the excellent research by наб about malloc(0)
> and realloc(p, 0) in historic UNIX systems and their descendents:
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
>
> We discussed last year about realloc(p, 0) being problematic currently
> in glibc. Ideally, realloc(p, n) should be consistent with malloc(n)
> in that:
>
> - It is equivalent to free(p) and malloc(n), regardless of the value of
> n, including when it is 0, and regardless of p, including when it is
> NULL.
>
> - Except that of course, if there's enough contiguous space, it doesn't
> free/malloc, and taht if malloc(n) fails, it doesn't free(p).
>
> This congruency existed in every UNIX system and their descendents,
> including the historic BSDs. It wasn't until new systems were written
> artificially by the letter of the standard, without regards to
> self-consistency, when this congruency was broken. I believe glibc was
> the first one to do that, and I don't know/remember if any other systems
> followed glibc.
>
> Paul Eggert and I are convinced that changing the implementation to
> conform to this behavior consistent with malloc(0) would be harmless.
> As such, we modified gnulib's realloc-posix module to conform to that.
> This was done in
>
> d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> After more than half a year of changing the behavior in gnulib, there
> have been no dramatic consequences. As expected, no fallout at all.
Do you have numbers for how many packages have actually pulled in the
updated gnulib and shipped with it? With this change, do you have it
replacing realloc on glibc, so that the new realloc actually gets
used? I would expect yes; I'm just asking so that this can be a more
clear sign to would-be detractors that nothing breaks.
> Now, can we please make the same change in glibc?
>
> I've CCed musl and Elliott (Bionic), because I don't remember what's the
> behavior in their libraries. Is it already self-consistent in musl and
> Bionic? Or do they need a similar fix?
It's always been self-consistent in musl. realloc(p,0) returns
non-null except of course on failure (such extreme OOM that allocation
of 0 bytes is not possible), in which case it behaves like any other
failure. Note that such failure is possible because our allocator
segregates allocations by size classes, and it might be impossible to
make a new slab of smallest-size slots even if a much larger slot
would be freed.
> glibc (and possibly libraries that attempt glibc compatibility) is the
> only libc implementation that is not self consistent, and by which ISO C
> has UB in realloc(p, 0). We expect that when glibc is fixed,
> realloc(p, 0) can be allowed again in ISO C, and can be specified to be
> consistent with malloc(0), thus removing a case of UB.
This would be excellent.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 12:24 ` [musl] " Rich Felker
@ 2025-06-16 12:43 ` Alejandro Colomar
2025-06-16 17:35 ` Paul Eggert
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 12:43 UTC (permalink / raw)
To: Rich Felker, Paul Eggert, Bruno Haible
Cc: libc-alpha, наб,
Robert Seacord, musl, Elliott Hughes, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 2077 bytes --]
Hi Rich, Paul, Bruno,
On Mon, Jun 16, 2025 at 08:24:15AM -0400, Rich Felker wrote:
> > This was done in
> >
> > d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
> >
> > After more than half a year of changing the behavior in gnulib, there
> > have been no dramatic consequences. As expected, no fallout at all.
>
> Do you have numbers for how many packages have actually pulled in the
> updated gnulib and shipped with it? With this change, do you have it
> replacing realloc on glibc, so that the new realloc actually gets
> used? I would expect yes; I'm just asking so that this can be a more
> clear sign to would-be detractors that nothing breaks.
Maybe Paul or Bruno can answer this. I don't know.
> > Now, can we please make the same change in glibc?
> >
> > I've CCed musl and Elliott (Bionic), because I don't remember what's the
> > behavior in their libraries. Is it already self-consistent in musl and
> > Bionic? Or do they need a similar fix?
>
> It's always been self-consistent in musl. realloc(p,0) returns
> non-null except of course on failure (such extreme OOM that allocation
> of 0 bytes is not possible), in which case it behaves like any other
> failure. Note that such failure is possible because our allocator
> segregates allocations by size classes, and it might be impossible to
> make a new slab of smallest-size slots even if a much larger slot
> would be freed.
Thanks!
> > glibc (and possibly libraries that attempt glibc compatibility) is the
> > only libc implementation that is not self consistent, and by which ISO C
> > has UB in realloc(p, 0). We expect that when glibc is fixed,
> > realloc(p, 0) can be allowed again in ISO C, and can be specified to be
> > consistent with malloc(0), thus removing a case of UB.
>
> This would be excellent.
Yep; I want to fix this eventually. (And also mandate that malloc(0)
returns non-null, but that's another story.)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 11:55 BUG: realloc(p,0) should be consistent with malloc(0) Alejandro Colomar
2025-06-16 12:24 ` [musl] " Rich Felker
@ 2025-06-16 16:40 ` enh
2025-06-16 21:21 ` Alejandro Colomar
2025-06-16 18:20 ` Adhemerval Zanella Netto
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
3 siblings, 1 reply; 143+ messages in thread
From: enh @ 2025-06-16 16:40 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, наб,
Paul Eggert, Robert Seacord, musl, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Mon, Jun 16, 2025 at 7:55 AM Alejandro Colomar <alx@kernel.org> wrote:
>
> Hi!
>
> For context, the old discussion was in this thread:
> <https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
>
> Also for context, here's the excellent research by наб about malloc(0)
> and realloc(p, 0) in historic UNIX systems and their descendents:
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
>
> We discussed last year about realloc(p, 0) being problematic currently
> in glibc. Ideally, realloc(p, n) should be consistent with malloc(n)
> in that:
>
> - It is equivalent to free(p) and malloc(n), regardless of the value of
> n, including when it is 0, and regardless of p, including when it is
> NULL.
android has these tests in the compatibility test suite (cts):
TEST(malloc, realloc_nullptr_0) {
// realloc(nullptr, size) is actually malloc(size).
void* p = realloc(nullptr, 0);
ASSERT_TRUE(p != nullptr);
free(p);
}
TEST(malloc, realloc_0) {
void* p = malloc(1024);
ASSERT_TRUE(p != nullptr);
// realloc(p, 0) is actually free(p).
void* p2 = realloc(p, 0);
ASSERT_TRUE(p2 == nullptr);
}
> - Except that of course, if there's enough contiguous space, it doesn't
> free/malloc, and taht if malloc(n) fails, it doesn't free(p).
>
> This congruency existed in every UNIX system and their descendents,
> including the historic BSDs. It wasn't until new systems were written
> artificially by the letter of the standard, without regards to
> self-consistency, when this congruency was broken. I believe glibc was
> the first one to do that, and I don't know/remember if any other systems
> followed glibc.
>
> Paul Eggert and I are convinced that changing the implementation to
> conform to this behavior consistent with malloc(0) would be harmless.
> As such, we modified gnulib's realloc-posix module to conform to that.
> This was done in
>
> d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> After more than half a year of changing the behavior in gnulib, there
> have been no dramatic consequences. As expected, no fallout at all.
>
> Now, can we please make the same change in glibc?
>
> I've CCed musl and Elliott (Bionic), because I don't remember what's the
> behavior in their libraries. Is it already self-consistent in musl and
> Bionic? Or do they need a similar fix?
>
> glibc (and possibly libraries that attempt glibc compatibility) is the
> only libc implementation that is not self consistent, and by which ISO C
> has UB in realloc(p, 0). We expect that when glibc is fixed,
> realloc(p, 0) can be allowed again in ISO C, and can be specified to be
> consistent with malloc(0), thus removing a case of UB.
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 12:24 ` [musl] " Rich Felker
2025-06-16 12:43 ` Alejandro Colomar
@ 2025-06-16 17:35 ` Paul Eggert
1 sibling, 0 replies; 143+ messages in thread
From: Paul Eggert @ 2025-06-16 17:35 UTC (permalink / raw)
To: Rich Felker
Cc: libc-alpha, наб,
Robert Seacord, musl, Elliott Hughes, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Alejandro Colomar
On 2025-06-16 05:24, Rich Felker wrote:
> Do you have numbers for how many packages have actually pulled in the
> updated gnulib and shipped with it? With this change, do you have it
> replacing realloc on glibc, so that the new realloc actually gets
> used? I would expect yes;
I don't have numbers, but I've helped to release new GNU coreutils,
diffutils, grep, and gzip with the new Gnulib. No realloc-related
problems have been reported. I expect other GNU apps have also been
released with the new Gnulib.
The new Gnulib replaces all app calls to realloc on platforms like glibc
where realloc does not have the desired behavior. It does not replace
realloc calls within libraries that are linked to.
Since these GNU apps are designed to be portable to musl etc. I would
not have expected the new Gnulib behavior to change observable app
behavior. The Gnulib change is mostly there to mask out any (unlikely)
bugs that would occur if a GNU app incorrectly assumed the current Glibc
realloc behavior (which is obviously bogus).
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 11:55 BUG: realloc(p,0) should be consistent with malloc(0) Alejandro Colomar
2025-06-16 12:24 ` [musl] " Rich Felker
2025-06-16 16:40 ` enh
@ 2025-06-16 18:20 ` Adhemerval Zanella Netto
2025-06-16 19:35 ` Florian Weimer
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
3 siblings, 1 reply; 143+ messages in thread
From: Adhemerval Zanella Netto @ 2025-06-16 18:20 UTC (permalink / raw)
To: musl, Alejandro Colomar, libc-alpha, Joseph Myers
Cc: наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
On 16/06/25 08:55, Alejandro Colomar wrote:
> Hi!
>
> For context, the old discussion was in this thread:
> <https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
>
> Also for context, here's the excellent research by наб about malloc(0)
> and realloc(p, 0) in historic UNIX systems and their descendents:
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
>
> We discussed last year about realloc(p, 0) being problematic currently
> in glibc. Ideally, realloc(p, n) should be consistent with malloc(n)
> in that:
>
> - It is equivalent to free(p) and malloc(n), regardless of the value of
> n, including when it is 0, and regardless of p, including when it is
> NULL.
>
> - Except that of course, if there's enough contiguous space, it doesn't
> free/malloc, and taht if malloc(n) fails, it doesn't free(p).
>
> This congruency existed in every UNIX system and their descendents,
> including the historic BSDs. It wasn't until new systems were written
> artificially by the letter of the standard, without regards to
> self-consistency, when this congruency was broken. I believe glibc was
> the first one to do that, and I don't know/remember if any other systems
> followed glibc.
>
> Paul Eggert and I are convinced that changing the implementation to
> conform to this behavior consistent with malloc(0) would be harmless.
> As such, we modified gnulib's realloc-posix module to conform to that.
> This was done in
>
> d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> After more than half a year of changing the behavior in gnulib, there
> have been no dramatic consequences. As expected, no fallout at all.
>
> Now, can we please make the same change in glibc?
>
> I've CCed musl and Elliott (Bionic), because I don't remember what's the
> behavior in their libraries. Is it already self-consistent in musl and
> Bionic? Or do they need a similar fix?
>
> glibc (and possibly libraries that attempt glibc compatibility) is the
> only libc implementation that is not self consistent, and by which ISO C
> has UB in realloc(p, 0). We expect that when glibc is fixed,
> realloc(p, 0) can be allowed again in ISO C, and can be specified to be
> consistent with malloc(0), thus removing a case of UB.
I have re-read the whole thread and it seems that most maintainers are OK
with this change and agree that current POSIX's realloc spec has some
drawbacks (albeit it still allows current glic behavior).
The only one involved in the previous thread that raised some objection to
this change was Joseph [1], but I will let to say if he still think this
potential change to glibc is ill-advised.
So what I would expect to move this forwards will be to.
1. Reopen https://sourceware.org/bugzilla/show_bug.cgi?id=12547
2. Follow the suggestions laid out by Siddhesh [2]. The Distribution-wide
verification seems already to be in progress, with some good results
from gnulib realloc replacement and some work by you on checking some
other projects (systemd for instance).
3. Prepare the patch to change it, along with the manual documentation,
regression testcase, and the NEW entry.
4. Since we are near to 2.42 release, this change should be done once
2.43 starts to give some time to check potential issue with rolling
distros like Fedora Rawhide.
What I do *not* think it would be worthwhile is adding either a compile
or runtime (through tunables) to reinstate old behaviour; this is really
adds some maintainability burden to what seems to be bad behavior that
eventually will be removed from standards. I am not sure about a
compatibility symbol.
[1] https://inbox.sourceware.org/libc-alpha/ece1e7ef-ad40-e5cd-9aa4-438854fe443e@redhat.com/
[2] https://inbox.sourceware.org/libc-alpha/a69a4a81-208e-4d8c-8201-05f657e9fe4c@gotplt.org/
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 18:20 ` Adhemerval Zanella Netto
@ 2025-06-16 19:35 ` Florian Weimer
2025-06-16 20:59 ` Adhemerval Zanella Netto
2025-06-16 21:44 ` Alejandro Colomar
0 siblings, 2 replies; 143+ messages in thread
From: Florian Weimer @ 2025-06-16 19:35 UTC (permalink / raw)
To: Adhemerval Zanella Netto
Cc: musl, Alejandro Colomar, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
* Adhemerval Zanella Netto:
> I have re-read the whole thread and it seems that most maintainers are OK
> with this change and agree that current POSIX's realloc spec has some
> drawbacks (albeit it still allows current glic behavior).
>
> The only one involved in the previous thread that raised some objection to
> this change was Joseph [1], but I will let to say if he still think this
> potential change to glibc is ill-advised.
I objected then, and I'm objecting now as well.
My rationale has not changed:
<https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
I believe Siddhesh's proposed patch as the time was mostly a device to
drive the discussion to a conclusion, which it did.
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 19:35 ` Florian Weimer
@ 2025-06-16 20:59 ` Adhemerval Zanella Netto
2025-06-16 21:44 ` Alejandro Colomar
1 sibling, 0 replies; 143+ messages in thread
From: Adhemerval Zanella Netto @ 2025-06-16 20:59 UTC (permalink / raw)
To: Florian Weimer
Cc: musl, Alejandro Colomar, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
On 16/06/25 16:35, Florian Weimer wrote:
> * Adhemerval Zanella Netto:
>
>> I have re-read the whole thread and it seems that most maintainers are OK
>> with this change and agree that current POSIX's realloc spec has some
>> drawbacks (albeit it still allows current glic behavior).
>>
>> The only one involved in the previous thread that raised some objection to
>> this change was Joseph [1], but I will let to say if he still think this
>> potential change to glibc is ill-advised.
>
> I objected then, and I'm objecting now as well.
>
> My rationale has not changed:
>
> <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
>
> I believe Siddhesh's proposed patch as the time was mostly a device to
> drive the discussion to a conclusion, which it did.
Alright, sorry if I missed it (for some reason Alejandro link did not have
your reply in the thread overview).
So I think we are far from consensus on this change.
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 16:40 ` enh
@ 2025-06-16 21:21 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 21:21 UTC (permalink / raw)
To: enh
Cc: libc-alpha, наб,
Paul Eggert, Robert Seacord, musl, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 1747 bytes --]
Hi Elliott,
On Mon, Jun 16, 2025 at 12:40:18PM -0400, enh wrote:
> On Mon, Jun 16, 2025 at 7:55 AM Alejandro Colomar <alx@kernel.org> wrote:
> >
> > Hi!
> >
> > For context, the old discussion was in this thread:
> > <https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
> >
> > Also for context, here's the excellent research by наб about malloc(0)
> > and realloc(p, 0) in historic UNIX systems and their descendents:
> > <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
> >
> > We discussed last year about realloc(p, 0) being problematic currently
> > in glibc. Ideally, realloc(p, n) should be consistent with malloc(n)
> > in that:
> >
> > - It is equivalent to free(p) and malloc(n), regardless of the value of
> > n, including when it is 0, and regardless of p, including when it is
> > NULL.
>
> android has these tests in the compatibility test suite (cts):
>
> TEST(malloc, realloc_nullptr_0) {
> // realloc(nullptr, size) is actually malloc(size).
> void* p = realloc(nullptr, 0);
> ASSERT_TRUE(p != nullptr);
> free(p);
> }
>
> TEST(malloc, realloc_0) {
> void* p = malloc(1024);
> ASSERT_TRUE(p != nullptr);
> // realloc(p, 0) is actually free(p).
> void* p2 = realloc(p, 0);
> ASSERT_TRUE(p2 == nullptr);
> }
Hmmm, then Bionic is broken, like glibc. The first test above is good,
but the second is not. realloc(p, 0) should be free(p) and malloc(0).
That's what we changed in gnulib last year. I suggest that you apply
the same fix. If you need convincing, I can try writing a summary of
the old thread.
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 19:35 ` Florian Weimer
2025-06-16 20:59 ` Adhemerval Zanella Netto
@ 2025-06-16 21:44 ` Alejandro Colomar
2025-06-16 23:20 ` Alejandro Colomar
` (2 more replies)
1 sibling, 3 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 21:44 UTC (permalink / raw)
To: Florian Weimer
Cc: Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 4419 bytes --]
Hi Florian,
On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> * Adhemerval Zanella Netto:
>
> > I have re-read the whole thread and it seems that most maintainers are OK
> > with this change and agree that current POSIX's realloc spec has some
> > drawbacks (albeit it still allows current glic behavior).
> >
> > The only one involved in the previous thread that raised some objection to
> > this change was Joseph [1], but I will let to say if he still think this
> > potential change to glibc is ill-advised.
>
> I objected then, and I'm objecting now as well.
>
> My rationale has not changed:
>
> <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
>
> I believe Siddhesh's proposed patch as the time was mostly a device to
> drive the discussion to a conclusion, which it did.
I'll quote your rationale from the link:
| * Siddhesh Poyarekar:
| | 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.
I'm working on that. I have a proposal for mandating that malloc(0),
but I can't present it until realloc(p, 0) is fixed. And the
C Committee has refused to fix realloc(p, 0) by decree, so until the
remaining implementations that are broken fix their implementations, we
can't think of having the standard fixed.
Since glibc and Bionic are the two implementations that are currently
broken, could you please fix your implementations? I'm sure the
C Committee will be much easier to convince if the implentations have
changed in a clear direction.
But if the committee says we're not fixing ISO C until the
implementations are fixed, and the implementations (you) refuse to
accept the fix until the committee standardizes something, then we'll
have the problem forever.
Also, I want to propose 0-length arrays when the arrays are parameters
to functions:
char *stpecpy(char *dst, char end[0], const char *restrict src);
But again, I can't present this proposal until another one that makes
[n] more meaningful than it is now is merged. Martin Uecker presented
that one, and I'm waiting for him to sent a revision of his proposal.
So, I'm working in various fronts on having 0-length arrays in the
standard, but there are dependencies before I can propose them.
| Getting aligned behavior between implementations on these
| fundamental interfaces seems quite valuable to programmers.
glibc and Bionic are the only implementations in which realloc(p,0) is
different from free(p) malloc(0). Fixing glibc (and assuming Bionic
agrees and follows) will solve the problem.
| But
| addressing one zero-object-size case and leaving all the others as
| unclear as before doesn't seem to be useful,
I have plans to address the other 0-size objects. Give me time.
| especially given that a
| ffuture standard may require different behavior anyway.
The C Committee seems in favour of removing the UB from realloc(p,0) if
the implementations are fixed first. Now it's your turn to move.
I'm sure I can convince the committee to follow existing practice.
But I can go a step further:
How about I write a proposal to the committee, in which I talk about the
current situation, and how glibc and Bionic are the only broken
implementation, and propose a change to ISO C which blesses the behavior
of all other implementations, rendering glibc and Bionic non-conforming?
I can then propose a very explicit question:
If glibc and Bionic agreed to change their behavior according to
this proposal, would the C Committee agree to this change?
And then come back to glibc with that. If I get such a conditional
approval of such a proposal, would glibc then apply the fix?
| 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.
The realloc(p, 0) is not onerous on programmers at all. See how the
gnulib went smoothly. Or do you mean on the implementors?
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 21:44 ` Alejandro Colomar
@ 2025-06-16 23:20 ` Alejandro Colomar
2025-06-16 23:39 ` Joseph Myers
2025-06-17 14:07 ` enh
2 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 23:20 UTC (permalink / raw)
To: Florian Weimer
Cc: Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide, Douglas McIlroy, Rich Felker,
Florian Weimer, Wilco Dijkstra, DJ Delorie, Siddhesh Poyarekar,
Sam James
[-- Attachment #1: Type: text/plain, Size: 7189 bytes --]
On Mon, Jun 16, 2025 at 11:44:53PM +0200, Alejandro Colomar wrote:
> How about I write a proposal to the committee, in which I talk about the
> current situation, and how glibc and Bionic are the only broken
> implementation, and propose a change to ISO C which blesses the behavior
> of all other implementations, rendering glibc and Bionic non-conforming?
> I can then propose a very explicit question:
>
> If glibc and Bionic agreed to change their behavior according to
> this proposal, would the C Committee agree to this change?
>
> And then come back to glibc with that. If I get such a conditional
> approval of such a proposal, would glibc then apply the fix?
Hi all,
Here's a draft of a proposal. Please let me know: if the C Committee
voted yes to the question in this proposal, would glibc change behavior?
See the proposal below.
Have a lovely night!
Alex
---
Name
alx-0029r0 - realloc(p,0) should be consistent with malloc(0)
Principles
- Codify existing practice to address evident deficiencies.
- Enable secure programming
Category
Remove UB
Author
Alejandro Colomar <alx@kernel.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Bruno Haible <bruno@clisp.org>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/>
Description
Most libc implementations have realloc(p,0) be consistent with
malloc(0). If their malloc(0) returns nonnull, realloc(p,0)
also returns nonnull. If their malloc(0) returns a null
pointer, realloc(p,0) also returns a null pointer.
That consistency was present in the old UNIX V7, where
realloc(3) was first introduced, and it has been consistent in
all the descendents of the UNIX system.
The original malloc(0) in UNIX v7 returned non-null. UNIX v6
had alloc(3), which was the precursor of V7's malloc(3), and it
also returned non-null. The BSD line kept this behavior, while
SysV eventually moved to returning a null pointer, in what seems
a documentation bug that eventually degenerated into the actual
behavior.
Regardless of the result of malloc(0) in the descendents of the
UNIX system, they all maintained a congruency between malloc(0)
and realloc(p,0).
Until the ISO C and POSIX standards were badly written, and also
badly rewritten, and they allowed behaviors that didn't make any
sense, and which didn't derive from any existing implementation.
Then, glibc was written by following the letter of the standard,
instead of trying to follow what other systems do. And this
resulted in a self-inconsistent implementation, whose malloc(0)
returns non-null, but whose realloc(p,0) returns a null pointer.
Bionic libc followed glibc, and has the same self-inconsistent
behavior.
There are no other known libc implementations whose realloc(p,0)
isn't consistent with malloc(0). Even musl libc, which coexists
in Linux systems with glibc, has a realloc(p,0) which is
consistent with malloc(0) and returns non-null.
There are glibc maintainers that agree with fixing glibc to be
self-consistent, but a few of them are worried that the
C Committee might standardize in a different way, so they are
reticent to fix their implementation if the C Committee doesn't
agree on a direction.
At the same time, the C Committee refuses standardization of
realloc(p,0) due to existing implementations being inconsistent
in dangerous ways.
Prior art
gnulib has a module realloc-posix, whose behavior was like the
one in glibc. After realizing how bad this behavior was, gnulib
has changed to be consistent with malloc(), and thus return
non-null. This was done in commit
gnulib.git d884e6fc4a60 (2024-11-03, 2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
After more than half a year since that, no regressions have been
reported. As expected, there was no fallout at all from this
change.
Questions to the committee
Does the C Committee agree on a change along the lines of the
proposed wording in this proposal, conditional to glibc and
Bionic libc fixing their implementations in this direction
first?
Future directions
malloc(0) returning a null pointer on success originated as a
documentation bug in SysV, and later became the actual behavior.
It was standardized in C89 maybe because early SVID
specifications documented that, or maybe simply because C89 had
this idea that 0-sized objects cannot exist. Regardless of the
rationale, it results in malloc(0) being hard to use correctly
on those systems. Fixing malloc(0) to return non-null would
result in many bugs fixed, and probably no regressions. I plan
to propose mandating malloc(0) to return non-null, as any other
malloc() call.
realloc(p,0) will then also return non-null, as the wording
below defines it as if it called malloc().
Proposed wording
Based on N3550.
7.25.4.8 Memory management functions :: The realloc function
@@ Description
+On success,
+the realloc function
behaves like <tt>free(ptr); malloc(size)</tt>.
deallocates the old object pointed to by ptr
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by size.
-that has the size specified by size
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object beyond the size of the old object
have unspecified values.
## The following sentence seems redundant with the specification
## above.
-If ptr is a null pointer,
-the realloc function behaves
-like the malloc function for the specified size.
-Otherwise,
-if <tt>ptr</tt> does not match
+If <tt>ptr</tt> does not match
a pointer earlier returned by a memory management function,
or if the space has been deallocated
by a call to the free or realloc function,
-or if the size is zero,
the behavior is undefined.
-If memory for the new object is not allocated,
+If allocation of the new object fails,
the old object is not deallocated and its value is unchanged.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 21:44 ` Alejandro Colomar
2025-06-16 23:20 ` Alejandro Colomar
@ 2025-06-16 23:39 ` Joseph Myers
2025-06-16 23:54 ` Alejandro Colomar
2025-06-17 14:07 ` enh
2 siblings, 1 reply; 143+ messages in thread
From: Joseph Myers @ 2025-06-16 23:39 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Florian Weimer, Adhemerval Zanella Netto, musl, libc-alpha,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
On Mon, 16 Jun 2025, Alejandro Colomar wrote:
> Since glibc and Bionic are the two implementations that are currently
> broken, could you please fix your implementations? I'm sure the
> C Committee will be much easier to convince if the implentations have
> changed in a clear direction.
>
> But if the committee says we're not fixing ISO C until the
> implementations are fixed, and the implementations (you) refuse to
> accept the fix until the committee standardizes something, then we'll
> have the problem forever.
I think a better way to eliminate UB here would be to require this
erroneous case to terminate execution. The sequence of changes to
semantics in past standard versions means that it's always a bad idea for
applications to try to use realloc with size 0 and preventing them more
strongly from doing so seems better to me than defining semantics that an
application might then be able to use in 10-15 years' time.
--
Joseph S. Myers
josmyers@redhat.com
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 23:39 ` Joseph Myers
@ 2025-06-16 23:54 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 23:54 UTC (permalink / raw)
To: Joseph Myers
Cc: Florian Weimer, Adhemerval Zanella Netto, musl, libc-alpha,
наб,
Paul Eggert, Robert Seacord, Elliott Hughes, Bruno Haible,
bug-gnulib, JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 1805 bytes --]
Hi Joseph,
On Mon, Jun 16, 2025 at 11:39:48PM +0000, Joseph Myers wrote:
> On Mon, 16 Jun 2025, Alejandro Colomar wrote:
>
> > Since glibc and Bionic are the two implementations that are currently
> > broken, could you please fix your implementations? I'm sure the
> > C Committee will be much easier to convince if the implentations have
> > changed in a clear direction.
> >
> > But if the committee says we're not fixing ISO C until the
> > implementations are fixed, and the implementations (you) refuse to
> > accept the fix until the committee standardizes something, then we'll
> > have the problem forever.
>
> I think a better way to eliminate UB here would be to require this
> erroneous case to terminate execution. The sequence of changes to
> semantics in past standard versions means that it's always a bad idea for
> applications to try to use realloc with size 0 and preventing them more
> strongly from doing so seems better to me than defining semantics that an
> application might then be able to use in 10-15 years' time.
You'd be imposing a breakage of libc implementations that have never
been broken, such as musl, and probably also the BSDs. I bet they'll
ignore the standard if it decides to irremediably break their
implementations without a reason.
You're allowed to break glibc beyond repair under the letter of the
standard, since UB allows you to terminate execution. However, I don't
see you convincing musl and the BSDs to break what isn't broken.
The standard has been broken, and implementations written by the letter
of the standard have been equally broken. However, most implementations
are sane; especially those that descend from UNIX V7.
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 21:44 ` Alejandro Colomar
2025-06-16 23:20 ` Alejandro Colomar
2025-06-16 23:39 ` Joseph Myers
@ 2025-06-17 14:07 ` enh
2025-06-17 14:40 ` Florian Weimer
2025-06-17 14:51 ` [musl] " Rich Felker
2 siblings, 2 replies; 143+ messages in thread
From: enh @ 2025-06-17 14:07 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Florian Weimer, Adhemerval Zanella Netto, musl, libc-alpha,
Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
>
> Hi Florian,
>
> On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > * Adhemerval Zanella Netto:
> >
> > > I have re-read the whole thread and it seems that most maintainers are OK
> > > with this change and agree that current POSIX's realloc spec has some
> > > drawbacks (albeit it still allows current glic behavior).
> > >
> > > The only one involved in the previous thread that raised some objection to
> > > this change was Joseph [1], but I will let to say if he still think this
> > > potential change to glibc is ill-advised.
> >
> > I objected then, and I'm objecting now as well.
> >
> > My rationale has not changed:
> >
> > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> >
> > I believe Siddhesh's proposed patch as the time was mostly a device to
> > drive the discussion to a conclusion, which it did.
>
> I'll quote your rationale from the link:
>
> | * Siddhesh Poyarekar:
> | | 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.
>
> I'm working on that. I have a proposal for mandating that malloc(0),
> but I can't present it until realloc(p, 0) is fixed. And the
> C Committee has refused to fix realloc(p, 0) by decree, so until the
> remaining implementations that are broken fix their implementations, we
> can't think of having the standard fixed.
>
> Since glibc and Bionic are the two implementations that are currently
> broken, could you please fix your implementations? I'm sure the
> C Committee will be much easier to convince if the implentations have
> changed in a clear direction.
>
> But if the committee says we're not fixing ISO C until the
> implementations are fixed, and the implementations (you) refuse to
> accept the fix until the committee standardizes something, then we'll
> have the problem forever.
is it really a problem though? you're basically asking to _add_ an
incompatibility with existing versions of glibc/bionic (so, you know,
"basically every non-Windows computer out there").
from my perspective:
pros:
+ code would then behave the same on android and ios
cons:
- code would behave differently on different versions of android
- code would behave differently on the host
unknowns:
? what does Windows do?
? does anyone actually care?
not having heard anyone but you bring this up in the last 15 years
(despite it apparently being an android/ios difference), i'm inclined
to assume this is a non-problem that's not worth the disruption of
changing anything...
> Also, I want to propose 0-length arrays when the arrays are parameters
> to functions:
>
> char *stpecpy(char *dst, char end[0], const char *restrict src);
>
> But again, I can't present this proposal until another one that makes
> [n] more meaningful than it is now is merged. Martin Uecker presented
> that one, and I'm waiting for him to sent a revision of his proposal.
>
> So, I'm working in various fronts on having 0-length arrays in the
> standard, but there are dependencies before I can propose them.
>
> | Getting aligned behavior between implementations on these
> | fundamental interfaces seems quite valuable to programmers.
>
> glibc and Bionic are the only implementations in which realloc(p,0) is
> different from free(p) malloc(0). Fixing glibc (and assuming Bionic
> agrees and follows) will solve the problem.
>
> | But
> | addressing one zero-object-size case and leaving all the others as
> | unclear as before doesn't seem to be useful,
>
> I have plans to address the other 0-size objects. Give me time.
>
> | especially given that a
> | ffuture standard may require different behavior anyway.
>
> The C Committee seems in favour of removing the UB from realloc(p,0) if
> the implementations are fixed first. Now it's your turn to move.
>
> I'm sure I can convince the committee to follow existing practice.
>
> But I can go a step further:
>
> How about I write a proposal to the committee, in which I talk about the
> current situation, and how glibc and Bionic are the only broken
> implementation, and propose a change to ISO C which blesses the behavior
> of all other implementations, rendering glibc and Bionic non-conforming?
> I can then propose a very explicit question:
>
> If glibc and Bionic agreed to change their behavior according to
> this proposal, would the C Committee agree to this change?
>
> And then come back to glibc with that. If I get such a conditional
> approval of such a proposal, would glibc then apply the fix?
>
> | 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.
>
> The realloc(p, 0) is not onerous on programmers at all. See how the
> gnulib went smoothly. Or do you mean on the implementors?
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 14:07 ` enh
@ 2025-06-17 14:40 ` Florian Weimer
2025-06-17 14:51 ` [musl] " Rich Felker
1 sibling, 0 replies; 143+ messages in thread
From: Florian Weimer @ 2025-06-17 14:40 UTC (permalink / raw)
To: enh
Cc: Alejandro Colomar, Adhemerval Zanella Netto, musl, libc-alpha,
Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
> ? what does Windows do?
There's quite a bit of documentation:
<https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/realloc?view=msvc-170>
My favorite part is actually this:
| realloc hasn't been updated to implement C17 behavior because the new
| behavior isn't compatible with the Windows operating system.
The realloc specification update was supposed to be an editorial change
in the standard! This is why I don't want to change anything until the
wording of the standard is finalized.
There's also this part:
| If size is zero, then the block pointed to by memblock is freed; the
| return value is NULL, and memblock is left pointing at a freed block.
And I think Windows (well, the Microsoft C runtime, which is not an
integral part of Windows, as far as I understand it) also matches the
Bionic/glibc behavior for malloc, not just for realloc:
| If size is 0, malloc allocates a zero-length item in the heap and
| returns a valid pointer to that item.
<https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/malloc?view=msvc-170>
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 14:07 ` enh
2025-06-17 14:40 ` Florian Weimer
@ 2025-06-17 14:51 ` Rich Felker
2025-06-17 15:28 ` Paul Eggert
2025-06-17 16:13 ` enh
1 sibling, 2 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-17 14:51 UTC (permalink / raw)
To: enh
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Tue, Jun 17, 2025 at 10:07:07AM -0400, enh wrote:
> On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
> >
> > Hi Florian,
> >
> > On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > > * Adhemerval Zanella Netto:
> > >
> > > > I have re-read the whole thread and it seems that most maintainers are OK
> > > > with this change and agree that current POSIX's realloc spec has some
> > > > drawbacks (albeit it still allows current glic behavior).
> > > >
> > > > The only one involved in the previous thread that raised some objection to
> > > > this change was Joseph [1], but I will let to say if he still think this
> > > > potential change to glibc is ill-advised.
> > >
> > > I objected then, and I'm objecting now as well.
> > >
> > > My rationale has not changed:
> > >
> > > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> > >
> > > I believe Siddhesh's proposed patch as the time was mostly a device to
> > > drive the discussion to a conclusion, which it did.
> >
> > I'll quote your rationale from the link:
> >
> > | * Siddhesh Poyarekar:
> > | | 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.
> >
> > I'm working on that. I have a proposal for mandating that malloc(0),
> > but I can't present it until realloc(p, 0) is fixed. And the
> > C Committee has refused to fix realloc(p, 0) by decree, so until the
> > remaining implementations that are broken fix their implementations, we
> > can't think of having the standard fixed.
> >
> > Since glibc and Bionic are the two implementations that are currently
> > broken, could you please fix your implementations? I'm sure the
> > C Committee will be much easier to convince if the implentations have
> > changed in a clear direction.
> >
> > But if the committee says we're not fixing ISO C until the
> > implementations are fixed, and the implementations (you) refuse to
> > accept the fix until the committee standardizes something, then we'll
> > have the problem forever.
>
> is it really a problem though? you're basically asking to _add_ an
> incompatibility with existing versions of glibc/bionic (so, you know,
> "basically every non-Windows computer out there").
>
> from my perspective:
>
> pros:
> + code would then behave the same on android and ios
> cons:
> - code would behave differently on different versions of android
> - code would behave differently on the host
> unknowns:
> ? what does Windows do?
> ? does anyone actually care?
>
> not having heard anyone but you bring this up in the last 15 years
> (despite it apparently being an android/ios difference), i'm inclined
> to assume this is a non-problem that's not worth the disruption of
> changing anything...
I'm not sure what you mean by "not having heard anyone but you bring
this up in the last 15 years". This has been a recurring issue on the
glibc bug tracker and in C and POSIX committees, and comes up all the
time with users of the language not understanding what the standard
says or if/how implementations are conforming. There have been
multiple bug reports against different versions of the wording in
different versions of the C and POSIX standards, and it's a perpetual
source of disagreements.
Indeed fixing the bug will not make any immediate improvement. For
decades applications will still need to assume they might be running
on a system with the broken (inconsistent) behavior like glibc or
Bionic, or apply a wrapper to fix it (ala gnulib). But maybe
eventually that can become a bad chapter of history we leave behind.
One thing I kinda would like to think about is if there's a way we can
signal at compile-time (without run tests that don't work for cross
compiling) that realloc is non-broken and doesn't need gnulib-style
wrapping/replacement. My hope is that such a mechanism would follow
the principles of the "Macro-based advertisement of libc extensions"
proposal on libc-coord.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 14:51 ` [musl] " Rich Felker
@ 2025-06-17 15:28 ` Paul Eggert
2025-06-17 16:13 ` enh
1 sibling, 0 replies; 143+ messages in thread
From: Paul Eggert @ 2025-06-17 15:28 UTC (permalink / raw)
To: Rich Felker
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Robert Seacord, Bruno Haible, bug-gnulib, JeanHeyd Meneide, enh
On 2025-06-17 07:51, Rich Felker wrote:
> I'm not sure what you mean by "not having heard anyone but you bring
> this up in the last 15 years". This has been a recurring issue
+1.
> One thing I kinda would like to think about is if there's a way we can
> signal at compile-time (without run tests that don't work for cross
> compiling) that realloc is non-broken and doesn't need gnulib-style
> wrapping/replacement.
That would help. Gnulib's gl_FUNC_REALLOC_0_NONNULL currently has some
painful hacks (see below) that we hope work even for cross-compiling,
for most platforms. But not everyone can or does use Gnulib, and it
would be nice if glibc and other platforms would tell us their behavior
in a standard way, to lessen our dependence on chewing gum and hope.
Even better, I'd like a simple way to tell glibc and bionic "Make
realloc (p, 0) return nonnull on success" and have it just work, without
all this rickety fooraw.
Here are some of the hacks Gnulib now uses for cross-compiles.
> # Modules that use this macro directly or indirectly should depend
> # on extensions-aix, so that _LINUX_SOURCE_COMPAT gets defined
> # before this macro gets invoked. This helps if !(__VEC__ || __AIXVEC),
> # and doesn't hurt otherwise.
> ...> [AS_CASE([$host_os],
> [# Guess yes on platforms where we know the result.
> freebsd* | netbsd* | openbsd* | darwin* | bitrig* \
> | *-musl* | midipix* | midnightbsd* \
> | hpux* | solaris* | cygwin*],
> [gl_cv_func_realloc_0_nonnull="guessing yes"],
> [# Guess as follows if we don't know.
> gl_cv_func_realloc_0_nonnull=$gl_cross_guess_normal])])])
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 14:51 ` [musl] " Rich Felker
2025-06-17 15:28 ` Paul Eggert
@ 2025-06-17 16:13 ` enh
2025-06-17 16:56 ` Rich Felker
2025-06-17 21:58 ` Alejandro Colomar
1 sibling, 2 replies; 143+ messages in thread
From: enh @ 2025-06-17 16:13 UTC (permalink / raw)
To: Rich Felker
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Tue, Jun 17, 2025 at 10:51 AM Rich Felker <dalias@libc.org> wrote:
>
> On Tue, Jun 17, 2025 at 10:07:07AM -0400, enh wrote:
> > On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
> > >
> > > Hi Florian,
> > >
> > > On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > > > * Adhemerval Zanella Netto:
> > > >
> > > > > I have re-read the whole thread and it seems that most maintainers are OK
> > > > > with this change and agree that current POSIX's realloc spec has some
> > > > > drawbacks (albeit it still allows current glic behavior).
> > > > >
> > > > > The only one involved in the previous thread that raised some objection to
> > > > > this change was Joseph [1], but I will let to say if he still think this
> > > > > potential change to glibc is ill-advised.
> > > >
> > > > I objected then, and I'm objecting now as well.
> > > >
> > > > My rationale has not changed:
> > > >
> > > > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> > > >
> > > > I believe Siddhesh's proposed patch as the time was mostly a device to
> > > > drive the discussion to a conclusion, which it did.
> > >
> > > I'll quote your rationale from the link:
> > >
> > > | * Siddhesh Poyarekar:
> > > | | 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.
> > >
> > > I'm working on that. I have a proposal for mandating that malloc(0),
> > > but I can't present it until realloc(p, 0) is fixed. And the
> > > C Committee has refused to fix realloc(p, 0) by decree, so until the
> > > remaining implementations that are broken fix their implementations, we
> > > can't think of having the standard fixed.
> > >
> > > Since glibc and Bionic are the two implementations that are currently
> > > broken, could you please fix your implementations? I'm sure the
> > > C Committee will be much easier to convince if the implentations have
> > > changed in a clear direction.
> > >
> > > But if the committee says we're not fixing ISO C until the
> > > implementations are fixed, and the implementations (you) refuse to
> > > accept the fix until the committee standardizes something, then we'll
> > > have the problem forever.
> >
> > is it really a problem though? you're basically asking to _add_ an
> > incompatibility with existing versions of glibc/bionic (so, you know,
> > "basically every non-Windows computer out there").
> >
> > from my perspective:
> >
> > pros:
> > + code would then behave the same on android and ios
> > cons:
> > - code would behave differently on different versions of android
> > - code would behave differently on the host
> > unknowns:
> > ? what does Windows do?
> > ? does anyone actually care?
> >
> > not having heard anyone but you bring this up in the last 15 years
> > (despite it apparently being an android/ios difference), i'm inclined
> > to assume this is a non-problem that's not worth the disruption of
> > changing anything...
>
> I'm not sure what you mean by "not having heard anyone but you bring
> this up in the last 15 years". This has been a recurring issue on the
> glibc bug tracker and in C and POSIX committees, and comes up all the
> time with users of the language not understanding what the standard
> says or if/how implementations are conforming. There have been
> multiple bug reports against different versions of the wording in
> different versions of the C and POSIX standards, and it's a perpetual
> source of disagreements.
yeah, i should probably have said that in my weighting, arguments over
standards count for nothing compared to existing practice, and only
"i'm a developer who had a real problem because of this" warrants a
behavior change. (with my biggest quandary is actually cases exactly
like this where ios -- which most app developers care about
compatibility with -- and glibc -- which linux tool developers care
about -- disagree. as for Windows, that mostly comes up in relation to
game developers.)
but, yeah, in 15 years of having both groups complain at me, i've yet
to see or hear about a single "my code works on ios but not on
android" bug from this, and obviously we're the same as glibc so
there's been nothing there.
> Indeed fixing the bug will not make any immediate improvement. For
> decades applications will still need to assume they might be running
> on a system with the broken (inconsistent) behavior like glibc or
> Bionic, or apply a wrapper to fix it (ala gnulib). But maybe
> eventually that can become a bad chapter of history we leave behind.
>
> One thing I kinda would like to think about is if there's a way we can
> signal at compile-time (without run tests that don't work for cross
> compiling) that realloc is non-broken and doesn't need gnulib-style
> wrapping/replacement. My hope is that such a mechanism would follow
> the principles of the "Macro-based advertisement of libc extensions"
> proposal on libc-coord.
>
> Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 16:13 ` enh
@ 2025-06-17 16:56 ` Rich Felker
2025-06-17 17:38 ` Re[2]: " Laurent Bercot
2025-06-17 21:58 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Rich Felker @ 2025-06-17 16:56 UTC (permalink / raw)
To: enh
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Tue, Jun 17, 2025 at 12:13:03PM -0400, enh wrote:
> On Tue, Jun 17, 2025 at 10:51 AM Rich Felker <dalias@libc.org> wrote:
> >
> > On Tue, Jun 17, 2025 at 10:07:07AM -0400, enh wrote:
> > > On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > >
> > > > Hi Florian,
> > > >
> > > > On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > > > > * Adhemerval Zanella Netto:
> > > > >
> > > > > > I have re-read the whole thread and it seems that most maintainers are OK
> > > > > > with this change and agree that current POSIX's realloc spec has some
> > > > > > drawbacks (albeit it still allows current glic behavior).
> > > > > >
> > > > > > The only one involved in the previous thread that raised some objection to
> > > > > > this change was Joseph [1], but I will let to say if he still think this
> > > > > > potential change to glibc is ill-advised.
> > > > >
> > > > > I objected then, and I'm objecting now as well.
> > > > >
> > > > > My rationale has not changed:
> > > > >
> > > > > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> > > > >
> > > > > I believe Siddhesh's proposed patch as the time was mostly a device to
> > > > > drive the discussion to a conclusion, which it did.
> > > >
> > > > I'll quote your rationale from the link:
> > > >
> > > > | * Siddhesh Poyarekar:
> > > > | | 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.
> > > >
> > > > I'm working on that. I have a proposal for mandating that malloc(0),
> > > > but I can't present it until realloc(p, 0) is fixed. And the
> > > > C Committee has refused to fix realloc(p, 0) by decree, so until the
> > > > remaining implementations that are broken fix their implementations, we
> > > > can't think of having the standard fixed.
> > > >
> > > > Since glibc and Bionic are the two implementations that are currently
> > > > broken, could you please fix your implementations? I'm sure the
> > > > C Committee will be much easier to convince if the implentations have
> > > > changed in a clear direction.
> > > >
> > > > But if the committee says we're not fixing ISO C until the
> > > > implementations are fixed, and the implementations (you) refuse to
> > > > accept the fix until the committee standardizes something, then we'll
> > > > have the problem forever.
> > >
> > > is it really a problem though? you're basically asking to _add_ an
> > > incompatibility with existing versions of glibc/bionic (so, you know,
> > > "basically every non-Windows computer out there").
> > >
> > > from my perspective:
> > >
> > > pros:
> > > + code would then behave the same on android and ios
> > > cons:
> > > - code would behave differently on different versions of android
> > > - code would behave differently on the host
> > > unknowns:
> > > ? what does Windows do?
> > > ? does anyone actually care?
> > >
> > > not having heard anyone but you bring this up in the last 15 years
> > > (despite it apparently being an android/ios difference), i'm inclined
> > > to assume this is a non-problem that's not worth the disruption of
> > > changing anything...
> >
> > I'm not sure what you mean by "not having heard anyone but you bring
> > this up in the last 15 years". This has been a recurring issue on the
> > glibc bug tracker and in C and POSIX committees, and comes up all the
> > time with users of the language not understanding what the standard
> > says or if/how implementations are conforming. There have been
> > multiple bug reports against different versions of the wording in
> > different versions of the C and POSIX standards, and it's a perpetual
> > source of disagreements.
>
> yeah, i should probably have said that in my weighting, arguments over
> standards count for nothing compared to existing practice, and only
> "i'm a developer who had a real problem because of this" warrants a
> behavior change. (with my biggest quandary is actually cases exactly
> like this where ios -- which most app developers care about
> compatibility with -- and glibc -- which linux tool developers care
> about -- disagree. as for Windows, that mostly comes up in relation to
> game developers.)
>
> but, yeah, in 15 years of having both groups complain at me, i've yet
> to see or hear about a single "my code works on ios but not on
> android" bug from this, and obviously we're the same as glibc so
> there's been nothing there.
I'm not sure how much folks would notice if it's broken. The likely
effect of the inconsistent behavior is DF/UAF bugs. And folks who do
know aren't complaining that it's broken, because the behavior is
currently conforming just bad. Rather they're tiptoeing around the
issue by special-casing zero and making sure they never pass zero to
realloc, or wrapping it to do that, etc.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re[2]: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 16:56 ` Rich Felker
@ 2025-06-17 17:38 ` Laurent Bercot
0 siblings, 0 replies; 143+ messages in thread
From: Laurent Bercot @ 2025-06-17 17:38 UTC (permalink / raw)
To: musl, enh
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
>And folks who do
>know aren't complaining that it's broken, because the behavior is
>currently conforming just bad. Rather they're tiptoeing around the
>issue by special-casing zero and making sure they never pass zero to
>realloc, or wrapping it to do that, etc.
Anecdotal data point: it's exactly what I do, wrapping all the malloc
and realloc calls so that they're never passed 0. It's by far the
simplest solution for now, at the occasional cost of 1 byte of memory.
It would be nice to have this fixed, *and standardized*; the workarounds
aren't going away until it is, and until every libc deployed under the
sun implements the new, better standard. I'm not holding my breath.
--
Laurent
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 16:13 ` enh
2025-06-17 16:56 ` Rich Felker
@ 2025-06-17 21:58 ` Alejandro Colomar
2025-06-18 15:20 ` enh
2025-06-18 15:42 ` Andreas Schwab
1 sibling, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 21:58 UTC (permalink / raw)
To: enh, Florian Weimer
Cc: Rich Felker, Adhemerval Zanella Netto, musl, libc-alpha,
Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 6487 bytes --]
Hi Elliott, Florian,
glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
proposing would make them conforming. Does conformance to POSIX.1-2024
mean something to you?
RETURN VALUE
If size is 0,
...
either:
- A null pointer shall be returned
and, if ptr is not a null pointer,
errno shall be set to EINVAL.
- A pointer to the allocated space shall be returned,
and the memory object pointed to by ptr shall be freed.
...
Please also reply to the message about my proposal for the C Committee.
And Elliott, thanks for noticing that Windows is also crap. That's
another one to add to the list.
Have a lovely day!
Alex
On Tue, Jun 17, 2025 at 12:13:03PM -0400, enh wrote:
> On Tue, Jun 17, 2025 at 10:51 AM Rich Felker <dalias@libc.org> wrote:
> >
> > On Tue, Jun 17, 2025 at 10:07:07AM -0400, enh wrote:
> > > On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > >
> > > > Hi Florian,
> > > >
> > > > On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > > > > * Adhemerval Zanella Netto:
> > > > >
> > > > > > I have re-read the whole thread and it seems that most maintainers are OK
> > > > > > with this change and agree that current POSIX's realloc spec has some
> > > > > > drawbacks (albeit it still allows current glic behavior).
> > > > > >
> > > > > > The only one involved in the previous thread that raised some objection to
> > > > > > this change was Joseph [1], but I will let to say if he still think this
> > > > > > potential change to glibc is ill-advised.
> > > > >
> > > > > I objected then, and I'm objecting now as well.
> > > > >
> > > > > My rationale has not changed:
> > > > >
> > > > > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> > > > >
> > > > > I believe Siddhesh's proposed patch as the time was mostly a device to
> > > > > drive the discussion to a conclusion, which it did.
> > > >
> > > > I'll quote your rationale from the link:
> > > >
> > > > | * Siddhesh Poyarekar:
> > > > | | 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.
> > > >
> > > > I'm working on that. I have a proposal for mandating that malloc(0),
> > > > but I can't present it until realloc(p, 0) is fixed. And the
> > > > C Committee has refused to fix realloc(p, 0) by decree, so until the
> > > > remaining implementations that are broken fix their implementations, we
> > > > can't think of having the standard fixed.
> > > >
> > > > Since glibc and Bionic are the two implementations that are currently
> > > > broken, could you please fix your implementations? I'm sure the
> > > > C Committee will be much easier to convince if the implentations have
> > > > changed in a clear direction.
> > > >
> > > > But if the committee says we're not fixing ISO C until the
> > > > implementations are fixed, and the implementations (you) refuse to
> > > > accept the fix until the committee standardizes something, then we'll
> > > > have the problem forever.
> > >
> > > is it really a problem though? you're basically asking to _add_ an
> > > incompatibility with existing versions of glibc/bionic (so, you know,
> > > "basically every non-Windows computer out there").
> > >
> > > from my perspective:
> > >
> > > pros:
> > > + code would then behave the same on android and ios
> > > cons:
> > > - code would behave differently on different versions of android
> > > - code would behave differently on the host
> > > unknowns:
> > > ? what does Windows do?
> > > ? does anyone actually care?
> > >
> > > not having heard anyone but you bring this up in the last 15 years
> > > (despite it apparently being an android/ios difference), i'm inclined
> > > to assume this is a non-problem that's not worth the disruption of
> > > changing anything...
> >
> > I'm not sure what you mean by "not having heard anyone but you bring
> > this up in the last 15 years". This has been a recurring issue on the
> > glibc bug tracker and in C and POSIX committees, and comes up all the
> > time with users of the language not understanding what the standard
> > says or if/how implementations are conforming. There have been
> > multiple bug reports against different versions of the wording in
> > different versions of the C and POSIX standards, and it's a perpetual
> > source of disagreements.
>
> yeah, i should probably have said that in my weighting, arguments over
> standards count for nothing compared to existing practice, and only
> "i'm a developer who had a real problem because of this" warrants a
> behavior change. (with my biggest quandary is actually cases exactly
> like this where ios -- which most app developers care about
> compatibility with -- and glibc -- which linux tool developers care
> about -- disagree. as for Windows, that mostly comes up in relation to
> game developers.)
>
> but, yeah, in 15 years of having both groups complain at me, i've yet
> to see or hear about a single "my code works on ios but not on
> android" bug from this, and obviously we're the same as glibc so
> there's been nothing there.
>
> > Indeed fixing the bug will not make any immediate improvement. For
> > decades applications will still need to assume they might be running
> > on a system with the broken (inconsistent) behavior like glibc or
> > Bionic, or apply a wrapper to fix it (ala gnulib). But maybe
> > eventually that can become a bad chapter of history we leave behind.
> >
> > One thing I kinda would like to think about is if there's a way we can
> > signal at compile-time (without run tests that don't work for cross
> > compiling) that realloc is non-broken and doesn't need gnulib-style
> > wrapping/replacement. My hope is that such a mechanism would follow
> > the principles of the "Macro-based advertisement of libc extensions"
> > proposal on libc-coord.
> >
> > Rich
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 21:58 ` Alejandro Colomar
@ 2025-06-18 15:20 ` enh
2025-06-18 15:37 ` Thorsten Glaser
2025-06-18 16:35 ` Rich Felker
2025-06-18 15:42 ` Andreas Schwab
1 sibling, 2 replies; 143+ messages in thread
From: enh @ 2025-06-18 15:20 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Florian Weimer, Rich Felker, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
>
> Hi Elliott, Florian,
>
> glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> proposing would make them conforming. Does conformance to POSIX.1-2024
> mean something to you?
not when POSIX screwed up and made a change that made most of the
existing implementations non-conformant, no. that sounds like a POSIX
bug to me...
(like i said, i care greatly about actual shipping code. a standard is
interesting for green-field stuff, but when it's at odds with reality
it's often worse to try to adapt than just ignore the stupidity/report
the bug and get it changed back.)
> RETURN VALUE
>
> If size is 0,
> ...
> either:
>
> - A null pointer shall be returned
> and, if ptr is not a null pointer,
> errno shall be set to EINVAL.
>
> - A pointer to the allocated space shall be returned,
> and the memory object pointed to by ptr shall be freed.
> ...
>
>
> Please also reply to the message about my proposal for the C Committee.
>
> And Elliott, thanks for noticing that Windows is also crap. That's
> another one to add to the list.
>
>
> Have a lovely day!
> Alex
>
> On Tue, Jun 17, 2025 at 12:13:03PM -0400, enh wrote:
> > On Tue, Jun 17, 2025 at 10:51 AM Rich Felker <dalias@libc.org> wrote:
> > >
> > > On Tue, Jun 17, 2025 at 10:07:07AM -0400, enh wrote:
> > > > On Mon, Jun 16, 2025 at 5:44 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > > >
> > > > > Hi Florian,
> > > > >
> > > > > On Mon, Jun 16, 2025 at 09:35:01PM +0200, Florian Weimer wrote:
> > > > > > * Adhemerval Zanella Netto:
> > > > > >
> > > > > > > I have re-read the whole thread and it seems that most maintainers are OK
> > > > > > > with this change and agree that current POSIX's realloc spec has some
> > > > > > > drawbacks (albeit it still allows current glic behavior).
> > > > > > >
> > > > > > > The only one involved in the previous thread that raised some objection to
> > > > > > > this change was Joseph [1], but I will let to say if he still think this
> > > > > > > potential change to glibc is ill-advised.
> > > > > >
> > > > > > I objected then, and I'm objecting now as well.
> > > > > >
> > > > > > My rationale has not changed:
> > > > > >
> > > > > > <https://inbox.sourceware.org/libc-alpha/8734kl1pim.fsf@oldenburg.str.redhat.com/>
> > > > > >
> > > > > > I believe Siddhesh's proposed patch as the time was mostly a device to
> > > > > > drive the discussion to a conclusion, which it did.
> > > > >
> > > > > I'll quote your rationale from the link:
> > > > >
> > > > > | * Siddhesh Poyarekar:
> > > > > | | 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.
> > > > >
> > > > > I'm working on that. I have a proposal for mandating that malloc(0),
> > > > > but I can't present it until realloc(p, 0) is fixed. And the
> > > > > C Committee has refused to fix realloc(p, 0) by decree, so until the
> > > > > remaining implementations that are broken fix their implementations, we
> > > > > can't think of having the standard fixed.
> > > > >
> > > > > Since glibc and Bionic are the two implementations that are currently
> > > > > broken, could you please fix your implementations? I'm sure the
> > > > > C Committee will be much easier to convince if the implentations have
> > > > > changed in a clear direction.
> > > > >
> > > > > But if the committee says we're not fixing ISO C until the
> > > > > implementations are fixed, and the implementations (you) refuse to
> > > > > accept the fix until the committee standardizes something, then we'll
> > > > > have the problem forever.
> > > >
> > > > is it really a problem though? you're basically asking to _add_ an
> > > > incompatibility with existing versions of glibc/bionic (so, you know,
> > > > "basically every non-Windows computer out there").
> > > >
> > > > from my perspective:
> > > >
> > > > pros:
> > > > + code would then behave the same on android and ios
> > > > cons:
> > > > - code would behave differently on different versions of android
> > > > - code would behave differently on the host
> > > > unknowns:
> > > > ? what does Windows do?
> > > > ? does anyone actually care?
> > > >
> > > > not having heard anyone but you bring this up in the last 15 years
> > > > (despite it apparently being an android/ios difference), i'm inclined
> > > > to assume this is a non-problem that's not worth the disruption of
> > > > changing anything...
> > >
> > > I'm not sure what you mean by "not having heard anyone but you bring
> > > this up in the last 15 years". This has been a recurring issue on the
> > > glibc bug tracker and in C and POSIX committees, and comes up all the
> > > time with users of the language not understanding what the standard
> > > says or if/how implementations are conforming. There have been
> > > multiple bug reports against different versions of the wording in
> > > different versions of the C and POSIX standards, and it's a perpetual
> > > source of disagreements.
> >
> > yeah, i should probably have said that in my weighting, arguments over
> > standards count for nothing compared to existing practice, and only
> > "i'm a developer who had a real problem because of this" warrants a
> > behavior change. (with my biggest quandary is actually cases exactly
> > like this where ios -- which most app developers care about
> > compatibility with -- and glibc -- which linux tool developers care
> > about -- disagree. as for Windows, that mostly comes up in relation to
> > game developers.)
> >
> > but, yeah, in 15 years of having both groups complain at me, i've yet
> > to see or hear about a single "my code works on ios but not on
> > android" bug from this, and obviously we're the same as glibc so
> > there's been nothing there.
> >
> > > Indeed fixing the bug will not make any immediate improvement. For
> > > decades applications will still need to assume they might be running
> > > on a system with the broken (inconsistent) behavior like glibc or
> > > Bionic, or apply a wrapper to fix it (ala gnulib). But maybe
> > > eventually that can become a bad chapter of history we leave behind.
> > >
> > > One thing I kinda would like to think about is if there's a way we can
> > > signal at compile-time (without run tests that don't work for cross
> > > compiling) that realloc is non-broken and doesn't need gnulib-style
> > > wrapping/replacement. My hope is that such a mechanism would follow
> > > the principles of the "Macro-based advertisement of libc extensions"
> > > proposal on libc-coord.
> > >
> > > Rich
>
> --
> <https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 15:20 ` enh
@ 2025-06-18 15:37 ` Thorsten Glaser
2025-06-18 16:26 ` enh
2025-06-18 16:35 ` Rich Felker
1 sibling, 1 reply; 143+ messages in thread
From: Thorsten Glaser @ 2025-06-18 15:37 UTC (permalink / raw)
To: musl
Cc: Alejandro Colomar, Florian Weimer, Rich Felker,
Adhemerval Zanella Netto, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Wed, 18 Jun 2025, enh wrote:
>not when POSIX screwed up and made a change that made most of the
>existing implementations non-conformant, no. that sounds like a POSIX
“most of”?
Looks to me like most implementations already do the latter,
and some might do the former, and only a minority (the three
mentioned earlier) don’t.
bye,
//mirabilos
--
“It is inappropriate to require that a time represented as
seconds since the Epoch precisely represent the number of
seconds between the referenced time and the Epoch.”
-- IEEE Std 1003.1b-1993 (POSIX) Section B.2.2.2
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 21:58 ` Alejandro Colomar
2025-06-18 15:20 ` enh
@ 2025-06-18 15:42 ` Andreas Schwab
2025-06-18 18:05 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Andreas Schwab @ 2025-06-18 15:42 UTC (permalink / raw)
To: Alejandro Colomar
Cc: enh, Florian Weimer, musl, Rich Felker, Adhemerval Zanella Netto,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Jun 17 2025, Alejandro Colomar wrote:
> RETURN VALUE
>
> If size is 0,
> ...
> either:
>
> - A null pointer shall be returned
> and, if ptr is not a null pointer,
> errno shall be set to EINVAL.
>
> - A pointer to the allocated space shall be returned,
> and the memory object pointed to by ptr shall be freed.
> ...
>
Note that all of this is marked obsolescent.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 15:37 ` Thorsten Glaser
@ 2025-06-18 16:26 ` enh
0 siblings, 0 replies; 143+ messages in thread
From: enh @ 2025-06-18 16:26 UTC (permalink / raw)
To: musl
Cc: Alejandro Colomar, Florian Weimer, Rich Felker,
Adhemerval Zanella Netto, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Wed, Jun 18, 2025 at 11:37 AM Thorsten Glaser <tg@mirbsd.de> wrote:
>
> On Wed, 18 Jun 2025, enh wrote:
>
> >not when POSIX screwed up and made a change that made most of the
> >existing implementations non-conformant, no. that sounds like a POSIX
>
> “most of”?
...weighted by number of users (who could be negatively affected by
change), of course.
> Looks to me like most implementations already do the latter,
> and some might do the former, and only a minority (the three
> mentioned earlier) don’t.
>
> bye,
> //mirabilos
> --
> “It is inappropriate to require that a time represented as
> seconds since the Epoch precisely represent the number of
> seconds between the referenced time and the Epoch.”
> -- IEEE Std 1003.1b-1993 (POSIX) Section B.2.2.2
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 15:20 ` enh
2025-06-18 15:37 ` Thorsten Glaser
@ 2025-06-18 16:35 ` Rich Felker
2025-06-18 19:04 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Rich Felker @ 2025-06-18 16:35 UTC (permalink / raw)
To: enh
Cc: Alejandro Colomar, Florian Weimer, Adhemerval Zanella Netto,
musl, libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> >
> > Hi Elliott, Florian,
> >
> > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > proposing would make them conforming. Does conformance to POSIX.1-2024
> > mean something to you?
>
> not when POSIX screwed up and made a change that made most of the
> existing implementations non-conformant, no. that sounds like a POSIX
> bug to me...
>
> (like i said, i care greatly about actual shipping code. a standard is
> interesting for green-field stuff, but when it's at odds with reality
> it's often worse to try to adapt than just ignore the stupidity/report
> the bug and get it changed back.)
I have not yet read the issues leading up to the change in detail, but
it looks like POSIX made 2 changes that might have been framed as 1.
The first is soft-removing the allowance for malloc(0) to return a
null pointer on success, by making this option technically a "failure"
for "invalid size", where any implementation that really wants to keep
doing what it's doing could theoretically just add errno=EINVAL; to
remain conforming.
To me this seems like a breaking change, since a caller that's doing
the previously-right errno-checking dance would now interpret it as
failure rather than success.
However, I suspect what they found is that there were no relevant
implementations where malloc(0) returns 0, and thereby that it doesn't
matter and nothing is broken.
The second change is applying the first to realloc, which would be
noncontroversial and wouldn't be a distinct chance except for the fact
that realloc(p,0) was already inconsistent with malloc(0) on a few
implementations. And this is a hard functional change. Since realloc
returning 0 is now mandated to be an error condition, it's required
that implementations where realloc(p,0) return 0 leave p alone and
don't free it.
I agree that making this change that's contrary to existing practice
and without discussing it with those implementors to reach consensus
is a bug, if indeed that's what happened. We should probably look at
how this happened.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 15:42 ` Andreas Schwab
@ 2025-06-18 18:05 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-18 18:05 UTC (permalink / raw)
To: Andreas Schwab
Cc: enh, Florian Weimer, musl, Rich Felker, Adhemerval Zanella Netto,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 852 bytes --]
Hi Andreas,
On Wed, Jun 18, 2025 at 05:42:40PM +0200, Andreas Schwab wrote:
> On Jun 17 2025, Alejandro Colomar wrote:
>
> > RETURN VALUE
> >
> > If size is 0,
> > ...
> > either:
> >
> > - A null pointer shall be returned
> > and, if ptr is not a null pointer,
> > errno shall be set to EINVAL.
> >
> > - A pointer to the allocated space shall be returned,
> > and the memory object pointed to by ptr shall be freed.
> > ...
> >
>
> Note that all of this is marked obsolescent.
Note that it's marked obsolescent because ISO C has turned this into UB.
If ISO C defines the behavior again, then the obsolescence of this could
be reverted.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 16:35 ` Rich Felker
@ 2025-06-18 19:04 ` Alejandro Colomar
2025-06-19 18:31 ` Eric Blake
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-18 19:04 UTC (permalink / raw)
To: Rich Felker
Cc: enh, Florian Weimer, Adhemerval Zanella Netto, musl, libc-alpha,
Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 3598 bytes --]
Hi Rich, Elliott,
On Wed, Jun 18, 2025 at 12:35:50PM -0400, Rich Felker wrote:
> On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> > On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> > >
> > > Hi Elliott, Florian,
> > >
> > > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > > proposing would make them conforming. Does conformance to POSIX.1-2024
> > > mean something to you?
> >
> > not when POSIX screwed up and made a change that made most of the
> > existing implementations non-conformant, no. that sounds like a POSIX
> > bug to me...
Not most. Only two POSIX implementations, plus Windows. And the
solution is easy: fix the implementations. There have been no
regression reports in gnulib since we fixed it last year.
> > (like i said, i care greatly about actual shipping code. a standard is
> > interesting for green-field stuff, but when it's at odds with reality
> > it's often worse to try to adapt than just ignore the stupidity/report
> > the bug and get it changed back.)
It's ironic that the standard should have never said that, because prior
to the existence of ANSI C and POSIX, all existing systems behaved like
the current POSIX specification. It was a consequence of the horrible
wording of the standards, that glibc was written so badly, by following
a bogus specification, when it should have been made compatible with the
existing systems.
Thus, this is a historical bug in ISO C, POSIX, which at least has been
finally fixed in POSIX.
BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
following bug <https://www.austingroupbugs.net/view.php?id=400>.
The motivation, from what I can read there, seems to be that C99 already
made POSIX.1 non-conforming, and this fix was intended to conform to
C99.
Indeed, glibc is non-conforming to C99 too. Although, I don't like the
wording from C99, either; it allows weird stuff: it allows an
implementation where malloc(0) returns NULL and realloc(p,0) non-null
(so, the opposite of glibc).
C11 is essentially identical to C99 in that regard, so glibc is also
non-conforming to C11.
C17 changed to something very weird. It seems to me that glibc is
conforming again to C17, but it also seems to me that it's impossible to
write code that uses realloc(p,0) in a portable way with this
specification. I think it's a good thing that C23 removed that crap.
Here's a summary of conformance to standards:
glibc conforms to:
- SysVr4
- ISO C89
- ISO C17
- XPG4
glibc doesn't conform to:
- SysIII
- SysV
- SysVr2
- SysVr3
- SVID Issue 2
- SVID Issue 3
- The X/Open System V Specification
- ISO C99
- ISO C11
- POSIX.1-2001
- POSIX.1-2008
- POSIX.1-2017
- POSIX.1-2024
Conformance to POSIX.1-2001 and POSIX.1-2008 is not clear. While glibc
conforms to the wording of these standards, these standards have the
following header in the realloc(3) specification:
The functionality described on this reference page is aligned
with the ISO C standard. Any conflict between the requirements
described here and the ISO C standard is unintentional. This
volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
Which means that POSIX's permissive wording is unintentional, and the
ISO C99 wording is the one that matters, so glibc is non-conforming.
(I didn't mention C23, since it's UB, so anything conforms.)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 19:04 ` Alejandro Colomar
@ 2025-06-19 18:31 ` Eric Blake
2025-06-19 23:37 ` Alejandro Colomar
2025-06-20 5:55 ` Paul Eggert
0 siblings, 2 replies; 143+ messages in thread
From: Eric Blake @ 2025-06-19 18:31 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> Hi Rich, Elliott,
>
> On Wed, Jun 18, 2025 at 12:35:50PM -0400, Rich Felker wrote:
> > On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> > > On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > >
> > > > Hi Elliott, Florian,
> > > >
> > > > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > > > proposing would make them conforming. Does conformance to POSIX.1-2024
> > > > mean something to you?
> > >
> > > not when POSIX screwed up and made a change that made most of the
> > > existing implementations non-conformant, no. that sounds like a POSIX
> > > bug to me...
>
> Not most. Only two POSIX implementations, plus Windows. And the
> solution is easy: fix the implementations. There have been no
> regression reports in gnulib since we fixed it last year.
Speaking as someone who participated in the POSIX standardization
process, I'm trying to pinpoint exactly which statements of which
versions of which standards you are claiming as nonconformance.
First, a disclaimer: because this thread has been very vocal, I
brought the topic up in today's Austin Group meeting. The members of
the group on the phone call remember _specifically_ trying to permit
existing glibc behavior (where realloc(p, 0) does NOT allocate), while
still jugging competing wording from the C standards, although we will
be the first to admit that we would not be surprised if the resulting
efforts are still not clear enough to be unambiguous. I mentioned in
the meeting that I would attempt to follow up on these threads to see
what, if anything, the Austin Group may need to do to assist in the
discussion.
Next, my overarching question. Is this about "realloc(non_null, 0)",
"realloc(NULL, 0)", or both? As the two are very distinct, I want to
make sure we are talking about the same usage patterns. For the rest
of this email, I'm assuming that your complaints are solely about
"realloc(non_null, 0)" - if I'm wrong, it may change the analysis done
below.
Now, on to some code archeology. In today's glibc source code, I see
this telling comment in malloc/malloc.c, making it clear that glibc
folks are aware that realloc(non_null, 0) has two useful behaviors,
and that glibc picks the behavior that does NOT behave consistently
with malloc(0), because of back-compat guarantees:
/*
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.
*/
More reading: https://www.austingroupbugs.net/view.php?id=400 shows
where earlier POSIX missed that C90 to C99 changed what was permitted
(and apparantly in a way to render glibc's implementation
non-conforming), and that's part of what drove the POSIX folks to ask
the C standard to improve the wording. POSIX 2024 is based on C17,
but Nick Stoughton was regularly communicating between both C and
POSIX groups on what wording(s) were being floated around, in order to
try and make it so that glibc would not have to change behavior, but
at the same time trying to make it possible for applications to be
able to make wise runtime decisions on how to use realloc that would
not leak memory or risk dereferencing a NULL pointer if not careful.
https://sourceware.org/bugzilla/show_bug.cgi?id=12547 shows where
glibc has, in the past, refused to change behavior on the grounds that
the standards were buggy. If the standards are still buggy, the best
course of action is to open a bug against them.
Also relevant are these documents
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
>
> > > (like i said, i care greatly about actual shipping code. a standard is
> > > interesting for green-field stuff, but when it's at odds with reality
> > > it's often worse to try to adapt than just ignore the stupidity/report
> > > the bug and get it changed back.)
>
> It's ironic that the standard should have never said that, because prior
> to the existence of ANSI C and POSIX, all existing systems behaved like
> the current POSIX specification. It was a consequence of the horrible
> wording of the standards, that glibc was written so badly, by following
> a bogus specification, when it should have been made compatible with the
> existing systems.
POSIX was originally released in 1988, before C90. glibc 1.0 came out
in 1992. I am not sure when glibc first cared about whether trending
towards POSIX compliance mattered, although I do know that in the
early days, Ulrich would very adamently argue along the lines of
(paraphrased) "if the standards don't match common sense, then we
don't care about the standards".
>
> Thus, this is a historical bug in ISO C, POSIX, which at least has been
> finally fixed in POSIX.
The fact that the wording has changed across multiple versions of C
and POSIX is indeed evidence that getting a specification that people
are happy with is difficult. What is harder is the decision of
whether the bug is in the standard (for not documenting reality) or in
the implementations (for not doing what the standard rightfully
requests), or even both. And "what people are happy with" differs on
who you ask - wording that permits disparate libc behavior is nicer to
the libraries (they don't have to change) but meaner to application
writers (the construct is not portable, so it is safer to avoid the
construct altogether rather than worry about which libraries have
which behaviors); whereas wording that locks down behavior is nicer to
applications (if I write this, it should work regardless of platform,
and if it doesn't, the standard exists as leverage to get libc fixed)
but meaner to libraries (forcing the library to version its symbols to
change behavior for newer standards while still providing ABI
stability guarantees to older apps that depend on the old behavior is
not cheap).
The sad fact of the matter is that _because_ it there are so many
differences in opinions, the C23 action of making realloc(p,0)
undefined is probably the simplest course that could be agreed on
(don't ever do that in your code, because you can't guarantee the
results), but simultaneously annoying to end users (because it is
undefined, rather than implementation-defined or unspecified, a
compiler can "optimize" your code to do WHATEVER IT WANTS - which
really means you CANNOT ever reliably call realloc(p,0) if your
compiler is aiming for C23).
On top of that, the POSIX standard usually defers to a (fixed version)
of C, but does have the liberty to impose well-defined behavior even
where the corresponding C standard left things undefined (for example,
POSIX 2017 was able to demand that a POSIX system can cast function
pointers to and from void* in order to implement dlsym(), even though
C99 said that was undefined). Put another way, just because C23 has
changed realloc(p,0) to be undefined does NOT require a future version
of POSIX to do likewise when it finally moves to a newer C than C17.
But at the same time, POSIX is unlikely to make things strict if it
risks alienating existing implementations; if glibc changes behavior,
that would go a long way towards POSIX changing wording to be
stricter.
>
> BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
> following bug <https://www.austingroupbugs.net/view.php?id=400>.
>
> The motivation, from what I can read there, seems to be that C99 already
> made POSIX.1 non-conforming, and this fix was intended to conform to
> C99.
>
> Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> wording from C99, either; it allows weird stuff: it allows an
> implementation where malloc(0) returns NULL and realloc(p,0) non-null
> (so, the opposite of glibc).
>
> C11 is essentially identical to C99 in that regard, so glibc is also
> non-conforming to C11.
>
> C17 changed to something very weird. It seems to me that glibc is
> conforming again to C17, but it also seems to me that it's impossible to
> write code that uses realloc(p,0) in a portable way with this
> specification. I think it's a good thing that C23 removed that crap.
>
>
> Here's a summary of conformance to standards:
>
> glibc conforms to:
> - SysVr4
> - ISO C89
I don't (currently) have a copy of C89 handy in front of me to quote
chapter and verse for this one, beyond what was already quoted in
Austin Group bug 400.
> - ISO C17
This one I _can_ quote. 7.22.3.4:
"If size is zero and memory for the new object is not allocated, it is
implementation-defined whether the old object is deallocated."
glibc documents that "realloc(non_null, 0)" deallocates non_null.
Therefore it is compliant. But that wording is still unfriendly to
users - there is no way to programmatically query the runtime what
behavior the implementation defined.
> - XPG4
>
> glibc doesn't conform to:
> - SysIII
> - SysV
> - SysVr2
> - SysVr3
> - SVID Issue 2
> - SVID Issue 3
> - The X/Open System V Specification
> - ISO C99
Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
a null pointer, the realloc function behaves like the malloc function
for the specified size." but NO wording about when ptr is non-null but
size is 0. As best I can tell, silence on the part of C99 means that
the standard is unspecified, and therefore glibc can do whatever it
wants and still claim compliance. But I'm open to correction if you
can quote the exact statement for why you claim glibc is non-compliant
here.
> - ISO C11
This wording appears to match C99.
> - POSIX.1-2001
This one defers to C89 anywhere that it is not explicitly documenting
with CX shading. It adds CX shading to document the use of
errno=ENOMEM on allocation failures, but otherwise omits shading when
it states:
"If size is 0 and ptr is not a null pointer, the object pointed to is
freed."
which sounds like glibc behavior. But without double-checking C89, it
is hard to say whether POSIX accidentally diverged from C89 in
allowing glibc as compliant.
> - POSIX.1-2008
This version of POSIX defers to C99, but still states "If size is 0
and ptr is not a null pointer, the object pointed to is freed."
without CX shading, even though C99 does NOT have the same wording as
C89. You could argue that this statement should be ignored since it
lacks CX shading and does not match any statement in C99. But even
so, unless you can demonstrate chapter-and-verse how glibc fails to
comply with C99, you also have a hard time convincing me that glibc
does not comply with POSIX 2008. And this issue was why Austin Group
bug 400 was created.
No mention of POSIX.1-2013? But just in case you're keeping track,
that is the version where Bug 400 was applied, and the text changed
to: "If the size of the space requested is zero, the behavior shall be
implementation-defined: either a null pointer is returned, or the
behavior shall be as if the size were some non-zero value, except that
the returned pointer shall not be used to access an object." But it
also has the problem that it requires "If size is 0, either: A null
pointer shall be returned <CX>and errno set to an
implementation-defined value</CX>. ..."
which glibc does NOT comply with. realloc(non_null,0) returns NULL
_without_ setting errno, precisely because it DID free the object
successfully. This requirement in POSIX 2013 is an explicit extension
not mentioned in C99, AND it was quickly pointed out that it forbids
glibc behavior, so:
> - POSIX.1-2017
This one additionally applies Bug 526 and 688, to try and clean up
wording differences from C99, in particular clarifying whether errno
has to be set when "realloc(non_null, 0)" frees a pointer:
https://www.austingroupbugs.net/view.php?id=526
https://www.austingroupbugs.net/view.php?id=688
where the wording is once again relaxed to "If the size of the space
requested is zero, the behavior shall be implementation-defined:
either a null pointer is returned, or the behavior shall be as if the
size were some non-zero value, except that the behavior is undefined
if the returned pointer is used to access an object. ... If size is 0,
either: A null pointer shall be returned <CX>and, if ptr is not a null
pointer, errno shall be set to an implementation-defined value</CX>."
which should once again allow glibc to be deemed compliant. At the
same time, the Austin Group was trying to get C17 fixed; that fix
turned out to be ugly, so the C committed tried again in C23.
> - POSIX.1-2024
Here, the standard defers to C17 rather than C99, but adds a lot more
CX shading. Given the changes between C99 and C17, POSIX tried to
match. Unfortunately, the DESCRIPTION section lost any mention of
non_null pointer plus 0 size, leaving only the RETURN VALUE secion,
which now uses the wording entirely in CX shading:
"If size is 0, or either nelem or elsize is 0, either: • A null
pointer shall be returned and, if ptr is not a null pointer, errno
shall be set to [EINVAL]. • A pointer to the allocated space shall be
returned, and the memory object pointed to by ptr shall be freed. The
application shall ensure that the pointer is not used to access an
object."
Despite the efforts of the Austin Group to not break back-compat, that
one clearly looks like glibc is not compliant (glibc returns NULL and
does NOT set errno to EINVAL). And if I recall the conversations, we
knew at the time of POSIX 2024 that C23 would be marking
realloc(non_null, 0) as undefined behavior, and wanted to capture that
directly rather than depending on C17, but we may have failed in our
efforts.
>
> Conformance to POSIX.1-2001 and POSIX.1-2008 is not clear. While glibc
> conforms to the wording of these standards, these standards have the
> following header in the realloc(3) specification:
>
> The functionality described on this reference page is aligned
> with the ISO C standard. Any conflict between the requirements
> described here and the ISO C standard is unintentional. This
> volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
>
> Which means that POSIX's permissive wording is unintentional, and the
> ISO C99 wording is the one that matters, so glibc is non-conforming.
The conflicts are unintentional only when <CX> shading is not
explicitly present.
>
> (I didn't mention C23, since it's UB, so anything conforms.)
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
If you've managed to make it this far, congratulations. We probably
still need to open bugs against POSIX to have POSIX-2024-TC1 improve
any ambiguous wording, and taking into account whatever the C
committee may decide to do with Alejandro's proposals for post-C23
behaviors, and whether glibc is willing to make realloc(non_null, 0)
allocate in the same manner as malloc(0) rather than being a hidden
call to free().
I don't know if I answered all of your questions, or raised even more,
but you have your work cut out for you before declaring the man pages
good enough.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 18:31 ` Eric Blake
@ 2025-06-19 23:37 ` Alejandro Colomar
2025-06-19 23:45 ` Alejandro Colomar
` (5 more replies)
2025-06-20 5:55 ` Paul Eggert
1 sibling, 6 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-19 23:37 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 24403 bytes --]
Hey Eric!
Thanks a lot for the detailed reply! Comments below.
On Thu, Jun 19, 2025 at 01:31:06PM -0500, Eric Blake wrote:
> On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> > Hi Rich, Elliott,
> >
> > On Wed, Jun 18, 2025 at 12:35:50PM -0400, Rich Felker wrote:
> > > On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> > > > On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > > >
> > > > > Hi Elliott, Florian,
> > > > >
> > > > > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > > > > proposing would make them conforming. Does conformance to POSIX.1-2024
> > > > > mean something to you?
> > > >
> > > > not when POSIX screwed up and made a change that made most of the
> > > > existing implementations non-conformant, no. that sounds like a POSIX
> > > > bug to me...
> >
> > Not most. Only two POSIX implementations, plus Windows. And the
> > solution is easy: fix the implementations. There have been no
> > regression reports in gnulib since we fixed it last year.
>
> Speaking as someone who participated in the POSIX standardization
> process, I'm trying to pinpoint exactly which statements of which
> versions of which standards you are claiming as nonconformance.
>
> First, a disclaimer: because this thread has been very vocal, I
> brought the topic up in today's Austin Group meeting. The members of
> the group on the phone call remember _specifically_ trying to permit
> existing glibc behavior (where realloc(p, 0) does NOT allocate), while
> still jugging competing wording from the C standards, although we will
> be the first to admit that we would not be surprised if the resulting
> efforts are still not clear enough to be unambiguous. I mentioned in
> the meeting that I would attempt to follow up on these threads to see
> what, if anything, the Austin Group may need to do to assist in the
> discussion.
Thanks! I'm in Denver for the Open Source Summit and LSS. If you'll
be around next week, we can have a chat in person, which might be more
useful. I'd like to have a long conversation about this.
> Next, my overarching question. Is this about "realloc(non_null, 0)",
> "realloc(NULL, 0)", or both? As the two are very distinct, I want to
> make sure we are talking about the same usage patterns. For the rest
> of this email, I'm assuming that your complaints are solely about
> "realloc(non_null, 0)" - if I'm wrong, it may change the analysis done
> below.
Yup, it's about realloc(non_null, 0). r(NULL,0) is fine.
> Now, on to some code archeology. In today's glibc source code, I see
> this telling comment in malloc/malloc.c, making it clear that glibc
> folks are aware that realloc(non_null, 0) has two useful behaviors,
> and that glibc picks the behavior that does NOT behave consistently
> with malloc(0), because of back-compat guarantees:
>
> /*
> 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.
> */
That comment is wrong. "common historical practice" is that realloc(3)
is consistent with malloc(3). This is true since the days of Unix V7.
I don't know what they were referring to. Maybe the behavior introduced
in SysVr2's -lmalloc which was later standardized in the SVID by AT&T?
That was never common, since all existing default (-lc) realloc(3)
implementations behaved as if realloc(p, 1). You had to use the
-lmalloc library to get it to return NULL and free the object.
See <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>.
> More reading: https://www.austingroupbugs.net/view.php?id=400 shows
> where earlier POSIX missed that C90 to C99 changed what was permitted
> (and apparantly in a way to render glibc's implementation
> non-conforming), and that's part of what drove the POSIX folks to ask
> the C standard to improve the wording. POSIX 2024 is based on C17,
> but Nick Stoughton was regularly communicating between both C and
> POSIX groups on what wording(s) were being floated around, in order to
> try and make it so that glibc would not have to change behavior, but
> at the same time trying to make it possible for applications to be
> able to make wise runtime decisions on how to use realloc that would
> not leak memory or risk dereferencing a NULL pointer if not careful.
>
> https://sourceware.org/bugzilla/show_bug.cgi?id=12547 shows where
> glibc has, in the past, refused to change behavior on the grounds that
> the standards were buggy. If the standards are still buggy, the best
> course of action is to open a bug against them.
All standards since C89 have been buggy. If you are pedantic reading
C89, the BSDs and all the historic implementations back to the original
Unix V7 are non-conforming:
<https://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4>
Which says:
| If size is zero and ptr is not a null pointer, the object it points to
| is freed.
It's not clear whether this means that the whole action of realloc(p,0)
is to free(3) the pointer, or if it can also allocate a new object.
Under the former interpretation, the standard is at odds with reality.
Under the latter interpretation, I'd interpret it as saying that
realloc(p,0) cannot fail (and thus must free(p)), which would be an
interesting guarantee. I guess we'll never know what was the intended
reading.
C99 changed the specification, probably because of how ambiguous it was.
glibc was also buggy, as it differed from every other Unix-like system.
All Unix systems behaved as if free(p) and malloc(n). glibc is the only
one that didn't follow this obvious consistency rule.
So, both are bogus.
> Also relevant are these documents
> https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
> https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
>
> > > > (like i said, i care greatly about actual shipping code. a standard is
> > > > interesting for green-field stuff, but when it's at odds with reality
> > > > it's often worse to try to adapt than just ignore the stupidity/report
> > > > the bug and get it changed back.)
> >
> > It's ironic that the standard should have never said that, because prior
> > to the existence of ANSI C and POSIX, all existing systems behaved like
> > the current POSIX specification. It was a consequence of the horrible
> > wording of the standards, that glibc was written so badly, by following
> > a bogus specification, when it should have been made compatible with the
> > existing systems.
>
> POSIX was originally released in 1988, before C90. glibc 1.0 came out
> in 1992. I am not sure when glibc first cared about whether trending
> towards POSIX compliance mattered, although I do know that in the
> early days, Ulrich would very adamently argue along the lines of
> (paraphrased) "if the standards don't match common sense, then we
> don't care about the standards".
It seems Ulrich didn't follow that in this case. I don't know who wrote
the original realloc(3) in glibc. Was it RMS? It would be interesting
to know how they came up with that implementation. If anyone knows who
wrote it and why, please CC them.
I don't have a copy of POSIX.1-1988, nor of any other POSIX.1 before
POSIX.1-2001. What do they say for realloc(3)?
> > Thus, this is a historical bug in ISO C, POSIX, which at least has been
> > finally fixed in POSIX.
>
> The fact that the wording has changed across multiple versions of C
> and POSIX is indeed evidence that getting a specification that people
> are happy with is difficult. What is harder is the decision of
> whether the bug is in the standard (for not documenting reality) or in
> the implementations (for not doing what the standard rightfully
> requests), or even both. And "what people are happy with" differs on
> who you ask - wording that permits disparate libc behavior is nicer to
> the libraries (they don't have to change) but meaner to application
> writers (the construct is not portable, so it is safer to avoid the
> construct altogether rather than worry about which libraries have
> which behaviors); whereas wording that locks down behavior is nicer to
> applications (if I write this, it should work regardless of platform,
> and if it doesn't, the standard exists as leverage to get libc fixed)
> but meaner to libraries (forcing the library to version its symbols to
> change behavior for newer standards while still providing ABI
> stability guarantees to older apps that depend on the old behavior is
> not cheap).
As can be seen from the change in gnulib, the only possible issues from
migrating from the current glibc behavior to the musl behavior is a few
leaks in cases where the programmer calls realloc(p,0) ignoring the
return value. Those leaks would leak 0 bytes plus the metadata.
A solution for those leaks would be to add a diagnostic for calls to
realloc(3) where the return value is unused. And even if those aren't
fully solved, they're leaks of a few bytes. There's nothing that should
cause real issues.
But the glibc maintainers mentioned that they're investigating about it
in distros, so I guess we'll eventually have the results of their
investigation.
> The sad fact of the matter is that _because_ it there are so many
> differences in opinions, the C23 action of making realloc(p,0)
> undefined is probably the simplest course that could be agreed on
> (don't ever do that in your code, because you can't guarantee the
> results), but simultaneously annoying to end users (because it is
> undefined, rather than implementation-defined or unspecified, a
> compiler can "optimize" your code to do WHATEVER IT WANTS - which
> really means you CANNOT ever reliably call realloc(p,0) if your
> compiler is aiming for C23).
Indeed. I think the move from C17 to C23 was good.
The issue with C17 is that it is very similar to POSIX.1-2008, but since
ISO C doesn't require that errno is set when the pointer is not freed,
it is impossible to portably determine if the input pointer was freed
after realloc(p,0). This is not an issue in POSIX.1, though, since it
can and does require that errno is set if the input pointer is not
freed.
Because it was impossible to determine whether r(p,0) has freed p after
returning NULL in C17, it was effectively UB. So, I consider C23 to be
a minor change from C17, and one which clarifies that it is UB, because
it already was before.
POSIX.1 is not limited by this limitation of ISO C.
> On top of that, the POSIX standard usually defers to a (fixed version)
> of C, but does have the liberty to impose well-defined behavior even
> where the corresponding C standard left things undefined (for example,
> POSIX 2017 was able to demand that a POSIX system can cast function
> pointers to and from void* in order to implement dlsym(), even though
> C99 said that was undefined). Put another way, just because C23 has
> changed realloc(p,0) to be undefined does NOT require a future version
> of POSIX to do likewise when it finally moves to a newer C than C17.
> But at the same time, POSIX is unlikely to make things strict if it
> risks alienating existing implementations; if glibc changes behavior,
> that would go a long way towards POSIX changing wording to be
> stricter.
Indeed; I think POSIX.1 doesn't need to make this undefined, and
shouldn't.
> > BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
> > following bug <https://www.austingroupbugs.net/view.php?id=400>.
> >
> > The motivation, from what I can read there, seems to be that C99 already
> > made POSIX.1 non-conforming, and this fix was intended to conform to
> > C99.
> >
> > Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> > wording from C99, either; it allows weird stuff: it allows an
> > implementation where malloc(0) returns NULL and realloc(p,0) non-null
> > (so, the opposite of glibc).
> >
> > C11 is essentially identical to C99 in that regard, so glibc is also
> > non-conforming to C11.
> >
> > C17 changed to something very weird. It seems to me that glibc is
> > conforming again to C17, but it also seems to me that it's impossible to
> > write code that uses realloc(p,0) in a portable way with this
> > specification. I think it's a good thing that C23 removed that crap.
> >
> >
> > Here's a summary of conformance to standards:
> >
> > glibc conforms to:
> > - SysVr4
> > - ISO C89
>
> I don't (currently) have a copy of C89 handy in front of me to quote
> chapter and verse for this one, beyond what was already quoted in
> Austin Group bug 400.
Here's the draft, in various formats:
<https://port70.net/~nsz/c/c89/>
> > - ISO C17
>
> This one I _can_ quote. 7.22.3.4:
>
> "If size is zero and memory for the new object is not allocated, it is
> implementation-defined whether the old object is deallocated."
>
> glibc documents that "realloc(non_null, 0)" deallocates non_null.
> Therefore it is compliant. But that wording is still unfriendly to
> users - there is no way to programmatically query the runtime what
> behavior the implementation defined.
> > - XPG4
> >
> > glibc doesn't conform to:
> > - SysIII
> > - SysV
> > - SysVr2
> > - SysVr3
> > - SVID Issue 2
> > - SVID Issue 3
> > - The X/Open System V Specification
> > - ISO C99
>
> Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
> a null pointer, the realloc function behaves like the malloc function
> for the specified size." but NO wording about when ptr is non-null but
> size is 0. As best I can tell, silence on the part of C99 means that
> the standard is unspecified, and therefore glibc can do whatever it
> wants and still claim compliance. But I'm open to correction if you
> can quote the exact statement for why you claim glibc is non-compliant
> here.
<https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
I'll quote the entire text:
Description
2 The realloc function
deallocates the old object pointed to by ptr and
returns a pointer to a new object that
has the size specified by size.
[...] // talks about the contents
3 If ptr is a null pointer, [...].
Otherwise,
if ptr does not match a pointer earlier returned by
the calloc, malloc, [...], the behavior is undefined.
If memory for the new object cannot be allocated,
the old object is not deallocated and its value is unchanged.
Returns
4 The realloc function returns a pointer to the new object
(which may have the same value as a pointer to the old object),
or a null pointer if the new object could not be allocated.
IMO, paragraph 4 rules out the possibility of returning a null pointer
on success.
Also, while it doesn't specify what happens in the case of size 0
explicitly, it mentions in paragraph 2 what happens for all sizes:
it returns a pointer to a new object that has the size specified by
size --which in this case is 0 bytes--.
This wording of C99 was relatively good, and fixes the problems from
C89 which had turned all historical implementations into
non-conformance. C99 seems to restore the common historical behavior of
realloc(3), turning glibc non-conforming as a consequence.
> > - ISO C11
>
> This wording appears to match C99.
Agree.
> > - POSIX.1-2001
>
> This one defers to C89 anywhere that it is not explicitly documenting
> with CX shading.
Ahh, I had thought it would defer to C99 because it's older, but I guess
it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
corrected, and glibc conforms to POSIX.1-2001.
> It adds CX shading to document the use of
> errno=ENOMEM on allocation failures, but otherwise omits shading when
> it states:
>
> "If size is 0 and ptr is not a null pointer, the object pointed to is
> freed."
>
> which sounds like glibc behavior. But without double-checking C89, it
> is hard to say whether POSIX accidentally diverged from C89 in
> allowing glibc as compliant.
>
> > - POSIX.1-2008
>
> This version of POSIX defers to C99, but still states "If size is 0
> and ptr is not a null pointer, the object pointed to is freed."
I don't have a copy of POSIX.1-2008, but I assume the text is identical
to POSIX.1-2001, except that it now defers to C99. Since C99 rules out
the possibility of returning a null pointer on success (7.20.3.4p4),
and POSIX.1-2008 doesn't seem to have shaded text to extend it, it is
bound by the C99 restriction. The allowances provided by POSIX.1-2008
are invalidated as unintentional.
> without CX shading, even though C99 does NOT have the same wording as
> C89. You could argue that this statement should be ignored since it
> lacks CX shading and does not match any statement in C99.
Indeed.
> But even
> so, unless you can demonstrate chapter-and-verse how glibc fails to
> comply with C99, you also have a hard time convincing me that glibc
> does not comply with POSIX 2008. And this issue was why Austin Group
> bug 400 was created.
I alreayd mentioned it above, but I'll copy for completeness:
n1256::7.20.3.4p4.
<https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4p4>
The realloc function returns a pointer to the new object
(which may have the same value as a pointer to the old object),
or a null pointer if the new object could not be allocated.
This seems to preclude the possibility of returning NULL on success.
Also, this sentence is complemented by n1256::7.20.3.4p3, last sentence:
If memory for the new object cannot be allocated,
the old object is not deallocated and its value is unchanged.
This sentence rules that if the implementation could consider that their
returning a null pointer is because they decide that they can't
allocate 0 bytes (this would be a valid interpretation), then they are
forced to leave the pointer not deallocated. glibc frees the object,
and thus it is not complying with this, and we must consider that glibc
has succeeded in the allocation, which brings us back to p4.
> No mention of POSIX.1-2013?
I didn't have a copy of that. Thanks! I'll add it to the list of
non-conforming standards.
> But just in case you're keeping track,
> that is the version where Bug 400 was applied, and the text changed
> to: "If the size of the space requested is zero, the behavior shall be
> implementation-defined: either a null pointer is returned, or the
> behavior shall be as if the size were some non-zero value, except that
> the returned pointer shall not be used to access an object." But it
> also has the problem that it requires "If size is 0, either: A null
> pointer shall be returned <CX>and errno set to an
> implementation-defined value</CX>. ..."
>
> which glibc does NOT comply with. realloc(non_null,0) returns NULL
> _without_ setting errno, precisely because it DID free the object
> successfully. This requirement in POSIX 2013 is an explicit extension
> not mentioned in C99, AND it was quickly pointed out that it forbids
> glibc behavior, so:
>
> > - POSIX.1-2017
>
> This one additionally applies Bug 526 and 688, to try and clean up
> wording differences from C99, in particular clarifying whether errno
> has to be set when "realloc(non_null, 0)" frees a pointer:
>
> https://www.austingroupbugs.net/view.php?id=526
> https://www.austingroupbugs.net/view.php?id=688
>
> where the wording is once again relaxed to "If the size of the space
> requested is zero, the behavior shall be implementation-defined:
> either a null pointer is returned, or the behavior shall be as if the
> size were some non-zero value, except that the behavior is undefined
> if the returned pointer is used to access an object. ... If size is 0,
> either: A null pointer shall be returned <CX>and, if ptr is not a null
> pointer, errno shall be set to an implementation-defined value</CX>."
>
> which should once again allow glibc to be deemed compliant. At the
> same time, the Austin Group was trying to get C17 fixed; that fix
> turned out to be ugly, so the C committed tried again in C23.
The last sentence clearly states that if size is 0 and ptr is non-null
and a null pointer is returned, then *errno shall be set*. glibc
doesn't set errno, and thus does not conform. Can you please clarify
how you consider glibc's behavior to comply with that last sentence from
your quote? For completeness, the sentence I'm talking about is
If size is 0,
either: A null pointer shall be returned <CX>and, if ptr is not a null
pointer, errno shall be set to an implementation-defined value</CX>."
> > - POSIX.1-2024
>
> Here, the standard defers to C17 rather than C99, but adds a lot more
> CX shading. Given the changes between C99 and C17, POSIX tried to
> match. Unfortunately, the DESCRIPTION section lost any mention of
> non_null pointer plus 0 size, leaving only the RETURN VALUE secion,
> which now uses the wording entirely in CX shading:
>
> "If size is 0, or either nelem or elsize is 0, either: • A null
> pointer shall be returned and, if ptr is not a null pointer, errno
> shall be set to [EINVAL]. • A pointer to the allocated space shall be
> returned, and the memory object pointed to by ptr shall be freed. The
> application shall ensure that the pointer is not used to access an
> object."
>
> Despite the efforts of the Austin Group to not break back-compat, that
> one clearly looks like glibc is not compliant (glibc returns NULL and
> does NOT set errno to EINVAL). And if I recall the conversations, we
> knew at the time of POSIX 2024 that C23 would be marking
> realloc(non_null, 0) as undefined behavior, and wanted to capture that
> directly rather than depending on C17, but we may have failed in our
> efforts.
TBH, I think POSIX.1-2024 has a decent specification. I'd prefer the
one from C99 and C11, but it is decent.
> > Conformance to POSIX.1-2001 and POSIX.1-2008 is not clear. While glibc
> > conforms to the wording of these standards, these standards have the
> > following header in the realloc(3) specification:
> >
> > The functionality described on this reference page is aligned
> > with the ISO C standard. Any conflict between the requirements
> > described here and the ISO C standard is unintentional. This
> > volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
> >
> > Which means that POSIX's permissive wording is unintentional, and the
> > ISO C99 wording is the one that matters, so glibc is non-conforming.
>
> The conflicts are unintentional only when <CX> shading is not
> explicitly present.
And POSIX.1-2001 .. POSIX.1-2008 doesn't have any CX shading.
> > (I didn't mention C23, since it's UB, so anything conforms.)
> >
> >
> > Have a lovely day!
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es/>
>
> If you've managed to make it this far, congratulations. We probably
> still need to open bugs against POSIX to have POSIX-2024-TC1 improve
> any ambiguous wording, and taking into account whatever the C
> committee may decide to do with Alejandro's proposals for post-C23
> behaviors, and whether glibc is willing to make realloc(non_null, 0)
> allocate in the same manner as malloc(0) rather than being a hidden
> call to free().
As always, I have trouble with using the Austin group interface. If
you're in Denver, this is another thing you could help me with. :)
> I don't know if I answered all of your questions, or raised even more,
> but you have your work cut out for you before declaring the man pages
> good enough.
Again, thanks a lot!!
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
@ 2025-06-19 23:45 ` Alejandro Colomar
2025-06-19 23:53 ` Alejandro Colomar
2025-06-20 2:11 ` Vincent Lefevre
` (4 subsequent siblings)
5 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-19 23:45 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 25941 bytes --]
On Fri, Jun 20, 2025 at 01:38:14AM +0200, Alejandro Colomar wrote:
> Hey Eric!
>
> Thanks a lot for the detailed reply! Comments below.
>
> On Thu, Jun 19, 2025 at 01:31:06PM -0500, Eric Blake wrote:
> > On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> > > Hi Rich, Elliott,
> > >
> > > On Wed, Jun 18, 2025 at 12:35:50PM -0400, Rich Felker wrote:
> > > > On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> > > > > On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > > > >
> > > > > > Hi Elliott, Florian,
> > > > > >
> > > > > > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > > > > > proposing would make them conforming. Does conformance to POSIX.1-2024
> > > > > > mean something to you?
> > > > >
> > > > > not when POSIX screwed up and made a change that made most of the
> > > > > existing implementations non-conformant, no. that sounds like a POSIX
> > > > > bug to me...
> > >
> > > Not most. Only two POSIX implementations, plus Windows. And the
> > > solution is easy: fix the implementations. There have been no
> > > regression reports in gnulib since we fixed it last year.
> >
> > Speaking as someone who participated in the POSIX standardization
> > process, I'm trying to pinpoint exactly which statements of which
> > versions of which standards you are claiming as nonconformance.
> >
> > First, a disclaimer: because this thread has been very vocal, I
> > brought the topic up in today's Austin Group meeting. The members of
> > the group on the phone call remember _specifically_ trying to permit
> > existing glibc behavior (where realloc(p, 0) does NOT allocate), while
> > still jugging competing wording from the C standards, although we will
> > be the first to admit that we would not be surprised if the resulting
> > efforts are still not clear enough to be unambiguous. I mentioned in
> > the meeting that I would attempt to follow up on these threads to see
> > what, if anything, the Austin Group may need to do to assist in the
> > discussion.
>
> Thanks! I'm in Denver for the Open Source Summit and LSS. If you'll
> be around next week, we can have a chat in person, which might be more
> useful. I'd like to have a long conversation about this.
>
> > Next, my overarching question. Is this about "realloc(non_null, 0)",
> > "realloc(NULL, 0)", or both? As the two are very distinct, I want to
> > make sure we are talking about the same usage patterns. For the rest
> > of this email, I'm assuming that your complaints are solely about
> > "realloc(non_null, 0)" - if I'm wrong, it may change the analysis done
> > below.
>
> Yup, it's about realloc(non_null, 0). r(NULL,0) is fine.
>
> > Now, on to some code archeology. In today's glibc source code, I see
> > this telling comment in malloc/malloc.c, making it clear that glibc
> > folks are aware that realloc(non_null, 0) has two useful behaviors,
> > and that glibc picks the behavior that does NOT behave consistently
> > with malloc(0), because of back-compat guarantees:
> >
> > /*
> > 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.
> > */
>
> That comment is wrong. "common historical practice" is that realloc(3)
> is consistent with malloc(3). This is true since the days of Unix V7.
> I don't know what they were referring to. Maybe the behavior introduced
> in SysVr2's -lmalloc which was later standardized in the SVID by AT&T?
> That was never common, since all existing default (-lc) realloc(3)
> implementations behaved as if realloc(p, 1). You had to use the
> -lmalloc library to get it to return NULL and free the object.
>
> See <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>.
>
> > More reading: https://www.austingroupbugs.net/view.php?id=400 shows
> > where earlier POSIX missed that C90 to C99 changed what was permitted
> > (and apparantly in a way to render glibc's implementation
> > non-conforming), and that's part of what drove the POSIX folks to ask
> > the C standard to improve the wording. POSIX 2024 is based on C17,
> > but Nick Stoughton was regularly communicating between both C and
> > POSIX groups on what wording(s) were being floated around, in order to
> > try and make it so that glibc would not have to change behavior, but
> > at the same time trying to make it possible for applications to be
> > able to make wise runtime decisions on how to use realloc that would
> > not leak memory or risk dereferencing a NULL pointer if not careful.
> >
> > https://sourceware.org/bugzilla/show_bug.cgi?id=12547 shows where
> > glibc has, in the past, refused to change behavior on the grounds that
> > the standards were buggy. If the standards are still buggy, the best
> > course of action is to open a bug against them.
>
> All standards since C89 have been buggy. If you are pedantic reading
> C89, the BSDs and all the historic implementations back to the original
> Unix V7 are non-conforming:
>
> <https://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4>
>
> Which says:
>
> | If size is zero and ptr is not a null pointer, the object it points to
> | is freed.
>
> It's not clear whether this means that the whole action of realloc(p,0)
> is to free(3) the pointer, or if it can also allocate a new object.
> Under the former interpretation, the standard is at odds with reality.
> Under the latter interpretation, I'd interpret it as saying that
> realloc(p,0) cannot fail (and thus must free(p)), which would be an
> interesting guarantee. I guess we'll never know what was the intended
> reading.
>
> C99 changed the specification, probably because of how ambiguous it was.
>
> glibc was also buggy, as it differed from every other Unix-like system.
> All Unix systems behaved as if free(p) and malloc(n). glibc is the only
> one that didn't follow this obvious consistency rule.
>
> So, both are bogus.
>
> > Also relevant are these documents
> > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
> > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
> >
> > > > > (like i said, i care greatly about actual shipping code. a standard is
> > > > > interesting for green-field stuff, but when it's at odds with reality
> > > > > it's often worse to try to adapt than just ignore the stupidity/report
> > > > > the bug and get it changed back.)
> > >
> > > It's ironic that the standard should have never said that, because prior
> > > to the existence of ANSI C and POSIX, all existing systems behaved like
> > > the current POSIX specification. It was a consequence of the horrible
> > > wording of the standards, that glibc was written so badly, by following
> > > a bogus specification, when it should have been made compatible with the
> > > existing systems.
> >
> > POSIX was originally released in 1988, before C90. glibc 1.0 came out
> > in 1992. I am not sure when glibc first cared about whether trending
> > towards POSIX compliance mattered, although I do know that in the
> > early days, Ulrich would very adamently argue along the lines of
> > (paraphrased) "if the standards don't match common sense, then we
> > don't care about the standards".
>
> It seems Ulrich didn't follow that in this case. I don't know who wrote
> the original realloc(3) in glibc. Was it RMS? It would be interesting
> to know how they came up with that implementation. If anyone knows who
> wrote it and why, please CC them.
>
> I don't have a copy of POSIX.1-1988, nor of any other POSIX.1 before
> POSIX.1-2001. What do they say for realloc(3)?
>
> > > Thus, this is a historical bug in ISO C, POSIX, which at least has been
> > > finally fixed in POSIX.
> >
> > The fact that the wording has changed across multiple versions of C
> > and POSIX is indeed evidence that getting a specification that people
> > are happy with is difficult. What is harder is the decision of
> > whether the bug is in the standard (for not documenting reality) or in
> > the implementations (for not doing what the standard rightfully
> > requests), or even both. And "what people are happy with" differs on
> > who you ask - wording that permits disparate libc behavior is nicer to
> > the libraries (they don't have to change) but meaner to application
> > writers (the construct is not portable, so it is safer to avoid the
> > construct altogether rather than worry about which libraries have
> > which behaviors); whereas wording that locks down behavior is nicer to
> > applications (if I write this, it should work regardless of platform,
> > and if it doesn't, the standard exists as leverage to get libc fixed)
> > but meaner to libraries (forcing the library to version its symbols to
> > change behavior for newer standards while still providing ABI
> > stability guarantees to older apps that depend on the old behavior is
> > not cheap).
>
> As can be seen from the change in gnulib, the only possible issues from
> migrating from the current glibc behavior to the musl behavior is a few
> leaks in cases where the programmer calls realloc(p,0) ignoring the
> return value. Those leaks would leak 0 bytes plus the metadata.
>
> A solution for those leaks would be to add a diagnostic for calls to
> realloc(3) where the return value is unused. And even if those aren't
> fully solved, they're leaks of a few bytes. There's nothing that should
> cause real issues.
>
> But the glibc maintainers mentioned that they're investigating about it
> in distros, so I guess we'll eventually have the results of their
> investigation.
>
> > The sad fact of the matter is that _because_ it there are so many
> > differences in opinions, the C23 action of making realloc(p,0)
> > undefined is probably the simplest course that could be agreed on
> > (don't ever do that in your code, because you can't guarantee the
> > results), but simultaneously annoying to end users (because it is
> > undefined, rather than implementation-defined or unspecified, a
> > compiler can "optimize" your code to do WHATEVER IT WANTS - which
> > really means you CANNOT ever reliably call realloc(p,0) if your
> > compiler is aiming for C23).
>
> Indeed. I think the move from C17 to C23 was good.
>
> The issue with C17 is that it is very similar to POSIX.1-2008, but since
> ISO C doesn't require that errno is set when the pointer is not freed,
> it is impossible to portably determine if the input pointer was freed
> after realloc(p,0). This is not an issue in POSIX.1, though, since it
> can and does require that errno is set if the input pointer is not
> freed.
Self correction: POSIX.1-2008 .. POSIX.1-2024 does allow setting errno
and freeing the input pointer, as Paul Eggert reminded. AIX does this.
This is brain damaged, and makes it also impossible to portably
determine whether the pointer was freed after realloc(p,0).
Thus, declaring it UB in POSIX.1 would also be an improvement.
>
> Because it was impossible to determine whether r(p,0) has freed p after
> returning NULL in C17, it was effectively UB. So, I consider C23 to be
> a minor change from C17, and one which clarifies that it is UB, because
> it already was before.
>
> POSIX.1 is not limited by this limitation of ISO C.
>
> > On top of that, the POSIX standard usually defers to a (fixed version)
> > of C, but does have the liberty to impose well-defined behavior even
> > where the corresponding C standard left things undefined (for example,
> > POSIX 2017 was able to demand that a POSIX system can cast function
> > pointers to and from void* in order to implement dlsym(), even though
> > C99 said that was undefined). Put another way, just because C23 has
> > changed realloc(p,0) to be undefined does NOT require a future version
> > of POSIX to do likewise when it finally moves to a newer C than C17.
> > But at the same time, POSIX is unlikely to make things strict if it
> > risks alienating existing implementations; if glibc changes behavior,
> > that would go a long way towards POSIX changing wording to be
> > stricter.
>
> Indeed; I think POSIX.1 doesn't need to make this undefined, and
> shouldn't.
>
> > > BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
> > > following bug <https://www.austingroupbugs.net/view.php?id=400>.
> > >
> > > The motivation, from what I can read there, seems to be that C99 already
> > > made POSIX.1 non-conforming, and this fix was intended to conform to
> > > C99.
> > >
> > > Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> > > wording from C99, either; it allows weird stuff: it allows an
> > > implementation where malloc(0) returns NULL and realloc(p,0) non-null
> > > (so, the opposite of glibc).
> > >
> > > C11 is essentially identical to C99 in that regard, so glibc is also
> > > non-conforming to C11.
> > >
> > > C17 changed to something very weird. It seems to me that glibc is
> > > conforming again to C17, but it also seems to me that it's impossible to
> > > write code that uses realloc(p,0) in a portable way with this
> > > specification. I think it's a good thing that C23 removed that crap.
> > >
> > >
> > > Here's a summary of conformance to standards:
> > >
> > > glibc conforms to:
> > > - SysVr4
> > > - ISO C89
> >
> > I don't (currently) have a copy of C89 handy in front of me to quote
> > chapter and verse for this one, beyond what was already quoted in
> > Austin Group bug 400.
>
> Here's the draft, in various formats:
>
> <https://port70.net/~nsz/c/c89/>
>
> > > - ISO C17
> >
> > This one I _can_ quote. 7.22.3.4:
> >
> > "If size is zero and memory for the new object is not allocated, it is
> > implementation-defined whether the old object is deallocated."
> >
> > glibc documents that "realloc(non_null, 0)" deallocates non_null.
> > Therefore it is compliant. But that wording is still unfriendly to
> > users - there is no way to programmatically query the runtime what
> > behavior the implementation defined.
>
> > > - XPG4
> > >
> > > glibc doesn't conform to:
> > > - SysIII
> > > - SysV
> > > - SysVr2
> > > - SysVr3
> > > - SVID Issue 2
> > > - SVID Issue 3
> > > - The X/Open System V Specification
> > > - ISO C99
> >
> > Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
> > a null pointer, the realloc function behaves like the malloc function
> > for the specified size." but NO wording about when ptr is non-null but
> > size is 0. As best I can tell, silence on the part of C99 means that
> > the standard is unspecified, and therefore glibc can do whatever it
> > wants and still claim compliance. But I'm open to correction if you
> > can quote the exact statement for why you claim glibc is non-compliant
> > here.
>
> <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
>
> I'll quote the entire text:
>
> Description
>
> 2 The realloc function
> deallocates the old object pointed to by ptr and
> returns a pointer to a new object that
> has the size specified by size.
> [...] // talks about the contents
>
> 3 If ptr is a null pointer, [...].
> Otherwise,
> if ptr does not match a pointer earlier returned by
> the calloc, malloc, [...], the behavior is undefined.
> If memory for the new object cannot be allocated,
> the old object is not deallocated and its value is unchanged.
>
> Returns
>
> 4 The realloc function returns a pointer to the new object
> (which may have the same value as a pointer to the old object),
> or a null pointer if the new object could not be allocated.
>
> IMO, paragraph 4 rules out the possibility of returning a null pointer
> on success.
>
> Also, while it doesn't specify what happens in the case of size 0
> explicitly, it mentions in paragraph 2 what happens for all sizes:
> it returns a pointer to a new object that has the size specified by
> size --which in this case is 0 bytes--.
>
> This wording of C99 was relatively good, and fixes the problems from
> C89 which had turned all historical implementations into
> non-conformance. C99 seems to restore the common historical behavior of
> realloc(3), turning glibc non-conforming as a consequence.
>
> > > - ISO C11
> >
> > This wording appears to match C99.
>
> Agree.
>
> > > - POSIX.1-2001
> >
> > This one defers to C89 anywhere that it is not explicitly documenting
> > with CX shading.
>
> Ahh, I had thought it would defer to C99 because it's older, but I guess
> it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
> corrected, and glibc conforms to POSIX.1-2001.
>
> > It adds CX shading to document the use of
> > errno=ENOMEM on allocation failures, but otherwise omits shading when
> > it states:
> >
> > "If size is 0 and ptr is not a null pointer, the object pointed to is
> > freed."
> >
> > which sounds like glibc behavior. But without double-checking C89, it
> > is hard to say whether POSIX accidentally diverged from C89 in
> > allowing glibc as compliant.
> >
> > > - POSIX.1-2008
> >
> > This version of POSIX defers to C99, but still states "If size is 0
> > and ptr is not a null pointer, the object pointed to is freed."
>
> I don't have a copy of POSIX.1-2008, but I assume the text is identical
> to POSIX.1-2001, except that it now defers to C99. Since C99 rules out
> the possibility of returning a null pointer on success (7.20.3.4p4),
> and POSIX.1-2008 doesn't seem to have shaded text to extend it, it is
> bound by the C99 restriction. The allowances provided by POSIX.1-2008
> are invalidated as unintentional.
>
> > without CX shading, even though C99 does NOT have the same wording as
> > C89. You could argue that this statement should be ignored since it
> > lacks CX shading and does not match any statement in C99.
>
> Indeed.
>
> > But even
> > so, unless you can demonstrate chapter-and-verse how glibc fails to
> > comply with C99, you also have a hard time convincing me that glibc
> > does not comply with POSIX 2008. And this issue was why Austin Group
> > bug 400 was created.
>
> I alreayd mentioned it above, but I'll copy for completeness:
>
> n1256::7.20.3.4p4.
>
> <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4p4>
>
> The realloc function returns a pointer to the new object
> (which may have the same value as a pointer to the old object),
> or a null pointer if the new object could not be allocated.
>
> This seems to preclude the possibility of returning NULL on success.
>
> Also, this sentence is complemented by n1256::7.20.3.4p3, last sentence:
>
> If memory for the new object cannot be allocated,
> the old object is not deallocated and its value is unchanged.
>
> This sentence rules that if the implementation could consider that their
> returning a null pointer is because they decide that they can't
> allocate 0 bytes (this would be a valid interpretation), then they are
> forced to leave the pointer not deallocated. glibc frees the object,
> and thus it is not complying with this, and we must consider that glibc
> has succeeded in the allocation, which brings us back to p4.
>
> > No mention of POSIX.1-2013?
>
> I didn't have a copy of that. Thanks! I'll add it to the list of
> non-conforming standards.
>
> > But just in case you're keeping track,
> > that is the version where Bug 400 was applied, and the text changed
> > to: "If the size of the space requested is zero, the behavior shall be
> > implementation-defined: either a null pointer is returned, or the
> > behavior shall be as if the size were some non-zero value, except that
> > the returned pointer shall not be used to access an object." But it
> > also has the problem that it requires "If size is 0, either: A null
> > pointer shall be returned <CX>and errno set to an
> > implementation-defined value</CX>. ..."
> >
> > which glibc does NOT comply with. realloc(non_null,0) returns NULL
> > _without_ setting errno, precisely because it DID free the object
> > successfully. This requirement in POSIX 2013 is an explicit extension
> > not mentioned in C99, AND it was quickly pointed out that it forbids
> > glibc behavior, so:
> >
> > > - POSIX.1-2017
> >
> > This one additionally applies Bug 526 and 688, to try and clean up
> > wording differences from C99, in particular clarifying whether errno
> > has to be set when "realloc(non_null, 0)" frees a pointer:
> >
> > https://www.austingroupbugs.net/view.php?id=526
> > https://www.austingroupbugs.net/view.php?id=688
> >
> > where the wording is once again relaxed to "If the size of the space
> > requested is zero, the behavior shall be implementation-defined:
> > either a null pointer is returned, or the behavior shall be as if the
> > size were some non-zero value, except that the behavior is undefined
> > if the returned pointer is used to access an object. ... If size is 0,
> > either: A null pointer shall be returned <CX>and, if ptr is not a null
> > pointer, errno shall be set to an implementation-defined value</CX>."
> >
> > which should once again allow glibc to be deemed compliant. At the
> > same time, the Austin Group was trying to get C17 fixed; that fix
> > turned out to be ugly, so the C committed tried again in C23.
>
> The last sentence clearly states that if size is 0 and ptr is non-null
> and a null pointer is returned, then *errno shall be set*. glibc
> doesn't set errno, and thus does not conform. Can you please clarify
> how you consider glibc's behavior to comply with that last sentence from
> your quote? For completeness, the sentence I'm talking about is
>
> If size is 0,
> either: A null pointer shall be returned <CX>and, if ptr is not a null
> pointer, errno shall be set to an implementation-defined value</CX>."
>
> > > - POSIX.1-2024
> >
> > Here, the standard defers to C17 rather than C99, but adds a lot more
> > CX shading. Given the changes between C99 and C17, POSIX tried to
> > match. Unfortunately, the DESCRIPTION section lost any mention of
> > non_null pointer plus 0 size, leaving only the RETURN VALUE secion,
> > which now uses the wording entirely in CX shading:
> >
> > "If size is 0, or either nelem or elsize is 0, either: • A null
> > pointer shall be returned and, if ptr is not a null pointer, errno
> > shall be set to [EINVAL]. • A pointer to the allocated space shall be
> > returned, and the memory object pointed to by ptr shall be freed. The
> > application shall ensure that the pointer is not used to access an
> > object."
> >
> > Despite the efforts of the Austin Group to not break back-compat, that
> > one clearly looks like glibc is not compliant (glibc returns NULL and
> > does NOT set errno to EINVAL). And if I recall the conversations, we
> > knew at the time of POSIX 2024 that C23 would be marking
> > realloc(non_null, 0) as undefined behavior, and wanted to capture that
> > directly rather than depending on C17, but we may have failed in our
> > efforts.
>
> TBH, I think POSIX.1-2024 has a decent specification. I'd prefer the
> one from C99 and C11, but it is decent.
>
> > > Conformance to POSIX.1-2001 and POSIX.1-2008 is not clear. While glibc
> > > conforms to the wording of these standards, these standards have the
> > > following header in the realloc(3) specification:
> > >
> > > The functionality described on this reference page is aligned
> > > with the ISO C standard. Any conflict between the requirements
> > > described here and the ISO C standard is unintentional. This
> > > volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
> > >
> > > Which means that POSIX's permissive wording is unintentional, and the
> > > ISO C99 wording is the one that matters, so glibc is non-conforming.
> >
> > The conflicts are unintentional only when <CX> shading is not
> > explicitly present.
>
> And POSIX.1-2001 .. POSIX.1-2008 doesn't have any CX shading.
>
> > > (I didn't mention C23, since it's UB, so anything conforms.)
> > >
> > >
> > > Have a lovely day!
> > > Alex
> > >
> > > --
> > > <https://www.alejandro-colomar.es/>
> >
> > If you've managed to make it this far, congratulations. We probably
> > still need to open bugs against POSIX to have POSIX-2024-TC1 improve
> > any ambiguous wording, and taking into account whatever the C
> > committee may decide to do with Alejandro's proposals for post-C23
> > behaviors, and whether glibc is willing to make realloc(non_null, 0)
> > allocate in the same manner as malloc(0) rather than being a hidden
> > call to free().
>
> As always, I have trouble with using the Austin group interface. If
> you're in Denver, this is another thing you could help me with. :)
>
> > I don't know if I answered all of your questions, or raised even more,
> > but you have your work cut out for you before declaring the man pages
> > good enough.
>
> Again, thanks a lot!!
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:45 ` Alejandro Colomar
@ 2025-06-19 23:53 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-19 23:53 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 27498 bytes --]
On Fri, Jun 20, 2025 at 01:45:28AM +0200, Alejandro Colomar wrote:
> On Fri, Jun 20, 2025 at 01:38:14AM +0200, Alejandro Colomar wrote:
> > Hey Eric!
> >
> > Thanks a lot for the detailed reply! Comments below.
> >
> > On Thu, Jun 19, 2025 at 01:31:06PM -0500, Eric Blake wrote:
> > > On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> > > > Hi Rich, Elliott,
> > > >
> > > > On Wed, Jun 18, 2025 at 12:35:50PM -0400, Rich Felker wrote:
> > > > > On Wed, Jun 18, 2025 at 11:20:54AM -0400, enh wrote:
> > > > > > On Tue, Jun 17, 2025 at 5:58 PM Alejandro Colomar <alx@kernel.org> wrote:
> > > > > > >
> > > > > > > Hi Elliott, Florian,
> > > > > > >
> > > > > > > glibc and Bionic are non-conforming to POSIX.1-2024. The fix that we're
> > > > > > > proposing would make them conforming. Does conformance to POSIX.1-2024
> > > > > > > mean something to you?
> > > > > >
> > > > > > not when POSIX screwed up and made a change that made most of the
> > > > > > existing implementations non-conformant, no. that sounds like a POSIX
> > > > > > bug to me...
> > > >
> > > > Not most. Only two POSIX implementations, plus Windows. And the
> > > > solution is easy: fix the implementations. There have been no
> > > > regression reports in gnulib since we fixed it last year.
> > >
> > > Speaking as someone who participated in the POSIX standardization
> > > process, I'm trying to pinpoint exactly which statements of which
> > > versions of which standards you are claiming as nonconformance.
> > >
> > > First, a disclaimer: because this thread has been very vocal, I
> > > brought the topic up in today's Austin Group meeting. The members of
> > > the group on the phone call remember _specifically_ trying to permit
> > > existing glibc behavior (where realloc(p, 0) does NOT allocate), while
> > > still jugging competing wording from the C standards, although we will
> > > be the first to admit that we would not be surprised if the resulting
> > > efforts are still not clear enough to be unambiguous. I mentioned in
> > > the meeting that I would attempt to follow up on these threads to see
> > > what, if anything, the Austin Group may need to do to assist in the
> > > discussion.
> >
> > Thanks! I'm in Denver for the Open Source Summit and LSS. If you'll
> > be around next week, we can have a chat in person, which might be more
> > useful. I'd like to have a long conversation about this.
> >
> > > Next, my overarching question. Is this about "realloc(non_null, 0)",
> > > "realloc(NULL, 0)", or both? As the two are very distinct, I want to
> > > make sure we are talking about the same usage patterns. For the rest
> > > of this email, I'm assuming that your complaints are solely about
> > > "realloc(non_null, 0)" - if I'm wrong, it may change the analysis done
> > > below.
> >
> > Yup, it's about realloc(non_null, 0). r(NULL,0) is fine.
> >
> > > Now, on to some code archeology. In today's glibc source code, I see
> > > this telling comment in malloc/malloc.c, making it clear that glibc
> > > folks are aware that realloc(non_null, 0) has two useful behaviors,
> > > and that glibc picks the behavior that does NOT behave consistently
> > > with malloc(0), because of back-compat guarantees:
> > >
> > > /*
> > > 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.
> > > */
> >
> > That comment is wrong. "common historical practice" is that realloc(3)
> > is consistent with malloc(3). This is true since the days of Unix V7.
> > I don't know what they were referring to. Maybe the behavior introduced
> > in SysVr2's -lmalloc which was later standardized in the SVID by AT&T?
> > That was never common, since all existing default (-lc) realloc(3)
> > implementations behaved as if realloc(p, 1). You had to use the
> > -lmalloc library to get it to return NULL and free the object.
> >
> > See <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>.
> >
> > > More reading: https://www.austingroupbugs.net/view.php?id=400 shows
> > > where earlier POSIX missed that C90 to C99 changed what was permitted
> > > (and apparantly in a way to render glibc's implementation
> > > non-conforming), and that's part of what drove the POSIX folks to ask
> > > the C standard to improve the wording. POSIX 2024 is based on C17,
> > > but Nick Stoughton was regularly communicating between both C and
> > > POSIX groups on what wording(s) were being floated around, in order to
> > > try and make it so that glibc would not have to change behavior, but
> > > at the same time trying to make it possible for applications to be
> > > able to make wise runtime decisions on how to use realloc that would
> > > not leak memory or risk dereferencing a NULL pointer if not careful.
> > >
> > > https://sourceware.org/bugzilla/show_bug.cgi?id=12547 shows where
> > > glibc has, in the past, refused to change behavior on the grounds that
> > > the standards were buggy. If the standards are still buggy, the best
> > > course of action is to open a bug against them.
> >
> > All standards since C89 have been buggy. If you are pedantic reading
> > C89, the BSDs and all the historic implementations back to the original
> > Unix V7 are non-conforming:
> >
> > <https://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4>
> >
> > Which says:
> >
> > | If size is zero and ptr is not a null pointer, the object it points to
> > | is freed.
> >
> > It's not clear whether this means that the whole action of realloc(p,0)
> > is to free(3) the pointer, or if it can also allocate a new object.
> > Under the former interpretation, the standard is at odds with reality.
> > Under the latter interpretation, I'd interpret it as saying that
> > realloc(p,0) cannot fail (and thus must free(p)), which would be an
> > interesting guarantee. I guess we'll never know what was the intended
> > reading.
> >
> > C99 changed the specification, probably because of how ambiguous it was.
> >
> > glibc was also buggy, as it differed from every other Unix-like system.
> > All Unix systems behaved as if free(p) and malloc(n). glibc is the only
> > one that didn't follow this obvious consistency rule.
> >
> > So, both are bogus.
> >
> > > Also relevant are these documents
> > > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
> > > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
> > >
> > > > > > (like i said, i care greatly about actual shipping code. a standard is
> > > > > > interesting for green-field stuff, but when it's at odds with reality
> > > > > > it's often worse to try to adapt than just ignore the stupidity/report
> > > > > > the bug and get it changed back.)
> > > >
> > > > It's ironic that the standard should have never said that, because prior
> > > > to the existence of ANSI C and POSIX, all existing systems behaved like
> > > > the current POSIX specification. It was a consequence of the horrible
> > > > wording of the standards, that glibc was written so badly, by following
> > > > a bogus specification, when it should have been made compatible with the
> > > > existing systems.
> > >
> > > POSIX was originally released in 1988, before C90. glibc 1.0 came out
> > > in 1992. I am not sure when glibc first cared about whether trending
> > > towards POSIX compliance mattered, although I do know that in the
> > > early days, Ulrich would very adamently argue along the lines of
> > > (paraphrased) "if the standards don't match common sense, then we
> > > don't care about the standards".
> >
> > It seems Ulrich didn't follow that in this case. I don't know who wrote
> > the original realloc(3) in glibc. Was it RMS? It would be interesting
> > to know how they came up with that implementation. If anyone knows who
> > wrote it and why, please CC them.
> >
> > I don't have a copy of POSIX.1-1988, nor of any other POSIX.1 before
> > POSIX.1-2001. What do they say for realloc(3)?
> >
> > > > Thus, this is a historical bug in ISO C, POSIX, which at least has been
> > > > finally fixed in POSIX.
> > >
> > > The fact that the wording has changed across multiple versions of C
> > > and POSIX is indeed evidence that getting a specification that people
> > > are happy with is difficult. What is harder is the decision of
> > > whether the bug is in the standard (for not documenting reality) or in
> > > the implementations (for not doing what the standard rightfully
> > > requests), or even both. And "what people are happy with" differs on
> > > who you ask - wording that permits disparate libc behavior is nicer to
> > > the libraries (they don't have to change) but meaner to application
> > > writers (the construct is not portable, so it is safer to avoid the
> > > construct altogether rather than worry about which libraries have
> > > which behaviors); whereas wording that locks down behavior is nicer to
> > > applications (if I write this, it should work regardless of platform,
> > > and if it doesn't, the standard exists as leverage to get libc fixed)
> > > but meaner to libraries (forcing the library to version its symbols to
> > > change behavior for newer standards while still providing ABI
> > > stability guarantees to older apps that depend on the old behavior is
> > > not cheap).
> >
> > As can be seen from the change in gnulib, the only possible issues from
> > migrating from the current glibc behavior to the musl behavior is a few
> > leaks in cases where the programmer calls realloc(p,0) ignoring the
> > return value. Those leaks would leak 0 bytes plus the metadata.
> >
> > A solution for those leaks would be to add a diagnostic for calls to
> > realloc(3) where the return value is unused. And even if those aren't
> > fully solved, they're leaks of a few bytes. There's nothing that should
> > cause real issues.
> >
> > But the glibc maintainers mentioned that they're investigating about it
> > in distros, so I guess we'll eventually have the results of their
> > investigation.
> >
> > > The sad fact of the matter is that _because_ it there are so many
> > > differences in opinions, the C23 action of making realloc(p,0)
> > > undefined is probably the simplest course that could be agreed on
> > > (don't ever do that in your code, because you can't guarantee the
> > > results), but simultaneously annoying to end users (because it is
> > > undefined, rather than implementation-defined or unspecified, a
> > > compiler can "optimize" your code to do WHATEVER IT WANTS - which
> > > really means you CANNOT ever reliably call realloc(p,0) if your
> > > compiler is aiming for C23).
> >
> > Indeed. I think the move from C17 to C23 was good.
> >
> > The issue with C17 is that it is very similar to POSIX.1-2008, but since
> > ISO C doesn't require that errno is set when the pointer is not freed,
> > it is impossible to portably determine if the input pointer was freed
> > after realloc(p,0). This is not an issue in POSIX.1, though, since it
> > can and does require that errno is set if the input pointer is not
> > freed.
>
> Self correction: POSIX.1-2008 .. POSIX.1-2024 does allow setting errno
> and freeing the input pointer, as Paul Eggert reminded. AIX does this.
> This is brain damaged, and makes it also impossible to portably
> determine whether the pointer was freed after realloc(p,0).
>
> Thus, declaring it UB in POSIX.1 would also be an improvement.
Hmm, self-correction correction: AIX sets errno to EINVAL, not ENOMEM,
and thus it is still possible to portably handle this, I guess? I don't
promise this works, but it could work:
new = realloc(old, 0);
if (new == NULL) {
if (errno == ENOMEM) {
free(old);
goto fail;
}
goto fail;
}
free(new);
>
> >
> > Because it was impossible to determine whether r(p,0) has freed p after
> > returning NULL in C17, it was effectively UB. So, I consider C23 to be
> > a minor change from C17, and one which clarifies that it is UB, because
> > it already was before.
> >
> > POSIX.1 is not limited by this limitation of ISO C.
> >
> > > On top of that, the POSIX standard usually defers to a (fixed version)
> > > of C, but does have the liberty to impose well-defined behavior even
> > > where the corresponding C standard left things undefined (for example,
> > > POSIX 2017 was able to demand that a POSIX system can cast function
> > > pointers to and from void* in order to implement dlsym(), even though
> > > C99 said that was undefined). Put another way, just because C23 has
> > > changed realloc(p,0) to be undefined does NOT require a future version
> > > of POSIX to do likewise when it finally moves to a newer C than C17.
> > > But at the same time, POSIX is unlikely to make things strict if it
> > > risks alienating existing implementations; if glibc changes behavior,
> > > that would go a long way towards POSIX changing wording to be
> > > stricter.
> >
> > Indeed; I think POSIX.1 doesn't need to make this undefined, and
> > shouldn't.
> >
> > > > BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
> > > > following bug <https://www.austingroupbugs.net/view.php?id=400>.
> > > >
> > > > The motivation, from what I can read there, seems to be that C99 already
> > > > made POSIX.1 non-conforming, and this fix was intended to conform to
> > > > C99.
> > > >
> > > > Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> > > > wording from C99, either; it allows weird stuff: it allows an
> > > > implementation where malloc(0) returns NULL and realloc(p,0) non-null
> > > > (so, the opposite of glibc).
> > > >
> > > > C11 is essentially identical to C99 in that regard, so glibc is also
> > > > non-conforming to C11.
> > > >
> > > > C17 changed to something very weird. It seems to me that glibc is
> > > > conforming again to C17, but it also seems to me that it's impossible to
> > > > write code that uses realloc(p,0) in a portable way with this
> > > > specification. I think it's a good thing that C23 removed that crap.
> > > >
> > > >
> > > > Here's a summary of conformance to standards:
> > > >
> > > > glibc conforms to:
> > > > - SysVr4
> > > > - ISO C89
> > >
> > > I don't (currently) have a copy of C89 handy in front of me to quote
> > > chapter and verse for this one, beyond what was already quoted in
> > > Austin Group bug 400.
> >
> > Here's the draft, in various formats:
> >
> > <https://port70.net/~nsz/c/c89/>
> >
> > > > - ISO C17
> > >
> > > This one I _can_ quote. 7.22.3.4:
> > >
> > > "If size is zero and memory for the new object is not allocated, it is
> > > implementation-defined whether the old object is deallocated."
> > >
> > > glibc documents that "realloc(non_null, 0)" deallocates non_null.
> > > Therefore it is compliant. But that wording is still unfriendly to
> > > users - there is no way to programmatically query the runtime what
> > > behavior the implementation defined.
> >
> > > > - XPG4
> > > >
> > > > glibc doesn't conform to:
> > > > - SysIII
> > > > - SysV
> > > > - SysVr2
> > > > - SysVr3
> > > > - SVID Issue 2
> > > > - SVID Issue 3
> > > > - The X/Open System V Specification
> > > > - ISO C99
> > >
> > > Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
> > > a null pointer, the realloc function behaves like the malloc function
> > > for the specified size." but NO wording about when ptr is non-null but
> > > size is 0. As best I can tell, silence on the part of C99 means that
> > > the standard is unspecified, and therefore glibc can do whatever it
> > > wants and still claim compliance. But I'm open to correction if you
> > > can quote the exact statement for why you claim glibc is non-compliant
> > > here.
> >
> > <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
> >
> > I'll quote the entire text:
> >
> > Description
> >
> > 2 The realloc function
> > deallocates the old object pointed to by ptr and
> > returns a pointer to a new object that
> > has the size specified by size.
> > [...] // talks about the contents
> >
> > 3 If ptr is a null pointer, [...].
> > Otherwise,
> > if ptr does not match a pointer earlier returned by
> > the calloc, malloc, [...], the behavior is undefined.
> > If memory for the new object cannot be allocated,
> > the old object is not deallocated and its value is unchanged.
> >
> > Returns
> >
> > 4 The realloc function returns a pointer to the new object
> > (which may have the same value as a pointer to the old object),
> > or a null pointer if the new object could not be allocated.
> >
> > IMO, paragraph 4 rules out the possibility of returning a null pointer
> > on success.
> >
> > Also, while it doesn't specify what happens in the case of size 0
> > explicitly, it mentions in paragraph 2 what happens for all sizes:
> > it returns a pointer to a new object that has the size specified by
> > size --which in this case is 0 bytes--.
> >
> > This wording of C99 was relatively good, and fixes the problems from
> > C89 which had turned all historical implementations into
> > non-conformance. C99 seems to restore the common historical behavior of
> > realloc(3), turning glibc non-conforming as a consequence.
> >
> > > > - ISO C11
> > >
> > > This wording appears to match C99.
> >
> > Agree.
> >
> > > > - POSIX.1-2001
> > >
> > > This one defers to C89 anywhere that it is not explicitly documenting
> > > with CX shading.
> >
> > Ahh, I had thought it would defer to C99 because it's older, but I guess
> > it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
> > corrected, and glibc conforms to POSIX.1-2001.
> >
> > > It adds CX shading to document the use of
> > > errno=ENOMEM on allocation failures, but otherwise omits shading when
> > > it states:
> > >
> > > "If size is 0 and ptr is not a null pointer, the object pointed to is
> > > freed."
> > >
> > > which sounds like glibc behavior. But without double-checking C89, it
> > > is hard to say whether POSIX accidentally diverged from C89 in
> > > allowing glibc as compliant.
> > >
> > > > - POSIX.1-2008
> > >
> > > This version of POSIX defers to C99, but still states "If size is 0
> > > and ptr is not a null pointer, the object pointed to is freed."
> >
> > I don't have a copy of POSIX.1-2008, but I assume the text is identical
> > to POSIX.1-2001, except that it now defers to C99. Since C99 rules out
> > the possibility of returning a null pointer on success (7.20.3.4p4),
> > and POSIX.1-2008 doesn't seem to have shaded text to extend it, it is
> > bound by the C99 restriction. The allowances provided by POSIX.1-2008
> > are invalidated as unintentional.
> >
> > > without CX shading, even though C99 does NOT have the same wording as
> > > C89. You could argue that this statement should be ignored since it
> > > lacks CX shading and does not match any statement in C99.
> >
> > Indeed.
> >
> > > But even
> > > so, unless you can demonstrate chapter-and-verse how glibc fails to
> > > comply with C99, you also have a hard time convincing me that glibc
> > > does not comply with POSIX 2008. And this issue was why Austin Group
> > > bug 400 was created.
> >
> > I alreayd mentioned it above, but I'll copy for completeness:
> >
> > n1256::7.20.3.4p4.
> >
> > <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4p4>
> >
> > The realloc function returns a pointer to the new object
> > (which may have the same value as a pointer to the old object),
> > or a null pointer if the new object could not be allocated.
> >
> > This seems to preclude the possibility of returning NULL on success.
> >
> > Also, this sentence is complemented by n1256::7.20.3.4p3, last sentence:
> >
> > If memory for the new object cannot be allocated,
> > the old object is not deallocated and its value is unchanged.
> >
> > This sentence rules that if the implementation could consider that their
> > returning a null pointer is because they decide that they can't
> > allocate 0 bytes (this would be a valid interpretation), then they are
> > forced to leave the pointer not deallocated. glibc frees the object,
> > and thus it is not complying with this, and we must consider that glibc
> > has succeeded in the allocation, which brings us back to p4.
> >
> > > No mention of POSIX.1-2013?
> >
> > I didn't have a copy of that. Thanks! I'll add it to the list of
> > non-conforming standards.
> >
> > > But just in case you're keeping track,
> > > that is the version where Bug 400 was applied, and the text changed
> > > to: "If the size of the space requested is zero, the behavior shall be
> > > implementation-defined: either a null pointer is returned, or the
> > > behavior shall be as if the size were some non-zero value, except that
> > > the returned pointer shall not be used to access an object." But it
> > > also has the problem that it requires "If size is 0, either: A null
> > > pointer shall be returned <CX>and errno set to an
> > > implementation-defined value</CX>. ..."
> > >
> > > which glibc does NOT comply with. realloc(non_null,0) returns NULL
> > > _without_ setting errno, precisely because it DID free the object
> > > successfully. This requirement in POSIX 2013 is an explicit extension
> > > not mentioned in C99, AND it was quickly pointed out that it forbids
> > > glibc behavior, so:
> > >
> > > > - POSIX.1-2017
> > >
> > > This one additionally applies Bug 526 and 688, to try and clean up
> > > wording differences from C99, in particular clarifying whether errno
> > > has to be set when "realloc(non_null, 0)" frees a pointer:
> > >
> > > https://www.austingroupbugs.net/view.php?id=526
> > > https://www.austingroupbugs.net/view.php?id=688
> > >
> > > where the wording is once again relaxed to "If the size of the space
> > > requested is zero, the behavior shall be implementation-defined:
> > > either a null pointer is returned, or the behavior shall be as if the
> > > size were some non-zero value, except that the behavior is undefined
> > > if the returned pointer is used to access an object. ... If size is 0,
> > > either: A null pointer shall be returned <CX>and, if ptr is not a null
> > > pointer, errno shall be set to an implementation-defined value</CX>."
> > >
> > > which should once again allow glibc to be deemed compliant. At the
> > > same time, the Austin Group was trying to get C17 fixed; that fix
> > > turned out to be ugly, so the C committed tried again in C23.
> >
> > The last sentence clearly states that if size is 0 and ptr is non-null
> > and a null pointer is returned, then *errno shall be set*. glibc
> > doesn't set errno, and thus does not conform. Can you please clarify
> > how you consider glibc's behavior to comply with that last sentence from
> > your quote? For completeness, the sentence I'm talking about is
> >
> > If size is 0,
> > either: A null pointer shall be returned <CX>and, if ptr is not a null
> > pointer, errno shall be set to an implementation-defined value</CX>."
> >
> > > > - POSIX.1-2024
> > >
> > > Here, the standard defers to C17 rather than C99, but adds a lot more
> > > CX shading. Given the changes between C99 and C17, POSIX tried to
> > > match. Unfortunately, the DESCRIPTION section lost any mention of
> > > non_null pointer plus 0 size, leaving only the RETURN VALUE secion,
> > > which now uses the wording entirely in CX shading:
> > >
> > > "If size is 0, or either nelem or elsize is 0, either: • A null
> > > pointer shall be returned and, if ptr is not a null pointer, errno
> > > shall be set to [EINVAL]. • A pointer to the allocated space shall be
> > > returned, and the memory object pointed to by ptr shall be freed. The
> > > application shall ensure that the pointer is not used to access an
> > > object."
> > >
> > > Despite the efforts of the Austin Group to not break back-compat, that
> > > one clearly looks like glibc is not compliant (glibc returns NULL and
> > > does NOT set errno to EINVAL). And if I recall the conversations, we
> > > knew at the time of POSIX 2024 that C23 would be marking
> > > realloc(non_null, 0) as undefined behavior, and wanted to capture that
> > > directly rather than depending on C17, but we may have failed in our
> > > efforts.
> >
> > TBH, I think POSIX.1-2024 has a decent specification. I'd prefer the
> > one from C99 and C11, but it is decent.
> >
> > > > Conformance to POSIX.1-2001 and POSIX.1-2008 is not clear. While glibc
> > > > conforms to the wording of these standards, these standards have the
> > > > following header in the realloc(3) specification:
> > > >
> > > > The functionality described on this reference page is aligned
> > > > with the ISO C standard. Any conflict between the requirements
> > > > described here and the ISO C standard is unintentional. This
> > > > volume of IEEE Std 1003.1-2001 defers to the ISO C standard.
> > > >
> > > > Which means that POSIX's permissive wording is unintentional, and the
> > > > ISO C99 wording is the one that matters, so glibc is non-conforming.
> > >
> > > The conflicts are unintentional only when <CX> shading is not
> > > explicitly present.
> >
> > And POSIX.1-2001 .. POSIX.1-2008 doesn't have any CX shading.
> >
> > > > (I didn't mention C23, since it's UB, so anything conforms.)
> > > >
> > > >
> > > > Have a lovely day!
> > > > Alex
> > > >
> > > > --
> > > > <https://www.alejandro-colomar.es/>
> > >
> > > If you've managed to make it this far, congratulations. We probably
> > > still need to open bugs against POSIX to have POSIX-2024-TC1 improve
> > > any ambiguous wording, and taking into account whatever the C
> > > committee may decide to do with Alejandro's proposals for post-C23
> > > behaviors, and whether glibc is willing to make realloc(non_null, 0)
> > > allocate in the same manner as malloc(0) rather than being a hidden
> > > call to free().
> >
> > As always, I have trouble with using the Austin group interface. If
> > you're in Denver, this is another thing you could help me with. :)
> >
> > > I don't know if I answered all of your questions, or raised even more,
> > > but you have your work cut out for you before declaring the man pages
> > > good enough.
> >
> > Again, thanks a lot!!
> >
> >
> > Have a lovely day!
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es/>
>
>
>
> --
> <https://www.alejandro-colomar.es/>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
2025-06-19 23:45 ` Alejandro Colomar
@ 2025-06-20 2:11 ` Vincent Lefevre
2025-06-20 11:58 ` Alejandro Colomar
2025-06-20 13:42 ` Mark Harris
` (3 subsequent siblings)
5 siblings, 1 reply; 143+ messages in thread
From: Vincent Lefevre @ 2025-06-20 2:11 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
On 2025-06-20 01:37:58 +0200, Alejandro Colomar wrote:
> On Thu, Jun 19, 2025 at 01:31:06PM -0500, Eric Blake wrote:
> > On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> > > glibc doesn't conform to:
> > > - SysIII
> > > - SysV
> > > - SysVr2
> > > - SysVr3
> > > - SVID Issue 2
> > > - SVID Issue 3
> > > - The X/Open System V Specification
> > > - ISO C99
> >
> > Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
> > a null pointer, the realloc function behaves like the malloc function
> > for the specified size." but NO wording about when ptr is non-null but
> > size is 0. As best I can tell, silence on the part of C99 means that
> > the standard is unspecified, and therefore glibc can do whatever it
> > wants and still claim compliance. But I'm open to correction if you
> > can quote the exact statement for why you claim glibc is non-compliant
> > here.
>
> <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
>
> I'll quote the entire text:
>
> Description
>
> 2 The realloc function
> deallocates the old object pointed to by ptr and
> returns a pointer to a new object that
> has the size specified by size.
> [...] // talks about the contents
>
> 3 If ptr is a null pointer, [...].
> Otherwise,
> if ptr does not match a pointer earlier returned by
> the calloc, malloc, [...], the behavior is undefined.
> If memory for the new object cannot be allocated,
> the old object is not deallocated and its value is unchanged.
>
> Returns
>
> 4 The realloc function returns a pointer to the new object
> (which may have the same value as a pointer to the old object),
> or a null pointer if the new object could not be allocated.
>
> IMO, paragraph 4 rules out the possibility of returning a null pointer
> on success.
Perhaps not if size is 0, see below.
> Also, while it doesn't specify what happens in the case of size 0
> explicitly, it mentions in paragraph 2 what happens for all sizes:
> it returns a pointer to a new object that has the size specified by
> size --which in this case is 0 bytes--.
You should also consider the end of 7.20.3p1 (which applies to calloc,
malloc and realloc):
If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that
the returned pointer shall not be used to access an object.
Consider the case "a null pointer is returned" with realloc(non_null,0).
I can see 2 possible interpretations:
1. This is always a failure (corresponding to "a null pointer if the
new object could not be allocated" in 7.20.3.4p4). But I doubt that
this is the intended behavior, as there would be major memory leaks
in practice with such an implementation and would make such a call
useless.
2. This means that the memory is entirely freed, which is a success.
This makes your assumption "paragraph 4 rules out the possibility of
returning a null pointer on success" above wrong. However, in order
to know whether a null pointer means a success (memory entirely freed)
or a failure (for the case "the behavior is as if the size were some
nonzero value"), the application would need to know which kind of
implementation it is (e.g. guessed by tools like configure tests).
--
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 18:31 ` Eric Blake
2025-06-19 23:37 ` Alejandro Colomar
@ 2025-06-20 5:55 ` Paul Eggert
2025-06-20 10:04 ` Vincent Lefevre
1 sibling, 1 reply; 143+ messages in thread
From: Paul Eggert @ 2025-06-20 5:55 UTC (permalink / raw)
To: Eric Blake, Alejandro Colomar
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Robert Seacord, Bruno Haible, bug-gnulib, JeanHeyd Meneide,
Thorsten Glaser
On 2025-06-19 11:31, Eric Blake wrote:
> glibc folks are aware that realloc(non_null, 0) has two useful behaviors,
> and that glibc picks the behavior that does NOT behave consistently
> with malloc(0), because of back-compat guarantees:
Thanks for the detailed summary. Here are a few more details. In this
summary "realloc(p,0)" assumes p is nonnull.
As I understand it:
(a) These guarantees are for compatibility with glibc 2.2+. In older
glibc versions realloc(p,0) behaved like (free(p),malloc(0)).
(b) Ulrich Drepper changed glibc 2.2 realloc(p,0) after Andreas Jaeger
told him[1] that draft C99 and UNIX98 required realloc(p,0) to free(p).
Conformance to these standards was the only motivation given for the
glibc change.
(c) Ulrich's change[2] to glibc was to make realloc(p,0) equivalent to
(free(p),0). Draft C99 and UNIX98 did not require this, and Ulrich could
have made realloc(p,0) continue to be equivalent to (free(p),malloc(0)).
The email thread for [1] indicates that people were confused about what
C89/C99 and UNIX98 required; people seemed to think mistakenly that, for
example, a somewhat-perverse implementation where realloc(p,0) always
returns p would not conform to C89/C99 and UNIX98.
> getting a specification that people are happy with is difficult.
The disagreement mostly came from two camps many years ago. One camp
(call it "traditional") wanted zero-size allocations a la 7th Edition
UNIX; the other camp (call it "SysV") wanted malloc(0) and
realloc(...,0) to fail (ideally with errno==EINVAL) because zero-sized
objects are problematic in C.
Although the traditional camp mostly won in practice, the SysV camp
arranged for POSIX to allow either traditional or SysV behavior without
requiring the implementation's behavior to be consistently traditional
or consistently SysV. This has resulted in a POSIX spec that seems to be
getting more confusing in each iteration, with POSIX.1-2024 prohibiting
current glibc behavior (apparently by accident).
[1]: https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html
[2]:
https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 5:55 ` Paul Eggert
@ 2025-06-20 10:04 ` Vincent Lefevre
0 siblings, 0 replies; 143+ messages in thread
From: Vincent Lefevre @ 2025-06-20 10:04 UTC (permalink / raw)
To: Paul Eggert
Cc: Eric Blake, Alejandro Colomar, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Robert Seacord, Bruno Haible, bug-gnulib, JeanHeyd Meneide,
Thorsten Glaser
On 2025-06-19 22:55:32 -0700, Paul Eggert wrote:
> Thanks for the detailed summary. Here are a few more details. In this
> summary "realloc(p,0)" assumes p is nonnull.
>
> As I understand it:
>
> (a) These guarantees are for compatibility with glibc 2.2+. In older glibc
> versions realloc(p,0) behaved like (free(p),malloc(0)).
>
> (b) Ulrich Drepper changed glibc 2.2 realloc(p,0) after Andreas Jaeger told
> him[1] that draft C99 and UNIX98 required realloc(p,0) to free(p).
> Conformance to these standards was the only motivation given for the glibc
> change.
BTW, at that time, the lack of consistency between realloc(...,0)
and malloc(0) was known and not regarded as an issue:
https://sourceware.org/pipermail/libc-alpha/1999-April/002398.html
> (c) Ulrich's change[2] to glibc was to make realloc(p,0) equivalent to
> (free(p),0). Draft C99 and UNIX98 did not require this, and Ulrich could
> have made realloc(p,0) continue to be equivalent to (free(p),malloc(0)).
[...]
Indeed, at least the N843 C9x draft (August 1998) did not require this.
It had new text, saying:
If the realloc function returns a null pointer when size is zero and
ptr is not a null pointer, the object it pointed to has been freed.
There are also some changes for realloc proposed in
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm
(Final CD Ballot for FCD 9899), but nothing for the case of size = 0.
--
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 2:11 ` Vincent Lefevre
@ 2025-06-20 11:58 ` Alejandro Colomar
2025-06-20 14:36 ` Vincent Lefevre
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-20 11:58 UTC (permalink / raw)
To: Vincent Lefevre, Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 4572 bytes --]
Hi Vincent,
On Fri, Jun 20, 2025 at 04:11:23AM +0200, Vincent Lefevre wrote:
> On 2025-06-20 01:37:58 +0200, Alejandro Colomar wrote:
> > On Thu, Jun 19, 2025 at 01:31:06PM -0500, Eric Blake wrote:
> > > On Wed, Jun 18, 2025 at 09:04:02PM +0200, Alejandro Colomar wrote:
> > > > glibc doesn't conform to:
> > > > - SysIII
> > > > - SysV
> > > > - SysVr2
> > > > - SysVr3
> > > > - SVID Issue 2
> > > > - SVID Issue 3
> > > > - The X/Open System V Specification
> > > > - ISO C99
> > >
> > > Here, section 7.20.3.4 is relevant. In there, I see wording "If ptr is
> > > a null pointer, the realloc function behaves like the malloc function
> > > for the specified size." but NO wording about when ptr is non-null but
> > > size is 0. As best I can tell, silence on the part of C99 means that
> > > the standard is unspecified, and therefore glibc can do whatever it
> > > wants and still claim compliance. But I'm open to correction if you
> > > can quote the exact statement for why you claim glibc is non-compliant
> > > here.
> >
> > <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
> >
> > I'll quote the entire text:
> >
> > Description
> >
> > 2 The realloc function
> > deallocates the old object pointed to by ptr and
> > returns a pointer to a new object that
> > has the size specified by size.
> > [...] // talks about the contents
> >
> > 3 If ptr is a null pointer, [...].
> > Otherwise,
> > if ptr does not match a pointer earlier returned by
> > the calloc, malloc, [...], the behavior is undefined.
> > If memory for the new object cannot be allocated,
> > the old object is not deallocated and its value is unchanged.
> >
> > Returns
> >
> > 4 The realloc function returns a pointer to the new object
> > (which may have the same value as a pointer to the old object),
> > or a null pointer if the new object could not be allocated.
> >
> > IMO, paragraph 4 rules out the possibility of returning a null pointer
> > on success.
>
> Perhaps not if size is 0, see below.
>
> > Also, while it doesn't specify what happens in the case of size 0
> > explicitly, it mentions in paragraph 2 what happens for all sizes:
> > it returns a pointer to a new object that has the size specified by
> > size --which in this case is 0 bytes--.
>
> You should also consider the end of 7.20.3p1 (which applies to calloc,
> malloc and realloc):
>
> If the size of the space requested is zero, the behavior is
> implementation-defined: either a null pointer is returned, or the
> behavior is as if the size were some nonzero value, except that
> the returned pointer shall not be used to access an object.
Right. I missed that part.
So, C99 and C11 allow realloc(p,0) to return a null pointer.
>
> Consider the case "a null pointer is returned" with realloc(non_null,0).
> I can see 2 possible interpretations:
>
> 1. This is always a failure (corresponding to "a null pointer if the
> new object could not be allocated" in 7.20.3.4p4). But I doubt that
> this is the intended behavior, as there would be major memory leaks
> in practice with such an implementation and would make such a call
> useless.
>
> 2. This means that the memory is entirely freed, which is a success.
> This makes your assumption "paragraph 4 rules out the possibility of
> returning a null pointer on success" above wrong. However, in order
> to know whether a null pointer means a success (memory entirely freed)
> or a failure (for the case "the behavior is as if the size were some
> nonzero value"), the application would need to know which kind of
> implementation it is (e.g. guessed by tools like configure tests).
To disambiguate, we need to read
<https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
First p4:
4 The realloc function returns a pointer to the new object
(which may have the same value as a pointer to the old object),
or a null pointer if the new object could not be allocated.
So, a null pointer, which is allowed for r(p,0), means that "the new
object could not be allocated".
And then we read the last sentence of p3:
3 [...]
If memory for the new object cannot be allocated,
the old object is not deallocated and its value is unchanged.
So, the input pointer must not be freed.
I'd say glibc is non-conforming, even if this could possibly have been
unintended by the C Committee.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
2025-06-19 23:45 ` Alejandro Colomar
2025-06-20 2:11 ` Vincent Lefevre
@ 2025-06-20 13:42 ` Mark Harris
2025-06-20 16:18 ` Joseph Myers
2025-06-20 16:30 ` Eric Blake
` (2 subsequent siblings)
5 siblings, 1 reply; 143+ messages in thread
From: Mark Harris @ 2025-06-20 13:42 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
Alejandro Colomar wrote:
> I don't have a copy of POSIX.1-1988, nor of any other POSIX.1 before
> POSIX.1-2001. What do they say for realloc(3)?
The first POSIX edition with a description of realloc is POSIX.1-2001.
Earlier editions only list it as a function provided by the C Language
Standard.
POSIX.1-1988 Appendix A states:
"This IEEE Std 1003.1-1988 is intended to complement others that
together would provide a comprehensive Open System Environment."
...
"This document refers to the C Language Standard effort presently
under development by Technical Committee X3J11 of the Accredited
Standards Committee X3—Information Processing Systems. The X3J11 and
1003.1 groups have been cooperating to ensure that the standards are
complementary and not overlapping."
There were three editions of POSIX prior to POSIX.1-2001:
IEEE Std 1003.1-1988:
https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub151-1.pdf
IEEE Std 1003.1-1990:
https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub151-2.pdf
IEEE Std 1003.1-1996 = 1003.1-1990 + 1003.1b-1993 + 1003.1c-1995 +
1003.1i-1995 (i.e., same as the previous version plus real-time and
threading extensions; no impact on realloc)
>
> I don't have a copy of POSIX.1-2008 ...
https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html
> > No mention of POSIX.1-2013?
>
> I didn't have a copy of that. ...
https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html
POSIX.1-2013 = POSIX.1-2008 + TC1;
TC1 includes the POSIX changes to realloc from bug 400.
https://austingroupbugs.net/view.php?id=400
https://open-std.org/JTC1/SC22/WG14/www/docs/dr_400.htm
- Mark
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 11:58 ` Alejandro Colomar
@ 2025-06-20 14:36 ` Vincent Lefevre
2025-06-20 14:56 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Vincent Lefevre @ 2025-06-20 14:36 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
On 2025-06-20 13:58:23 +0200, Alejandro Colomar wrote:
> On Fri, Jun 20, 2025 at 04:11:23AM +0200, Vincent Lefevre wrote:
> > You should also consider the end of 7.20.3p1 (which applies to calloc,
> > malloc and realloc):
> >
> > If the size of the space requested is zero, the behavior is
> > implementation-defined: either a null pointer is returned, or the
> > behavior is as if the size were some nonzero value, except that
> > the returned pointer shall not be used to access an object.
>
> Right. I missed that part.
>
> So, C99 and C11 allow realloc(p,0) to return a null pointer.
>
> > Consider the case "a null pointer is returned" with realloc(non_null,0).
> > I can see 2 possible interpretations:
> >
> > 1. This is always a failure (corresponding to "a null pointer if the
> > new object could not be allocated" in 7.20.3.4p4). But I doubt that
> > this is the intended behavior, as there would be major memory leaks
> > in practice with such an implementation and would make such a call
> > useless.
> >
> > 2. This means that the memory is entirely freed, which is a success.
> > This makes your assumption "paragraph 4 rules out the possibility of
> > returning a null pointer on success" above wrong. However, in order
> > to know whether a null pointer means a success (memory entirely freed)
> > or a failure (for the case "the behavior is as if the size were some
> > nonzero value"), the application would need to know which kind of
> > implementation it is (e.g. guessed by tools like configure tests).
>
> To disambiguate, we need to read
>
> <https://port70.net/~nsz/c/c99/n1256.html#7.20.3.4>
>
> First p4:
>
> 4 The realloc function returns a pointer to the new object
> (which may have the same value as a pointer to the old object),
> or a null pointer if the new object could not be allocated.
>
> So, a null pointer, which is allowed for r(p,0), means that "the new
> object could not be allocated".
>
> And then we read the last sentence of p3:
>
> 3 [...]
> If memory for the new object cannot be allocated,
> the old object is not deallocated and its value is unchanged.
>
> So, the input pointer must not be freed.
Yes, this is what I mentioned in interpretation 1 above (so with
memory leaks in practice, as applications call realloc(non_null,0)
to free memory, while memory will never be freed).
But I believe that this was not the intent of the C Committee.
If it were, the text would have been different, IMHO.
This is too late for a defect report anyway, as the current standard
is C23 and realloc(non_null,0) is now UB. But if one wants to avoid
the UB in a future standard, the above remarks need to be considered.
> I'd say glibc is non-conforming, even if this could possibly have been
> unintended by the C Committee.
Well, strictly speaking, with the UB, it is conforming (at least with
a non-null pointer). I'm not sure that speaking of conformity against
old standards makes sense if there is a suspicion of defect (and in
particular, if this has been resolved by a change in the current
standard, like here).
--
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 14:36 ` Vincent Lefevre
@ 2025-06-20 14:56 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-20 14:56 UTC (permalink / raw)
To: Vincent Lefevre, Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 1786 bytes --]
Hi Vincent,
On Fri, Jun 20, 2025 at 04:36:31PM +0200, Vincent Lefevre wrote:
> > So, the input pointer must not be freed.
>
> Yes, this is what I mentioned in interpretation 1 above (so with
> memory leaks in practice, as applications call realloc(non_null,0)
> to free memory, while memory will never be freed).
>
> But I believe that this was not the intent of the C Committee.
> If it were, the text would have been different, IMHO.
>
> This is too late for a defect report anyway, as the current standard
> is C23 and realloc(non_null,0) is now UB. But if one wants to avoid
> the UB in a future standard, the above remarks need to be considered.
There's a way forward that would prevent the leaks: force the musl
behavior: free the object, and return a non-null pointer, like
malloc(0). You'd still leak 0 bytes + metadata in a few rare cases, but
that's not a big deal.
> > I'd say glibc is non-conforming, even if this could possibly have been
> > unintended by the C Committee.
>
> Well, strictly speaking, with the UB, it is conforming (at least with
> a non-null pointer). I'm not sure that speaking of conformity against
> old standards makes sense if there is a suspicion of defect (and in
> particular, if this has been resolved by a change in the current
> standard, like here).
I think it is important in this case, because if glibc decided to
conform to older standards --and it very well could, just as gnulib
did--, the UB could be reverted, and users could use realloc(p,0) in the
future safely.
Now, the ball is in glibc. (Or the standard might unilaterally impose
that glibc be fixed, but it would be nicer if glibc would do so
voluntarily.)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 13:42 ` Mark Harris
@ 2025-06-20 16:18 ` Joseph Myers
0 siblings, 0 replies; 143+ messages in thread
From: Joseph Myers @ 2025-06-20 16:18 UTC (permalink / raw)
To: Mark Harris
Cc: Alejandro Colomar, Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
On Fri, 20 Jun 2025, Mark Harris wrote:
> There were three editions of POSIX prior to POSIX.1-2001:
Also the Trial-Use 1986 edition. (Appendix D, Comparison with Related
Systems, marks realloc with "**", where "Functions marked ** are
considered to be included in this standard by reference to the C
Information Bulletin July 1985 or subsequent standard for the C
programming language.)
--
Joseph S. Myers
josmyers@redhat.com
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
` (2 preceding siblings ...)
2025-06-20 13:42 ` Mark Harris
@ 2025-06-20 16:30 ` Eric Blake
2025-06-20 18:03 ` Alejandro Colomar
2025-06-20 19:41 ` Paul Eggert
2025-06-21 3:57 ` Sam James
2025-07-04 23:31 ` Alejandro Colomar
5 siblings, 2 replies; 143+ messages in thread
From: Eric Blake @ 2025-06-20 16:30 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
On Fri, Jun 20, 2025 at 01:37:58AM +0200, Alejandro Colomar wrote:
> Hey Eric!
>
> Thanks a lot for the detailed reply! Comments below.
Ditto.
> > Now, on to some code archeology. In today's glibc source code, I see
> > this telling comment in malloc/malloc.c, making it clear that glibc
> > folks are aware that realloc(non_null, 0) has two useful behaviors,
> > and that glibc picks the behavior that does NOT behave consistently
> > with malloc(0), because of back-compat guarantees:
> >
> > /*
> > 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.
> > */
>
> That comment is wrong. "common historical practice" is that realloc(3)
> is consistent with malloc(3). This is true since the days of Unix V7.
Careful. "common historical practice" can just as easily be read
along the lines of "common historical practice of glibc, independent
of other implementations" (since glibc does not inherit a code base
from other implementations). And "historical" also doesn't give a
time frame; if the comment was only written at a time where glibc
behavior had already been in place for many years, that could be
impetus for using "historical", even if it disregards even more
history pre-glibc from other implementatinos.
> I don't know what they were referring to. Maybe the behavior introduced
> in SysVr2's -lmalloc which was later standardized in the SVID by AT&T?
> That was never common, since all existing default (-lc) realloc(3)
> implementations behaved as if realloc(p, 1). You had to use the
> -lmalloc library to get it to return NULL and free the object.
At the time I wrote my mail, I had not researched when glibc made the
change. Paul's additional research proving that glibc 1.0 behaves
differently than glibc 2.2, and that glibc 2.2 changed behavior
because of a wording change in the C99 draft (whether or not that
change was strictly necessary, and whether or not the actual C99
required what glibc claimed it required by making the behavior
change), throws more fuel onto the fire.
Checking the provenance of that comment in glibc, I've further
determined:
malloc/malloc.c was rewritten in Jan 2002 by Wolfram Gloger (committed
by Ulrich) to borrow ideas from Doug Lea's malloc-2.7.0.c, in commit
fa8d436c (glibc-2.3).
- REALLOC_ZERO_BYTES_FREE was defined 1 at that time, with this comment:
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to
realloc with zero bytes should be the same as a call to free.
This is required by the C standard. Otherwise, since this malloc
returns a unique pointer for malloc(0), so does realloc(p, 0).
*/
- given the timing, C99 would have been the current C standard
Before that rewrite, the feature switch macro was merely defined
(rather than set to 1) as of commit 7c2b945e (April 1999, glibc
2.1.1), the commit by Andreas that Paul already mentioned, and
directly in response to C99 drafting efforts (remember, C99 was not
formally adopted until May 2000); I am less certain without more
research whether the wording that Andreas was referring to at the time
the feature knob was switched made it into the approved C99 unchanged,
or if that was still undergoing debates in the C committee. The
comment at that time read:
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to
realloc with zero bytes should be the same as a call to free.
Some people think it should. Otherwise, since this malloc
returns a unique pointer for malloc(0), so does realloc(p, 0).
*/
and Worlfram Gloger reworded the comment again in 431c33c0 (May 1999,
also glibc 2.1.1), as part of synchronizing with ptmalloc:
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to realloc with
zero bytes should be the same as a call to free. The C standard
requires this. Otherwise, since this malloc returns a unique pointer
for malloc(0), so does realloc(p, 0).
*/
So, the switch from "Some people think it should" to "The C standard
requires this" was independent from the change from actually flipping
the knob, under different authors, but both changes happened in close
proximity, and the list archives from that time are relevant to the
thinking as to why it was assumed C99 required the change.
Going even further in time, the knob itself (although disabled)
appears to have been around since at least commit f65fd747 (Dec 1996,
importing glibc history into version control, glibc 2.0.4; glibc.git
lacks older history), where it was disabled with documentation of:
REALLOC_ZERO_BYTES_FREES (default: NOT defined)
Define this if you think that realloc(p, 0) should be equivalent
to free(p). Otherwise, since malloc returns a unique pointer for
malloc(0), so does realloc(p, 0).
so long before C99 was even close to final, glibc was aware of other
implementations having that behavior enough to offer it as a
compile-time knob even if not using it back then. ChangeLog.1 at that
time (now living in ChangeLog.old/ChangeLog.1) mentions the existence
of malloc/malloc.c in a change by Roland McGrath in April 1992 (that's
the oldest mention of the file within accessible glibc sources, but no
actual view of what the file looked like at the time). The first
mention of REALLOC_ZERO in ChangeLog.old/ChangeLog.* is in
ChangeLog.10, corresponding to changes in 2000 (and already covered
above in this email). I lack the resources to see how glibc evolved
before the commit history currently tracked in git, which would be
needed if we want to learn how glibc 1.0 behaved, or when that comment
may have first been added.
Going in the other direction, Paul modified the comment, in commits
dff9e592 and 9f1bed18 (both in April 2021, glibc-2.34):
- first attempt
/*
REALLOC_ZERO_BYTES_FREES controls the behavior of realloc (p, 0)
when p is nonnull. If nonzero, realloc (p, 0) should free p and
return NULL. Otherwise, realloc (p, 0) should do the equivalent
of freeing p and returning what malloc (0) would return.
ISO C17 says the behavior is implementation-defined here; glibc
follows historical practice and defines it to be nonzero.
*/
- current wording
/*
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.
*/
In that light, 2021 is far enough removed from 2002 that Paul's use of
"historical" could easily be read in terms of glibc history
(independent of other implementations). And this corresponds to the
point in time after the Austin Group had pointed out to the C
committee that C99 wording and POSIX 2008 were possibly at odds, such
that POSIX was trying to get the C wording improved before C17 was out
so that future POSIX (now POSIX 2024) would be easier.
>
> See <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>.
Thank you for starting this. It will probably need some revisions
before it is ready for the C committee, but hopefully this thread helps.
>
> > More reading: https://www.austingroupbugs.net/view.php?id=400 shows
> > where earlier POSIX missed that C90 to C99 changed what was permitted
> > (and apparantly in a way to render glibc's implementation
> > non-conforming), and that's part of what drove the POSIX folks to ask
> > the C standard to improve the wording. POSIX 2024 is based on C17,
> > but Nick Stoughton was regularly communicating between both C and
> > POSIX groups on what wording(s) were being floated around, in order to
> > try and make it so that glibc would not have to change behavior, but
> > at the same time trying to make it possible for applications to be
> > able to make wise runtime decisions on how to use realloc that would
> > not leak memory or risk dereferencing a NULL pointer if not careful.
> >
> > https://sourceware.org/bugzilla/show_bug.cgi?id=12547 shows where
> > glibc has, in the past, refused to change behavior on the grounds that
> > the standards were buggy. If the standards are still buggy, the best
> > course of action is to open a bug against them.
>
> All standards since C89 have been buggy. If you are pedantic reading
> C89, the BSDs and all the historic implementations back to the original
> Unix V7 are non-conforming:
>
> <https://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4>
>
> Which says:
>
> | If size is zero and ptr is not a null pointer, the object it points to
> | is freed.
>
> It's not clear whether this means that the whole action of realloc(p,0)
> is to free(3) the pointer, or if it can also allocate a new object.
> Under the former interpretation, the standard is at odds with reality.
> Under the latter interpretation, I'd interpret it as saying that
> realloc(p,0) cannot fail (and thus must free(p)), which would be an
> interesting guarantee. I guess we'll never know what was the intended
> reading.
C89 also says (4.10.3):
"If the size of the space requested is zero, the behavior is
implementation-defined; the value returned shall be either a null
pointer or a unique pointer."
Putting those two sentences together, I can make a very strong case
that an implementation where "realloc(p,0)" frees p, and then returns
NULL, and documents that it does so, complies (the old object pointed
to by p is free, and the size being zero means that the new object
being NULL rather than a distinct pointer to non-dereferenceable
storage is what the implementation documented); and this is true
whether or not malloc(0) and realloc(p,0) differ on whether they
return NULL, as long as both of them document their behavior on zero
size.
Another observation on C89 - it has different wording in most places
about "if the space has been deallocated by a call to the free or
realloc function"; where free() documents that "The free function
causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation." The phrase "is deallocated" is
thus precisely defined. However, the only use of the phrase "is
freed" in that document is the one sentence you quoted about realloc
with non-null pointer and zero size. I can argue that as an
undocumented term, "is freed" is NOT intended to be synonymous with
"is free()d", and is instead distinct in meaning from "is deallocated"
(ie. "is deallocated" is how a pointer can be reused by a future
malloc, and is no longer a distinct memory location; but "is freed"
could be defined as contents are no longer referenceable but the
pointer might still be allocated as a distinct location in memory and
still safe to pass to a later "free()"). But then, you might ask, why
does the standard talk about space that "has been deallocated by a
call to the free or realloc function" if "realloc(p,0)" is not
deallocating? My answer: there is another case where it is obvious
that realloc does deallocation: namely, when realloc(p,non-zero)
returns a new pointer that was (presumably larger) than the contents
of the old p - the old value of p "was deallocated" and can now be
reused by a future malloc. With that definition in hand, I can now
argue that whether realloc(p,0) returns p (truncated and freed of its
contents, but p is still allocated), or returns a new non-NULL pointer
(p was freed of its contents AND deallocated in order to return the
new pointer), an implementation where realloc(p,0) returns a non-NULL
pointer has successfully freed p. Strenuous logic, perhaps, but we're
already in the weeds.
So, I think we can make arguments that ALL of the following can be
considered compliant under C89 rules (although the argument is easier
for some cases than others):
1. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
realloc(p,0) returns p [free(p) is still safe, but it is no longer
safe to access contents of p]
2. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
realloc(p,0) returns non-NULL other than p [free(p) is unsafe]
3. malloc(0) returns NULL [0-sized objects are unsupported; presumably
errno=EINVAL but C89 is silent on that], realloc(0,0) returns NULL,
realloc(p,0) deallocates p and then returns NULL [presumably with
errno set, at any rate free(p) is unsafe]
4. malloc(0) returns NULL, realloc(0,0) returns NULL, realloc(p,0)
returns p unchanged [free(p) is still safe, but dereferencing its
contents is no longer safe]
5. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
realloc(p,0) deallocates p and returns NULL [free(p) is unsafe]
Of those, it looks like glibc 2.1 would be case 1 (the same pointer is
returned, but truncated down to minimum size), glibc 2.2 to present
would be case 5 (the pointer is freed, the function returns NULL even
though it inconsistent with malloc(0) being able to return zero-sized
objects), and other traditional implementations could be either case 2
(a non-NULL pointer is returned because zero-sized objects are always
possible, but because it was distinct from p it also met the rule
about having freed p) or case 1 (the call "freed" the contents of p,
but did not deallocate it).
>
> C99 changed the specification, probably because of how ambiguous it was.
>
> glibc was also buggy, as it differed from every other Unix-like system.
> All Unix systems behaved as if free(p) and malloc(n). glibc is the only
> one that didn't follow this obvious consistency rule.
Maybe the intended wording was that "if realloc(p,s) returns a
non-NULL value distinct from p, then p was deallocated and the new
value obtained as if by malloc(n)". After all, the reason realloc()
exists is for the cases where realloc(p,s) can return p (ie. resized
in place, whether by truncating and optionally handing back an unused
tail to the system, or by expanding where the new tail was already
accessible in place even though it has unspecified contents); it's
only when the resize-in-place can't happen that realloc() must then
arrange to copy contents from the old pointer to the new. In fact,
even though portable code must not expect specific contents in the new
pointer if the sequence free(p);malloc(s) happens to reuse p, I could
totally see how some (possibly-older) implementations of malloc() have
sufficient locking in place where it may be easier to try and resize
pointer p by free(p)malloc(s) and only if the resize changed locations
then do the copying - as long as the rest of the application can't
corrupt the contents in the old location before the new location is
finally returned to the user. But even if it was the intended wording
(or if that is the wording that we hope to have in place in the
future), unfortunately it is not the actual wording.
>
> So, both are bogus.
>
> > Also relevant are these documents
> > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm
> > https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf
> >
> > > > > (like i said, i care greatly about actual shipping code. a standard is
> > > > > interesting for green-field stuff, but when it's at odds with reality
> > > > > it's often worse to try to adapt than just ignore the stupidity/report
> > > > > the bug and get it changed back.)
> > >
> > > It's ironic that the standard should have never said that, because prior
> > > to the existence of ANSI C and POSIX, all existing systems behaved like
> > > the current POSIX specification. It was a consequence of the horrible
> > > wording of the standards, that glibc was written so badly, by following
> > > a bogus specification, when it should have been made compatible with the
> > > existing systems.
> >
> > POSIX was originally released in 1988, before C90. glibc 1.0 came out
> > in 1992. I am not sure when glibc first cared about whether trending
> > towards POSIX compliance mattered, although I do know that in the
> > early days, Ulrich would very adamently argue along the lines of
> > (paraphrased) "if the standards don't match common sense, then we
> > don't care about the standards".
>
> It seems Ulrich didn't follow that in this case. I don't know who wrote
> the original realloc(3) in glibc. Was it RMS? It would be interesting
> to know how they came up with that implementation. If anyone knows who
> wrote it and why, please CC them.
>
> I don't have a copy of POSIX.1-1988, nor of any other POSIX.1 before
> POSIX.1-2001. What do they say for realloc(3)?
>
> > > Thus, this is a historical bug in ISO C, POSIX, which at least has been
> > > finally fixed in POSIX.
> >
> > The fact that the wording has changed across multiple versions of C
> > and POSIX is indeed evidence that getting a specification that people
> > are happy with is difficult. What is harder is the decision of
> > whether the bug is in the standard (for not documenting reality) or in
> > the implementations (for not doing what the standard rightfully
> > requests), or even both. And "what people are happy with" differs on
> > who you ask - wording that permits disparate libc behavior is nicer to
> > the libraries (they don't have to change) but meaner to application
> > writers (the construct is not portable, so it is safer to avoid the
> > construct altogether rather than worry about which libraries have
> > which behaviors); whereas wording that locks down behavior is nicer to
> > applications (if I write this, it should work regardless of platform,
> > and if it doesn't, the standard exists as leverage to get libc fixed)
> > but meaner to libraries (forcing the library to version its symbols to
> > change behavior for newer standards while still providing ABI
> > stability guarantees to older apps that depend on the old behavior is
> > not cheap).
>
> As can be seen from the change in gnulib, the only possible issues from
> migrating from the current glibc behavior to the musl behavior is a few
> leaks in cases where the programmer calls realloc(p,0) ignoring the
> return value. Those leaks would leak 0 bytes plus the metadata.
>
> A solution for those leaks would be to add a diagnostic for calls to
> realloc(3) where the return value is unused. And even if those aren't
> fully solved, they're leaks of a few bytes. There's nothing that should
> cause real issues.
>
> But the glibc maintainers mentioned that they're investigating about it
> in distros, so I guess we'll eventually have the results of their
> investigation.
>
> > The sad fact of the matter is that _because_ it there are so many
> > differences in opinions, the C23 action of making realloc(p,0)
> > undefined is probably the simplest course that could be agreed on
> > (don't ever do that in your code, because you can't guarantee the
> > results), but simultaneously annoying to end users (because it is
> > undefined, rather than implementation-defined or unspecified, a
> > compiler can "optimize" your code to do WHATEVER IT WANTS - which
> > really means you CANNOT ever reliably call realloc(p,0) if your
> > compiler is aiming for C23).
>
> Indeed. I think the move from C17 to C23 was good.
>
> The issue with C17 is that it is very similar to POSIX.1-2008, but since
> ISO C doesn't require that errno is set when the pointer is not freed,
> it is impossible to portably determine if the input pointer was freed
> after realloc(p,0). This is not an issue in POSIX.1, though, since it
> can and does require that errno is set if the input pointer is not
> freed.
>
> Because it was impossible to determine whether r(p,0) has freed p after
> returning NULL in C17, it was effectively UB. So, I consider C23 to be
> a minor change from C17, and one which clarifies that it is UB, because
> it already was before.
>
> POSIX.1 is not limited by this limitation of ISO C.
>
> > On top of that, the POSIX standard usually defers to a (fixed version)
> > of C, but does have the liberty to impose well-defined behavior even
> > where the corresponding C standard left things undefined (for example,
> > POSIX 2017 was able to demand that a POSIX system can cast function
> > pointers to and from void* in order to implement dlsym(), even though
> > C99 said that was undefined). Put another way, just because C23 has
> > changed realloc(p,0) to be undefined does NOT require a future version
> > of POSIX to do likewise when it finally moves to a newer C than C17.
> > But at the same time, POSIX is unlikely to make things strict if it
> > risks alienating existing implementations; if glibc changes behavior,
> > that would go a long way towards POSIX changing wording to be
> > stricter.
>
> Indeed; I think POSIX.1 doesn't need to make this undefined, and
> shouldn't.
>
> > > BTW, the same text is present in POSIX.1-2017. It was changed in a TC,
> > > following bug <https://www.austingroupbugs.net/view.php?id=400>.
That one mentions WG14 document N872 paragraph 19.c as the change from
C89 to C99; and it matches what I see in the final C99 document in
7.20.3.4:
19c. realloc rewrite (Meyers)
The *realloc* function deallocates the old object pointed to
by *ptr* and returns a pointer to a new object that has the
size specified by *size*. The contents of the new object
shall be the same as the old object before deallocation up
to the lesser of the size of the old object and *size*. Any
bytes in the new object beyond the size of the old object
have indeterminate values.
If *ptr* is a null pointer, the *realloc* function behaves
like the *malloc* function for the specified size.
Otherwise, if *ptr* does not match a pointer earlier
returned by the *calloc*, *malloc*, or *realloc* function,
or if the space has been deallocated by a call to the *free*
or *realloc* function, the behavior is undefined. If memory
for the new object cannot be allocated, the old object is
not deallocated and its value is unchanged.
Returns
The *realloc* function returns a pointer to the new object,
which may have the same value as *ptr*, or a null pointer if
the new object could not be allocated.
C99 also has the disclaimer in 7.20.3, changed slightly in wording
from the disclaimer in C89, that: "If the size of the space requested
is zero, the behavior is implementation-defined: either a null pointer
is returned, or the behavior is as if the size were some nonzero
value, except that the returned pointer shall not be used to access an
object."
> > >
> > > The motivation, from what I can read there, seems to be that C99 already
> > > made POSIX.1 non-conforming, and this fix was intended to conform to
> > > C99.
Or worded differently in light of the above: C89 had a strong
requirement about p "is freed" after realloc(p,0), which was confusing
in itself, so C99 tried to change the wording to get rid of the
undefined phrase, and make it clear that the old object is deallocated
and the new object (even if it has the same value of p as the old
object) is allocated and has initial contents that match the old
object (whether the implementation can optimize by resizing in place
without copying is not observable from the caller's perspective, and
the C99 mandating that realloc() always produces a new object on
success, even if the new pointer is the same as the old, makes
lifetime analysis elsewhere in the standard easier).
In fact, I'm almost inclined to say that it was C89's wording (and not
C99's) that was the reason that glibc flipped their default to having
realloc(p,0) return NULL (because it was C89's wording that made it
into POSIX); and it was C99's debate on newer wording that brought the
issue to light. And yet, here we are, STILL trying to get better
wording into both C and POSIX.
> > >
> > > Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> > > wording from C99, either; it allows weird stuff: it allows an
> > > implementation where malloc(0) returns NULL and realloc(p,0) non-null
> > > (so, the opposite of glibc).
> > >
> > > C11 is essentially identical to C99 in that regard, so glibc is also
> > > non-conforming to C11.
Here, I'm inclined to argue the opposite: glibc IS compliant with C99
and C11, and the commit history in glibc shows that the change to have
realloc(p,0) return NULL was made at the time of C99 on the grounds of
a compliance argument, even if it might have been misguided. It was
C89, not C99, that explicitly required p to be freed; and C99 was
clarifying that the old object is deallocated before the new object
(if any) is returned, even if the pointer is the same. And again, it
stems back to the fact that C says it is implementation-defined
whether a size of 0 returns NULL or a distinct pointer, and has no
requirements on errno being set. Presumably, as long as you are
willing to set errno=0, call realloc(p,0), and then on NULL check if
errno==EINVAL (p is still valid) or still unset (p was freed), then
glibc's implementation complies, even though it does not match
historical behavior of either the implemenations where malloc(0)
always fails (a zero-sized object is not possible) nor the
implementations where realloc(p,0) always returns non-NULL. POSIX
then tried to add the rules to be able to distinguish between NULL
meaning success and being an EINVAL error (since C99 didn't).
I'm out of time today to reply to anything later in your email.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 16:30 ` Eric Blake
@ 2025-06-20 18:03 ` Alejandro Colomar
2025-06-20 19:41 ` Paul Eggert
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-20 18:03 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 10679 bytes --]
Hi Eric,
On Fri, Jun 20, 2025 at 11:30:59AM -0500, Eric Blake wrote:
> On Fri, Jun 20, 2025 at 01:37:58AM +0200, Alejandro Colomar wrote:
> > Hey Eric!
> >
> > Thanks a lot for the detailed reply! Comments below.
>
> Ditto.
>
[...]
> > See <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>.
>
> Thank you for starting this. It will probably need some revisions
> before it is ready for the C committee, but hopefully this thread helps.
I'll CC you when I write a proposal for realloc(3) for C2y. I have a
clear idea of the wording we want for realloc(3) to be safe again. The
main issue I see is convoncing implementations to agree with it.
And hopefully this thread helps with that.
[...]
> > All standards since C89 have been buggy. If you are pedantic reading
> > C89, the BSDs and all the historic implementations back to the original
> > Unix V7 are non-conforming:
> >
> > <https://port70.net/~nsz/c/c89/c89-draft.html#4.10.3.4>
> >
> > Which says:
> >
> > | If size is zero and ptr is not a null pointer, the object it points to
> > | is freed.
> >
> > It's not clear whether this means that the whole action of realloc(p,0)
> > is to free(3) the pointer, or if it can also allocate a new object.
> > Under the former interpretation, the standard is at odds with reality.
> > Under the latter interpretation, I'd interpret it as saying that
> > realloc(p,0) cannot fail (and thus must free(p)), which would be an
> > interesting guarantee. I guess we'll never know what was the intended
> > reading.
>
> C89 also says (4.10.3):
> "If the size of the space requested is zero, the behavior is
> implementation-defined; the value returned shall be either a null
> pointer or a unique pointer."
>
> Putting those two sentences together, I can make a very strong case
> that an implementation where "realloc(p,0)" frees p, and then returns
> NULL, and documents that it does so, complies (the old object pointed
> to by p is free, and the size being zero means that the new object
> being NULL rather than a distinct pointer to non-dereferenceable
> storage is what the implementation documented); and this is true
> whether or not malloc(0) and realloc(p,0) differ on whether they
> return NULL, as long as both of them document their behavior on zero
> size.
>
> Another observation on C89 - it has different wording in most places
> about "if the space has been deallocated by a call to the free or
> realloc function"; where free() documents that "The free function
> causes the space pointed to by ptr to be deallocated, that is, made
> available for further allocation." The phrase "is deallocated" is
> thus precisely defined. However, the only use of the phrase "is
> freed" in that document is the one sentence you quoted about realloc
> with non-null pointer and zero size. I can argue that as an
> undocumented term, "is freed" is NOT intended to be synonymous with
> "is free()d", and is instead distinct in meaning from "is deallocated"
> (ie. "is deallocated" is how a pointer can be reused by a future
> malloc, and is no longer a distinct memory location; but "is freed"
> could be defined as contents are no longer referenceable but the
> pointer might still be allocated as a distinct location in memory and
> still safe to pass to a later "free()"). But then, you might ask, why
> does the standard talk about space that "has been deallocated by a
> call to the free or realloc function" if "realloc(p,0)" is not
> deallocating? My answer: there is another case where it is obvious
> that realloc does deallocation: namely, when realloc(p,non-zero)
> returns a new pointer that was (presumably larger) than the contents
> of the old p - the old value of p "was deallocated" and can now be
> reused by a future malloc. With that definition in hand, I can now
> argue that whether realloc(p,0) returns p (truncated and freed of its
> contents, but p is still allocated), or returns a new non-NULL pointer
> (p was freed of its contents AND deallocated in order to return the
> new pointer), an implementation where realloc(p,0) returns a non-NULL
> pointer has successfully freed p. Strenuous logic, perhaps, but we're
> already in the weeds.
>
> So, I think we can make arguments that ALL of the following can be
> considered compliant under C89 rules (although the argument is easier
> for some cases than others):
>
> 1. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
> realloc(p,0) returns p [free(p) is still safe, but it is no longer
> safe to access contents of p]
>
> 2. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
> realloc(p,0) returns non-NULL other than p [free(p) is unsafe]
>
> 3. malloc(0) returns NULL [0-sized objects are unsupported; presumably
> errno=EINVAL but C89 is silent on that], realloc(0,0) returns NULL,
> realloc(p,0) deallocates p and then returns NULL [presumably with
> errno set, at any rate free(p) is unsafe]
>
> 4. malloc(0) returns NULL, realloc(0,0) returns NULL, realloc(p,0)
> returns p unchanged [free(p) is still safe, but dereferencing its
> contents is no longer safe]
>
> 5. malloc(0) returns non-NULL, realloc(0,0) returns non-NULL,
> realloc(p,0) deallocates p and returns NULL [free(p) is unsafe]
>
> Of those, it looks like glibc 2.1 would be case 1 (the same pointer is
> returned, but truncated down to minimum size), glibc 2.2 to present
> would be case 5 (the pointer is freed, the function returns NULL even
> though it inconsistent with malloc(0) being able to return zero-sized
> objects), and other traditional implementations could be either case 2
> (a non-NULL pointer is returned because zero-sized objects are always
> possible, but because it was distinct from p it also met the rule
> about having freed p) or case 1 (the call "freed" the contents of p,
> but did not deallocate it).
Agreed. It seems then that C89 allowed basically everything. I'll
update the manual page patch to reflect that.
[...]
> > C99 changed the specification, probably because of how ambiguous it was.
> >
> > glibc was also buggy, as it differed from every other Unix-like system.
> > All Unix systems behaved as if free(p) and malloc(n). glibc is the only
> > one that didn't follow this obvious consistency rule.
>
> Maybe the intended wording was that "if realloc(p,s) returns a
> non-NULL value distinct from p, then p was deallocated and the new
> value obtained as if by malloc(n)". After all, the reason realloc()
> exists is for the cases where realloc(p,s) can return p (ie. resized
> in place, whether by truncating and optionally handing back an unused
> tail to the system, or by expanding where the new tail was already
> accessible in place even though it has unspecified contents); it's
> only when the resize-in-place can't happen that realloc() must then
> arrange to copy contents from the old pointer to the new. In fact,
> even though portable code must not expect specific contents in the new
> pointer if the sequence free(p);malloc(s) happens to reuse p, I could
> totally see how some (possibly-older) implementations of malloc() have
> sufficient locking in place where it may be easier to try and resize
> pointer p by free(p)malloc(s) and only if the resize changed locations
> then do the copying - as long as the rest of the application can't
> corrupt the contents in the old location before the new location is
> finally returned to the user. But even if it was the intended wording
> (or if that is the wording that we hope to have in place in the
> future), unfortunately it is not the actual wording.
Indeed, the Unix V7 implementation did something like
free(p);
malloc(s);
and _after_ that it copied the contents if necessary. I'm talking from
memory, but I remember having had fun reading that code.
[...]
> In fact, I'm almost inclined to say that it was C89's wording (and not
> C99's) that was the reason that glibc flipped their default to having
> realloc(p,0) return NULL (because it was C89's wording that made it
> into POSIX); and it was C99's debate on newer wording that brought the
> issue to light. And yet, here we are, STILL trying to get better
> wording into both C and POSIX.
Fully agree. That seems the most likely reason, after reading the
mailing list archives that Paul shared. I suspect they saw a draft of
C9x that was still essentially C89 (so before the changes that ended up
in C99), and they thought it was new text, while it was actually old C89
text that they weren't aware of.
> > > > Indeed, glibc is non-conforming to C99 too. Although, I don't like the
> > > > wording from C99, either; it allows weird stuff: it allows an
> > > > implementation where malloc(0) returns NULL and realloc(p,0) non-null
> > > > (so, the opposite of glibc).
> > > >
> > > > C11 is essentially identical to C99 in that regard, so glibc is also
> > > > non-conforming to C11.
>
> Here, I'm inclined to argue the opposite: glibc IS compliant with C99
> and C11, and the commit history in glibc shows that the change to have
> realloc(p,0) return NULL was made at the time of C99 on the grounds of
> a compliance argument, even if it might have been misguided. It was
> C89, not C99, that explicitly required p to be freed; and C99 was
> clarifying that the old object is deallocated before the new object
> (if any) is returned, even if the pointer is the same. And again, it
> stems back to the fact that C says it is implementation-defined
> whether a size of 0 returns NULL or a distinct pointer, and has no
> requirements on errno being set. Presumably, as long as you are
> willing to set errno=0, call realloc(p,0), and then on NULL check if
> errno==EINVAL (p is still valid) or still unset (p was freed), then
> glibc's implementation complies, even though it does not match
> historical behavior of either the implemenations where malloc(0)
> always fails (a zero-sized object is not possible) nor the
> implementations where realloc(p,0) always returns non-NULL. POSIX
> then tried to add the rules to be able to distinguish between NULL
> meaning success and being an EINVAL error (since C99 didn't).
I disagree with this. When you have a chance to read the rest of my
previous email, you'll see why I think glibc doesn't conform to C99.
> I'm out of time today to reply to anything later in your email.
Okay; thanks! Please reply to the rest when you have time.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 16:30 ` Eric Blake
2025-06-20 18:03 ` Alejandro Colomar
@ 2025-06-20 19:41 ` Paul Eggert
1 sibling, 0 replies; 143+ messages in thread
From: Paul Eggert @ 2025-06-20 19:41 UTC (permalink / raw)
To: Eric Blake, Alejandro Colomar
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Robert Seacord, Bruno Haible, bug-gnulib, JeanHeyd Meneide,
Thorsten Glaser
On 2025-06-20 09:30, Eric Blake wrote:
> I am less certain without more
> research whether the wording that Andreas was referring to at the time
> the feature knob was switched made it into the approved C99 unchanged,
> or if that was still undergoing debates in the C committee.
As I understand it Andreas quoted the C89 wording which did not make it
into C99 unchanged. So (as Alex suggested an hour or two ago) perhaps
the C99 draft Andreas used hadn't changed the wording from C89 yet, or
perhaps he simply quoted UNIX98 which I presume used the C89 wording.
Wherever Andreas got the wording from, he misinterpreted it as
necessitating a change to glibc.
> Paul's [2021] use of
> "historical" could easily be read in terms of glibc history
> (independent of other implementations).
As I recall that was most of it, though I was probably also thinking of
System V (of which AIX is the only current survivor).
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-16 11:55 BUG: realloc(p,0) should be consistent with malloc(0) Alejandro Colomar
` (2 preceding siblings ...)
2025-06-16 18:20 ` Adhemerval Zanella Netto
@ 2025-06-20 21:26 ` Alejandro Colomar
2025-06-20 21:44 ` Alejandro Colomar
` (8 more replies)
3 siblings, 9 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-20 21:26 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 13975 bytes --]
Hi!
After the useful discussion with Eric and Paul, I've rewritten a draft
of a proposal I had for realloc(3) for C2y. Here it is (see below).
I'll present it here before presenting it to the C Committee (although
several members are CCd).
This time, I opted for an all-in-one change that puts us in the end
goal, since some people were concerned that step-by-step might be less
feasible. Also, the wording is more consistent doing this at once, and
people know what to expect from the begining.
Have a lovely day!
Alex
---
Name
alx-0029r1 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Laurent Bercot <ska-dietlibc@skarnet.org>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
Description
Let's start by quoting the author of realloc(3).
On 2024-10-18 05:30, Douglas McIlroy wrote:
> The discussion has taken a turn that's astonishing to one who
> doesn't know the inside details of real compilers.
>
> Regardless of the behavior of malloc(0), one expects this
> theorem to hold:
>
> Given that p = malloc(n) is not NULL,
> that 0<=m<=n,
> and that malloc(m) could in some circumstance
> return a non-null pointer,
> then realloc(p,m) will return a non-null pointer.
>
> REALLOC_ZERO_BYTES_FREES flies in the face of this rational
> expectation about dynamic storage allocation. A diabolical
> invention.
>
> Doug
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in Unix V7, inherited by
the BSDs, and currently available in range of systems, including
musl libc, doesn't have any issues.
Code written for platforms returning a null can be migrated to
platforms returning non-null, without significant issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem.
If the realloc(3) specification was changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory, and to require that it sets
errno to ENOMEM, then code written for AIX or glibc would
continue working just fine, since the errno check would be
redundant with the null check. Simply, the conditional
(errno == ENOMEM) would always be true when (new == NULL).
This makes handling of realloc(3) as straightforward as one
would expect, with only two states: success or error.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return an .
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
Unix V7
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in Unix V7,
and also present in the BSDs.
Design decisions
This change needs three changes, which can be applied both at
once, or in two separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have.
The first step would require changes to (at least) the following
implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer.
The second step would require changes to (at least) the
following implementations: AIX.
The third step would be to require that on error, errno is set
to ENOMEM.
This proposal has merged all steps into a single proposal.
This proposal also needs to add ENOMEM to the standard, since it
hasn't been standardized yet.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes it redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
Proposed wording
Based on N3550.
7.5 Errors <errno.h>
## Add ENOMEM in p2.
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
+is returned
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
@ 2025-06-20 21:44 ` Alejandro Colomar
2025-06-20 22:06 ` Thorsten Glaser
` (7 subsequent siblings)
8 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-20 21:44 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 14928 bytes --]
[CC -= Laurent, since it bounces]
On Fri, Jun 20, 2025 at 11:26:55PM +0200, Alejandro Colomar wrote:
> Hi!
>
> After the useful discussion with Eric and Paul, I've rewritten a draft
> of a proposal I had for realloc(3) for C2y. Here it is (see below).
>
> I'll present it here before presenting it to the C Committee (although
> several members are CCd).
>
> This time, I opted for an all-in-one change that puts us in the end
> goal, since some people were concerned that step-by-step might be less
> feasible. Also, the wording is more consistent doing this at once, and
> people know what to expect from the begining.
>
>
> Have a lovely day!
> Alex
>
> ---
> Name
> alx-0029r1 - Restore the traditional realloc(3) specification
>
> Principles
> - Uphold the character of the language
> - Keep the language small and simple
> - Facilitate portability
> - Avoid ambiguities
> - Pay attention to performance
> - Codify existing practice to address evident deficiencies.
> - Avoid quiet changes
> - Enable secure programming
>
> Category
> Remove UB.
>
> Author
> Alejandro Colomar <alx@kernel.org>
>
> Cc: <bug-gnulib@gnu.org>
> Cc: <musl@lists.openwall.com>
> Cc: <libc-alpha@sourceware.org>
> Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Cc: Paul Eggert <eggert@cs.ucla.edu>
> Cc: Robert Seacord <rcseacord@gmail.com>
> Cc: Elliott Hughes <enh@google.com>
> Cc: Bruno Haible <bruno@clisp.org>
> Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> Cc: Joseph Myers <josmyers@redhat.com>
> Cc: Florian Weimer <fweimer@redhat.com>
> Cc: Laurent Bercot <ska-dietlibc@skarnet.org>
> Cc: Andreas Schwab <schwab@suse.de>
> Cc: Thorsten Glaser <tg@mirbsd.de>
> Cc: Eric Blake <eblake@redhat.com>
> Cc: Vincent Lefevre <vincent@vinc17.net>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: Collin Funk <collin.funk1@gmail.com>
> Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
> Cc: DJ Delorie <dj@redhat.com>
> Cc: Cristian Rodríguez <cristian@rodriguez.im>
> Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
> Cc: Sam James <sam@gentoo.org>
> Cc: Mark Wielaard <mark@klomp.org>
> Cc: "Maciej W. Rozycki" <macro@redhat.com>
> Cc: Martin Uecker <ma.uecker@gmail.com>
> Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
> Cc: <eskil@obsession.se>
>
> History
> <https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
>
> r0 (2025-06-17):
> - Initial draft.
>
> r1 (2025-06-20):
> - Full rewrite after the recent glibc discussion.
>
> See also
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
> <https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
> <https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
> <https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
> <https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
> <https://www.austingroupbugs.net/view.php?id=400>
> <https://www.austingroupbugs.net/view.php?id=526>
> <https://www.austingroupbugs.net/view.php?id=688>
> <https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
>
> Description
> Let's start by quoting the author of realloc(3).
>
> On 2024-10-18 05:30, Douglas McIlroy wrote:
> > The discussion has taken a turn that's astonishing to one who
> > doesn't know the inside details of real compilers.
> >
> > Regardless of the behavior of malloc(0), one expects this
> > theorem to hold:
> >
> > Given that p = malloc(n) is not NULL,
> > that 0<=m<=n,
> > and that malloc(m) could in some circumstance
> > return a non-null pointer,
> > then realloc(p,m) will return a non-null pointer.
> >
> > REALLOC_ZERO_BYTES_FREES flies in the face of this rational
> > expectation about dynamic storage allocation. A diabolical
> > invention.
> >
> > Doug
>
> The specification of realloc(3) has been problematic since the
> very first standards, even before ISO C. The wording has
> changed significantly, trying to forcedly permit implementations
> to return a null pointer when the requested size is zero. This
> originated from the intent of banning zero-sized objects from
> the language in C89, but that never worked well in
> retrospective, as we can see from the fallout.
>
> None of the specifications have been good, and C23 finally gave
> up and made it undefined behavior.
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in Unix V7, inherited by
> the BSDs, and currently available in range of systems, including
> musl libc, doesn't have any issues.
>
> Code written for platforms returning a null can be migrated to
> platforms returning non-null, without significant issues.
>
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
>
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
>
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem.
>
> If the realloc(3) specification was changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory, and to require that it sets
> errno to ENOMEM, then code written for AIX or glibc would
> continue working just fine, since the errno check would be
> redundant with the null check. Simply, the conditional
> (errno == ENOMEM) would always be true when (new == NULL).
>
> This makes handling of realloc(3) as straightforward as one
> would expect, with only two states: success or error.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return an .
>
> Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> Unix V7
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in Unix V7,
> and also present in the BSDs.
>
> Design decisions
> This change needs three changes, which can be applied both at
> once, or in two separate steps.
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have.
>
> The first step would require changes to (at least) the following
> implementations: glibc, Bionic, Windows.
>
> The second step would be to require that malloc(0) returns a
> non-null pointer.
>
> The second step would require changes to (at least) the
> following implementations: AIX.
>
> The third step would be to require that on error, errno is set
> to ENOMEM.
>
> This proposal has merged all steps into a single proposal.
>
> This proposal also needs to add ENOMEM to the standard, since it
> hasn't been standardized yet.
>
> Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes it redundant several mentions of
> realloc(3) next to either free(3) or malloc(3) in the standard.
> We could remove them in this proposal, or clean up that in a
> separate (mostly editorial) proposal. Let's keep it for a
> future proposal for now.
>
> Caveats
> Code written today should be careful, in case it can run on
> older systems that are not fixed to comply with this stricter
> specification. Thus, code written today should call realloc(3)
> similar to this:
>
> realloc(p, n?n:1);
>
> When all existing implementations are fixed to comply with this
> stricter specification, that workaround can be removed.
>
> Proposed wording
> Based on N3550.
>
> 7.5 Errors <errno.h>
> ## Add ENOMEM in p2.
>
> 7.25.4.1 Memory management functions :: General
> @@ p1
> ...
> If the size of the space requested is zero,
> -the behavior is implementation-defined:
> -either
> -a null pointer is returned to indicate the error,
> -or
> the behavior is as if the size were some nonzero value,
> except that the returned pointer shall not be used
> to access an object.
>
> 7.25.4.2 The aligned_alloc function
> @@ Returns, p3
> The <b>aligned_alloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned,
> +and the value of the macro <b>ENOMEM</b>
> +is stored in <b>errno</b>.
>
> 7.25.4.3 The calloc function
> @@ Returns, p3
> The <b>calloc</b> function returns
> -either
> a pointer to the allocated space
> +on success.
> -or a null pointer
> -if
> +If
> the space cannot be allocated
> or if the product <tt>nmemb * size</tt>
> -would wraparound <b>size_t</b>.
> +would wraparound <b>size_t</b>,
> +a null pointer is returned,
> +and the value of the macro <b>ENOMEM</b>
> +is stored in <b>errno</b>.
>
> 7.25.4.7 The malloc function
> @@ Returns, p3
> The <b>malloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned,
> +and the value of the macro <b>ENOMEM</b>
> +is stored in <b>errno</b>.
>
> 7.25.4.8 The realloc function
> @@ Description, p2
> The <b>realloc</b> function
> deallocates the old object pointed to by <tt>ptr</tt>
> +as if by a call to <b>free</b>,
> and returns a pointer to a new object
> -that has the size specified by <tt>size</tt>.
> +that has the size specified by <tt>size</tt>
> +as if by a call to <b>malloc</b>.
> The contents of the new object
> shall be the same as that of the old object prior to deallocation,
> up to the lesser of the new and old sizes.
> Any bytes in the new object
> beyond the size of the old object
> have unspecified values.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
>
> @@ Returns, p4
> The <b>realloc</b> function returns
> a pointer to the new object
> (which can have the same value
> -as a pointer to the old object),
> +as a pointer to the old object)
> +on success.
> -or
> +If
> +space cannot be allocated,
> a null pointer
> +is returned
> +and the value of the macro <b>ENOMEM</b>
> +is stored in <b>errno</b>.
>
> --
> <https://www.alejandro-colomar.es/>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
2025-06-20 21:44 ` Alejandro Colomar
@ 2025-06-20 22:06 ` Thorsten Glaser
2025-06-21 1:06 ` Alejandro Colomar
2025-06-20 22:31 ` Christopher Bazley
` (6 subsequent siblings)
8 siblings, 1 reply; 143+ messages in thread
From: Thorsten Glaser @ 2025-06-20 22:06 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
On Fri, 20 Jun 2025, Alejandro Colomar wrote:
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer.
16 bytes or so on OpenBSD and derivatives, which return individual
suitably-aligned pointers into pages mapped as inaccessible (so that
accesses of the returned pointer of a 0-byte {m,re}alloc fail) plus,
again, metadata.
+1 on warning on that.
> For consistency, all the other allocation functions are updated
> to both return an .
an…?
FWIW, I’m in favour of the proposed change, but I don’t have any
stakes in this, I don’t think I wrote anything that mallocs or
reallocs 0 ever. Plus I’d likely not have to change… much.
(I now see that, in the case realloc() is passed a pointer the
implementation cannot find in its pool, an error is written (and
if an option is set, the execution terminated), and NULL is
returned without setting errno, but that’s UB already anyway.)
Also no need to Cc me, I get this via the musl mailing list, in
which I have some interest (dalias generally knows his stuff).
bye,
//mirabilos
--
22:20⎜<asarch> The crazy that persists in his craziness becomes a master
22:21⎜<asarch> And the distance between the craziness and geniality is
only measured by the success 18:35⎜<asarch> "Psychotics are consistently
inconsistent. The essence of sanity is to be inconsistently inconsistent
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
2025-06-20 21:44 ` Alejandro Colomar
2025-06-20 22:06 ` Thorsten Glaser
@ 2025-06-20 22:31 ` Christopher Bazley
2025-06-21 1:59 ` Alejandro Colomar
2025-06-20 22:58 ` Maciej W. Rozycki
` (5 subsequent siblings)
8 siblings, 1 reply; 143+ messages in thread
From: Christopher Bazley @ 2025-06-20 22:31 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
eskil
Hi Alex,
On Fri, Jun 20, 2025 at 10:26 PM Alejandro Colomar <alx@kernel.org> wrote:
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
I have a feeling that I wrote something like this in one of my emails
but I have since realised that the as-if rule allows "deallocates the
old object pointed to by ptr and returns a pointer to a new object
that has the size specified by size" to be a no-op: an implementation
of realloc could return a pointer to the same heap block whenever the
new requested size is less than or equal to the current size.
Effectively the amount of memory leaked would then be bounded only by
the maximum size of the allocation.
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
This behaviour does not sound good, but I think you are assuming
something about the usage of realloc(p,0): might it be called if and
only if p != NULL?
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
The above code looks suspect to me anyway because it does 'goto fail'
in a scenario where errno != ENOMEM, which presumably includes errno
== 0, which should not be considered a failure.
Anyway, isn't there a simpler example that illustrates your point
without relying on errno?
What about this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0) {
free(old);
goto fail;
}
}
...
free(new);
If you are absolutely sure that ENOMEM is required for this case then
the argument that such implementations are compliant with the ISO C
standard is weaker than I initially realised.
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem.
Also perhaps because ENOMEM isn't part of ISO standard C therefore it
is not necessarily defined. (e.g., it is not defined in the headers
for the Norcroft C compiler for RISC OS.)
It's interesting to think about why ENOMEM might not be part of the
ISO standard. I suspect the reason is that it was not believed to be
necessary for completeness of any of the standard library interfaces.
Your email suggests otherwise.
> If the realloc(3) specification was changed to require that
"Were", not "was". Sorry for being pedantic.
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory, and to require that it sets
> errno to ENOMEM, then code written for AIX or glibc would
You can't require that errno be set to a value that does not exist in
the standard. I see you are planning to add it, but I'm not yet
convinced that is necessary.
> continue working just fine, since the errno check would be
> redundant with the null check. Simply, the conditional
> (errno == ENOMEM) would always be true when (new == NULL).
>
> This makes handling of realloc(3) as straightforward as one
> would expect, with only two states: success or error.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return an .
Missing text?
> Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> Unix V7
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in Unix V7,
> and also present in the BSDs.
>
> Design decisions
> This change needs three changes, which can be applied both at
> once, or in two separate steps.
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have.
>
> The first step would require changes to (at least) the following
> implementations: glibc, Bionic, Windows.
>
> The second step would be to require that malloc(0) returns a
> non-null pointer.
>
> The second step would require changes to (at least) the
> following implementations: AIX.
>
> The third step would be to require that on error, errno is set
> to ENOMEM.
>
> This proposal has merged all steps into a single proposal.
>
> This proposal also needs to add ENOMEM to the standard, since it
> hasn't been standardized yet.
I think this change would be better served by a separate proposal
unless you believe both:
- that ENOMEM serves a special purpose for realloc, and
- that WG14 can only be persuaded to accept ENOMEM on account of that
special purpose.
I have doubts about both points.
> Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes it redundant several mentions of
Extra 'it'.
Overall, I wholeheartedly support your direction.
Chris
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (2 preceding siblings ...)
2025-06-20 22:31 ` Christopher Bazley
@ 2025-06-20 22:58 ` Maciej W. Rozycki
2025-06-21 2:07 ` Alejandro Colomar
2025-06-21 2:44 ` Rich Felker
2025-06-21 14:00 ` alx-0029r2 " Alejandro Colomar
` (4 subsequent siblings)
8 siblings, 2 replies; 143+ messages in thread
From: Maciej W. Rozycki @ 2025-06-20 22:58 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Martin Uecker, Christopher Bazley, eskil
Hi Alejandro,
> After the useful discussion with Eric and Paul, I've rewritten a draft
> of a proposal I had for realloc(3) for C2y. Here it is (see below).
I've been following all the previous discussion (despite not having been
explicitly cc'd) and I'm yet going to read through and chew over your
proposal, but not before next week as it's quite late into this one here
already. A thought has struck me however, at the high level.
Given all the (understandable) fuss and the lack of symmetry (at least in
some implementation variants) between `malloc(0)' and `realloc(p, 0)', and
last but not least conflicting desires, both of which having their pros
and cons, how about we actually fulfil the desires of both camps and come
up with a set of entirely new APIs, say (1) `mallocn'/`reallocn' and (2)
`mallocz'/`reallocz', that for zero-sized allocation requests consistently
respectively:
(1) return NULL, in the case of `reallocn' having freed any previous
allocation,
(2) return a pointer to a valid allocation, forbidden to access and the
size of which might be zero, in the case of `reallocz' having freed or
possibly shrunk any previous allocation?
Implementations could then provide either semantics with their legacy
`malloc'/`realloc' APIs, possibly following your proposal, mostly for
software that does not ever care about zero-sized allocations, which I
think is quite substantial a subset.
It seems to me that the overhead for such separate APIs wouldn't be that
high, and where suitable they could even be exported from system headers
as macros or static always inline functions wrapping around the one core
implementation of each, so as to let the compiler optimise away the
uninterested case with many calls to these APIs where the compiler can
statically prove size to be zero or nonzero.
Thank you for your perseverance with this matter anyway, and have a good
weekend!
Maciej
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 22:06 ` Thorsten Glaser
@ 2025-06-21 1:06 ` Alejandro Colomar
2025-06-21 1:34 ` Paul Eggert
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 1:06 UTC (permalink / raw)
To: Thorsten Glaser
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 3243 bytes --]
Hi Thorsten,
On Sat, Jun 21, 2025 at 12:06:41AM +0200, Thorsten Glaser wrote:
> On Fri, 20 Jun 2025, Alejandro Colomar wrote:
>
> > There are two kinds of code that call realloc(p,0). One
> > hard-codes the 0, and is used as a replacement of free(p). This
> > code ignores the return value, since it's unimportant. This
> > code currently produces a leak of 0 bytes plus associated
> > metadata on platforms such as musl libc, where it returns a
> > non-null pointer.
>
> 16 bytes or so on OpenBSD and derivatives, which return individual
> suitably-aligned pointers into pages mapped as inaccessible (so that
> accesses of the returned pointer of a 0-byte {m,re}alloc fail) plus,
> again, metadata.
>
> +1 on warning on that.
Thanks!
>
> > For consistency, all the other allocation functions are updated
> > to both return an .
>
> an…?
:-)
Here's the diff for the next revision, where I've fixed that accident:
diff --git i/alx-0029.txt w/alx-0029.txt
index a1a96c4..572c51c 100644
--- i/alx-0029.txt
+++ w/alx-0029.txt
@@ -31,7 +31,6 @@ Author
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
- Cc: Laurent Bercot <ska-dietlibc@skarnet.org>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
@@ -58,6 +57,10 @@ History
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
+ r2 ():
+ - Remove bouncing CC.
+ - wfix.
+
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
@@ -192,7 +195,7 @@ Description
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
- to both return an .
+ to both return a null pointer and set errno to ENOMEM.
Prior art
gnulib
> FWIW, I’m in favour of the proposed change, but I don’t have any
> stakes in this, I don’t think I wrote anything that mallocs or
> reallocs 0 ever. Plus I’d likely not have to change… much.
>
> (I now see that, in the case realloc() is passed a pointer the
> implementation cannot find in its pool, an error is written (and
> if an option is set, the execution terminated), and NULL is
> returned without setting errno, but that’s UB already anyway.)
Indeed, UB covers everything. As long as you return a different errno
code and document what it does, your users will likely remain happy.
> Also no need to Cc me, I get this via the musl mailing list, in
> which I have some interest (dalias generally knows his stuff).
Okay.
>
> bye,
> //mirabilos
> --
> 22:20⎜<asarch> The crazy that persists in his craziness becomes a master
> 22:21⎜<asarch> And the distance between the craziness and geniality is
> only measured by the success 18:35⎜<asarch> "Psychotics are consistently
> inconsistent. The essence of sanity is to be inconsistently inconsistent
:-)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 1:06 ` Alejandro Colomar
@ 2025-06-21 1:34 ` Paul Eggert
2025-06-21 2:42 ` Alejandro Colomar
2025-06-21 2:42 ` [musl] " Rich Felker
0 siblings, 2 replies; 143+ messages in thread
From: Paul Eggert @ 2025-06-21 1:34 UTC (permalink / raw)
To: Alejandro Colomar, Thorsten Glaser
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Rich Felker, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Laurent Bercot, Andreas Schwab,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On 2025-06-20 18:06, Alejandro Colomar wrote:
> + to both return a null pointer and set errno to ENOMEM.
Given the proposed change, I don't see why ENOMEM must be added to the C
spec. The spec changes malloc and realloc so that returning null means
failure (i.e., out of memory) and returning non-null means success. In
that case, there seems to be no pressing need for ENOMEM in the C
standard (as opposed to POSIX).
If ENOMEM is really needed, its presence should be justified in the
proposal. Alternatively, the proposal could be divided into two: the
main proposal is the null-if-and-only-if-failure part, and ENOMEM could
be the secondary proposal.
One other thing: Doug McIlroy's point was that realloc(p,n) should never
fail when p already addresses storage of size n or greater. As a
corollary, realloc(p,0) should never fail when p is non-null. It might
be helpful to add this as a third proposal. A lot of apps already assume
McIlroy's point, after all.
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 22:31 ` Christopher Bazley
@ 2025-06-21 1:59 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 1:59 UTC (permalink / raw)
To: Christopher Bazley
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker, eskil
[-- Attachment #1: Type: text/plain, Size: 13732 bytes --]
Hi Chris,
On Fri, Jun 20, 2025 at 11:31:45PM +0100, Christopher Bazley wrote:
> Hi Alex,
>
> On Fri, Jun 20, 2025 at 10:26 PM Alejandro Colomar <alx@kernel.org> wrote:
> > There are two kinds of code that call realloc(p,0). One
> > hard-codes the 0, and is used as a replacement of free(p). This
> > code ignores the return value, since it's unimportant. This
> > code currently produces a leak of 0 bytes plus associated
>
> I have a feeling that I wrote something like this in one of my emails
> but I have since realised that the as-if rule allows "deallocates the
> old object pointed to by ptr and returns a pointer to a new object
> that has the size specified by size" to be a no-op: an implementation
> of realloc could return a pointer to the same heap block whenever the
> new requested size is less than or equal to the current size.
> Effectively the amount of memory leaked would then be bounded only by
> the maximum size of the allocation.
Yes, that could be a valid implementation. Although the implementation
would be a bit stupid if it didn't allow that memory to be reclaimed by
another call to malloc(3). Especially with a call with size 0, where
none of the old contents are alive anymore, and so any pointer would
work.
> > metadata on platforms such as musl libc, where it returns a
> > non-null pointer. However, assuming that there are programs
> > written with the knowledge that they won't ever be run on such
> > platforms, we should take care of that, and make sure they don't
> > leak. A way of accomplishing this would be to recommend
> > implementations to issue a diagnostic when realloc(3) is called
> > with a hardcoded zero. This is only an informal recommendation
> > made by this proposal, as this is a matter of QoI, and the
> > standard shouldn't say anything about it. This would prevent
> > this class of minor leaks.
> >
> > Moreover, in glibc, realloc(p,0) may return non-null, in the
> > case where p is NULL, so code must already take that into
> > account, and thus code that simply takes realloc(p,0) as a
> > synonym of free(p) is already leaky, as free(NULL) is a no-op,
> > but realloc(NULL,0) allocates 0 bytes.
>
> This behaviour does not sound good, but I think you are assuming
> something about the usage of realloc(p,0): might it be called if and
> only if p != NULL?
Yeah, one could call
if (p != NULL)
realloc(p, 0);
I somehow expect people to not be that insane. :)
>
> > The other kind of code is in algorithms that realloc(3) an
> > arbitrary size, which might eventually be zero. This gets more
> > complex.
> >
> > Here's the code that should be written for AIX or glibc:
> >
> > errno = 0;
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > Failing to check for ENOMEM in these platforms before freeing
> > the old pointer would result in a double-free. If the program
> > decides to continue using the old pointer instead of freeing it,
> > it would result in a use-after-free.
>
> The above code looks suspect to me anyway because it does 'goto fail'
> in a scenario where errno != ENOMEM, which presumably includes errno
> == 0, which should not be considered a failure.
Let's quote C17 (not C23 because it's UB):
<https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#subsection.7.22.3>
C17::7.22.3p1:
If the size of the space requested is zero,
the behavior is implementation-defined:
either a null pointer is returned to indicate an error,
or the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used to access an object.
It clearly says that it is considered an error. It's a partial error,
because the object is successfully deallocated, but the new memory is
not allocated.
POSIX.1-2024 is slightly different: it says the function may consider it
an error, by reporting EINVAL. However, the implementation is free to
not set errno at all.
In any case, as a programmer, I'd consider it an error. I don't want to
treat a NULL pointer as a valid pointer, because something's going to
break further down when I pass this pointer around and compare it to
NULL. So I better fail already soon. It's about null pointer hygiene;
you know what I'm talking about. ;)
>
> Anyway, isn't there a simpler example that illustrates your point
> without relying on errno?
>
> What about this:
>
> new = realloc(old, size);
> if (new == NULL) {
> if (size != 0) {
> free(old);
> goto fail;
> }
> }
> ...
> free(new);
This is pedantically not valid. An implementation might ENOMEM on
size==0 (let's say you're on an implementation that returns non-null,
and it wasn't able to find enough space for the metadata needed for the
allocation; in such a case, the old pointer must not be freed, as in any
other ENOMEM situations). It would be weird, but not impossible. In
such a case, your code would leak 'old'.
So, the only way to call realloc(p,s) portably today to POSIX systems is
by checking ENOMEM.
> If you are absolutely sure that ENOMEM is required for this case then
> the argument that such implementations are compliant with the ISO C
> standard is weaker than I initially realised.
I think you need it.
>
> > In the platforms where realloc(p,0) returns non-null, such as
> > the BSDs or musl libc, it is simpler to handle it:
> >
> > new = realloc(old, size);
> > if (new == NULL) { // errno is ENOMEM
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > Whenever the result is a null pointer, these platforms are
> > reporting an ENOMEM error, and thus it is superfluous to check
> > errno there.
> >
> > Most code is written in this way, even if run on platforms
> > returning a null pointer. This is because most programmers are
> > just unaware of this problem.
>
> Also perhaps because ENOMEM isn't part of ISO standard C therefore it
> is not necessarily defined. (e.g., it is not defined in the headers
> for the Norcroft C compiler for RISC OS.)
They should define it now. As for old code, if they didn't have ENOMEM
available, they didn't have a portable way to call realloc(3). They
could use your version, but it could leak on platforms that don't return
NULL. Or they could just use the realloc(p,n?n:1) trick, which is
simpler, and works like a charm.
> It's interesting to think about why ENOMEM might not be part of the
> ISO standard. I suspect the reason is that it was not believed to be
> necessary for completeness of any of the standard library interfaces.
> Your email suggests otherwise.
Indeed. Actually, it wouldn't be necessariy if standards and
implementations hadn't broken realloc(3) so much. In an ideal world,
NULL would be enough to know that realloc(3) failed. But we're not in
that world. Current code needs to check ENOMEM. And thus, the new
specification should use ENOMEM, to support that code. We need an
implementation that is fully backwards-compatible. Otherwise, I'm
worried that we won't convince the implementations to risk introducing
silent bugs in code that works today.
> > If the realloc(3) specification was changed to require that
>
> "Were", not "was". Sorry for being pedantic.
Thanks! I'm not a native English speaker; these corrections help. :)
> > realloc(p,0) returns non-null on success, and that realloc(p,0)
> > only fails when out-of-memory, and to require that it sets
> > errno to ENOMEM, then code written for AIX or glibc would
>
> You can't require that errno be set to a value that does not exist in
> the standard. I see you are planning to add it, but I'm not yet
> convinced that is necessary.
See above. I had previously thought that it wasn't necessary, but after
spending some time thinking about it today, I'm pretty sure we need it.
> > continue working just fine, since the errno check would be
> > redundant with the null check. Simply, the conditional
> > (errno == ENOMEM) would always be true when (new == NULL).
> >
> > This makes handling of realloc(3) as straightforward as one
> > would expect, with only two states: success or error.
> >
> > The resulting wording in the standard is also much simpler, as
> > it doesn't need to define so many special cases.
> >
> > For consistency, all the other allocation functions are updated
> > to both return an .
>
> Missing text?
Yep. I have it fixed for the next revision:
diff --git i/alx-0029.txt w/alx-0029.txt
index a1a96c4..f8835cf 100644
--- i/alx-0029.txt
+++ w/alx-0029.txt
@@ -31,7 +31,6 @@ Author
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
- Cc: Laurent Bercot <ska-dietlibc@skarnet.org>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
@@ -58,6 +57,10 @@ History
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
+ r2 ():
+ - Remove CC.
+ - wfix.
+
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
@@ -192,7 +195,7 @@ Description
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
- to both return an .
+ to both return a null pointer and set errno to ENOMEM.
Prior art
gnulib
>
> > Prior art
> > gnulib
> > gnulib provides the realloc-posix module, which aims to wrap the
> > system realloc(3) and reallocarray(3) functions so that they
> > behave in a POSIX-complying manner.
> >
> > It previously behaved like glibc. After I reported that it was
> > non-conforming to POSIX, we discussed the best way forward,
> > which we agreed was the same direction that this paper is
> > proposing now for C2y. The implementation was changed in
> >
> > gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
> >
> > There have been no regression reports since then, as we
> > expected.
> >
> > Unix V7
> > The proposed behavior is the one endorsed by Doug McIlroy, the
> > author of the original implementation of realloc(3) in Unix V7,
> > and also present in the BSDs.
> >
> > Design decisions
> > This change needs three changes, which can be applied both at
> > once, or in two separate steps.
> >
> > The first step would make realloc(p,s) be consistent with
> > free(p) and malloc(s), including when p is a null pointer, when
> > s is zero, and also when both corner cases happen at the same
> > time. This change would already turn the implementations where
> > malloc(0) returns non-null into the end goal we have.
> >
> > The first step would require changes to (at least) the following
> > implementations: glibc, Bionic, Windows.
> >
> > The second step would be to require that malloc(0) returns a
> > non-null pointer.
> >
> > The second step would require changes to (at least) the
> > following implementations: AIX.
> >
> > The third step would be to require that on error, errno is set
> > to ENOMEM.
> >
> > This proposal has merged all steps into a single proposal.
> >
> > This proposal also needs to add ENOMEM to the standard, since it
> > hasn't been standardized yet.
>
> I think this change would be better served by a separate proposal
> unless you believe both:
> - that ENOMEM serves a special purpose for realloc, and
> - that WG14 can only be persuaded to accept ENOMEM on account of that
> special purpose.
For backwards compatiblity, I think we need to add ENOMEM. Otherwise,
code that is perfect today might have small issues with the new
specification.
And of course, if we have issues with the new specification, it won't
even be accepted or implemented.
> I have doubts about both points.
>
> > Future directions
> > This proposal, by specifying realloc(3) as-if by calling
> > free(3) and malloc(3), makes it redundant several mentions of
>
> Extra 'it'.
Thanks!
>
> Overall, I wholeheartedly support your direction.
Thanks! :-)
Have a lovely day!
Alex
>
> Chris
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 22:58 ` Maciej W. Rozycki
@ 2025-06-21 2:07 ` Alejandro Colomar
2025-06-21 14:49 ` [musl] " Markus Wichmann
2025-06-21 2:44 ` Rich Felker
1 sibling, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 2:07 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 3004 bytes --]
Hi Maciej,
On Fri, Jun 20, 2025 at 11:58:15PM +0100, Maciej W. Rozycki wrote:
> Hi Alejandro,
>
> > After the useful discussion with Eric and Paul, I've rewritten a draft
> > of a proposal I had for realloc(3) for C2y. Here it is (see below).
>
> I've been following all the previous discussion (despite not having been
> explicitly cc'd) and I'm yet going to read through and chew over your
> proposal, but not before next week as it's quite late into this one here
> already. A thought has struck me however, at the high level.
>
> Given all the (understandable) fuss and the lack of symmetry (at least in
> some implementation variants) between `malloc(0)' and `realloc(p, 0)', and
> last but not least conflicting desires, both of which having their pros
> and cons,
I honestly still don't see the point in the camp returning NULL. The
only reason it hasn't died, I think, is because of fear of breaking
existing code, but I don't see anyone asking for that behavior.
> how about we actually fulfil the desires of both camps and come
> up with a set of entirely new APIs, say (1) `mallocn'/`reallocn' and (2)
> `mallocz'/`reallocz', that for zero-sized allocation requests consistently
> respectively:
>
> (1) return NULL, in the case of `reallocn' having freed any previous
> allocation,
>
> (2) return a pointer to a valid allocation, forbidden to access and the
> size of which might be zero, in the case of `reallocz' having freed or
> possibly shrunk any previous allocation?
I don't think (1) is useful. But if anyone thinks so for their code,
I suggest they write an obvious inline wrapper for themselves. But such
a wrapper should not be provided in the standard library, as it is
error-prone.
> Implementations could then provide either semantics with their legacy
> `malloc'/`realloc' APIs, possibly following your proposal, mostly for
> software that does not ever care about zero-sized allocations, which I
> think is quite substantial a subset.
Code using realloc(3) would continue having hidden bugs. Part of my
goal is being able to use realloc(3) in the future without all these
issues, but another goal is fixing many existing bugs that no-one even
realized their code has.
I'm pretty sure you lift a rock and find three bugs in a call to
realloc(3).
> It seems to me that the overhead for such separate APIs wouldn't be that
> high, and where suitable they could even be exported from system headers
> as macros or static always inline functions wrapping around the one core
> implementation of each, so as to let the compiler optimise away the
> uninterested case with many calls to these APIs where the compiler can
> statically prove size to be zero or nonzero.
>
> Thank you for your perseverance with this matter anyway, and have a good
> weekend!
Thanks!
Have a lovely weekend!
Alex
>
> Maciej
>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 1:34 ` Paul Eggert
@ 2025-06-21 2:42 ` Alejandro Colomar
2025-06-21 3:50 ` Paul Eggert
2025-06-21 2:42 ` [musl] " Rich Felker
1 sibling, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 2:42 UTC (permalink / raw)
To: Paul Eggert
Cc: Thorsten Glaser, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Rich Felker, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Laurent Bercot, Andreas Schwab,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 2911 bytes --]
Hi Paul,
On Fri, Jun 20, 2025 at 06:34:25PM -0700, Paul Eggert wrote:
> On 2025-06-20 18:06, Alejandro Colomar wrote:
> > + to both return a null pointer and set errno to ENOMEM.
>
> Given the proposed change, I don't see why ENOMEM must be added to the C
> spec. The spec changes malloc and realloc so that returning null means
> failure (i.e., out of memory) and returning non-null means success. In that
> case, there seems to be no pressing need for ENOMEM in the C standard (as
> opposed to POSIX).
>
> If ENOMEM is really needed, its presence should be justified in the
> proposal.
I wrote it, although maybe it would need to be clearer?
Here's the part that justifies it:
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
[...]
If the realloc(3) specification was changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory, and to require that it sets
errno to ENOMEM, then code written for AIX or glibc would
continue working just fine, since the errno check would be
redundant with the null check. Simply, the conditional
(errno == ENOMEM) would always be true when (new == NULL).
If that old code runs on a new system that doesn't set ENOMEM, it will
leak 'old'. Admittedly, that code is currently only portable to POSIX
systems, which already require ENOMEM, and I don't expect POSIX would
lift that requirement (and would recommend not lifting it), so I expect
people won't run that code in arbitrary C2y platforms. However, to
fully support that code, we need to set ENOMEM.
Please let me know if this sounds reasonable.
> Alternatively, the proposal could be divided into two: the main
> proposal is the null-if-and-only-if-failure part, and ENOMEM could be the
> secondary proposal.
I added the ENOMEM part because of the above, to make sure that the new
realloc(3) specification is fully backwards compatible to every existing
implementation, so that you can take your AIX or glibc or musl or
whatever code, and say "hey, I'll run this code on any C2y-conforming
platform; it should Just Work".
> One other thing: Doug McIlroy's point was that realloc(p,n) should never
> fail when p already addresses storage of size n or greater.
Hmmm, I hadn't read it that way.
> As a corollary,
> realloc(p,0) should never fail when p is non-null. It might be helpful to
> add this as a third proposal. A lot of apps already assume McIlroy's point,
> after all.
Yes, sounds like a useful guarantee. Shrinking an object shouldn't
ENOMEM, as the implementation could just return the old pointer. I'll
keep this for a separate paper.
Cheers,
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 1:34 ` Paul Eggert
2025-06-21 2:42 ` Alejandro Colomar
@ 2025-06-21 2:42 ` Rich Felker
2025-06-21 2:55 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Rich Felker @ 2025-06-21 2:42 UTC (permalink / raw)
To: Paul Eggert
Cc: Alejandro Colomar, Thorsten Glaser, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Adhemerval Zanella Netto, Joseph Myers,
Florian Weimer, Laurent Bercot, Andreas Schwab, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
On Fri, Jun 20, 2025 at 06:34:25PM -0700, Paul Eggert wrote:
> On 2025-06-20 18:06, Alejandro Colomar wrote:
> > + to both return a null pointer and set errno to ENOMEM.
>
> Given the proposed change, I don't see why ENOMEM must be added to the C
> spec. The spec changes malloc and realloc so that returning null means
> failure (i.e., out of memory) and returning non-null means success. In that
> case, there seems to be no pressing need for ENOMEM in the C standard (as
> opposed to POSIX).
>
> If ENOMEM is really needed, its presence should be justified in the
> proposal. Alternatively, the proposal could be divided into two: the main
> proposal is the null-if-and-only-if-failure part, and ENOMEM could be the
> secondary proposal.
>
> One other thing: Doug McIlroy's point was that realloc(p,n) should never
> fail when p already addresses storage of size n or greater. As a corollary,
> realloc(p,0) should never fail when p is non-null. It might be helpful to
> add this as a third proposal. A lot of apps already assume McIlroy's point,
> after all.
I'm very much opposed to such a change. Many (I would say most good)
allocator designs do not admit resizing down in-place due to
segregating allocations by size in slab-like strategies where the size
of the object may even be implicit in its address. While it's possible
to make them simply decline to resize-down, leaving the original
full-sized allocation, this has multiple very negative properties:
1. It exacerbates OOM conditions by preventing the application from
knowing about them and being able to react. If the application
thinks N operations of resizing allocations each of some large size
M down to some small size K succeeded, it has tied up N*(M-k) bytes
more than it expected, and it does not know it can free these
objects to reclaim this memory.
2. It undermines hardening. Our malloc implementation guarantees
catching even single-nonzero-byte buffer overflows past the nominal
allocation size. This requires having the ability to represent the
exact size. Because the difference between the slot size and
nominal allocation size is bounded and small, the representation of
the difference can be stored very compactly. This cannot be done as
efficiently or in the same manner if the difference can be
arbitrarily large.
3. It breaks the common extension function malloc_usable_size. As in
(2) above, we have malloc_usable_size return the exact nominal
size. This is necessary to prevent inconsistencies where the
compiler optimizes with knowledge that the object size is at most
N1 but malloc_usable_size returns a larger value N2, breaking
catastrophically. (Admittedly having malloc_usable_size to begin
with was the mistake here, but that ship has sailed.)
Applications have always needed to be prepared for the possibility
that realloc can always fail, including when reducing the size. Unless
I'm mistaken, this happens even on dlmalloc-like allocators when the
old size is above the "mmap threshold" and the new size is below. A
good allocator is not going to leave an object that was originally
allocated at a large 256k buffer using at least a whole page when it's
resized to 16 bytes.
As this proposal is completely unrelated to fixing the UB and
inconsistency of realloc(p,0) on some implementations, and it's a
breaking change as described above, I would like to see it dropped
from the agenda.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-20 22:58 ` Maciej W. Rozycki
2025-06-21 2:07 ` Alejandro Colomar
@ 2025-06-21 2:44 ` Rich Felker
1 sibling, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-21 2:44 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Andreas Schwab, Thorsten Glaser,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Martin Uecker,
Christopher Bazley, eskil
On Fri, Jun 20, 2025 at 11:58:15PM +0100, Maciej W. Rozycki wrote:
> Hi Alejandro,
>
> > After the useful discussion with Eric and Paul, I've rewritten a draft
> > of a proposal I had for realloc(3) for C2y. Here it is (see below).
>
> I've been following all the previous discussion (despite not having been
> explicitly cc'd) and I'm yet going to read through and chew over your
> proposal, but not before next week as it's quite late into this one here
> already. A thought has struck me however, at the high level.
>
> Given all the (understandable) fuss and the lack of symmetry (at least in
> some implementation variants) between `malloc(0)' and `realloc(p, 0)', and
> last but not least conflicting desires, both of which having their pros
> and cons, how about we actually fulfil the desires of both camps and come
> up with a set of entirely new APIs, say (1) `mallocn'/`reallocn' and (2)
> `mallocz'/`reallocz', that for zero-sized allocation requests consistently
> respectively:
>
> (1) return NULL, in the case of `reallocn' having freed any previous
> allocation,
>
> (2) return a pointer to a valid allocation, forbidden to access and the
> size of which might be zero, in the case of `reallocz' having freed or
> possibly shrunk any previous allocation?
>
> Implementations could then provide either semantics with their legacy
> `malloc'/`realloc' APIs, possibly following your proposal, mostly for
> software that does not ever care about zero-sized allocations, which I
> think is quite substantial a subset.
>
> It seems to me that the overhead for such separate APIs wouldn't be that
> high, and where suitable they could even be exported from system headers
> as macros or static always inline functions wrapping around the one core
> implementation of each, so as to let the compiler optimise away the
> uninterested case with many calls to these APIs where the compiler can
> statically prove size to be zero or nonzero.
>
> Thank you for your perseverance with this matter anyway, and have a good
> weekend!
I really don't think anyone wants this. Nobody wants the bad behavior
with regard to returing null. Neither applications nor implementors.
All that some implementors (understandably) want is to avoid making
changes. The above-proposed change just makes *everyone* unhappy and
makes the situation much more confusing and worse.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 2:42 ` [musl] " Rich Felker
@ 2025-06-21 2:55 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 2:55 UTC (permalink / raw)
To: Rich Felker
Cc: Paul Eggert, Thorsten Glaser, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Adhemerval Zanella Netto, Joseph Myers,
Florian Weimer, Laurent Bercot, Andreas Schwab, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 3995 bytes --]
Hi Rich,
On Fri, Jun 20, 2025 at 10:42:58PM -0400, Rich Felker wrote:
> On Fri, Jun 20, 2025 at 06:34:25PM -0700, Paul Eggert wrote:
> > On 2025-06-20 18:06, Alejandro Colomar wrote:
> > > + to both return a null pointer and set errno to ENOMEM.
> >
> > Given the proposed change, I don't see why ENOMEM must be added to the C
> > spec. The spec changes malloc and realloc so that returning null means
> > failure (i.e., out of memory) and returning non-null means success. In that
> > case, there seems to be no pressing need for ENOMEM in the C standard (as
> > opposed to POSIX).
> >
> > If ENOMEM is really needed, its presence should be justified in the
> > proposal. Alternatively, the proposal could be divided into two: the main
> > proposal is the null-if-and-only-if-failure part, and ENOMEM could be the
> > secondary proposal.
> >
> > One other thing: Doug McIlroy's point was that realloc(p,n) should never
> > fail when p already addresses storage of size n or greater. As a corollary,
> > realloc(p,0) should never fail when p is non-null. It might be helpful to
> > add this as a third proposal. A lot of apps already assume McIlroy's point,
> > after all.
>
> I'm very much opposed to such a change. Many (I would say most good)
> allocator designs do not admit resizing down in-place due to
> segregating allocations by size in slab-like strategies where the size
> of the object may even be implicit in its address. While it's possible
> to make them simply decline to resize-down, leaving the original
> full-sized allocation, this has multiple very negative properties:
>
> 1. It exacerbates OOM conditions by preventing the application from
> knowing about them and being able to react. If the application
> thinks N operations of resizing allocations each of some large size
> M down to some small size K succeeded, it has tied up N*(M-k) bytes
> more than it expected, and it does not know it can free these
> objects to reclaim this memory.
>
> 2. It undermines hardening. Our malloc implementation guarantees
> catching even single-nonzero-byte buffer overflows past the nominal
> allocation size. This requires having the ability to represent the
> exact size. Because the difference between the slot size and
> nominal allocation size is bounded and small, the representation of
> the difference can be stored very compactly. This cannot be done as
> efficiently or in the same manner if the difference can be
> arbitrarily large.
>
> 3. It breaks the common extension function malloc_usable_size. As in
> (2) above, we have malloc_usable_size return the exact nominal
> size. This is necessary to prevent inconsistencies where the
> compiler optimizes with knowledge that the object size is at most
> N1 but malloc_usable_size returns a larger value N2, breaking
> catastrophically. (Admittedly having malloc_usable_size to begin
> with was the mistake here, but that ship has sailed.)
Interesting. Thanks!
>
> Applications have always needed to be prepared for the possibility
> that realloc can always fail, including when reducing the size. Unless
> I'm mistaken, this happens even on dlmalloc-like allocators when the
> old size is above the "mmap threshold" and the new size is below. A
> good allocator is not going to leave an object that was originally
> allocated at a large 256k buffer using at least a whole page when it's
> resized to 16 bytes.
>
> As this proposal is completely unrelated to fixing the UB and
> inconsistency of realloc(p,0) on some implementations, and it's a
> breaking change as described above, I would like to see it dropped
> from the agenda.
Yep, it was never part of this proposal of mine.
I'll drop the quote, as I had understood it from another point of view.
This will avoid confusion.
Have a lovely day!
Alex
>
> Rich
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 2:42 ` Alejandro Colomar
@ 2025-06-21 3:50 ` Paul Eggert
2025-06-21 11:59 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Paul Eggert @ 2025-06-21 3:50 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Thorsten Glaser, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Rich Felker, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Laurent Bercot, Andreas Schwab,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On 2025-06-20 19:42, Alejandro Colomar wrote:
>> If ENOMEM is really needed, its presence should be justified in the
>> proposal.
>
> I wrote it, although maybe it would need to be clearer?
It would need reworking as it didn't feel like a justification to me.
> Here's the part that justifies it:
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> [...]
>
> If the realloc(3) specification was changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory, and to require that it sets
> errno to ENOMEM, then code written for AIX or glibc would
> continue working just fine, since the errno check would be
> redundant with the null check. Simply, the conditional
> (errno == ENOMEM) would always be true when (new == NULL).
>
> If that old code runs on a new system that doesn't set ENOMEM, it will
> leak 'old'. Admittedly, that code is currently only portable to POSIX
> systems, which already require ENOMEM, and I don't expect POSIX would
> lift that requirement (and would recommend not lifting it), so I expect
> people won't run that code in arbitrary C2y platforms.
But this is exactly why the main proposal (i.e., null if-and-only-if
failure) shouldn't require ENOMEM. The C standardizers have already
thought through the ENOMEM issue and decided not to require ENOMEM.
Whatever reasons they have, won't change because of the main proposal.
ENOMEM belongs to POSIX, and POSIX will retain ENOMEM regardless of
whether C2y accepts the main proposal. We shouldn't try to link the two
proposals together in the C standardization process, as that's more
likely to gum up the works entirely.
> I added the ENOMEM part because of the above, to make sure that the new
> realloc(3) specification is fully backwards compatible to every existing
> implementation, so that you can take your AIX or glibc or musl or
> whatever code, and say "hey, I'll run this code on any C2y-conforming
> platform; it should Just Work".
Those platforms are all POSIXish, and you'll be able to say "hey I'll
run this code on any POSIX-202y system (which extends C2y) and it should
Just Work". That's all anyone can reasonably expect. One can't
reasonably expect to run a POSIXish program on any C2y system and let's
not try to make that a goal.
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
` (3 preceding siblings ...)
2025-06-20 16:30 ` Eric Blake
@ 2025-06-21 3:57 ` Sam James
2025-06-21 12:08 ` Alejandro Colomar
2025-07-04 23:31 ` Alejandro Colomar
5 siblings, 1 reply; 143+ messages in thread
From: Sam James @ 2025-06-21 3:57 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
Alejandro Colomar <alx@kernel.org> writes:
> [...]
>
> But the glibc maintainers mentioned that they're investigating about it
> in distros, so I guess we'll eventually have the results of their
> investigation.
>
To manage expectations: I haven't seen anyone say they're going to work
on this. I recall Sid mentioning it *could* be done (not offering to do
it) and Adhemerval made a similar remark, but I don't think anyone has
said they're undertaking this work.
If I've missed some other remark (very possible with the length of the
thread!), let me know of course.
sam
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 3:50 ` Paul Eggert
@ 2025-06-21 11:59 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 11:59 UTC (permalink / raw)
To: Paul Eggert
Cc: Thorsten Glaser, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Robert Seacord, Elliott Hughes, Bruno Haible,
JeanHeyd Meneide, Rich Felker, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Laurent Bercot, Andreas Schwab,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 3001 bytes --]
Hi Paul,
On Fri, Jun 20, 2025 at 08:50:07PM -0700, Paul Eggert wrote:
> On 2025-06-20 19:42, Alejandro Colomar wrote:
>
> > > If ENOMEM is really needed, its presence should be justified in the
> > > proposal.
> >
> > I wrote it, although maybe it would need to be clearer?
>
> It would need reworking as it didn't feel like a justification to me.
>
>
> > Here's the part that justifies it:
> >
> > Here's the code that should be written for AIX or glibc:
> >
> > errno = 0;
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > [...]
> >
> > If the realloc(3) specification was changed to require that
> > realloc(p,0) returns non-null on success, and that realloc(p,0)
> > only fails when out-of-memory, and to require that it sets
> > errno to ENOMEM, then code written for AIX or glibc would
> > continue working just fine, since the errno check would be
> > redundant with the null check. Simply, the conditional
> > (errno == ENOMEM) would always be true when (new == NULL).
> >
> > If that old code runs on a new system that doesn't set ENOMEM, it will
> > leak 'old'. Admittedly, that code is currently only portable to POSIX
> > systems, which already require ENOMEM, and I don't expect POSIX would
> > lift that requirement (and would recommend not lifting it), so I expect
> > people won't run that code in arbitrary C2y platforms.
>
> But this is exactly why the main proposal (i.e., null if-and-only-if
> failure) shouldn't require ENOMEM. The C standardizers have already thought
> through the ENOMEM issue and decided not to require ENOMEM. Whatever reasons
> they have, won't change because of the main proposal.
>
> ENOMEM belongs to POSIX, and POSIX will retain ENOMEM regardless of whether
> C2y accepts the main proposal. We shouldn't try to link the two proposals
> together in the C standardization process, as that's more likely to gum up
> the works entirely.
>
>
> > I added the ENOMEM part because of the above, to make sure that the new
> > realloc(3) specification is fully backwards compatible to every existing
> > implementation, so that you can take your AIX or glibc or musl or
> > whatever code, and say "hey, I'll run this code on any C2y-conforming
> > platform; it should Just Work".
>
> Those platforms are all POSIXish, and you'll be able to say "hey I'll run
> this code on any POSIX-202y system (which extends C2y) and it should Just
> Work". That's all anyone can reasonably expect. One can't reasonably expect
> to run a POSIXish program on any C2y system and let's not try to make that a
> goal.
Hmmmm, sounds reasonable. I'll have both options available in the
proposal, and let the C Committee decide if they want to add ENOMEM,
without blocking the proposal by ENOMEM.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 3:57 ` Sam James
@ 2025-06-21 12:08 ` Alejandro Colomar
2025-06-21 12:11 ` Sam James
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 12:08 UTC (permalink / raw)
To: Sam James
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 2261 bytes --]
Hi Sam,
On Sat, Jun 21, 2025 at 04:57:32AM +0100, Sam James wrote:
> Alejandro Colomar <alx@kernel.org> writes:
>
> > [...]
> >
> > But the glibc maintainers mentioned that they're investigating about it
> > in distros, so I guess we'll eventually have the results of their
> > investigation.
> >
>
> To manage expectations: I haven't seen anyone say they're going to work
> on this. I recall Sid mentioning it *could* be done (not offering to do
> it) and Adhemerval made a similar remark, but I don't think anyone has
> said they're undertaking this work.
>
> If I've missed some other remark (very possible with the length of the
> thread!), let me know of course.
Adhemerval mentioned in
Message-ID: <14fd8d0b-d32d-421f-8262-9c7ff9b1a22b@linaro.org>
| So what I would expect to move this forwards will be to.
|
| 1. Reopen https://sourceware.org/bugzilla/show_bug.cgi?id=12547
|
| 2. Follow the suggestions laid out by Siddhesh [2]. The Distribution-wide
| verification seems already to be in progress, with some good results
| from gnulib realloc replacement and some work by you on checking some
| other projects (systemd for instance).
|
| 3. Prepare the patch to change it, along with the manual documentation,
| regression testcase, and the NEW entry.
|
| 4. Since we are near to 2.42 release, this change should be done once
| 2.43 starts to give some time to check potential issue with rolling
| distros like Fedora Rawhide.
Point 2 said there's some work checking other projects such as systemd,
although now I realize it probably refers to some inspection someone
already did last time and not something really new? Maybe I
misunderstood that.
On the other hand, I think I proved yesterday in the proposal that the
change could be done with full backwards compatibility and zero leaks
(in code that wasn't leaky before, that is). The only thing that's
needed is that a compiler diagnostic for constant expression 0s in
realloc(3) calls (and of course that we retain ENOMEM). See the
proposal I sent (alx-0029r1), which details how and why. So the whole
verification would be superfluous.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 12:08 ` Alejandro Colomar
@ 2025-06-21 12:11 ` Sam James
2025-06-21 15:14 ` Adhemerval Zanella
0 siblings, 1 reply; 143+ messages in thread
From: Sam James @ 2025-06-21 12:11 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
Alejandro Colomar <alx@kernel.org> writes:
> Hi Sam,
>
> On Sat, Jun 21, 2025 at 04:57:32AM +0100, Sam James wrote:
>> Alejandro Colomar <alx@kernel.org> writes:
>>
>> > [...]
>> >
>> > But the glibc maintainers mentioned that they're investigating about it
>> > in distros, so I guess we'll eventually have the results of their
>> > investigation.
>> >
>>
>> To manage expectations: I haven't seen anyone say they're going to work
>> on this. I recall Sid mentioning it *could* be done (not offering to do
>> it) and Adhemerval made a similar remark, but I don't think anyone has
>> said they're undertaking this work.
>>
>> If I've missed some other remark (very possible with the length of the
>> thread!), let me know of course.
>
> Adhemerval mentioned in
> Message-ID: <14fd8d0b-d32d-421f-8262-9c7ff9b1a22b@linaro.org>
>
> | So what I would expect to move this forwards will be to.
The bit before that is important ;)
That's where he said (and later corrected himself) that there was
consensus, and so the next steps would be ...
> |
> | 1. Reopen https://sourceware.org/bugzilla/show_bug.cgi?id=12547
> |
> | 2. Follow the suggestions laid out by Siddhesh [2]. The Distribution-wide
> | verification seems already to be in progress, with some good results
> | from gnulib realloc replacement and some work by you on checking some
> | other projects (systemd for instance).
> |
> | 3. Prepare the patch to change it, along with the manual documentation,
> | regression testcase, and the NEW entry.
> |
> | 4. Since we are near to 2.42 release, this change should be done once
> | 2.43 starts to give some time to check potential issue with rolling
> | distros like Fedora Rawhide.
I don't think anybody is doing such distro-wide work other than things
using gnulib where we'd notice if tests started to fail. That's what I'm
trying to clarify: please don't wait on anybody doing it, because
nobody's declared they're working on it.
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r2 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (3 preceding siblings ...)
2025-06-20 22:58 ` Maciej W. Rozycki
@ 2025-06-21 14:00 ` Alejandro Colomar
2025-06-24 5:01 ` alx-0029r3 " Alejandro Colomar
2025-06-25 1:58 ` alx-0029r4 " Alejandro Colomar
` (3 subsequent siblings)
8 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 14:00 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler
[-- Attachment #1: Type: text/plain, Size: 16810 bytes --]
Hi!
Here's a revision of the proposal, where the main changes are
- Justify why ENOMEM is being proposed.
- Make ENOMEM optional.
after the feedback from Chris and Paul. See the git repository
mentioned in History for the exact diff.
Have a lovely day!
Alex
---
Name
alx-0029r2 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention unavoidable --but exceptional-- leak in code checking
(size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
Description
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in Unix V7, inherited by
the BSDs, and currently available in range of systems, including
musl libc, doesn't have any issues.
Code written for platforms returning a null can be migrated to
platforms returning non-null, without significant issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory, and to require that it sets
errno to ENOMEM, then code written for AIX or glibc would
continue working just fine, since the errno check would be
redundant with the null check. Simply, the conditional
(errno == ENOMEM) would always be true when (new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs.
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer and set errno to ENOMEM.
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
Unix V7
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in Unix V7,
and also present in the BSDs.
Design decisions
This change needs three changes, which can be applied all at
once, or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have.
This first step would require changes to (at least) the
following implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer.
The second step would require changes to (at least) the
following implementations: AIX.
The third step would be to require that on error, errno is set
to ENOMEM. This step is optional (see Caveats below).
This proposal has merged all steps into a single proposal.
This proposal also needs to add ENOMEM to the standard, since it
hasn't been standardized yet.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
If this proposal didn't use ENOMEM, code that is currently
portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would not be portable to arbitrary C2y platforms.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, we could say that this is not
an issue in ISO C: if we proceed with this proposal removing all
mentions to errno:
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
So, the addition of ENOMEM in this proposal achieves the
following goal:
- Code written for POSIX.1-2024 and older platforms
will continue working on arbitrary C2y platforms.
Maybe this is unnecessary. Thus, the following questions.
In any case, ENOMEM is only meant for backwards compatibility,
and code aiming for C2y would only need to check for NULL.
ENOMEM would be redundant for such programs.
Questions to the C Committee
- Does the C Committee want to retain ENOMEM in this proposal?
(If the answer is not, editorially remove all mentions of
ENOMEM before merging the proposal into C2y.)
- Does the C Committee want to accept this proposal, defining
the behavior of the memory management functions to their
traditional implementation?
Proposed wording
Based on N3550.
7.5 Errors <errno.h>
## Add ENOMEM in p2.
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned,
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
+is returned
+and the value of the macro <b>ENOMEM</b>
+is stored in <b>errno</b>.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 2:07 ` Alejandro Colomar
@ 2025-06-21 14:49 ` Markus Wichmann
2025-06-21 16:27 ` Rich Felker
0 siblings, 1 reply; 143+ messages in thread
From: Markus Wichmann @ 2025-06-21 14:49 UTC (permalink / raw)
To: musl
Cc: Maciej W. Rozycki, libc-alpha, bug-gnulib, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Martin Uecker, Christopher Bazley, eskil
Am Sat, Jun 21, 2025 at 04:07:54AM +0200 schrieb Alejandro Colomar:
> I honestly still don't see the point in the camp returning NULL. The
> only reason it hasn't died, I think, is because of fear of breaking
> existing code, but I don't see anyone asking for that behavior.
>
I invert the question: What is the point of requesting zero bytes? If
you just want a pointer you cannot deref, but can validly plug into both
realloc() and free(), I have this perfectly good null pointer right
here. You can even statically initialize your variables with it. And you
can easily tell it apart from pointers you can dereference.
I see malloc(0) as an error. According to all standards I could get my
hands on, the argument to malloc is supposed to be the size of an
object, and in C, there are no objects without type, and no zero-sized
types. Maybe that's different in C++, I don't know. I have read a bit of
C code in my life, and have written some as well, and debugged even more
of it. I cannot recall an instance of anyone ever requesting to allocate
zero bytes except in error (e.g. uncaught overflow).
Indeed, a strict reading of the spec would be that the argument must be
the result of a sizeof expression, and the common idiom
A *a = malloc(sizeof (A) + sizeof (B));
B *b = (B *)(a + 1);
is undefined. And indeed, it potentially crashes on strict-alignment
architectures if alignof(B) > alignof(A).
Back to zero-sized allocations: The fact that they were traditionally
supported on UNIX means nothing if they were always undefined. At that
point it just becomes a quirk of the implementation, but nothing an
application should depend on.
The C89 mandate to free the pointer in case of zero-sized realloc (which
I read as a command to *only* free the pointer and not do anything else)
seems actively harmful: If an application can be tricked into doing
that, and an implementation chooses to return the now freed pointer
(since realloc must return /something/), then the application now holds
a pointer it thinks is still valid, but is actually dangling.
The way C89 (or rather FIPS160, which is the version I've read) has
added the realloc-0-frees mandate, it looks like a badly thought through
afterthought. But then, C89 also contains sprintf() and gets(), so badly
thought through afterthoughts are certainly not a novelty to that
particular writ.
All standards after it have tinkered with the semantics of zero-sized
allocation, to the point that C23 just made it explicitly undefined. As
application developer, what inference do I draw from this, except to
avoid zero-sized allocation like the plague? If anyone actually writes
"realloc(p, 0)" intending it to be the same as "free(p)", I would tell
them to then write what they mean and stop being so clever.
As implementation developer, I would therefore treat these requests as
errors, and do the appropriate thing: Return the error value and set
errno to EINVAL (and have no further side effects). And such an
implementation conforms to everything from C99 onwards.
Ciao,
Markus
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 12:11 ` Sam James
@ 2025-06-21 15:14 ` Adhemerval Zanella
2025-06-21 15:34 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Adhemerval Zanella @ 2025-06-21 15:14 UTC (permalink / raw)
To: Sam James
Cc: Alejandro Colomar, Eric Blake, Rich Felker, enh, Florian Weimer,
musl, libc-alpha, Joseph Myers, nabijaczleweli, Paul Eggert,
Robert Seacord, Bruno Haible, bug-gnulib, JeanHeyd Meneide,
Thorsten Glaser
> Em 21 de jun. de 2025, à(s) 09:12, Sam James <sam@gentoo.org> escreveu:
>
> Alejandro Colomar <alx@kernel.org> writes:
>
>> Hi Sam,
>>
>>> On Sat, Jun 21, 2025 at 04:57:32AM +0100, Sam James wrote:
>>> Alejandro Colomar <alx@kernel.org> writes:
>>>
>>>> [...]
>>>>
>>>> But the glibc maintainers mentioned that they're investigating about it
>>>> in distros, so I guess we'll eventually have the results of their
>>>> investigation.
>>>>
>>>
>>> To manage expectations: I haven't seen anyone say they're going to work
>>> on this. I recall Sid mentioning it *could* be done (not offering to do
>>> it) and Adhemerval made a similar remark, but I don't think anyone has
>>> said they're undertaking this work.
>>>
>>> If I've missed some other remark (very possible with the length of the
>>> thread!), let me know of course.
>>
>> Adhemerval mentioned in
>> Message-ID: <14fd8d0b-d32d-421f-8262-9c7ff9b1a22b@linaro.org>
>>
>> | So what I would expect to move this forwards will be to.
>
> The bit before that is important ;)
>
> That's where he said (and later corrected himself) that there was
> consensus, and so the next steps would be ...
>
>> |
>> | 1. Reopen https://sourceware.org/bugzilla/show_bug.cgi?id=12547
>> |
>> | 2. Follow the suggestions laid out by Siddhesh [2]. The Distribution-wide
>> | verification seems already to be in progress, with some good results
>> | from gnulib realloc replacement and some work by you on checking some
>> | other projects (systemd for instance).
>> |
>> | 3. Prepare the patch to change it, along with the manual documentation,
>> | regression testcase, and the NEW entry.
>> |
>> | 4. Since we are near to 2.42 release, this change should be done once
>> | 2.43 starts to give some time to check potential issue with rolling
>> | distros like Fedora Rawhide.
>
> I don't think anybody is doing such distro-wide work other than things
> using gnulib where we'd notice if tests started to fail. That's what I'm
> trying to clarify: please don't wait on anybody doing it, because
> nobody's declared they're working on it.
I do not plan to work on this, I had the very wrong idea that fixing this was not a contention point in glibc; but this thread proved me wrong.
My bullet points are how I envision if someone would want to track this on glibc (assuming he had some consensus em fixing it). I think now we are somewhat far from it…
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 15:14 ` Adhemerval Zanella
@ 2025-06-21 15:34 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 15:34 UTC (permalink / raw)
To: Adhemerval Zanella, Florian Weimer
Cc: Sam James, Eric Blake, Rich Felker, enh, musl, libc-alpha,
Joseph Myers, nabijaczleweli, Paul Eggert, Robert Seacord,
Bruno Haible, bug-gnulib, JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 636 bytes --]
Hi Adhemerval, Florian,
On Sat, Jun 21, 2025 at 12:14:59PM -0300, Adhemerval Zanella wrote:
> (assuming he had some consensus em fixing it). I think now we are somewhat far from it…
It would be good if the few people that still disagree, such as Florian,
would comment on the proposal, which I think addresses how this
change would be safe.
If someone shows an example of code that I hadn't considered and which
would be broken by the proposal, I can try addressing it.
Being silent after spreading FUD doesn't help. Please check alx-0029r2.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r1 - Restore the traditional realloc(3) specification
2025-06-21 14:49 ` [musl] " Markus Wichmann
@ 2025-06-21 16:27 ` Rich Felker
0 siblings, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-21 16:27 UTC (permalink / raw)
To: Markus Wichmann
Cc: musl, Maciej W. Rozycki, libc-alpha, bug-gnulib,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Andreas Schwab, Thorsten Glaser,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Martin Uecker,
Christopher Bazley, eskil
On Sat, Jun 21, 2025 at 04:49:17PM +0200, Markus Wichmann wrote:
> Am Sat, Jun 21, 2025 at 04:07:54AM +0200 schrieb Alejandro Colomar:
> > I honestly still don't see the point in the camp returning NULL. The
> > only reason it hasn't died, I think, is because of fear of breaking
> > existing code, but I don't see anyone asking for that behavior.
> >
>
> I invert the question: What is the point of requesting zero bytes? If
> you just want a pointer you cannot deref, but can validly plug into both
> realloc() and free(), I have this perfectly good null pointer right
> here. You can even statically initialize your variables with it. And you
> can easily tell it apart from pointers you can dereference.
>
> I see malloc(0) as an error. According to all standards I could get my
> hands on, the argument to malloc is supposed to be the size of an
> object, and in C, there are no objects without type, and no zero-sized
> types. Maybe that's different in C++, I don't know. I have read a bit of
> C code in my life, and have written some as well, and debugged even more
> of it. I cannot recall an instance of anyone ever requesting to allocate
> zero bytes except in error (e.g. uncaught overflow).
Can we please not relitigate why people want this to work right? It's
actually not about any need for zero-sized allocations, but rather a
need for contractual consistency and avoiding violations of
least-surprise principle.
If malloc(0) or realloc(p,0) can return a null pointer for a condition
that's not a "failure", this means you eiter need error-path logic to
distinguish the "failure" from the "non-failure", or you need
entry-path logic to make sure 0 is never passed to malloc or realloc
as the result of any size computation.
We all know it's hard enough to get programmers to write even simple
error-path logic right with respect to malloc. So much code doesn't
even check the return value at all, or just aborts on a null return.
Likewise, it's hard enough to get programmers to write even simple
entry-path logic for allocation, avoiding integer overflows in the
computation of the size needed. Having to go to extra effort to also
avoid passing zero just makes a new non-forced footgun on top of the
other footguns that are more "essential".
Nobody wants the "make it worse" change you're asking for. So please
don't derail the work of people actually trying to make it better. At
least "we don't want to impose compatibility burdens on implementors
by forcing them to change something" is a reasonable argument some
folks might have against doing something here. "Let's make it worse
for nothing but ideological reasons" is not.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-21 14:00 ` alx-0029r2 " Alejandro Colomar
@ 2025-06-24 5:01 ` Alejandro Colomar
2025-06-24 9:07 ` Florian Weimer
2025-06-24 21:18 ` Eric Blake
0 siblings, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-24 5:01 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 17197 bytes --]
Hi!
Here's a new revision of the proposal. I've removed ENOMEM, since it's
not strictly necessary; it's only necessary that those systems that
already set it continue setting it (and my proposal for POSIX will
certainly include ENOMEM).
I've also added links to real bugs caused by this issue, as some people
seem to be blind to those. There have been RCE vulnerabilities caused
by people having to work around the brain damage of realloc(3) returning
NULL on success.
Below goes the proposal.
Have a lovely night!
Alex
---
Name
alx-0029r3 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention exceptional leak in code checking (size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
r3 (2025-06-23):
- Fix diff missing line.
- Remove ENOMEM from the proposal.
- Clarify that ENOMEM should be retained by platforms already
using it.
- Add mention that LLVM's address sanitizer will catch the leak
mentioned in r2.
- Add links to real bugs (including an RCE bug).
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://github.com/llvm/llvm-project/issues/113065>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
<https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
<https://gbhackers.com/whatsapp-double-free-vulnerability/>
Description
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
The problem is not only theoretical. Programmers don't know how
to use realloc(3) correctly, and have written weird code in
their attempts. This has resulted in a lot of non-sensical code
in configure scripts[1], and even bugs in actual programs[2].
[1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
[2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
In some cases, this non-sensical code has resulted in RCEs[3].
[3] <https://gbhackers.com/whatsapp-double-free-vulnerability/>
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in Unix V7, inherited by
the BSDs, and currently available in range of systems, including
musl libc, doesn't have any issues.
Code written for platforms returning a null can be migrated to
platforms returning non-null, without significant issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory (and assuming the implementations
will continue setting errno to ENOMEM), then code written for
AIX or glibc would continue working just fine, since the errno
check would be redundant with the null check. Simply, the
conditional (errno == ENOMEM) would always be true when
(new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs. Also, LLVM's address
sanitizer will soon able to catch such a leak:
<https://github.com/llvm/llvm-project/issues/113065>
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer on error, and use consistent
wording.
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
Unix V7
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in Unix V7,
and also present in the BSDs.
Design decisions
This change needs three changes, which can be applied all at
once, or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have.
This first step would require changes to (at least) the
following implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer.
The second step would require changes to (at least) the
following implementations: AIX.
This proposal has merged all steps into a single proposal.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
Existing implementations that set errno to ENOMEM must continue
doing so when the input pointer is not freed. If they didn't,
code that is currently portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would leak on error.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, this is not an issue in
ISO C.
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
The only important thing is that platforms that did set ENOMEM
should continue setting it, to avoid introducing leaks.
Proposed wording
Based on N3550.
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
-if the new object has not been allocated.
+is returned.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 5:01 ` alx-0029r3 " Alejandro Colomar
@ 2025-06-24 9:07 ` Florian Weimer
2025-06-24 9:54 ` Bruno Haible
` (3 more replies)
2025-06-24 21:18 ` Eric Blake
1 sibling, 4 replies; 143+ messages in thread
From: Florian Weimer @ 2025-06-24 9:07 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
* Alejandro Colomar:
> Here's a new revision of the proposal. I've removed ENOMEM, since it's
> not strictly necessary; it's only necessary that those systems that
> already set it continue setting it (and my proposal for POSIX will
> certainly include ENOMEM).
As far as I can see, this changes specification across all allocation
functions and requires them to be able to produce zero-sized objects.
Previously, the discussion was about changing realloc only.
Is this really the right direction, given that
int a[n];
is still undefined, and that C does not support zero-sized objects in
general?
Wouldn't it be more consistent to move in the other direction, and
require that allocations of zero size fail because C does not support
zero-sized objects?
(This is why I don't want to make any changes today—we just don't know
what the tightened specification will look like in the published
standard. There are just too many totally reasonable variations.)
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 9:07 ` Florian Weimer
@ 2025-06-24 9:54 ` Bruno Haible
2025-06-24 13:04 ` [musl] " Rich Felker
` (2 subsequent siblings)
3 siblings, 0 replies; 143+ messages in thread
From: Bruno Haible @ 2025-06-24 9:54 UTC (permalink / raw)
To: Florian Weimer
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
JeanHeyd Meneide, Rich Felker, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
Florian Weimer wrote:
> Wouldn't it be more consistent to move in the other direction, and
> require that allocations of zero size fail because C does not support
> zero-sized objects?
This would be a terrible idea. In all programming domains, the special
case of 0 needs to be specified in a way that most naturally extends
the sequence of cases 1, 2, 3, ... If this is not done, application
code must constantly have special code for 0, and since programmers
are not good at doing this consistently, this would produce a large
number of bugs.
So, it is the task of the ISO C committee to *simplify* application
programming by *integrating* the case of 0 smoothly with the cases
1, 2, 3, ...
They are not entirely there yet, but the direction (e.g. of N3322)
is the correct one.
> int a[n];
>
> is still undefined, and that C does not support zero-sized objects in
> general?
They are working on it; they are just not there yet.
Bruno
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 9:07 ` Florian Weimer
2025-06-24 9:54 ` Bruno Haible
@ 2025-06-24 13:04 ` Rich Felker
2025-06-24 14:18 ` Alejandro Colomar
2025-06-24 21:35 ` Eric Blake
3 siblings, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-24 13:04 UTC (permalink / raw)
To: Florian Weimer
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > Here's a new revision of the proposal. I've removed ENOMEM, since it's
> > not strictly necessary; it's only necessary that those systems that
> > already set it continue setting it (and my proposal for POSIX will
> > certainly include ENOMEM).
>
> As far as I can see, this changes specification across all allocation
> functions and requires them to be able to produce zero-sized objects.
> Previously, the discussion was about changing realloc only.
>
> Is this really the right direction, given that
>
> int a[n];
>
> is still undefined, and that C does not support zero-sized objects in
> general?
>
> Wouldn't it be more consistent to move in the other direction, and
> require that allocations of zero size fail because C does not support
> zero-sized objects?
>
> (This is why I don't want to make any changes today—we just don't know
> what the tightened specification will look like in the published
> standard. There are just too many totally reasonable variations.)
This is the change that pretty much nobody wants, except perhaps
ideologues out to inflict pain on users and implementors of the
language. It makes it harder to write correct code, requiring
special-casing of zero all over the place in addition to existing
essential difficulties for handlng overflow, and has no tangible
advantages. Even the ideological motivation is ill-posed, as the issue
of "no such thing as zero-sized objects" was solved *over 3 decades*
ago with the language in the standard (it's not a zero-sized object
but a pointer you can't dereference that compares non-equal to
others).
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 9:07 ` Florian Weimer
2025-06-24 9:54 ` Bruno Haible
2025-06-24 13:04 ` [musl] " Rich Felker
@ 2025-06-24 14:18 ` Alejandro Colomar
2025-06-25 0:14 ` [musl] " Jeffrey Walton
2025-06-24 21:35 ` Eric Blake
3 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-24 14:18 UTC (permalink / raw)
To: Florian Weimer
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 4273 bytes --]
Hi Florian,
On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > Here's a new revision of the proposal. I've removed ENOMEM, since it's
> > not strictly necessary; it's only necessary that those systems that
> > already set it continue setting it (and my proposal for POSIX will
> > certainly include ENOMEM).
>
> As far as I can see, this changes specification across all allocation
> functions and requires them to be able to produce zero-sized objects.
> Previously, the discussion was about changing realloc only.
Yes, but the changes in malloc(0) are a no-op for most implementations,
including glibc.
AIX is the only one we know that would need to be updated. Plus maybe
some small embedded imoplementations.
The previous discussion was about realloc(p,0) because that's the main
issue, which results in vulnerabilities, but committee members asked me
to merge both proposals at once --I had a _separate_ proposal for
malloc(0), which of course I didn't mention to glibc, as it's not a
problem here--, so that people would see the entire goal at once, so as
to avoid confusion.
But continue reading.
> Is this really the right direction, given that
>
> int a[n];
>
> is still undefined, and that C does not support zero-sized objects in
> general?
My plan is to address that after realloc(p,0).
In fact, Doug McIlroy suggested me in private to try to propose
standardizing
int a[0];
first, and then use it to convince that malloc(0) and realloc(p,0) should
follow.
However, I see more opposition for that than for malloc(0) and
realloc(0). One of the reasons is there are some platforms that
support malloc(0) and realloc(p,0), but none that support a[0].
>
> Wouldn't it be more consistent to move in the other direction, and
> require that allocations of zero size fail because C does not support
> zero-sized objects?
That's what some people have attempted since the times of SysV and C89.
Three decades after, people haven't achieved that, and we see the
fallout.
Plus, the only direction in which moving is relatively safe is from
returning-NULL behavior to returning-non-null behavior. Consider this
code written for a realloc(p,0) that returns NULL:
errno = 0;
new = realloc(old, n);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
If you suddenly return non-null from realloc(p,0), that code will
continue behaving well. In some other cases, as you can see in my
proposal, a memory leak would be introduced, which is a very mild
problem.
Now see some code written for platforms like musl or the BSDs:
new = realloc(old, n);
if (new == NULL)
free(old);
goto fail;
}
...
free(new);
Now, if realloc(p,0) starts returning NULL and freeing the object, like
in glibc, line 3 will result in a double-free. This is a serious bug,
which can result in RCE vulnerabilities.
See the Whatsapp RCE mentioned in the proposal. Here's another link
that talks about it which Bruno found, which mentions reallocarray():
<https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
So, we can't move in the direction of glibc or AIX, as it would break
the world.
> (This is why I don't want to make any changes today—we just don't know
> what the tightened specification will look like in the published
> standard. There are just too many totally reasonable variations.)
The thing is, someone in the C Committee asked me yesterday if glibc is
really into this if the committee agrees to move to the BSD behavior.
If you --glibc maintainers-- publicly agree to do this change with the
condition that the C Committee standardizes exactly this first, I'm
pretty sure I can convince the committee to standardize it. The
committee is worried that if it standardizes that, then glibc might
still ignore it.
So, both parties are worried that the other might not agree. The
solution I see is that you agree to the change conditional to having the
change in the standard first, and then the committee can change the
standard.
Would you agree to that?
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 5:01 ` alx-0029r3 " Alejandro Colomar
2025-06-24 9:07 ` Florian Weimer
@ 2025-06-24 21:18 ` Eric Blake
2025-06-24 23:01 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Eric Blake @ 2025-06-24 21:18 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Tue, Jun 24, 2025 at 07:01:50AM +0200, Alejandro Colomar wrote:
> Hi!
>
> Here's a new revision of the proposal. I've removed ENOMEM, since it's
> not strictly necessary; it's only necessary that those systems that
> already set it continue setting it (and my proposal for POSIX will
> certainly include ENOMEM).
>
> I've also added links to real bugs caused by this issue, as some people
> seem to be blind to those. There have been RCE vulnerabilities caused
> by people having to work around the brain damage of realloc(3) returning
> NULL on success.
>
> Below goes the proposal.
Some feedback to consider:
...
>
> Description
> The specification of realloc(3) has been problematic since the
> very first standards, even before ISO C. The wording has
> changed significantly, trying to forcedly permit implementations
> to return a null pointer when the requested size is zero. This
> originated from the intent of banning zero-sized objects from
> the language in C89, but that never worked well in
> retrospective, as we can see from the fallout.
>
> None of the specifications have been good, and C23 finally gave
> up and made it undefined behavior.
>
> The problem is not only theoretical. Programmers don't know how
> to use realloc(3) correctly, and have written weird code in
> their attempts. This has resulted in a lot of non-sensical code
> in configure scripts[1], and even bugs in actual programs[2].
>
> [1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
> [2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
>
> In some cases, this non-sensical code has resulted in RCEs[3].
>
> [3] <https://gbhackers.com/whatsapp-double-free-vulnerability/>
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in Unix V7, inherited by
> the BSDs, and currently available in range of systems, including
> musl libc, doesn't have any issues.
It may be worth specifically mentioning that glibc 2.1 and earlier
also had that behavior, even though it was an independent
implementation not derived from Unix V7; and that the _only_ reason
glibc 2.2 and later changed in 2000 to just freeing p instead of
returning a result like malloc(0) was because someone argued that the
C89 wording required that change, despite the new wording in C99 in
discussion under the time that was trying to remove the warts in the
C89 definition.
>
> Code written for platforms returning a null can be migrated to
> platforms returning non-null, without significant issues.
>
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
>
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
>
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem.
It may also be worth pointing out that any time code behaves one way
for 3, 2, 1, and then suddenly changes behavior at 0, it is much
harder to code the use of that interface correctly; when compared to
an interface where each successive call is merely one byte less in
effect than the previous-larger call.
>
> If the realloc(3) specification were changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory (and assuming the implementations
> will continue setting errno to ENOMEM), then code written for
> AIX or glibc would continue working just fine, since the errno
> check would be redundant with the null check. Simply, the
> conditional (errno == ENOMEM) would always be true when
> (new == NULL).
>
> Then, there are non-POSIX platforms that don't set ENOMEM. In
> those platforms, code might do this:
>
> new = realloc(old, size);
> if (new == NULL) {
> if (size != 0)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> That code would continue working with this proposal, except for
> a very rare corner case, in which it would leak. In the normal
> case, (size != 0) would never be true under (new == NULL),
> because a reallocation of 0 bytes would almost always succeed,
> and thus not return a null pointer under this proposal.
> However, in some cases, the system might not find space even for
> the small metadata needed for a 0-byte allocation. In such
> case, the (size != 0) conditional would prevent deallocating
> 'old', and thus cause a memory leak. This case is exceptional
> enough that it shouldn't stop us from fixing realloc(3).
> Anyway, on an out-of-memory case, the program is likely to
> terminate rather soon, so the issue is even less likely to have
> an impact on any existing programs. Also, LLVM's address
> sanitizer will soon able to catch such a leak:
> <https://github.com/llvm/llvm-project/issues/113065>
>
> This proposal makes handling of realloc(3) as straightforward as
> one would expect, with only two states: success or error. There
> are no in-between states.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return a null pointer on error, and use consistent
> wording.
>
> Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> Unix V7
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in Unix V7,
> and also present in the BSDs.
Would calling out glibc 2.1 as prior art help?
>
> Design decisions
> This change needs three changes, which can be applied all at
> once, or in separate steps.
You document "three" here...
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have.
>
> This first step would require changes to (at least) the
> following implementations: glibc, Bionic, Windows.
...then mention the first step twice...
>
> The second step would be to require that malloc(0) returns a
> non-null pointer.
>
> The second step would require changes to (at least) the
> following implementations: AIX.
...and the second step twice...
>
> This proposal has merged all steps into a single proposal.
...and no mention of the third step. You'll want to clean that up.
>
> Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes redundant several mentions of
> realloc(3) next to either free(3) or malloc(3) in the standard.
> We could remove them in this proposal, or clean up that in a
> separate (mostly editorial) proposal. Let's keep it for a
> future proposal for now.
>
> Caveats
> n?n:1
> Code written today should be careful, in case it can run on
> older systems that are not fixed to comply with this stricter
> specification. Thus, code written today should call realloc(3)
> similar to this:
>
> realloc(p, n?n:1);
>
> When all existing implementations are fixed to comply with this
> stricter specification, that workaround can be removed.
>
> ENOMEM
> Existing implementations that set errno to ENOMEM must continue
> doing so when the input pointer is not freed. If they didn't,
> code that is currently portable to all POSIX systems
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> would leak on error.
Would it also be worth mentioning (either here or as a footnote to be
added in the standard) that while atypical, realloc() is allowed to
fail with ENOMEM even when the new size is smaller than the previous
size of the pointer? This might be seen as a non-intuitive result,
but as Rich Felker pointed out, there ARE implementations of malloc()
that use alignment properties on the returned pointer itself as part
of the information encoding how much memory the region points to (such
as whether the allocation comes from mmap or the heap, for example),
and the standard should not be precluding these types of
implementations. With such a mention in place, it may also be worth
mentioning that when new==NULL, it is not necessary to call free(old)
immediately, if the programmer would rather ignore the fact that the
system cannot move the allocation to a more efficient location and
that the tail of the old pointer is now wasted space.
>
> Since it is currently impossible to write code today that is
> portable to arbitrary C17 systems, this is not an issue in
> ISO C.
>
> - New code written for C2y will only need to check for
> NULL to detect errors.
>
> - Code written for specific C17 and older platforms
> that don't set errno will continue to work for those
> specific platforms.
>
> - Code written for POSIX.1-2024 and older platforms
> will continue working on POSIX C2y platforms,
> assuming that POSIX will continue mandating ENOMEM.
>
> - Code written for POSIX.1-2024 and older will not be
> able to be run on non-POSIX C2y platforms, but that
> could be expected.
>
> The only important thing is that platforms that did set ENOMEM
> should continue setting it, to avoid introducing leaks.
>
> Proposed wording
> Based on N3550.
>
> 7.25.4.1 Memory management functions :: General
> @@ p1
> ...
> If the size of the space requested is zero,
> -the behavior is implementation-defined:
> -either
> -a null pointer is returned to indicate the error,
> -or
> the behavior is as if the size were some nonzero value,
> except that the returned pointer shall not be used
> to access an object.
>
> 7.25.4.2 The aligned_alloc function
> @@ Returns, p3
> The <b>aligned_alloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.3 The calloc function
> @@ Returns, p3
> The <b>calloc</b> function returns
> -either
> a pointer to the allocated space
> +on success.
> -or a null pointer
> -if
> +If
> the space cannot be allocated
> or if the product <tt>nmemb * size</tt>
> -would wraparound <b>size_t</b>.
> +would wraparound <b>size_t</b>,
> +a null pointer is returned.
Not part of this paper, but would it make sense for implementations
that return different errno for the two different classes of failures
here? ENOMEM when the pointer can't be allocated, and EINVAL (or
maybe ERANGE or EOVERFLOW) when nmemb*size overflows?
If C2y standardizes reallocarray(), then this becomes important.
Without sane errno values, it is impossible to tell whether
reallocarray() failed due to the inability to allocate the new
pointer, or whether it failed because the parameters overflowed and no
reallocation could be attempted. In fact, glibc documents that
attempting to malloc(PTRDIFF_MAX+1 bytes is treated as a failure, even
if that value is positive and less than the total amount of memory
available in the system, because objects cannot be so large as to
cause problems when computing pointer differences. But as long as the
interface is documented as leaving the old pointer unchanged on ANY
failures (whether allocation or overflow), then the semantics are
still easy to work with even when not having errno to rely on.
>
> 7.25.4.7 The malloc function
> @@ Returns, p3
> The <b>malloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.8 The realloc function
> @@ Description, p2
> The <b>realloc</b> function
> deallocates the old object pointed to by <tt>ptr</tt>
> +as if by a call to <b>free</b>,
> and returns a pointer to a new object
> -that has the size specified by <tt>size</tt>.
> +that has the size specified by <tt>size</tt>
> +as if by a call to <b>malloc</b>.
> The contents of the new object
> shall be the same as that of the old object prior to deallocation,
> up to the lesser of the new and old sizes.
> Any bytes in the new object
> beyond the size of the old object
> have unspecified values.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> ## We can probably remove all of the above, because of the
> ## behavior now being defined as-if by calls to malloc(3) and
> ## free(3). But let's do that editorially in a separate change.
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
>
> @@ Returns, p4
> The <b>realloc</b> function returns
> a pointer to the new object
> (which can have the same value
> -as a pointer to the old object),
> +as a pointer to the old object)
> +on success.
> -or
> +If
> +space cannot be allocated,
> a null pointer
> -if the new object has not been allocated.
> +is returned.
I'm liking the direction this proposal is headed in. If it is down to
a question of whether glibc or the C standard will blink first, I'm
hoping that we can get some agreement from both sides, rather than
being stuck in a stalemate with each arguing that the other is the
reason to not fix things.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 9:07 ` Florian Weimer
` (2 preceding siblings ...)
2025-06-24 14:18 ` Alejandro Colomar
@ 2025-06-24 21:35 ` Eric Blake
3 siblings, 0 replies; 143+ messages in thread
From: Eric Blake @ 2025-06-24 21:35 UTC (permalink / raw)
To: Florian Weimer
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > Here's a new revision of the proposal. I've removed ENOMEM, since it's
> > not strictly necessary; it's only necessary that those systems that
> > already set it continue setting it (and my proposal for POSIX will
> > certainly include ENOMEM).
>
> As far as I can see, this changes specification across all allocation
> functions and requires them to be able to produce zero-sized objects.
> Previously, the discussion was about changing realloc only.
>
> Is this really the right direction, given that
>
> int a[n];
>
> is still undefined, and that C does not support zero-sized objects in
> general?
>
> Wouldn't it be more consistent to move in the other direction, and
> require that allocations of zero size fail because C does not support
> zero-sized objects?
No. As long as there are two camps of behaviors (traditional
vs. SysV) [well, three camps if you count glibc's behavior that
mixes-and-matches traditional for malloc(0) and realloc(0,0) but SysV
for realloc(p,0) because of unfortunate C89 wording]), you need to
consider the impact of code written for one camp being run on a
platform that implements the other camp. Code written for traditional
but run on a SysV platform can double-free, which can lead to RCE;
while code written for SysV but run on traditional merely leaks the
non-NULL pointer returned for a 0-byte allocation. Of those two
failure scenarios, an eye towards safer programming should have no
hesitation in recognizing that getting rid of SysV behavior is the
only sensible approach that won't introduce double-free and RCEs in
code. (Code written portably avoids realloc(p,0) altogether, often by
the n?n:1 trick - and that code will still work regardless of what the
standard ends up saying for size 0)
>
> (This is why I don't want to make any changes today—we just don't know
> what the tightened specification will look like in the published
> standard. There are just too many totally reasonable variations.)
I can understand not wanting to make the changes if you aren't sure if
the C committee will go with wording that differs from the changes you
might make; but that sounds like a different stance ("wait and see
what the committee says before changing things to match") than never
making the change at all ("over my dead body, so any change the
committee makes will be ignored"). Expressing a willingness to make a
future change if the C committee's change is good will help in getting
the consensus for the C committee to make a good change.
We also need a bug filed against POSIX to either match or extend what
the C committee decides. For example, even if the C committee ends up
sticking with C23's "size 0 is undefined behavior", POSIX may still
want to go with a tighter definition that "size 0 is well-defined and
always allocates a distinct pointer on success" if it will not
alienate too many existing POSIX implementations. And glibc is one of
those interesting cases where there is no one single vendor trying to
keep glibc and the Linux kernel in sync with POSIX, but where POSIX
generally tries hard to not alienate glibc behavior.
[Counter-examples being things like Linux behavior on
mkdir("dangling_symlink/") are an entirely different discussion...]
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 21:18 ` Eric Blake
@ 2025-06-24 23:01 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-24 23:01 UTC (permalink / raw)
To: Eric Blake
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 9748 bytes --]
Hi Eric,
On Tue, Jun 24, 2025 at 04:18:40PM -0500, Eric Blake wrote:
> On Tue, Jun 24, 2025 at 07:01:50AM +0200, Alejandro Colomar wrote:
[...]
> Some feedback to consider:
> ...
>
> > Description
[...]
> > [3] <https://gbhackers.com/whatsapp-double-free-vulnerability/>
> >
> > However, this doesn't need to be like that. The traditional
> > implementation of realloc(3), present in Unix V7, inherited by
> > the BSDs, and currently available in range of systems, including
> > musl libc, doesn't have any issues.
>
> It may be worth specifically mentioning that glibc 2.1 and earlier
> also had that behavior, even though it was an independent
> implementation not derived from Unix V7; and that the _only_ reason
> glibc 2.2 and later changed in 2000 to just freeing p instead of
> returning a result like malloc(0) was because someone argued that the
> C89 wording required that change, despite the new wording in C99 in
> discussion under the time that was trying to remove the warts in the
> C89 definition.
Certainly; I'll add that.
[...]
> > Most code is written in this way, even if run on platforms
> > returning a null pointer. This is because most programmers are
> > just unaware of this problem.
>
> It may also be worth pointing out that any time code behaves one way
> for 3, 2, 1, and then suddenly changes behavior at 0, it is much
> harder to code the use of that interface correctly; when compared to
> an interface where each successive call is merely one byte less in
> effect than the previous-larger call.
Yup.
[...]
> > Prior art
[...]
> > Unix V7
> > The proposed behavior is the one endorsed by Doug McIlroy, the
> > author of the original implementation of realloc(3) in Unix V7,
> > and also present in the BSDs.
>
> Would calling out glibc 2.1 as prior art help?
Yup.
> > Design decisions
> > This change needs three changes, which can be applied all at
> > once, or in separate steps.
>
> You document "three" here...
Yeah, I forgot to s/three/two/ when I removed the requirement to set
ENOMEM.
[...]
> ...and no mention of the third step. You'll want to clean that up.
Yup.
[...]
> > Caveats
> > n?n:1
> > Code written today should be careful, in case it can run on
> > older systems that are not fixed to comply with this stricter
> > specification. Thus, code written today should call realloc(3)
> > similar to this:
> >
> > realloc(p, n?n:1);
> >
> > When all existing implementations are fixed to comply with this
> > stricter specification, that workaround can be removed.
> >
> > ENOMEM
> > Existing implementations that set errno to ENOMEM must continue
> > doing so when the input pointer is not freed. If they didn't,
> > code that is currently portable to all POSIX systems
> >
> > errno = 0;
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > would leak on error.
>
> Would it also be worth mentioning (either here or as a footnote to be
> added in the standard) that while atypical, realloc() is allowed to
> fail with ENOMEM even when the new size is smaller than the previous
> size of the pointer? This might be seen as a non-intuitive result,
> but as Rich Felker pointed out, there ARE implementations of malloc()
> that use alignment properties on the returned pointer itself as part
> of the information encoding how much memory the region points to (such
> as whether the allocation comes from mmap or the heap, for example),
> and the standard should not be precluding these types of
> implementations. With such a mention in place, it may also be worth
> mentioning that when new==NULL, it is not necessary to call free(old)
> immediately, if the programmer would rather ignore the fact that the
> system cannot move the allocation to a more efficient location and
> that the tail of the old pointer is now wasted space.
Yeah, that would make sense in a footnote.
[...]
> > @@ Returns, p3
> > The <b>calloc</b> function returns
> > -either
> > a pointer to the allocated space
> > +on success.
> > -or a null pointer
> > -if
> > +If
> > the space cannot be allocated
> > or if the product <tt>nmemb * size</tt>
> > -would wraparound <b>size_t</b>.
> > +would wraparound <b>size_t</b>,
> > +a null pointer is returned.
>
> Not part of this paper, but would it make sense for implementations
> that return different errno for the two different classes of failures
> here? ENOMEM when the pointer can't be allocated, and EINVAL (or
> maybe ERANGE or EOVERFLOW) when nmemb*size overflows?
If designing realloc(3) from scratch, it could make sense. But for
backwards compatibility, we should use ENOMEM for all errors that keep
the old pointer alive.
That's because reallocarray(3) uses ENOMEM for the overflow case too,
and so, if some code depends on that, we could keep it working. I
expect the consequence wouldn't be terrible if we change that --I expect
at most a leak--, but I'm not sure; we'd have to be careful.
For example, this code would be a portable way to call reallocarray(3)
in POSIX:
errno = 0;
new = reallocarray(old, n, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Using EOVERFLOW for n*size overflow would result in a leak of 'old' when
the size overflows.
We could decide that we can live with that leak, in return for a better
future where programs can differentiate between "OOM will trigger soon"
and "I just asked for an insane amount of memory".
Luckily, ISO C doesn't know about ENOMEM, so we have some time to talk
about it after fixing realloc(3) in ISO C.
> If C2y standardizes reallocarray(), then this becomes important.
> Without sane errno values, it is impossible to tell whether
> reallocarray() failed due to the inability to allocate the new
> pointer, or whether it failed because the parameters overflowed and no
> reallocation could be attempted. In fact, glibc documents that
> attempting to malloc(PTRDIFF_MAX+1 bytes is treated as a failure, even
> if that value is positive and less than the total amount of memory
> available in the system, because objects cannot be so large as to
> cause problems when computing pointer differences. But as long as the
> interface is documented as leaving the old pointer unchanged on ANY
> failures (whether allocation or overflow), then the semantics are
> still easy to work with even when not having errno to rely on.
Yeah, I think we have time to discuss this, as NULL will mean error, and
thus 'old' be unchanged. That is, if and only if realloc(3) returns
NULL, the original pointer should remain unchanged, per my proposal.
> > 7.25.4.7 The malloc function
> > @@ Returns, p3
> > The <b>malloc</b> function returns
> > -either
> > -a null pointer
> > -or
> > -a pointer to the allocated space.
> > +a pointer to the allocated space
> > +on success.
> > +If
> > +the space cannot be allocated,
> > +a null pointer is returned.
> >
> > 7.25.4.8 The realloc function
> > @@ Description, p2
> > The <b>realloc</b> function
> > deallocates the old object pointed to by <tt>ptr</tt>
> > +as if by a call to <b>free</b>,
> > and returns a pointer to a new object
> > -that has the size specified by <tt>size</tt>.
> > +that has the size specified by <tt>size</tt>
> > +as if by a call to <b>malloc</b>.
> > The contents of the new object
> > shall be the same as that of the old object prior to deallocation,
> > up to the lesser of the new and old sizes.
> > Any bytes in the new object
> > beyond the size of the old object
> > have unspecified values.
> >
> > @@ p3
> > If <tt>ptr</tt> is a null pointer,
> > the <b>realloc</b> function behaves
> > like the <b>malloc</b> function for the specified size.
> > Otherwise,
> > if <tt>ptr</tt> does not match a pointer
> > earlier returned by a memory management function,
> > or
> > if the space has been deallocated
> > by a call to the <b>free</b> or <b>realloc</b> function,
> > ## We can probably remove all of the above, because of the
> > ## behavior now being defined as-if by calls to malloc(3) and
> > ## free(3). But let's do that editorially in a separate change.
> > -or
> > -if the size is zero,
> > ## We're defining the behavior.
> > the behavior is undefined.
> > If
> > -memory for the new object is not allocated,
> > +the space cannot be allocated,
> > ## Editorial; for consistency with the wording of the other functions.
> > the old object is not deallocated
> > and its value is unchanged.
> >
> > @@ Returns, p4
> > The <b>realloc</b> function returns
> > a pointer to the new object
> > (which can have the same value
> > -as a pointer to the old object),
> > +as a pointer to the old object)
> > +on success.
> > -or
> > +If
> > +space cannot be allocated,
> > a null pointer
> > -if the new object has not been allocated.
> > +is returned.
>
> I'm liking the direction this proposal is headed in. If it is down to
> a question of whether glibc or the C standard will blink first, I'm
> hoping that we can get some agreement from both sides, rather than
> being stuck in a stalemate with each arguing that the other is the
> reason to not fix things.
Thanks a lot! I feel like we're closer to reach that.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-24 14:18 ` Alejandro Colomar
@ 2025-06-25 0:14 ` Jeffrey Walton
2025-06-25 0:19 ` Rich Felker
0 siblings, 1 reply; 143+ messages in thread
From: Jeffrey Walton @ 2025-06-25 0:14 UTC (permalink / raw)
To: musl
Cc: Florian Weimer, libc-alpha, bug-gnulib, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Tue, Jun 24, 2025 at 10:18 AM Alejandro Colomar <alx@kernel.org> wrote:
>
> Hi Florian,
>
> On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> > [...]
> > Wouldn't it be more consistent to move in the other direction, and
> > require that allocations of zero size fail because C does not support
> > zero-sized objects?
>
> That's what some people have attempted since the times of SysV and C89.
> Three decades after, people haven't achieved that, and we see the
> fallout.
>
> Plus, the only direction in which moving is relatively safe is from
> returning-NULL behavior to returning-non-null behavior. Consider this
> code written for a realloc(p,0) that returns NULL:
>
> errno = 0;
> new = realloc(old, n);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> If you suddenly return non-null from realloc(p,0), that code will
> continue behaving well. In some other cases, as you can see in my
> proposal, a memory leak would be introduced, which is a very mild
> problem.
I don't think a small memory leak is always a mild problem. On
Android, it could [eventually] use up all device memory as shared
objects are unloaded/loaded during the lifetime of an activity. I know
OpenSSL used to give the Java folks a lot of problems because they
(OpenSSL) was not cleaning up memory during the unload.
Jeff
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-25 0:14 ` [musl] " Jeffrey Walton
@ 2025-06-25 0:19 ` Rich Felker
2025-06-25 14:10 ` enh
0 siblings, 1 reply; 143+ messages in thread
From: Rich Felker @ 2025-06-25 0:19 UTC (permalink / raw)
To: Jeffrey Walton
Cc: musl, Florian Weimer, libc-alpha, bug-gnulib,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
On Tue, Jun 24, 2025 at 08:14:32PM -0400, Jeffrey Walton wrote:
> On Tue, Jun 24, 2025 at 10:18 AM Alejandro Colomar <alx@kernel.org> wrote:
> >
> > Hi Florian,
> >
> > On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> > > [...]
> > > Wouldn't it be more consistent to move in the other direction, and
> > > require that allocations of zero size fail because C does not support
> > > zero-sized objects?
> >
> > That's what some people have attempted since the times of SysV and C89.
> > Three decades after, people haven't achieved that, and we see the
> > fallout.
> >
> > Plus, the only direction in which moving is relatively safe is from
> > returning-NULL behavior to returning-non-null behavior. Consider this
> > code written for a realloc(p,0) that returns NULL:
> >
> > errno = 0;
> > new = realloc(old, n);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > If you suddenly return non-null from realloc(p,0), that code will
> > continue behaving well. In some other cases, as you can see in my
> > proposal, a memory leak would be introduced, which is a very mild
> > problem.
>
> I don't think a small memory leak is always a mild problem. On
> Android, it could [eventually] use up all device memory as shared
> objects are unloaded/loaded during the lifetime of an activity. I know
> OpenSSL used to give the Java folks a lot of problems because they
> (OpenSSL) was not cleaning up memory during the unload.
Isn't it normal/expected that Android apps leak memory all over the
place, in significant amounts not malloc(0)'s, and that the system
just keeps killing and restarting activities?
Small memory leaks can be a problem, like if they were in pid 1 or
something long-lived and critical, but that kind of software really
should be well-audited/tested for this kind of bug. I don't think
Android apps are one of the cases where it matters, though.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (4 preceding siblings ...)
2025-06-21 14:00 ` alx-0029r2 " Alejandro Colomar
@ 2025-06-25 1:58 ` Alejandro Colomar
2025-06-25 12:58 ` Eric Blake
2025-06-26 22:00 ` alx-0029r5 " Alejandro Colomar
` (2 subsequent siblings)
8 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 1:58 UTC (permalink / raw)
To: libc-alpha, Eric Blake, Florian Weimer
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 18583 bytes --]
Hi!
Here's a revision addressing a few things:
- Eric's comments for adding stuff to the rationale and prior art.
- Added a better link for the Whatsapp RCE, which specifically mentions
reallocarray(3).
- Add a footnote mentioning that realloc(3) is allowed to fail while
shrinking.
Eric, I could write something similar for POSIX. Do you prefer to do it
in parallel, or maybe after this is discussed in the C Committee meeting
in Brno (2025-08)?
Have a lovely day!
Alex
---
Name
alx-0029r4 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention exceptional leak in code checking (size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
r3 (2025-06-23):
- Fix diff missing line.
- Remove ENOMEM from the proposal.
- Clarify that ENOMEM should be retained by platforms already
using it.
- Add mention that LLVM's address sanitizer will catch the leak
mentioned in r2.
- Add links to real bugs (including an RCE bug).
r4 (2025-06-24):
- Use a better link for the Whatsapp RCE.
- s/Description/Rationale/
- wfix
- Mention that glibc <2.2 had the BSD behavior.
- Add footnote that realloc(3) may fail while shrinking.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://github.com/llvm/llvm-project/issues/113065>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
<https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
<https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
<https://gbhackers.com/whatsapp-double-free-vulnerability/>
Rationale
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
The problem is not only theoretical. Programmers don't know how
to use realloc(3) correctly, and have written weird code in
their attempts. This has resulted in a lot of non-sensical code
in configure scripts[1], and even bugs in actual programs[2].
[1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
[2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
In some cases, this non-sensical code has resulted in RCEs[3].
[3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in Unix V7, inherited by
the BSDs, and currently available in range of systems, including
musl libc, doesn't have any issues. glibc --which an
independent implementtion, not a Unix derivative-- also had this
behavior originally; it changed to the current behavior in 1999
(glibc 2.2), only for compatibility with C89, even though
ironically C99 was released soon after and made it
non-conforming.
Code written for platforms returning a null pointer can be
migrated to platforms returning non-null, without significant
issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem. Part of the reason is also that
returning a non-null pointer with zero bytes is the natural
extension of the behavior, which is what programmers intuitively
expect from libc; that is, if realloc(p,3) allocates 3 bytes,
r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
natural by induction to expect that r(p,0) will allocate zero
bytes. Most algorithms naturally extend to 0 just fine, and
special casing 0 is artificial.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory (and assuming the implementations
will continue setting errno to ENOMEM), then code written for
AIX or glibc would continue working just fine, since the errno
check would be redundant with the null check. Simply, the
conditional (errno == ENOMEM) would always be true when
(new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs. Also, LLVM's address
sanitizer will soon able to catch such a leak:
<https://github.com/llvm/llvm-project/issues/113065>
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer on error, and use consistent
wording.
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
Unix V7, BSD
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in Unix V7,
and also present in the BSDs.
glibc <= 2.1
glibc was implemented originally to return non-null. It was
only in 1999, and purely to comply with the standards --with no
requests by users to do so--, that the glibc maintainers decided
to switch to the current behavior.
Design decisions
This change needs two changes, which can be applied all at once,
or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have.
This first step would require changes to (at least) the
following implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer.
The second step would require changes to (at least) the
following implementations: AIX.
This proposal has merged all steps into a single proposal.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
Existing implementations that set errno to ENOMEM must continue
doing so when the input pointer is not freed. If they didn't,
code that is currently portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would leak on error.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, this is not an issue in
ISO C.
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
The only important thing is that platforms that did set ENOMEM
should continue setting it, to avoid introducing leaks.
Proposed wording
Based on N3550.
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
+XXX)
@@ New footnote XXX
+XXX)
+While atypical,
+<b>realloc</b> may fail
+for a call that shrinks the block of memory.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
-if the new object has not been allocated.
+is returned.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 1:58 ` alx-0029r4 " Alejandro Colomar
@ 2025-06-25 12:58 ` Eric Blake
2025-06-25 13:15 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Eric Blake @ 2025-06-25 12:58 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On Wed, Jun 25, 2025 at 03:58:31AM +0200, Alejandro Colomar wrote:
> Hi!
>
> Here's a revision addressing a few things:
>
...
> Rationale
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in Unix V7, inherited by
> the BSDs, and currently available in range of systems, including
> musl libc, doesn't have any issues. glibc --which an
> independent implementtion, not a Unix derivative-- also had this
Typos. Maybe:
glibc --which uses an independent implementation rather than a Unix
derivative--
> behavior originally; it changed to the current behavior in 1999
> (glibc 2.2), only for compatibility with C89, even though
> ironically C99 was released soon after and made it
> non-conforming.
glibc 2.2 was released Nov 2000; the change to realloc made it into
2.1.1 of May 1999. Elsewhere, you are clear that it it glibc <= 2.1
that has the old behavior. Maybe it's worth being more precise to
name it glibc 2.1.1 here (yes, I know that Paul Eggert originally
mentioned 2.2 in this thread and I've carelessly copied that value; I
guess this is evidence that glibc's version numbering policy has
changed slightly over the years).
I'm still not completely convinced that C99 declares the glibc
behavior to be non-conforming. On the one hand, it does have the
escape clause of "If the size of the space requested is zero, the
behavior is implementation-defined: either a null pointer is returned,
or the behavior is as if the size were some nonzero value, except that
the returned pointer shall not be used to access an object." in
7.20.3, with no clause requiring a null pointer return to be treated
as an error. On the other hand, it has both of these sentences on
realloc proper in 7.20.3.4: "If memory for the new object cannot be
allocated, the old object is not deallocated and its value is
unchanged. The realloc function returns a pointer to the new object
(which may have the same value as a pointer to the old object), or a
null pointer if the new object could not be allocated." So if you can
argue that a null pointer being returned MUST imply allocation
failure, it logically follows that allocation failure MUST imply the
original pointer was not deallocated (but glibc deallocated that
pointer, hence non-compliance); but the counter-argument is that if
the implementation defined that returning NULL is possible for reasons
other than allocation failure (which glibc has done), then an
application aware of the implementation-defined behavior can
distinguish between whether a NULL return implies that the old pointer
was deallocated (in glibc's case, the documentation is that errno will
be ENOMEM on allocation failure, and unchanged on the realloc(p,0)
successfully freeing a pointer). It is not obvious whether the C99
wording claims to have exhaustively listed all possible return types
into just two buckets, or whether it has has merely listed the two
possibilities that apply only to unconditional requirements, while
leaving a third possibility (namely, the return value when the
implementation-defined behavior has kicked in) unwritten.
Therefore, I'm not sure whether a blanket statement that glibc is
non-compliant to C99 will help; can you instead word it along the
lines of "There is an unsettled debate on whether glibc's behavior is
a compliant use of implementation-defined behavior for a size of zero
or a non-compliant case of returning NULL for more than just an
allocation failure"? I don't think weakening this sentence along
these lines will negatively impact the overall call for C2y to tighten
behavior going forward. Rather, it is easy to defend as fact that
there is (still!) a community debate (just point to this thread!) and
easy enough to call out two possible interpretations; and that comes
across as less argumentative than declaring as fact that glibc is
either compliant or non-compliant. Choosing a side alienates people
who have the opposite view, while acknowledging that there are (at
least) two possible viewpoints lets people of both persuasions still
feel included. (Declaring compliance becomes a lot easier if you can
state that "implementation abc fails standards-conformance text xyz" -
but I'm not sure anyone can point to a comprehensive industry-accepted
standards-conformance test for C99 compliance; the POSIX folks _do_
have a standards conformance test suite, but then you have the problem
that most vendors that have tried to pass that have not been using
glibc)
>
> Proposed wording
> Based on N3550.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> ## We can probably remove all of the above, because of the
> ## behavior now being defined as-if by calls to malloc(3) and
> ## free(3). But let's do that editorially in a separate change.
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
> +XXX)
>
> @@ New footnote XXX
> +XXX)
> +While atypical,
> +<b>realloc</b> may fail
> +for a call that shrinks the block of memory.
Is it worth wording this as "may fail or return a different pointer
for a call that shrinks the block of memory"?
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 12:58 ` Eric Blake
@ 2025-06-25 13:15 ` Alejandro Colomar
2025-06-25 17:35 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 13:15 UTC (permalink / raw)
To: Eric Blake
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 6507 bytes --]
Hi Eric,
On Wed, Jun 25, 2025 at 07:58:06AM -0500, Eric Blake wrote:
> On Wed, Jun 25, 2025 at 03:58:31AM +0200, Alejandro Colomar wrote:
[...]
> > implementation of realloc(3), present in Unix V7, inherited by
> > the BSDs, and currently available in range of systems, including
> > musl libc, doesn't have any issues. glibc --which an
> > independent implementtion, not a Unix derivative-- also had this
>
> Typos. Maybe:
>
> glibc --which uses an independent implementation rather than a Unix
> derivative--
Thanks!
> > behavior originally; it changed to the current behavior in 1999
> > (glibc 2.2), only for compatibility with C89, even though
> > ironically C99 was released soon after and made it
> > non-conforming.
>
> glibc 2.2 was released Nov 2000; the change to realloc made it into
> 2.1.1 of May 1999. Elsewhere, you are clear that it it glibc <= 2.1
> that has the old behavior. Maybe it's worth being more precise to
> name it glibc 2.1.1 here (yes, I know that Paul Eggert originally
> mentioned 2.2 in this thread and I've carelessly copied that value; I
> guess this is evidence that glibc's version numbering policy has
> changed slightly over the years).
Thanks!
>
> I'm still not completely convinced that C99 declares the glibc
> behavior to be non-conforming. On the one hand, it does have the
> escape clause of "If the size of the space requested is zero, the
> behavior is implementation-defined: either a null pointer is returned,
> or the behavior is as if the size were some nonzero value, except that
> the returned pointer shall not be used to access an object." in
> 7.20.3, with no clause requiring a null pointer return to be treated
> as an error. On the other hand, it has both of these sentences on
> realloc proper in 7.20.3.4: "If memory for the new object cannot be
> allocated, the old object is not deallocated and its value is
> unchanged. The realloc function returns a pointer to the new object
> (which may have the same value as a pointer to the old object), or a
> null pointer if the new object could not be allocated." So if you can
> argue that a null pointer being returned MUST imply allocation
> failure, it logically follows that allocation failure MUST imply the
> original pointer was not deallocated (but glibc deallocated that
> pointer, hence non-compliance);
Yup, that is my point of view.
> but the counter-argument is that if
> the implementation defined that returning NULL is possible for reasons
> other than allocation failure (which glibc has done), then an
> application aware of the implementation-defined behavior can
> distinguish between whether a NULL return implies that the old pointer
> was deallocated (in glibc's case, the documentation is that errno will
> be ENOMEM on allocation failure, and unchanged on the realloc(p,0)
> successfully freeing a pointer). It is not obvious whether the C99
> wording claims to have exhaustively listed all possible return types
> into just two buckets, or whether it has has merely listed the two
> possibilities that apply only to unconditional requirements, while
> leaving a third possibility (namely, the return value when the
> implementation-defined behavior has kicked in) unwritten.
While I consider that this might have been the intention of the
committee, I think the wording as is doesn't allow that interpretation
(the wording under 7.20.3.4 is quite explicit, and doesn't seem to
allow such an extension).
> Therefore, I'm not sure whether a blanket statement that glibc is
> non-compliant to C99 will help; can you instead word it along the
> lines of "There is an unsettled debate on whether glibc's behavior is
> a compliant use of implementation-defined behavior for a size of zero
> or a non-compliant case of returning NULL for more than just an
> allocation failure"? I don't think weakening this sentence along
> these lines will negatively impact the overall call for C2y to tighten
> behavior going forward. Rather, it is easy to defend as fact that
> there is (still!) a community debate (just point to this thread!) and
> easy enough to call out two possible interpretations; and that comes
> across as less argumentative than declaring as fact that glibc is
> either compliant or non-compliant. Choosing a side alienates people
> who have the opposite view, while acknowledging that there are (at
> least) two possible viewpoints lets people of both persuasions still
> feel included. (Declaring compliance becomes a lot easier if you can
> state that "implementation abc fails standards-conformance text xyz" -
> but I'm not sure anyone can point to a comprehensive industry-accepted
> standards-conformance test for C99 compliance; the POSIX folks _do_
> have a standards conformance test suite, but then you have the problem
> that most vendors that have tried to pass that have not been using
> glibc)
However, for these reasons, I find it useful to use less strong language
and not state non-conformance. I could say the wording is confusing
(which it is).
[...]
> > @@ p3
> > If <tt>ptr</tt> is a null pointer,
> > the <b>realloc</b> function behaves
> > like the <b>malloc</b> function for the specified size.
> > Otherwise,
> > if <tt>ptr</tt> does not match a pointer
> > earlier returned by a memory management function,
> > or
> > if the space has been deallocated
> > by a call to the <b>free</b> or <b>realloc</b> function,
> > ## We can probably remove all of the above, because of the
> > ## behavior now being defined as-if by calls to malloc(3) and
> > ## free(3). But let's do that editorially in a separate change.
> > -or
> > -if the size is zero,
> > ## We're defining the behavior.
> > the behavior is undefined.
> > If
> > -memory for the new object is not allocated,
> > +the space cannot be allocated,
> > ## Editorial; for consistency with the wording of the other functions.
> > the old object is not deallocated
> > and its value is unchanged.
> > +XXX)
> >
> > @@ New footnote XXX
> > +XXX)
> > +While atypical,
> > +<b>realloc</b> may fail
> > +for a call that shrinks the block of memory.
>
> Is it worth wording this as "may fail or return a different pointer
> for a call that shrinks the block of memory"?
Yeah, we can add that.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r3 - Restore the traditional realloc(3) specification
2025-06-25 0:19 ` Rich Felker
@ 2025-06-25 14:10 ` enh
0 siblings, 0 replies; 143+ messages in thread
From: enh @ 2025-06-25 14:10 UTC (permalink / raw)
To: Rich Felker
Cc: Jeffrey Walton, musl, Florian Weimer, libc-alpha, bug-gnulib,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Bruno Haible,
JeanHeyd Meneide, Adhemerval Zanella Netto, Joseph Myers,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
On Tue, Jun 24, 2025 at 8:19 PM Rich Felker <dalias@libc.org> wrote:
>
> On Tue, Jun 24, 2025 at 08:14:32PM -0400, Jeffrey Walton wrote:
> > On Tue, Jun 24, 2025 at 10:18 AM Alejandro Colomar <alx@kernel.org> wrote:
> > >
> > > Hi Florian,
> > >
> > > On Tue, Jun 24, 2025 at 11:07:53AM +0200, Florian Weimer wrote:
> > > > [...]
> > > > Wouldn't it be more consistent to move in the other direction, and
> > > > require that allocations of zero size fail because C does not support
> > > > zero-sized objects?
> > >
> > > That's what some people have attempted since the times of SysV and C89.
> > > Three decades after, people haven't achieved that, and we see the
> > > fallout.
> > >
> > > Plus, the only direction in which moving is relatively safe is from
> > > returning-NULL behavior to returning-non-null behavior. Consider this
> > > code written for a realloc(p,0) that returns NULL:
> > >
> > > errno = 0;
> > > new = realloc(old, n);
> > > if (new == NULL) {
> > > if (errno == ENOMEM)
> > > free(old);
> > > goto fail;
> > > }
> > > ...
> > > free(new);
> > >
> > > If you suddenly return non-null from realloc(p,0), that code will
> > > continue behaving well. In some other cases, as you can see in my
> > > proposal, a memory leak would be introduced, which is a very mild
> > > problem.
> >
> > I don't think a small memory leak is always a mild problem. On
> > Android, it could [eventually] use up all device memory as shared
> > objects are unloaded/loaded during the lifetime of an activity. I know
> > OpenSSL used to give the Java folks a lot of problems because they
> > (OpenSSL) was not cleaning up memory during the unload.
>
> Isn't it normal/expected that Android apps leak memory all over the
> place, in significant amounts not malloc(0)'s, and that the system
> just keeps killing and restarting activities?
no. if the system has to do that, that's considered a negative app
quality signal.
this is like saying "aren't all linux apps expected to leak memory and
be killed by the OOM killer?". sure, you can do that, but expect to
hear complaints.
> Small memory leaks can be a problem, like if they were in pid 1 or
> something long-lived and critical, but that kind of software really
> should be well-audited/tested for this kind of bug. I don't think
> Android apps are one of the cases where it matters, though.
>
> Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 13:15 ` Alejandro Colomar
@ 2025-06-25 17:35 ` Alejandro Colomar
2025-06-25 17:47 ` Alejandro Colomar
2025-06-25 18:03 ` Eric Blake
0 siblings, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 17:35 UTC (permalink / raw)
To: Eric Blake
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 911 bytes --]
Hi Eric,
On Wed, Jun 25, 2025 at 03:16:06PM +0200, Alejandro Colomar wrote:
> > > @@ New footnote XXX
> > > +XXX)
> > > +While atypical,
> > > +<b>realloc</b> may fail
> > > +for a call that shrinks the block of memory.
> >
> > Is it worth wording this as "may fail or return a different pointer
> > for a call that shrinks the block of memory"?
>
> Yeah, we can add that.
I've changed my mind; the current wording of ISO C makes it that all
realloc(3) successful return values are new pointers, and it doesn't
seem to mention that the old pointer could be kept (I remember having
seen such text in older standards, I think; or maybe in POSIX), so let's
keep in that sense, and assume that realloc(3) always moves the memory,
even if sometimes it doesn't, as that is not observable by a conforming
program.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 17:35 ` Alejandro Colomar
@ 2025-06-25 17:47 ` Alejandro Colomar
2025-06-25 18:07 ` Wilco Dijkstra
2025-06-25 18:10 ` Eric Blake
2025-06-25 18:03 ` Eric Blake
1 sibling, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 17:47 UTC (permalink / raw)
To: Eric Blake
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 1328 bytes --]
Hi Eric,
On Wed, Jun 25, 2025 at 07:36:01PM +0200, Alejandro Colomar wrote:
> On Wed, Jun 25, 2025 at 03:16:06PM +0200, Alejandro Colomar wrote:
> > > > @@ New footnote XXX
> > > > +XXX)
> > > > +While atypical,
> > > > +<b>realloc</b> may fail
> > > > +for a call that shrinks the block of memory.
> > >
> > > Is it worth wording this as "may fail or return a different pointer
> > > for a call that shrinks the block of memory"?
> >
> > Yeah, we can add that.
>
> I've changed my mind; the current wording of ISO C makes it that all
> realloc(3) successful return values are new pointers, and it doesn't
> seem to mention that the old pointer could be kept (I remember having
> seen such text in older standards, I think; or maybe in POSIX), so let's
> keep in that sense, and assume that realloc(3) always moves the memory,
> even if sometimes it doesn't, as that is not observable by a conforming
> program.
Oh, the text is still there; I didn't see it. :)
The realloc function returns a pointer to the new object
(which can have the same value as a pointer to the old object),
or a null pointer if the new object has not been allocated
I think I'll just remove that parenthetical, since it serves little
purpose.
Cheers,
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 17:35 ` Alejandro Colomar
2025-06-25 17:47 ` Alejandro Colomar
@ 2025-06-25 18:03 ` Eric Blake
2025-06-25 18:10 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Eric Blake @ 2025-06-25 18:03 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On Wed, Jun 25, 2025 at 07:35:58PM +0200, Alejandro Colomar wrote:
> Hi Eric,
>
> On Wed, Jun 25, 2025 at 03:16:06PM +0200, Alejandro Colomar wrote:
> > > > @@ New footnote XXX
> > > > +XXX)
> > > > +While atypical,
> > > > +<b>realloc</b> may fail
> > > > +for a call that shrinks the block of memory.
> > >
> > > Is it worth wording this as "may fail or return a different pointer
> > > for a call that shrinks the block of memory"?
> >
> > Yeah, we can add that.
>
> I've changed my mind; the current wording of ISO C makes it that all
> realloc(3) successful return values are new pointers, and it doesn't
> seem to mention that the old pointer could be kept (I remember having
> seen such text in older standards, I think; or maybe in POSIX), so let's
> keep in that sense, and assume that realloc(3) always moves the memory,
> even if sometimes it doesn't, as that is not observable by a conforming
> program.
Quoting from n3299 (I'm not sure if that's the best draft of C23, or
if I should attempt to get my hands on a more-precise pdf):
"The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by
size. The contents of the new object shall be the same as that of the
old object prior to deallocation, up to the lesser of the new and old
sizes. Any bytes in the new object beyond the size of the old object
have unspecified values."
"The realloc function returns a pointer to the new object (which can
have the same value as a pointer to the old object),..."
Return values are not "new pointers", but "new objects". Your
proposal is not changing the fact that all successful allocations are
"new objects", and the standard is already clear that new objects can
live in the same pointer. But I still feel that it is worth the
footnote to remind casual readers that even though reusing the pointer
is permissible, it is not mandatory, and as a result, code that
blindly assumes that shrinking an object will reuse the old pointer is
broken. (That change is independent of whether we fix realloc(p,0),
but since we're in the area, we might as well make the standard easier
to use).
By the way, even though the most common reason that the old and new
pointer are the same is in order to avoid having to copy/move data
from the old object to the new, there is another interesting case that
the standard permits: an implementation that uses mmap() can have
cases where the old pointer is different from the new pointer but
where none of the object data had to be copied or moved, and that's
because implementations can take advantage of MMU mapping metadata to
direct the new pointer into resolving to the same underlying storage
as the old pointer used to see.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 17:47 ` Alejandro Colomar
@ 2025-06-25 18:07 ` Wilco Dijkstra
2025-06-25 18:20 ` Eric Blake
` (2 more replies)
2025-06-25 18:10 ` Eric Blake
1 sibling, 3 replies; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-25 18:07 UTC (permalink / raw)
To: Alejandro Colomar, Eric Blake
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
Hi Alejandro,
> > > > +XXX)
> > > > +While atypical,
> > > > +<b>realloc</b> may fail
> > > > +for a call that shrinks the block of memory.
> > >
> > > Is it worth wording this as "may fail or return a different pointer
> > > for a call that shrinks the block of memory"?
> Oh, the text is still there; I didn't see it. :)
> The realloc function returns a pointer to the new object
> (which can have the same value as a pointer to the old object),
> or a null pointer if the new object has not been allocated
In principle a realloc that shrinks a non-NULL block does never need to fail.
If it can't shrink the current block (either because internal design means it
can't make it any smaller or because it doesn't have memory for a new
smaller block) then it should preferably return the original pointer instead
of returning NULL and taking the failure path.
So I'm wondering whether we should more clearly specify this - whenever
it's possible to not fail, don't return NULL?
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 18:03 ` Eric Blake
@ 2025-06-25 18:10 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 18:10 UTC (permalink / raw)
To: Eric Blake
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 3233 bytes --]
On Wed, Jun 25, 2025 at 01:03:13PM -0500, Eric Blake wrote:
> On Wed, Jun 25, 2025 at 07:35:58PM +0200, Alejandro Colomar wrote:
> > Hi Eric,
> >
> > On Wed, Jun 25, 2025 at 03:16:06PM +0200, Alejandro Colomar wrote:
> > > > > @@ New footnote XXX
> > > > > +XXX)
> > > > > +While atypical,
> > > > > +<b>realloc</b> may fail
> > > > > +for a call that shrinks the block of memory.
> > > >
> > > > Is it worth wording this as "may fail or return a different pointer
> > > > for a call that shrinks the block of memory"?
> > >
> > > Yeah, we can add that.
> >
> > I've changed my mind; the current wording of ISO C makes it that all
> > realloc(3) successful return values are new pointers, and it doesn't
> > seem to mention that the old pointer could be kept (I remember having
> > seen such text in older standards, I think; or maybe in POSIX), so let's
> > keep in that sense, and assume that realloc(3) always moves the memory,
> > even if sometimes it doesn't, as that is not observable by a conforming
> > program.
>
> Quoting from n3299 (I'm not sure if that's the best draft of C23, or
> if I should attempt to get my hands on a more-precise pdf):
>
> "The realloc function deallocates the old object pointed to by ptr and
> returns a pointer to a new object that has the size specified by
> size. The contents of the new object shall be the same as that of the
> old object prior to deallocation, up to the lesser of the new and old
> sizes. Any bytes in the new object beyond the size of the old object
> have unspecified values."
>
> "The realloc function returns a pointer to the new object (which can
> have the same value as a pointer to the old object),..."
>
> Return values are not "new pointers", but "new objects". Your
> proposal is not changing the fact that all successful allocations are
> "new objects", and the standard is already clear that new objects can
> live in the same pointer. But I still feel that it is worth the
> footnote to remind casual readers that even though reusing the pointer
> is permissible, it is not mandatory, and as a result, code that
> blindly assumes that shrinking an object will reuse the old pointer is
> broken. (That change is independent of whether we fix realloc(p,0),
> but since we're in the area, we might as well make the standard easier
> to use).
Okay, sounds reasonable for just a few words; I'll add them.
Cheers,
Alex
>
> By the way, even though the most common reason that the old and new
> pointer are the same is in order to avoid having to copy/move data
> from the old object to the new, there is another interesting case that
> the standard permits: an implementation that uses mmap() can have
> cases where the old pointer is different from the new pointer but
> where none of the object data had to be copied or moved, and that's
> because implementations can take advantage of MMU mapping metadata to
> direct the new pointer into resolving to the same underlying storage
> as the old pointer used to see.
>
> --
> Eric Blake, Principal Software Engineer
> Red Hat, Inc.
> Virtualization: qemu.org | libguestfs.org
>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 17:47 ` Alejandro Colomar
2025-06-25 18:07 ` Wilco Dijkstra
@ 2025-06-25 18:10 ` Eric Blake
1 sibling, 0 replies; 143+ messages in thread
From: Eric Blake @ 2025-06-25 18:10 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On Wed, Jun 25, 2025 at 07:47:55PM +0200, Alejandro Colomar wrote:
> Hi Eric,
>
> On Wed, Jun 25, 2025 at 07:36:01PM +0200, Alejandro Colomar wrote:
> > On Wed, Jun 25, 2025 at 03:16:06PM +0200, Alejandro Colomar wrote:
> > > > > @@ New footnote XXX
> > > > > +XXX)
> > > > > +While atypical,
> > > > > +<b>realloc</b> may fail
> > > > > +for a call that shrinks the block of memory.
> > > >
> > > > Is it worth wording this as "may fail or return a different pointer
> > > > for a call that shrinks the block of memory"?
> > >
> > > Yeah, we can add that.
> >
> > I've changed my mind; the current wording of ISO C makes it that all
> > realloc(3) successful return values are new pointers, and it doesn't
> > seem to mention that the old pointer could be kept (I remember having
> > seen such text in older standards, I think; or maybe in POSIX), so let's
> > keep in that sense, and assume that realloc(3) always moves the memory,
> > even if sometimes it doesn't, as that is not observable by a conforming
> > program.
>
> Oh, the text is still there; I didn't see it. :)
>
> The realloc function returns a pointer to the new object
> (which can have the same value as a pointer to the old object),
> or a null pointer if the new object has not been allocated
>
> I think I'll just remove that parenthetical, since it serves little
> purpose.
On the contrary, I think the parenthetical has a great purpose - it is
a hint to implementors that preserving the pointer values between the
old and new object is a desirable (but not mandatory) characteristic.
Deleting the parenthetical might cause people to misinterpret the
standard by mixing up "new object" with "new pointer", at which point
they might try to "fix" realloc(p,s) to never return p but always copy
data. When doing valgrind or other similar malloc-checking analysis
of a program, I'd actually WANT that behavior for realloc, to make
sure my code is always safely dealing with any potential of a moved
pointer; but in the common case, I do NOT want the speed penalty of an
implementation that always has to copy memory rather than reusing
pointers just because the standard omitted the parenthetical hinting
that such behavior is permissible.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 18:07 ` Wilco Dijkstra
@ 2025-06-25 18:20 ` Eric Blake
2025-06-25 18:21 ` Alejandro Colomar
2025-06-25 20:27 ` [musl] " Rich Felker
2 siblings, 0 replies; 143+ messages in thread
From: Eric Blake @ 2025-06-25 18:20 UTC (permalink / raw)
To: Wilco Dijkstra
Cc: Alejandro Colomar, libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
On Wed, Jun 25, 2025 at 06:07:06PM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > > > > +XXX)
> > > > > +While atypical,
> > > > > +<b>realloc</b> may fail
> > > > > +for a call that shrinks the block of memory.
> > > >
> > > > Is it worth wording this as "may fail or return a different pointer
> > > > for a call that shrinks the block of memory"?
>
> > Oh, the text is still there; I didn't see it. :)
>
> > The realloc function returns a pointer to the new object
> > (which can have the same value as a pointer to the old object),
> > or a null pointer if the new object has not been allocated
>
> In principle a realloc that shrinks a non-NULL block does never need to fail.
> If it can't shrink the current block (either because internal design means it
> can't make it any smaller or because it doesn't have memory for a new
> smaller block) then it should preferably return the original pointer instead
> of returning NULL and taking the failure path.
>
> So I'm wondering whether we should more clearly specify this - whenever
> it's possible to not fail, don't return NULL?
Rich already made the strong argument that for an implementation that
encodes which arena a pointer was allocated from, shrinking in place
only works so long as the unused tail of the allocation does not
exceed a certain bound; and Alejandro removed the quote from Doug
McIlroy from an earlier version of the draft precisely because we do
NOT want the standard to mandate the inability to fail on shrinking an
object, as that will harm quality-of-implementation.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 18:07 ` Wilco Dijkstra
2025-06-25 18:20 ` Eric Blake
@ 2025-06-25 18:21 ` Alejandro Colomar
2025-06-25 20:27 ` [musl] " Rich Felker
2 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-25 18:21 UTC (permalink / raw)
To: Wilco Dijkstra
Cc: Eric Blake, libc-alpha, Florian Weimer, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Vincent Lefevre, Mark Harris,
Collin Funk, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil
[-- Attachment #1: Type: text/plain, Size: 764 bytes --]
Hi Wilco,
On Wed, Jun 25, 2025 at 06:07:06PM +0000, Wilco Dijkstra wrote:
> In principle a realloc that shrinks a non-NULL block does never need to fail.
> If it can't shrink the current block (either because internal design means it
> can't make it any smaller or because it doesn't have memory for a new
> smaller block) then it should preferably return the original pointer instead
> of returning NULL and taking the failure path.
>
> So I'm wondering whether we should more clearly specify this - whenever
> it's possible to not fail, don't return NULL?
As Rich Felker said, musl has very good reasons for failing, several of
them. Look at his comments in the thread.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r4 - Restore the traditional realloc(3) specification
2025-06-25 18:07 ` Wilco Dijkstra
2025-06-25 18:20 ` Eric Blake
2025-06-25 18:21 ` Alejandro Colomar
@ 2025-06-25 20:27 ` Rich Felker
2 siblings, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-25 20:27 UTC (permalink / raw)
To: Wilco Dijkstra
Cc: Alejandro Colomar, Eric Blake, libc-alpha, Florian Weimer,
bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Thorsten Glaser,
Vincent Lefevre, Mark Harris, Collin Funk, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil
On Wed, Jun 25, 2025 at 06:07:06PM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > > > > +XXX)
> > > > > +While atypical,
> > > > > +<b>realloc</b> may fail
> > > > > +for a call that shrinks the block of memory.
> > > >
> > > > Is it worth wording this as "may fail or return a different pointer
> > > > for a call that shrinks the block of memory"?
>
> > Oh, the text is still there; I didn't see it. :)
>
> > The realloc function returns a pointer to the new object
> > (which can have the same value as a pointer to the old object),
> > or a null pointer if the new object has not been allocated
>
> In principle a realloc that shrinks a non-NULL block does never need to fail.
> If it can't shrink the current block (either because internal design means it
> can't make it any smaller or because it doesn't have memory for a new
> smaller block) then it should preferably return the original pointer instead
> of returning NULL and taking the failure path.
No, it should not. I've addressed this at least two times already in
this thread.
> So I'm wondering whether we should more clearly specify this - whenever
> it's possible to not fail, don't return NULL?
No, it should fail if it cannot meaningfully perform the operation.
The application is always free to keep using the old object if it was
larger than needed, but this way, the application *knows it still has
that memory tied up* rather than being lied to that it was freed, and
can make an informed decision about what to do. Usually the right
thing is to back out the operation it was performing, because
something along the way was tying up an inordinate amount of memory.
Continuing with forward progress unaware of this condition is much
more likely to lead to a state where it can't back out and recover.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (5 preceding siblings ...)
2025-06-25 1:58 ` alx-0029r4 " Alejandro Colomar
@ 2025-06-26 22:00 ` Alejandro Colomar
2025-06-27 0:44 ` Mark Harris
2025-06-27 8:52 ` Florian Weimer
2025-06-27 14:01 ` alx-0029r6 " Alejandro Colomar
2025-10-09 22:37 ` alx-0029r8 " Alejandro Colomar
8 siblings, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-26 22:00 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 20140 bytes --]
Hi!
Here's a new revision addressing the suggestions by Eric in v4. I've
added a new subsection in the Rationale explaining why not go the other
way around, as people keep suggesting that every now and then.
I'll submit this version to the C Committee today, since there seems to
be more consensus now, and the recent iterations have seen only minor
wording improvements, but no major changes. Of course, we can continue
improving the paper, though. Please suggest any improvements you may
consider appropriate.
It would be good to have explicit replies by glibc maintainers about it,
so that the C Committee understands better what the maintainers think
about it. I've got word from some committee members that if I can
convince the maintainers, they'll vote for standardizing it. So, it
would be great it people could emit 'Acked-by:' tags, or otherwise
explain their position.
Have a lovely day!
Alex
---
Name
alx-0029r5 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention exceptional leak in code checking (size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
r3 (2025-06-23):
- Fix diff missing line.
- Remove ENOMEM from the proposal.
- Clarify that ENOMEM should be retained by platforms already
using it.
- Add mention that LLVM's address sanitizer will catch the leak
mentioned in r2.
- Add links to real bugs (including an RCE bug).
r4 (2025-06-24):
- Use a better link for the Whatsapp RCE.
- s/Description/Rationale/
- wfix
- Mention that glibc <2.1.1 had the BSD behavior.
- Add footnote that realloc(3) may fail while shrinking.
r5 (2025-06-26):
- It was glibc 2.1.1 that broke it, not glibc 2.2.
- wfix
- Mention in the footnote that the pointer may change.
- Document why not go the other way around. It was explained
several times during discussion, but people keep suggesting
it.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://github.com/llvm/llvm-project/issues/113065>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
<https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
<https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
<https://gbhackers.com/whatsapp-double-free-vulnerability/>
Rationale
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
The problem is not only theoretical. Programmers don't know how
to use realloc(3) correctly, and have written weird code in
their attempts. This has resulted in a lot of non-sensical code
in configure scripts[1], and even bugs in actual programs[2].
[1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
[2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
In some cases, this non-sensical code has resulted in RCEs[3].
[3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in Unix V7, inherited by
the BSDs, and currently available in range of systems, including
musl libc, doesn't have any issues. glibc --which uses an
independent implemention rather than a Unix derivative-- also
had this behavior originally; it changed to the current behavior
in 1999 (glibc 2.1.1), only for compatibility with C89, even
though ironically C99 was released soon after and removed the
text that glibc was trying to comply to, and introduced some new
text that was very confusing, and one of its interpretations
would make the new glibc behavior non-conforming.
Code written for platforms returning a null pointer can be
migrated to platforms returning non-null, without significant
issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem. Part of the reason is also that
returning a non-null pointer with zero bytes is the natural
extension of the behavior, which is what programmers intuitively
expect from libc; that is, if realloc(p,3) allocates 3 bytes,
r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
natural by induction to expect that r(p,0) will allocate zero
bytes. Most algorithms naturally extend to 0 just fine, and
special casing 0 is artificial.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory (and assuming the implementations
will continue setting errno to ENOMEM), then code written for
AIX or glibc would continue working just fine, since the errno
check would be redundant with the null check. Simply, the
conditional (errno == ENOMEM) would always be true when
(new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs. Also, LLVM's address
sanitizer will soon able to catch such a leak:
<https://github.com/llvm/llvm-project/issues/113065>
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer on error, and use consistent
wording.
Why not go the other way around?
Some people keep asking why not go the other way around: why not
force the BSDs and musl to return a null pointer if size is 0.
This would result in double-free and use-after-free bugs, which
can result in RCE vulnerabilities (remote code execution), which
is clearly unacceptable.
Consider this code, which is the usual code for calling
realloc(3) in such systems:
new = realloc(old, size);
if (new == NULL) {
free(old);
goto fail;
}
...
free(new);
If realoc(p,0) would return a null pointer and free the old
block, then the third line would be a double-free bug.
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
Unix V7, BSD
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in Unix V7,
and also present in the BSDs.
glibc <= 2.1
glibc was implemented originally to return non-null. It was
only in 1999, and purely to comply with the standards --with no
requests by users to do so--, that the glibc maintainers decided
to switch to the current behavior.
Design decisions
This change needs two changes, which can be applied all at once,
or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have. This
would require changes to (at least) the following
implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer. This would require changes to (at least) the
following implementations: AIX.
This proposal has merged all steps into a single proposal.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
Existing implementations that set errno to ENOMEM must continue
doing so when the input pointer is not freed. If they didn't,
code that is currently portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would leak on error.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, this is not an issue in
ISO C.
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
The only important thing is that platforms that did set ENOMEM
should continue setting it, to avoid introducing leaks.
Proposed wording
Based on N3550.
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
+XXX)
@@ New footnote XXX
+XXX)
+While atypical,
+<b>realloc</b> may fail
+or return a different pointer
+for a call that shrinks the block of memory.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
-if the new object has not been allocated.
+is returned.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-26 22:00 ` alx-0029r5 " Alejandro Colomar
@ 2025-06-27 0:44 ` Mark Harris
2025-06-27 1:51 ` Alejandro Colomar
2025-06-27 8:52 ` Florian Weimer
1 sibling, 1 reply; 143+ messages in thread
From: Mark Harris @ 2025-06-27 0:44 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
Alejandro Colomar wrote:
>
> Hi!
>
> Here's a new revision addressing the suggestions by Eric in v4. I've
> added a new subsection in the Rationale explaining why not go the other
> way around, as people keep suggesting that every now and then.
>
> I'll submit this version to the C Committee today, since there seems to
> be more consensus now, and the recent iterations have seen only minor
> wording improvements, but no major changes. Of course, we can continue
> improving the paper, though. Please suggest any improvements you may
> consider appropriate.
>
> It would be good to have explicit replies by glibc maintainers about it,
> so that the C Committee understands better what the maintainers think
> about it. I've got word from some committee members that if I can
> convince the maintainers, they'll vote for standardizing it. So, it
> would be great it people could emit 'Acked-by:' tags, or otherwise
> explain their position.
>
>
> Have a lovely day!
> Alex
>
> ---
> Name
> alx-0029r5 - Restore the traditional realloc(3) specification
>
> Principles
> - Uphold the character of the language
> - Keep the language small and simple
> - Facilitate portability
> - Avoid ambiguities
> - Pay attention to performance
> - Codify existing practice to address evident deficiencies.
> - Do not prefer any implementation over others
> - Ease migration to newer language editions
> - Avoid quiet changes
> - Enable secure programming
>
> Category
> Remove UB.
>
> Author
> Alejandro Colomar <alx@kernel.org>
>
> Cc: <bug-gnulib@gnu.org>
> Cc: <musl@lists.openwall.com>
> Cc: <libc-alpha@sourceware.org>
> Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Cc: Paul Eggert <eggert@cs.ucla.edu>
> Cc: Robert Seacord <rcseacord@gmail.com>
> Cc: Elliott Hughes <enh@google.com>
> Cc: Bruno Haible <bruno@clisp.org>
> Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> Cc: Joseph Myers <josmyers@redhat.com>
> Cc: Florian Weimer <fweimer@redhat.com>
> Cc: Andreas Schwab <schwab@suse.de>
> Cc: Thorsten Glaser <tg@mirbsd.de>
> Cc: Eric Blake <eblake@redhat.com>
> Cc: Vincent Lefevre <vincent@vinc17.net>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: Collin Funk <collin.funk1@gmail.com>
> Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
> Cc: DJ Delorie <dj@redhat.com>
> Cc: Cristian Rodríguez <cristian@rodriguez.im>
> Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
> Cc: Sam James <sam@gentoo.org>
> Cc: Mark Wielaard <mark@klomp.org>
> Cc: "Maciej W. Rozycki" <macro@redhat.com>
> Cc: Martin Uecker <ma.uecker@gmail.com>
> Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
> Cc: <eskil@obsession.se>
> Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
>
> History
> <https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
>
> r0 (2025-06-17):
> - Initial draft.
>
> r1 (2025-06-20):
> - Full rewrite after the recent glibc discussion.
>
> r2 (2025-06-21):
> - Remove CC. Add CC.
> - wfix.
> - Drop quote.
> - Add a few more principles
> - Clarify why ENOMEM is used in this proposal, and make it
> optional.
> - Mention exceptional leak in code checking (size != 0).
> - Clarify that part of the description of realloc can be
> editorially removed after this change.
>
> r3 (2025-06-23):
> - Fix diff missing line.
> - Remove ENOMEM from the proposal.
> - Clarify that ENOMEM should be retained by platforms already
> using it.
> - Add mention that LLVM's address sanitizer will catch the leak
> mentioned in r2.
> - Add links to real bugs (including an RCE bug).
>
> r4 (2025-06-24):
> - Use a better link for the Whatsapp RCE.
> - s/Description/Rationale/
> - wfix
> - Mention that glibc <2.1.1 had the BSD behavior.
> - Add footnote that realloc(3) may fail while shrinking.
>
> r5 (2025-06-26):
> - It was glibc 2.1.1 that broke it, not glibc 2.2.
> - wfix
> - Mention in the footnote that the pointer may change.
> - Document why not go the other way around. It was explained
> several times during discussion, but people keep suggesting
> it.
>
> See also
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
> <https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
> <https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
> <https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
> <https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
> <https://github.com/llvm/llvm-project/issues/113065>
> <https://www.austingroupbugs.net/view.php?id=400>
> <https://www.austingroupbugs.net/view.php?id=526>
> <https://www.austingroupbugs.net/view.php?id=688>
> <https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
> <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
> <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
> <https://gbhackers.com/whatsapp-double-free-vulnerability/>
>
> Rationale
> The specification of realloc(3) has been problematic since the
> very first standards, even before ISO C. The wording has
> changed significantly, trying to forcedly permit implementations
> to return a null pointer when the requested size is zero. This
> originated from the intent of banning zero-sized objects from
> the language in C89, but that never worked well in
> retrospective, as we can see from the fallout.
I support the outcome that you are trying to achieve, however I think
the proposal could use a better explanation upfront as to what problem
this is attempting to solve. Calling a well-established function like
realloc "problematic" without explaining what is problematic about it
isn't helpful. This states that the wording has changed
significantly, but if that was the problem then a proposal to change
the wording again is clearly not addressing that.
Also I'm pretty sure that this did not originate from the intent of
banning zero-sized objects from the language. If there was an actual
effort to ban the possibility of zero-sized objects then it would have
been easier for them to simply define malloc(0) and realloc(p, 0) to
always be an error. It seems far more likely that the committee was
just trying to document the current behavior that programmers could
rely on, without unnecessarily making current implementations
nonconforming.
>
> None of the specifications have been good, and C23 finally gave
> up and made it undefined behavior.
>
> The problem is not only theoretical. Programmers don't know how
> to use realloc(3) correctly, and have written weird code in
> their attempts. This has resulted in a lot of non-sensical code
> in configure scripts[1], and even bugs in actual programs[2].
>
> [1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
It's not clear what you are claiming is nonsensical here or why. You
think that configure checks are in general nonsensical, or what is it
about these checks in particular that is nonsensical?
> [2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
>
> In some cases, this non-sensical code has resulted in RCEs[3].
>
> [3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
This is using reallocarray(), which comes from OpenBSD, and is not in
ISO C at all. In OpenBSD it already had the desired behavior for size
0; an implementation of the OpenBSD reallocarray() that does not
correctly match its behavior is just buggy. POSIX picked it up after
Glibc and Bionic already created an implementation that did not match
the OpenBSD behavior.
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in Unix V7, inherited by
> the BSDs, and currently available in range of systems, including
> musl libc, doesn't have any issues. glibc --which uses an
> independent implemention rather than a Unix derivative-- also
> had this behavior originally; it changed to the current behavior
> in 1999 (glibc 2.1.1), only for compatibility with C89, even
> though ironically C99 was released soon after and removed the
> text that glibc was trying to comply to, and introduced some new
> text that was very confusing, and one of its interpretations
> would make the new glibc behavior non-conforming.
s/in range/in a range/
s/implemention/implementation/
s/comply to/comply with/
Assuming that you are referring to Seventh Edition Unix from 1979, it
would be better to spell it out or write V7 Unix, to distinguish it
from UNIX V7 (https://unix.org/unixv7.html) which refers to Issue 7 of
the standard commonly known as POSIX.
Your claim that this 1979 implementation of realloc()[1] "doesn't have
any issues" is grossly incongruent with reality. This realloc(p, n)
first calls free(p), then malloc(n), and finally copies the data if
the pointer has changed; at the time this was safe because there was
no multithreading and free() and malloc() did not modify the contents
of the memory block, however if realloc() failed it returned NULL with
p freed (but you could still read the data from the freed block at
least until the next allocation). Also this implementation of
realloc() did not accept p == NULL; that would crash. However, a
documented bonus feature not found in modern realloc() was that p
could be "a block freed since the last call of malloc, realloc or
calloc".[2]
[1] https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/libc/gen/malloc.c
[2] https://man.cat-v.org/unix_7th/3/malloc
Is this really the "traditional realloc(3) specification" that the
title of this proposal is suggesting be restored? I suggest being
more specific as to what you actually want.
>
> Code written for platforms returning a null pointer can be
> migrated to platforms returning non-null, without significant
> issues.
>
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
It used to be a common thing for a C library or function to allow
overriding the allocator that it uses internally with your own custom
allocator. Rather than having to specify multiple function pointers
for your own custom malloc, free, realloc, etc. sometimes it would
just take one function pointer for your custom realloc, with the
default being libc realloc. The library would allocate memory using
(*customalloc)(NULL, n), free it using (*customalloc)(p, 0), and
realloc using (*customalloc)(p, n). This simplified the interface for
overriding the allocator, and if it stored the custom allocator in
each object then it only had to store one function pointer. The point
is that I would expect calls to realloc that intend to only free
memory are more likely to be indirect calls than direct calls that are
easily diagnosed at compile time or with a simple search; for a direct
call it is easier to just call free. That said, I haven't seen this
kind of interface for years.
Both Glibc and Bionic already annotate realloc with
__attribute__((__warn_unused_result__)), so that should already warn
about direct calls to realloc(p, 0) that don't use the result
(indicating that the caller was likely intending to only free the
memory and not get another block in return). I would consider that
existing warning to be better than warning on any realloc(p, 0),
because it avoids false positives when the caller is already handling
any pointer that may be returned.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
>
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
That is ridiculous; I've never seen anyone else check errno to
determine whether realloc has failed. It is far easier to just ensure
that size is non-zero. Yes it would be even easier if you didn't need
to ensure that, which is why I support your proposal, but it doesn't
have anything to do with errno.
The primary benefit of malloc and realloc setting errno to ENOMEM on
failure is that if you are writing a function that reports errors in
errno you can just return -1 on malloc or realloc failure, or if you
are calling other functions that report errors in errno you can share
a common error handling path with them that logs the error from errno
and don't need a custom error handling path for logging memory
allocation failures.
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
>
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem. Part of the reason is also that
> returning a non-null pointer with zero bytes is the natural
> extension of the behavior, which is what programmers intuitively
> expect from libc; that is, if realloc(p,3) allocates 3 bytes,
> r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
> natural by induction to expect that r(p,0) will allocate zero
> bytes. Most algorithms naturally extend to 0 just fine, and
> special casing 0 is artificial.
>
> If the realloc(3) specification were changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory (and assuming the implementations
> will continue setting errno to ENOMEM), then code written for
> AIX or glibc would continue working just fine, since the errno
> check would be redundant with the null check. Simply, the
> conditional (errno == ENOMEM) would always be true when
> (new == NULL).
>
> Then, there are non-POSIX platforms that don't set ENOMEM. In
> those platforms, code might do this:
>
> new = realloc(old, size);
> if (new == NULL) {
> if (size != 0)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> That code would continue working with this proposal, except for
> a very rare corner case, in which it would leak. In the normal
> case, (size != 0) would never be true under (new == NULL),
This is backwards.
s/never // ?
> because a reallocation of 0 bytes would almost always succeed,
> and thus not return a null pointer under this proposal.
> However, in some cases, the system might not find space even for
> the small metadata needed for a 0-byte allocation. In such
> case, the (size != 0) conditional would prevent deallocating
> 'old', and thus cause a memory leak. This case is exceptional
> enough that it shouldn't stop us from fixing realloc(3).
> Anyway, on an out-of-memory case, the program is likely to
> terminate rather soon, so the issue is even less likely to have
> an impact on any existing programs. Also, LLVM's address
> sanitizer will soon able to catch such a leak:
> <https://github.com/llvm/llvm-project/issues/113065>
>
> This proposal makes handling of realloc(3) as straightforward as
> one would expect, with only two states: success or error. There
> are no in-between states.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return a null pointer on error, and use consistent
> wording.
>
> Why not go the other way around?
> Some people keep asking why not go the other way around: why not
> force the BSDs and musl to return a null pointer if size is 0.
> This would result in double-free and use-after-free bugs, which
> can result in RCE vulnerabilities (remote code execution), which
> is clearly unacceptable.
>
> Consider this code, which is the usual code for calling
> realloc(3) in such systems:
>
> new = realloc(old, size);
> if (new == NULL) {
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> If realoc(p,0) would return a null pointer and free the old
s/realoc/realloc/
> block, then the third line would be a double-free bug.
>
> Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> Unix V7, BSD
s/Unix V7/Seventh Edition Unix/
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in Unix V7,
> and also present in the BSDs.
>
> glibc <= 2.1
> glibc was implemented originally to return non-null. It was
> only in 1999, and purely to comply with the standards --with no
> requests by users to do so--, that the glibc maintainers decided
> to switch to the current behavior.
>
> Design decisions
> This change needs two changes, which can be applied all at once,
> or in separate steps.
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have. This
> would require changes to (at least) the following
> implementations: glibc, Bionic, Windows.
>
> The second step would be to require that malloc(0) returns a
> non-null pointer. This would require changes to (at least) the
> following implementations: AIX.
I appreciate that you no longer claim that these are the ONLY
implementations that would require changes.
>
> This proposal has merged all steps into a single proposal.
>
> Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes redundant several mentions of
> realloc(3) next to either free(3) or malloc(3) in the standard.
> We could remove them in this proposal, or clean up that in a
> separate (mostly editorial) proposal. Let's keep it for a
> future proposal for now.
>
> Caveats
> n?n:1
> Code written today should be careful, in case it can run on
> older systems that are not fixed to comply with this stricter
> specification. Thus, code written today should call realloc(3)
> similar to this:
>
> realloc(p, n?n:1);
>
> When all existing implementations are fixed to comply with this
> stricter specification, that workaround can be removed.
>
> ENOMEM
> Existing implementations that set errno to ENOMEM must continue
> doing so when the input pointer is not freed. If they didn't,
> code that is currently portable to all POSIX systems
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> would leak on error.
>
> Since it is currently impossible to write code today that is
> portable to arbitrary C17 systems, this is not an issue in
> ISO C.
Current code that calls realloc in a manner that is portable to
arbitrary C17 or C23 systems will already ensure that the size
argument is non-zero (for example, using the n?n:1 method above).
Such code is very possible and requires no changes.
>
> - New code written for C2y will only need to check for
> NULL to detect errors.
>
> - Code written for specific C17 and older platforms
> that don't set errno will continue to work for those
> specific platforms.
>
> - Code written for POSIX.1-2024 and older platforms
> will continue working on POSIX C2y platforms,
> assuming that POSIX will continue mandating ENOMEM.
>
> - Code written for POSIX.1-2024 and older will not be
> able to be run on non-POSIX C2y platforms, but that
> could be expected.
I don't see how this is relevant to this proposal.
>
> The only important thing is that platforms that did set ENOMEM
> should continue setting it, to avoid introducing leaks.
>
> Proposed wording
> Based on N3550.
>
> 7.25.4.1 Memory management functions :: General
> @@ p1
> ...
> If the size of the space requested is zero,
> -the behavior is implementation-defined:
> -either
> -a null pointer is returned to indicate the error,
> -or
> the behavior is as if the size were some nonzero value,
> except that the returned pointer shall not be used
> to access an object.
>
> 7.25.4.2 The aligned_alloc function
> @@ Returns, p3
> The <b>aligned_alloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.3 The calloc function
> @@ Returns, p3
> The <b>calloc</b> function returns
> -either
> a pointer to the allocated space
> +on success.
> -or a null pointer
> -if
> +If
> the space cannot be allocated
> or if the product <tt>nmemb * size</tt>
> -would wraparound <b>size_t</b>.
> +would wraparound <b>size_t</b>,
> +a null pointer is returned.
>
> 7.25.4.7 The malloc function
> @@ Returns, p3
> The <b>malloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.8 The realloc function
> @@ Description, p2
> The <b>realloc</b> function
> deallocates the old object pointed to by <tt>ptr</tt>
> +as if by a call to <b>free</b>,
> and returns a pointer to a new object
> -that has the size specified by <tt>size</tt>.
> +that has the size specified by <tt>size</tt>
> +as if by a call to <b>malloc</b>.
> The contents of the new object
> shall be the same as that of the old object prior to deallocation,
> up to the lesser of the new and old sizes.
> Any bytes in the new object
> beyond the size of the old object
> have unspecified values.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> ## We can probably remove all of the above, because of the
> ## behavior now being defined as-if by calls to malloc(3) and
> ## free(3). But let's do that editorially in a separate change.
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
> +XXX)
>
> @@ New footnote XXX
> +XXX)
> +While atypical,
> +<b>realloc</b> may fail
> +or return a different pointer
> +for a call that shrinks the block of memory.
>
> @@ Returns, p4
> The <b>realloc</b> function returns
> a pointer to the new object
> (which can have the same value
> -as a pointer to the old object),
> +as a pointer to the old object)
> +on success.
> -or
> +If
> +space cannot be allocated,
> a null pointer
> -if the new object has not been allocated.
> +is returned.
>
The actual changes seem quite reasonable, for those that make it this far.
- Mark
> --
> <https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 0:44 ` Mark Harris
@ 2025-06-27 1:51 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 1:51 UTC (permalink / raw)
To: Mark Harris
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 30900 bytes --]
Hi Mark,
On Thu, Jun 26, 2025 at 05:44:49PM -0700, Mark Harris wrote:
> > Rationale
> > The specification of realloc(3) has been problematic since the
> > very first standards, even before ISO C. The wording has
> > changed significantly, trying to forcedly permit implementations
> > to return a null pointer when the requested size is zero. This
> > originated from the intent of banning zero-sized objects from
> > the language in C89, but that never worked well in
> > retrospective, as we can see from the fallout.
>
> I support the outcome that you are trying to achieve, however I think
> the proposal could use a better explanation upfront as to what problem
> this is attempting to solve. Calling a well-established function like
> realloc "problematic" without explaining what is problematic about it
> isn't helpful.
If you go a few paragraphs below, you'll find that there have been RCE
vulnerabilities because people don't know how to use it correctly.
I've personally fixed many bugs in old calls to realloc(3) in projects
like shadow utils.
> This states that the wording has changed
> significantly, but if that was the problem then a proposal to change
> the wording again is clearly not addressing that.
The problem was in trying to come up with a wording that accepted all
existing implementations, which isn't easy or even useful.
My proposal is not to attempt to reword the same thing, but rather
prohibit the weird implementations so that we remain with something
simple that can be worded without mistakes.
> Also I'm pretty sure that this did not originate from the intent of
> banning zero-sized objects from the language. If there was an actual
> effort to ban the possibility of zero-sized objects then it would have
> been easier for them to simply define malloc(0) and realloc(p, 0) to
> always be an error.
They probably weren't brave enough to break the BSDs. But I know there
was intention in the committee of removing zero-sized objects.
> It seems far more likely that the committee was
> just trying to document the current behavior that programmers could
> rely on, without unnecessarily making current implementations
> nonconforming.
>
> >
> > None of the specifications have been good, and C23 finally gave
> > up and made it undefined behavior.
> >
> > The problem is not only theoretical. Programmers don't know how
> > to use realloc(3) correctly, and have written weird code in
> > their attempts. This has resulted in a lot of non-sensical code
> > in configure scripts[1], and even bugs in actual programs[2].
> >
> > [1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
>
> It's not clear what you are claiming is nonsensical here or why. You
> think that configure checks are in general nonsensical, or what is it
> about these checks in particular that is nonsensical?
I'm claiming that people write code that doesn't do what they think it
does, just because they know realloc(3) is broken and try to workaround
it. Without going too far, here's the code I've seen in FreeBSD's
implementation of reallocf(3):
$ grepc -htfd reallocf .
void *
reallocf(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size);
/*
* When the System V compatibility option (malloc "V" flag) is
* in effect, realloc(ptr, 0) frees the memory and returns NULL.
* So, to avoid double free, call free() only when size != 0.
* realloc(ptr, 0) can't fail when ptr != NULL.
*/
if (!nptr && ptr && size != 0)
free(ptr);
return (nptr);
}
The last sentence in the comment is false, and this code has a memory
leak. I'll leave it as an exercise to the reader to figure out why.
(Of course, I've already reported the bug, just a few days ago.)
> > [2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
> >
> > In some cases, this non-sensical code has resulted in RCEs[3].
> >
> > [3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
>
> This is using reallocarray(), which comes from OpenBSD, and is not in
> ISO C at all. In OpenBSD it already had the desired behavior for size
> 0; an implementation of the OpenBSD reallocarray() that does not
> correctly match its behavior is just buggy. POSIX picked it up after
> Glibc and Bionic already created an implementation that did not match
> the OpenBSD behavior.
As you say, reallocarray(3) was later picked by glibc, where it was
implemented consistent with the realloc(3) in glibc, so both APIs have
inherently the same portability issues, and the only difference is in
the prevention of overflow in the multiplication.
Are you claiming that glibc has a buggy implementation of
reallocarray(3)?
alx@debian:~/tmp$ cat ra.c
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
printf("%p\n", reallocarray(malloc(1), 0, 42));
perror("realloc");
}
alx@debian:~/tmp$ gcc ra.c
alx@debian:~/tmp$ ./a.out
(nil)
realloc: Success
I certainly agree with you; glibc is buggy; please fix it. On the other
hand, that has no effect on the matter:
People are confused about how to properly use realloc(3), and people
(correctly) assume reallocarray(3) ain't different, and thus they remain
confused about it too.
> > However, this doesn't need to be like that. The traditional
> > implementation of realloc(3), present in Unix V7, inherited by
> > the BSDs, and currently available in range of systems, including
> > musl libc, doesn't have any issues. glibc --which uses an
> > independent implemention rather than a Unix derivative-- also
> > had this behavior originally; it changed to the current behavior
> > in 1999 (glibc 2.1.1), only for compatibility with C89, even
> > though ironically C99 was released soon after and removed the
> > text that glibc was trying to comply to, and introduced some new
> > text that was very confusing, and one of its interpretations
> > would make the new glibc behavior non-conforming.
>
> s/in range/in a range/
> s/implemention/implementation/
> s/comply to/comply with/
Thanks!
> Assuming that you are referring to Seventh Edition Unix from 1979, it
> would be better to spell it out or write V7 Unix, to distinguish it
> from UNIX V7 (https://unix.org/unixv7.html) which refers to Issue 7 of
> the standard commonly known as POSIX.
Agree.
> Your claim that this 1979 implementation of realloc()[1] "doesn't have
> any issues" is grossly incongruent with reality. This realloc(p, n)
> first calls free(p), then malloc(n), and finally copies the data if
> the pointer has changed; at the time this was safe because there was
> no multithreading and free() and malloc() did not modify the contents
> of the memory block, however if realloc() failed it returned NULL with
> p freed (but you could still read the data from the freed block at
> least until the next allocation).
Hmm, yeah, I was aware of that, but my wording doesn't reflect it. I
should rephrase as "didn't have any issues regarding the handling of
zero-sized objects".
> Also this implementation of
> realloc() did not accept p == NULL; that would crash. However, a
> documented bonus feature not found in modern realloc() was that p
> could be "a block freed since the last call of malloc, realloc or
> calloc".[2]
That's an issue of free(3) in reality. If we say that the specification
of realloc(3) was to free(3) and malloc(3), then it's not realloc(3)'s
problem to say whether NULL is acceptable or not.
> [1] https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/libc/gen/malloc.c
> [2] https://man.cat-v.org/unix_7th/3/malloc
>
> Is this really the "traditional realloc(3) specification" that the
> title of this proposal is suggesting be restored? I suggest being
> more specific as to what you actually want.
Regarding zero-sized objects, yes. I guess what I want is the BSD
specification, which is the traditional one, but with the unrelated bugs
fixed, and a better free(3) that accepts NULL, which results in a
realloc(3) that accepts NULL. See the proposed wording, which just
defers to the specification of free(3).
> > Code written for platforms returning a null pointer can be
> > migrated to platforms returning non-null, without significant
> > issues.
> >
> > There are two kinds of code that call realloc(p,0). One
> > hard-codes the 0, and is used as a replacement of free(p). This
> > code ignores the return value, since it's unimportant. This
> > code currently produces a leak of 0 bytes plus associated
> > metadata on platforms such as musl libc, where it returns a
> > non-null pointer. However, assuming that there are programs
> > written with the knowledge that they won't ever be run on such
> > platforms, we should take care of that, and make sure they don't
> > leak. A way of accomplishing this would be to recommend
> > implementations to issue a diagnostic when realloc(3) is called
> > with a hardcoded zero. This is only an informal recommendation
> > made by this proposal, as this is a matter of QoI, and the
> > standard shouldn't say anything about it. This would prevent
> > this class of minor leaks.
>
> It used to be a common thing for a C library or function to allow
> overriding the allocator that it uses internally with your own custom
> allocator. Rather than having to specify multiple function pointers
> for your own custom malloc, free, realloc, etc. sometimes it would
> just take one function pointer for your custom realloc, with the
> default being libc realloc. The library would allocate memory using
> (*customalloc)(NULL, n), free it using (*customalloc)(p, 0), and
> realloc using (*customalloc)(p, n). This simplified the interface for
> overriding the allocator, and if it stored the custom allocator in
> each object then it only had to store one function pointer. The point
> is that I would expect calls to realloc that intend to only free
> memory are more likely to be indirect calls than direct calls that are
> easily diagnosed at compile time or with a simple search; for a direct
> call it is easier to just call free. That said, I haven't seen this
> kind of interface for years.
Good. For the few cases that may exist today, I guess they'll have
minor leaks of 0 bytes. I can live with that.
> Both Glibc and Bionic already annotate realloc with
> __attribute__((__warn_unused_result__)), so that should already warn
> about direct calls to realloc(p, 0) that don't use the result
> (indicating that the caller was likely intending to only free the
> memory and not get another block in return). I would consider that
> existing warning to be better than warning on any realloc(p, 0),
> because it avoids false positives when the caller is already handling
> any pointer that may be returned.
This is not enough. There are programs that call
p = realloc(p, 0);
as a compact spelling of
free(p);
p = NULL;
In such code, the attribute wouldn't trigger a diagnostic. Not that I
care much though. As I said, such a minor leak shouldn't be important.
But I mention that for completeness. Also, GCC maintainers seem in
favour of the diagnostic, even if just because C23 made it UB and thus
a diagnostic is needed, regardless of the outcome of this proposal.
> > Moreover, in glibc, realloc(p,0) may return non-null, in the
> > case where p is NULL, so code must already take that into
> > account, and thus code that simply takes realloc(p,0) as a
> > synonym of free(p) is already leaky, as free(NULL) is a no-op,
> > but realloc(NULL,0) allocates 0 bytes.
> >
> > The other kind of code is in algorithms that realloc(3) an
> > arbitrary size, which might eventually be zero. This gets more
> > complex.
> >
> > Here's the code that should be written for AIX or glibc:
> >
> > errno = 0;
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
>
> That is ridiculous; I've never seen anyone else check errno to
> determine whether realloc has failed. It is far easier to just ensure
> that size is non-zero. Yes it would be even easier if you didn't need
> to ensure that, which is why I support your proposal, but it doesn't
> have anything to do with errno.
I had to take into account the most unreasonable cases, to make sure no
issues are possible. Of course, one can call realloc(p,n?:1) --which
I do--, or even not call it in the first place. But in those cases,
we don't even care about it. It's not my problem. I only need to care
about those calling it with a size that might be zero.
Some people were irrationally claiming that my proposal would result in
double-free and use-after-free bugs, so I had to take every possible
code people could write around realloc(3) and consider what would
happen if we changed the behavior.
And also, remember that FreeBSD wrote this:
$ grepc -htfd reallocf .
void *
reallocf(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size);
/*
* When the System V compatibility option (malloc "V" flag) is
* in effect, realloc(ptr, 0) frees the memory and returns NULL.
* So, to avoid double free, call free() only when size != 0.
* realloc(ptr, 0) can't fail when ptr != NULL.
*/
if (!nptr && ptr && size != 0)
free(ptr);
return (nptr);
}
It doesn't check errno, but it does try to recover post-morten from a
terrible situation. Indeed, it should have checked errno to prevent the
leak. Or it could have done realloc(p,n?:1) and call it a day.
[...]
> > Failing to check for ENOMEM in these platforms before freeing
> > the old pointer would result in a double-free. If the program
> > decides to continue using the old pointer instead of freeing it,
> > it would result in a use-after-free.
> >
> > In the platforms where realloc(p,0) returns non-null, such as
> > the BSDs or musl libc, it is simpler to handle it:
> >
> > new = realloc(old, size);
> > if (new == NULL) { // errno is ENOMEM
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > Whenever the result is a null pointer, these platforms are
> > reporting an ENOMEM error, and thus it is superfluous to check
> > errno there.
> >
> > Most code is written in this way, even if run on platforms
> > returning a null pointer. This is because most programmers are
> > just unaware of this problem. Part of the reason is also that
> > returning a non-null pointer with zero bytes is the natural
> > extension of the behavior, which is what programmers intuitively
> > expect from libc; that is, if realloc(p,3) allocates 3 bytes,
> > r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
> > natural by induction to expect that r(p,0) will allocate zero
> > bytes. Most algorithms naturally extend to 0 just fine, and
> > special casing 0 is artificial.
> >
> > If the realloc(3) specification were changed to require that
> > realloc(p,0) returns non-null on success, and that realloc(p,0)
> > only fails when out-of-memory (and assuming the implementations
> > will continue setting errno to ENOMEM), then code written for
> > AIX or glibc would continue working just fine, since the errno
> > check would be redundant with the null check. Simply, the
> > conditional (errno == ENOMEM) would always be true when
> > (new == NULL).
> >
> > Then, there are non-POSIX platforms that don't set ENOMEM. In
> > those platforms, code might do this:
> >
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (size != 0)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > That code would continue working with this proposal, except for
> > a very rare corner case, in which it would leak. In the normal
> > case, (size != 0) would never be true under (new == NULL),
>
> This is backwards.
> s/never // ?
Nope. It is correct. Maybe I should append to the sentence to clarify:
In the normal case, (size != 0) would never be true under
(new == NULL) after this proposed change.
> > because a reallocation of 0 bytes would almost always succeed,
> > and thus not return a null pointer under this proposal.
> > However, in some cases, the system might not find space even for
> > the small metadata needed for a 0-byte allocation. In such
> > case, the (size != 0) conditional would prevent deallocating
> > 'old', and thus cause a memory leak. This case is exceptional
> > enough that it shouldn't stop us from fixing realloc(3).
> > Anyway, on an out-of-memory case, the program is likely to
> > terminate rather soon, so the issue is even less likely to have
> > an impact on any existing programs. Also, LLVM's address
> > sanitizer will soon able to catch such a leak:
> > <https://github.com/llvm/llvm-project/issues/113065>
> >
> > This proposal makes handling of realloc(3) as straightforward as
> > one would expect, with only two states: success or error. There
> > are no in-between states.
> >
> > The resulting wording in the standard is also much simpler, as
> > it doesn't need to define so many special cases.
> >
> > For consistency, all the other allocation functions are updated
> > to both return a null pointer on error, and use consistent
> > wording.
> >
> > Why not go the other way around?
> > Some people keep asking why not go the other way around: why not
> > force the BSDs and musl to return a null pointer if size is 0.
> > This would result in double-free and use-after-free bugs, which
> > can result in RCE vulnerabilities (remote code execution), which
> > is clearly unacceptable.
> >
> > Consider this code, which is the usual code for calling
> > realloc(3) in such systems:
> >
> > new = realloc(old, size);
> > if (new == NULL) {
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > If realoc(p,0) would return a null pointer and free the old
>
> s/realoc/realloc/
Thanks!
> > block, then the third line would be a double-free bug.
> >
> > Prior art
> > gnulib
> > gnulib provides the realloc-posix module, which aims to wrap the
> > system realloc(3) and reallocarray(3) functions so that they
> > behave in a POSIX-complying manner.
> >
> > It previously behaved like glibc. After I reported that it was
> > non-conforming to POSIX, we discussed the best way forward,
> > which we agreed was the same direction that this paper is
> > proposing now for C2y. The implementation was changed in
> >
> > gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
> >
> > There have been no regression reports since then, as we
> > expected.
> >
> > Unix V7, BSD
>
> s/Unix V7/Seventh Edition Unix/
Thanks!
> > The proposed behavior is the one endorsed by Doug McIlroy, the
> > author of the original implementation of realloc(3) in Unix V7,
> > and also present in the BSDs.
> >
> > glibc <= 2.1
> > glibc was implemented originally to return non-null. It was
> > only in 1999, and purely to comply with the standards --with no
> > requests by users to do so--, that the glibc maintainers decided
> > to switch to the current behavior.
> >
> > Design decisions
> > This change needs two changes, which can be applied all at once,
> > or in separate steps.
> >
> > The first step would make realloc(p,s) be consistent with
> > free(p) and malloc(s), including when p is a null pointer, when
> > s is zero, and also when both corner cases happen at the same
> > time. This change would already turn the implementations where
> > malloc(0) returns non-null into the end goal we have. This
> > would require changes to (at least) the following
> > implementations: glibc, Bionic, Windows.
> >
> > The second step would be to require that malloc(0) returns a
> > non-null pointer. This would require changes to (at least) the
> > following implementations: AIX.
>
> I appreciate that you no longer claim that these are the ONLY
> implementations that would require changes.
As far as libc is concerned, and especially for the first change, I'm
quite sure those are the only three. I'm purposefully not considering
interposed implementations, though; only libc. But yeah, this is
probably easier to state without wording mistakes.
> >
> > This proposal has merged all steps into a single proposal.
> >
> > Future directions
> > This proposal, by specifying realloc(3) as-if by calling
> > free(3) and malloc(3), makes redundant several mentions of
> > realloc(3) next to either free(3) or malloc(3) in the standard.
> > We could remove them in this proposal, or clean up that in a
> > separate (mostly editorial) proposal. Let's keep it for a
> > future proposal for now.
> >
> > Caveats
> > n?n:1
> > Code written today should be careful, in case it can run on
> > older systems that are not fixed to comply with this stricter
> > specification. Thus, code written today should call realloc(3)
> > similar to this:
> >
> > realloc(p, n?n:1);
> >
> > When all existing implementations are fixed to comply with this
> > stricter specification, that workaround can be removed.
> >
> > ENOMEM
> > Existing implementations that set errno to ENOMEM must continue
> > doing so when the input pointer is not freed. If they didn't,
> > code that is currently portable to all POSIX systems
> >
> > errno = 0;
> > new = realloc(old, size);
> > if (new == NULL) {
> > if (errno == ENOMEM)
> > free(old);
> > goto fail;
> > }
> > ...
> > free(new);
> >
> > would leak on error.
> >
> > Since it is currently impossible to write code today that is
> > portable to arbitrary C17 systems, this is not an issue in
> > ISO C.
>
> Current code that calls realloc in a manner that is portable to
> arbitrary C17 or C23 systems will already ensure that the size
> argument is non-zero (for example, using the n?n:1 method above).
> Such code is very possible and requires no changes.
Again, I'm only concerned about code that calls realloc(p,n) where n
might be zero. If it can't be zero, it's not my problem. It is not
possible to call realloc(p,0) in a manner that is portable to C17
without leaking (or worse, DF or UAF).
As you've seen above, FreeBSD does this. At least, they are only
concerned about POSIX, so they could have written it without a leak.
However, they wrote it in the C17-portable manner, which resulted in
a leak. Why? Because both standards have been confusing people about
realloc(3) for a long time.
> > - New code written for C2y will only need to check for
> > NULL to detect errors.
> >
> > - Code written for specific C17 and older platforms
> > that don't set errno will continue to work for those
> > specific platforms.
> >
> > - Code written for POSIX.1-2024 and older platforms
> > will continue working on POSIX C2y platforms,
> > assuming that POSIX will continue mandating ENOMEM.
> >
> > - Code written for POSIX.1-2024 and older will not be
> > able to be run on non-POSIX C2y platforms, but that
> > could be expected.
>
> I don't see how this is relevant to this proposal.
I want to be comprehensive. The C Committee is also concerned about
POSIX, since they have attepted to coordinated changes to realloc(3) in
the past, even if it didn't work well. In fact, this time it better is
well coordinated, or we rather not do it at all.
> > The only important thing is that platforms that did set ENOMEM
> > should continue setting it, to avoid introducing leaks.
> >
> > Proposed wording
> > Based on N3550.
> >
> > 7.25.4.1 Memory management functions :: General
> > @@ p1
> > ...
> > If the size of the space requested is zero,
> > -the behavior is implementation-defined:
> > -either
> > -a null pointer is returned to indicate the error,
> > -or
> > the behavior is as if the size were some nonzero value,
> > except that the returned pointer shall not be used
> > to access an object.
> >
> > 7.25.4.2 The aligned_alloc function
> > @@ Returns, p3
> > The <b>aligned_alloc</b> function returns
> > -either
> > -a null pointer
> > -or
> > -a pointer to the allocated space.
> > +a pointer to the allocated space
> > +on success.
> > +If
> > +the space cannot be allocated,
> > +a null pointer is returned.
> >
> > 7.25.4.3 The calloc function
> > @@ Returns, p3
> > The <b>calloc</b> function returns
> > -either
> > a pointer to the allocated space
> > +on success.
> > -or a null pointer
> > -if
> > +If
> > the space cannot be allocated
> > or if the product <tt>nmemb * size</tt>
> > -would wraparound <b>size_t</b>.
> > +would wraparound <b>size_t</b>,
> > +a null pointer is returned.
> >
> > 7.25.4.7 The malloc function
> > @@ Returns, p3
> > The <b>malloc</b> function returns
> > -either
> > -a null pointer
> > -or
> > -a pointer to the allocated space.
> > +a pointer to the allocated space
> > +on success.
> > +If
> > +the space cannot be allocated,
> > +a null pointer is returned.
> >
> > 7.25.4.8 The realloc function
> > @@ Description, p2
> > The <b>realloc</b> function
> > deallocates the old object pointed to by <tt>ptr</tt>
> > +as if by a call to <b>free</b>,
> > and returns a pointer to a new object
> > -that has the size specified by <tt>size</tt>.
> > +that has the size specified by <tt>size</tt>
> > +as if by a call to <b>malloc</b>.
> > The contents of the new object
> > shall be the same as that of the old object prior to deallocation,
> > up to the lesser of the new and old sizes.
> > Any bytes in the new object
> > beyond the size of the old object
> > have unspecified values.
> >
> > @@ p3
> > If <tt>ptr</tt> is a null pointer,
> > the <b>realloc</b> function behaves
> > like the <b>malloc</b> function for the specified size.
> > Otherwise,
> > if <tt>ptr</tt> does not match a pointer
> > earlier returned by a memory management function,
> > or
> > if the space has been deallocated
> > by a call to the <b>free</b> or <b>realloc</b> function,
> > ## We can probably remove all of the above, because of the
> > ## behavior now being defined as-if by calls to malloc(3) and
> > ## free(3). But let's do that editorially in a separate change.
> > -or
> > -if the size is zero,
> > ## We're defining the behavior.
> > the behavior is undefined.
> > If
> > -memory for the new object is not allocated,
> > +the space cannot be allocated,
> > ## Editorial; for consistency with the wording of the other functions.
> > the old object is not deallocated
> > and its value is unchanged.
> > +XXX)
> >
> > @@ New footnote XXX
> > +XXX)
> > +While atypical,
> > +<b>realloc</b> may fail
> > +or return a different pointer
> > +for a call that shrinks the block of memory.
> >
> > @@ Returns, p4
> > The <b>realloc</b> function returns
> > a pointer to the new object
> > (which can have the same value
> > -as a pointer to the old object),
> > +as a pointer to the old object)
> > +on success.
> > -or
> > +If
> > +space cannot be allocated,
> > a null pointer
> > -if the new object has not been allocated.
> > +is returned.
> >
>
> The actual changes seem quite reasonable, for those that make it this far.
Thanks!
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-26 22:00 ` alx-0029r5 " Alejandro Colomar
2025-06-27 0:44 ` Mark Harris
@ 2025-06-27 8:52 ` Florian Weimer
2025-06-27 12:54 ` Martin Uecker
` (2 more replies)
1 sibling, 3 replies; 143+ messages in thread
From: Florian Weimer @ 2025-06-27 8:52 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
* Alejandro Colomar:
> It would be good to have explicit replies by glibc maintainers about it,
> so that the C Committee understands better what the maintainers think
> about it. I've got word from some committee members that if I can
> convince the maintainers, they'll vote for standardizing it. So, it
> would be great it people could emit 'Acked-by:' tags, or otherwise
> explain their position.
This is not how ISO standardization works. In the end, it comes down to
how the national bodies vote.
I think the proposal is not clear about its intent. It looks to me you
are trying to accomplish at least the following things:
* all allocation functions can be used to allocate zero-sized objects
* calloc, reallocarray can be used to allocate arbitrary large arrays
of zero-sized objects
* calloc, reallocarray can be used to allocate zero-length arrays
of arbitrarily-sized objects
* realloc, reallocarray can no longer be used to deallocate storage
If you frame your proposal in terms of aligning with traditional
behavior, you are inviting a discussion what the traditional behavior
is. But this doesn't really matter.
I'm not sure if your changes to the calloc, reallocarray are sufficient
text. I assume we want full symmetry of the arguments because the
argument order in existing programs is not very consistent. This
requires dropping the requirement that one of the arguments is an object
size (which rules out zero as a valid argument value).
From an implementation perspective, we need clarification that the
allocation functions (except aligned_alloc) may reduce the alignment of
the returned pointer to a power of two greater or equal to the requested
size, for allocation sizes that are less than the fundamental alignment.
(Some existing implementations already do this today, in violation of
the standard.)
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 8:52 ` Florian Weimer
@ 2025-06-27 12:54 ` Martin Uecker
2025-06-27 13:49 ` Alejandro Colomar
2025-06-27 13:26 ` Alejandro Colomar
2025-06-27 16:15 ` Joseph Myers
2 siblings, 1 reply; 143+ messages in thread
From: Martin Uecker @ 2025-06-27 12:54 UTC (permalink / raw)
To: Florian Weimer, Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Christopher Bazley, eskil,
Daniel Krügler, Kees Cook, Valdis Klētnieks
Am Freitag, dem 27.06.2025 um 10:52 +0200 schrieb Florian Weimer:
> * Alejandro Colomar:
...
>
> From an implementation perspective, we need clarification that the
> allocation functions (except aligned_alloc) may reduce the alignment of
> the returned pointer to a power of two greater or equal to the requested
> size, for allocation sizes that are less than the fundamental alignment.
> (Some existing implementations already do this today, in violation of
> the standard.)
There is a general rule for all allocation functions:
"The pointer returned if the allocation succeeds is suitably aligned so
that it may be assigned to a pointer to any type of object with a fundamental
alignment requirement and size less than or equal to the size requested."
There is some disagreement whether the "requested size" refers to the
size argument or the total size. IMHO it is a reasonable interpretation
that it refers to the size argument, but if implementation would explict
this this could then break programs that swap arguments (the standard
has no symmetry, it makes clear which is which)
Martin
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 8:52 ` Florian Weimer
2025-06-27 12:54 ` Martin Uecker
@ 2025-06-27 13:26 ` Alejandro Colomar
2025-06-27 14:44 ` Florian Weimer
2025-06-27 16:15 ` Joseph Myers
2 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 13:26 UTC (permalink / raw)
To: Florian Weimer
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 3953 bytes --]
Hi Florian,
On Fri, Jun 27, 2025 at 10:52:02AM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > It would be good to have explicit replies by glibc maintainers about it,
> > so that the C Committee understands better what the maintainers think
> > about it. I've got word from some committee members that if I can
> > convince the maintainers, they'll vote for standardizing it. So, it
> > would be great it people could emit 'Acked-by:' tags, or otherwise
> > explain their position.
>
> This is not how ISO standardization works.
I'm member of the C Committee; I know how it works.
> In the end, it comes down to
> how the national bodies vote.
It comes down to this:
- I present a proposal (which I've already submitted; soon it will
appear in the document log).
<https://www.open-std.org/jtc1/sc22/wg14/www/wg14_document_log>
- The proposal is voted by the committee members in the next meeting
(that is, in two months, most likely). If it's approved, it is put
into the working draft.
- Close to release of C2y, National Bodies may issue comments opposing
the draft. This is unlikely to change things meaningfully, if the
previous step had strong consensus.
> I think the proposal is not clear about its intent. It looks to me you
> are trying to accomplish at least the following things:
>
> * all allocation functions can be used to allocate zero-sized objects
> * calloc, reallocarray can be used to allocate arbitrary large arrays
> of zero-sized objects
> * calloc, reallocarray can be used to allocate zero-length arrays
> of arbitrarily-sized objects
> * realloc, reallocarray can no longer be used to deallocate storage
Yes, that's correct. That's how they behave in musl and the BSDs (and
for calloc(3), in glibc too).
BTW, reallocarray(3) is not in ISO C (yet).
> If you frame your proposal in terms of aligning with traditional
> behavior, you are inviting a discussion what the traditional behavior
> is. But this doesn't really matter.
>
> I'm not sure if your changes to the calloc, reallocarray are sufficient
> text. I assume we want full symmetry of the arguments because the
> argument order in existing programs is not very consistent. This
> requires dropping the requirement that one of the arguments is an object
> size (which rules out zero as a valid argument value).
No, that doesn't rule it out. calloc(3) is implemented in the BSDs,
musl, and glibc, that way, and it is conforming to ISO C (I think nobody
disputes that). The relevant text in the standard is this:
7.25.4.1 Memory management functions :: General
@@ p1
...
If the size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
That paragraph talks about the total size, not the size of each object.
If you read calloc(3), "the space" refers to the entire space, while
each object within the array is referred to as "each object". So, this
paragraph, which says what happens when the total size is zero, doesn't
care if it's due to 'n' being 0 or 'size' being 0.
> From an implementation perspective, we need clarification that the
> allocation functions (except aligned_alloc) may reduce the alignment of
> the returned pointer to a power of two greater or equal to the requested
> size, for allocation sizes that are less than the fundamental alignment.
I think I shouldn't include that in my proposal, as it's not necessary,
and may hinder it's approval.
> (Some existing implementations already do this today, in violation of
> the standard.)
Feel free to propose such a change.
Have a llovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 12:54 ` Martin Uecker
@ 2025-06-27 13:49 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 13:49 UTC (permalink / raw)
To: Martin Uecker
Cc: Florian Weimer, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Christopher Bazley, eskil,
Daniel Krügler, Kees Cook, Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 1419 bytes --]
Hi Martin, Florian,
On Fri, Jun 27, 2025 at 02:54:34PM +0200, Martin Uecker wrote:
> Am Freitag, dem 27.06.2025 um 10:52 +0200 schrieb Florian Weimer:
> > * Alejandro Colomar:
> ...
>
> >
> > From an implementation perspective, we need clarification that the
> > allocation functions (except aligned_alloc) may reduce the alignment of
> > the returned pointer to a power of two greater or equal to the requested
> > size, for allocation sizes that are less than the fundamental alignment.
> > (Some existing implementations already do this today, in violation of
> > the standard.)
>
> There is a general rule for all allocation functions:
>
> "The pointer returned if the allocation succeeds is suitably aligned so
> that it may be assigned to a pointer to any type of object with a fundamental
> alignment requirement and size less than or equal to the size requested."
>
> There is some disagreement whether the "requested size" refers to the
> size argument or the total size. IMHO it is a reasonable interpretation
> that it refers to the size argument, but if implementation would explict
> this this could then break programs that swap arguments (the standard
> has no symmetry, it makes clear which is which)
Okay, I'll tweak the wording to make it clear it's the total size.
That's an easy fix.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r6 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (6 preceding siblings ...)
2025-06-26 22:00 ` alx-0029r5 " Alejandro Colomar
@ 2025-06-27 14:01 ` Alejandro Colomar
2025-06-29 16:25 ` H. Peter Anvin
2025-06-30 2:27 ` Alejandro Colomar
2025-10-09 22:37 ` alx-0029r8 " Alejandro Colomar
8 siblings, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 14:01 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 19783 bytes --]
Hi!
Here's a new revision of the proposal, addressing some points raised by
Mark, plus clarifying that the paragraph about when size is zero refers
to the total size, as Florian was concerned that it might not be
symmetric.
Have a lovely day!
Alex
---
Name
alx-0029r6 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention exceptional leak in code checking (size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
r3 (2025-06-23):
- Fix diff missing line.
- Remove ENOMEM from the proposal.
- Clarify that ENOMEM should be retained by platforms already
using it.
- Add mention that LLVM's address sanitizer will catch the leak
mentioned in r2.
- Add links to real bugs (including an RCE bug).
r4 (2025-06-24):
- Use a better link for the Whatsapp RCE.
- s/Description/Rationale/
- wfix
- Mention that glibc <2.1.1 had the BSD behavior.
- Add footnote that realloc(3) may fail while shrinking.
r5 (2025-06-26):
- It was glibc 2.1.1 that broke it, not glibc 2.2.
- wfix
- Mention in the footnote that the pointer may change.
- Document why not go the other way around. It was explained
several times during discussion, but people keep suggesting
it.
r6 (2025-06-27):
- Clarify that the paragraph about what happens when the size
is zero refers to when the total size is zero (for calloc(3)
that is nmemb*size).
- s/Unix V7/V7 Unix/
- tfix.
- wfix.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://github.com/llvm/llvm-project/issues/113065>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
<https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
<https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
<https://gbhackers.com/whatsapp-double-free-vulnerability/>
Rationale
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
The problem is not only theoretical. Programmers don't know how
to use realloc(3) correctly, and have written weird code in
their attempts. This has resulted in a lot of non-sensical code
in configure scripts[1], and even bugs in actual programs[2].
[1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
[2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
In some cases, this non-sensical code has resulted in RCEs[3].
[3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in V7 Unix, inherited by
the BSDs, and currently available in a range of systems,
including musl libc, doesn't have any issues regarding zero-size
allocations. glibc --which uses an independent implementation
rather than a Unix derivative-- also had this behavior
originally; it changed to the current behavior in 1999
(glibc 2.1.1), only for compatibility with C89, even though
ironically C99 was released soon after and removed the text that
glibc was trying to comply with, and introduced some new text
that was very confusing, and one of its interpretations would
make the new glibc behavior non-conforming.
Code written for platforms returning a null pointer can be
migrated to platforms returning non-null, without significant
issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem. Part of the reason is also that
returning a non-null pointer with zero bytes is the natural
extension of the behavior, which is what programmers intuitively
expect from libc; that is, if realloc(p,3) allocates 3 bytes,
r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
natural by induction to expect that r(p,0) will allocate zero
bytes. Most algorithms naturally extend to 0 just fine, and
special casing 0 is artificial.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory (and assuming the implementations
will continue setting errno to ENOMEM), then code written for
AIX or glibc would continue working just fine, since the errno
check would be redundant with the null check. Simply, the
conditional (errno == ENOMEM) would always be true when
(new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs. Also, LLVM's address
sanitizer will soon able to catch such a leak:
<https://github.com/llvm/llvm-project/issues/113065>
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer on error, and use consistent
wording.
Why not go the other way around?
Some people keep asking why not go the other way around: why not
force the BSDs and musl to return a null pointer if size is 0.
This would result in double-free and use-after-free bugs, which
can result in RCE vulnerabilities (remote code execution), which
is clearly unacceptable.
Consider this code, which is the usual code for calling
realloc(3) in such systems:
new = realloc(old, size);
if (new == NULL) {
free(old);
goto fail;
}
...
free(new);
If realloc(p,0) would return a null pointer and free the old
block, then the third line would be a double-free bug.
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
V7 Unix, BSD
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in V7 Unix,
and also present in the BSDs.
glibc <= 2.1
glibc was implemented originally to return non-null. It was
only in 1999, and purely to comply with the standards --with no
requests by users to do so--, that the glibc maintainers decided
to switch to the current behavior.
Design decisions
This change needs two changes, which can be applied all at once,
or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have. This
would require changes to (at least) the following
implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer. This would require changes to (at least) the
following implementations: AIX.
This proposal has merged all steps into a single proposal.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
Existing implementations that set errno to ENOMEM must continue
doing so when the input pointer is not freed. If they didn't,
code that is currently portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would leak on error.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, this is not an issue in
ISO C.
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
The only important thing is that platforms that did set ENOMEM
should continue setting it, to avoid introducing leaks.
Proposed wording
Based on N3550.
7.25.4.1 Memory management functions :: General
@@ p1
...
-If the size of the space requested is zero,
+If the total size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
+XXX)
@@ New footnote XXX
+XXX)
+While atypical,
+<b>realloc</b> may fail
+or return a different pointer
+for a call that shrinks the block of memory.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
-if the new object has not been allocated.
+is returned.
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 13:26 ` Alejandro Colomar
@ 2025-06-27 14:44 ` Florian Weimer
2025-06-27 15:04 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Florian Weimer @ 2025-06-27 14:44 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
* Alejandro Colomar:
>> If you frame your proposal in terms of aligning with traditional
>> behavior, you are inviting a discussion what the traditional behavior
>> is. But this doesn't really matter.
>>
>> I'm not sure if your changes to the calloc, reallocarray are sufficient
>> text. I assume we want full symmetry of the arguments because the
>> argument order in existing programs is not very consistent. This
>> requires dropping the requirement that one of the arguments is an object
>> size (which rules out zero as a valid argument value).
>
> No, that doesn't rule it out.
C11 says this in 6.2.6.1p2:
| Except for bit-fields, objects are composed of contiguous sequences of
| one or more bytes,
And in 7.22.3.2p2:
| The calloc function allocates space for an array of nmemb objects,
| each of whose size is size.
I think this requires size to be positive: it denotes an object size,
and that is at least 1 because zero-sized objects do not exist according
to 6.2.6.1p2.
If this is fixed in the current draft, that's fine.
Failing with EINVAL for zero in one of the calloc arguments but not the
other would be awkard and needlessly break applications.
>> From an implementation perspective, we need clarification that the
>> allocation functions (except aligned_alloc) may reduce the alignment of
>> the returned pointer to a power of two greater or equal to the requested
>> size, for allocation sizes that are less than the fundamental alignment.
>
> I think I shouldn't include that in my proposal, as it's not necessary,
> and may hinder it's approval.
Martin says it's already there. You just need to make sure it applies
to all zero-sized allocations, including zero-member allocations of
larger-than-fundamental-arlignment objects with calloc.
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 14:44 ` Florian Weimer
@ 2025-06-27 15:04 ` Alejandro Colomar
2025-06-27 16:17 ` Florian Weimer
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 15:04 UTC (permalink / raw)
To: Florian Weimer
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
Hi Florian,
On Fri, Jun 27, 2025 at 04:44:14PM +0200, Florian Weimer wrote:
> >> If you frame your proposal in terms of aligning with traditional
> >> behavior, you are inviting a discussion what the traditional behavior
> >> is. But this doesn't really matter.
> >>
> >> I'm not sure if your changes to the calloc, reallocarray are sufficient
> >> text. I assume we want full symmetry of the arguments because the
> >> argument order in existing programs is not very consistent. This
> >> requires dropping the requirement that one of the arguments is an object
> >> size (which rules out zero as a valid argument value).
> >
> > No, that doesn't rule it out.
>
> C11 says this in 6.2.6.1p2:
>
> | Except for bit-fields, objects are composed of contiguous sequences of
> | one or more bytes,
>
> And in 7.22.3.2p2:
>
> | The calloc function allocates space for an array of nmemb objects,
> | each of whose size is size.
>
> I think this requires size to be positive: it denotes an object size,
> and that is at least 1 because zero-sized objects do not exist according
> to 6.2.6.1p2.
C11 also says this:
<https://port70.net/~nsz/c/c11/n1570.html#7.22.3p1>
If the size of the space requested is zero,
the behavior is implementation-defined:
either a null pointer is returned,
or the behavior is as if the size were some nonzero value,
except that the returned pointer
shall not be used to access an object.
The size of *the space* is not the size of the object, it's the
multiplication of nmemb*size.
I think everybody agrees that glibc's calloc(3) is conforming to C11,
and it behaves like my proposal suggests.
alx@debian:~/tmp$ cat c.c
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
printf("%p\n", calloc(0, 42));
perror("calloc(0, 42)");
printf("%p\n", calloc(42, 0));
perror("calloc(42, 0)");
printf("%p\n", calloc(0, 0));
perror("calloc(0, 0)");
}
alx@debian:~/tmp$ gcc c.c
alx@debian:~/tmp$ ./a.out
0x564b188332a0
calloc(0, 42): Success
0x564b188338d0
calloc(42, 0): Success
0x564b18833d00
calloc(0, 0): Success
In any case, I've improved the wording to be more explicit about it.
See alx-0029r6.
[...]
> >> From an implementation perspective, we need clarification that the
> >> allocation functions (except aligned_alloc) may reduce the alignment of
> >> the returned pointer to a power of two greater or equal to the requested
> >> size, for allocation sizes that are less than the fundamental alignment.
> >
> > I think I shouldn't include that in my proposal, as it's not necessary,
> > and may hinder it's approval.
>
> Martin says it's already there. You just need to make sure it applies
> to all zero-sized allocations, including zero-member allocations of
> larger-than-fundamental-arlignment objects with calloc.
I'm not an expert in this part, so I'd like to have more discussion
about it. I can prepare a separate paper for it, if we agree that's
what we want.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 8:52 ` Florian Weimer
2025-06-27 12:54 ` Martin Uecker
2025-06-27 13:26 ` Alejandro Colomar
@ 2025-06-27 16:15 ` Joseph Myers
2 siblings, 0 replies; 143+ messages in thread
From: Joseph Myers @ 2025-06-27 16:15 UTC (permalink / raw)
To: Florian Weimer
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Laurent Bercot, Andreas Schwab,
Thorsten Glaser, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Fri, 27 Jun 2025, Florian Weimer wrote:
> From an implementation perspective, we need clarification that the
> allocation functions (except aligned_alloc) may reduce the alignment of
> the returned pointer to a power of two greater or equal to the requested
> size, for allocation sizes that are less than the fundamental alignment.
That was already changed in C23 (see N2293, accepted October 2018),
reversing the previous rule from C90 DR#075.
--
Joseph S. Myers
josmyers@redhat.com
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 15:04 ` Alejandro Colomar
@ 2025-06-27 16:17 ` Florian Weimer
2025-06-27 17:11 ` Alejandro Colomar
2025-06-27 17:22 ` [musl] " Rich Felker
0 siblings, 2 replies; 143+ messages in thread
From: Florian Weimer @ 2025-06-27 16:17 UTC (permalink / raw)
To: Alejandro Colomar
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Laurent Bercot,
Andreas Schwab, Thorsten Glaser, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
* Alejandro Colomar:
> I think everybody agrees that glibc's calloc(3) is conforming to C11,
> and it behaves like my proposal suggests.
>
> alx@debian:~/tmp$ cat c.c
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main(void)
> {
> printf("%p\n", calloc(0, 42));
> perror("calloc(0, 42)");
>
> printf("%p\n", calloc(42, 0));
> perror("calloc(42, 0)");
>
> printf("%p\n", calloc(0, 0));
> perror("calloc(0, 0)");
> }
> alx@debian:~/tmp$ gcc c.c
> alx@debian:~/tmp$ ./a.out
> 0x564b188332a0
> calloc(0, 42): Success
> 0x564b188338d0
> calloc(42, 0): Success
> 0x564b18833d00
> calloc(0, 0): Success
>
> In any case, I've improved the wording to be more explicit about it.
> See alx-0029r6.
I think the wording still allows calloc (42, 0) to fail with EINVAL (or
some other error code) because 0 is not a valid object size.
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 16:17 ` Florian Weimer
@ 2025-06-27 17:11 ` Alejandro Colomar
2025-06-27 17:22 ` [musl] " Rich Felker
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 17:11 UTC (permalink / raw)
To: Florian Weimer, Joseph Myers
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Laurent Bercot, Andreas Schwab,
Thorsten Glaser, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 1705 bytes --]
Hi Florian, Joseph,
On Fri, Jun 27, 2025 at 06:17:14PM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > I think everybody agrees that glibc's calloc(3) is conforming to C11,
> > and it behaves like my proposal suggests.
> >
> > alx@debian:~/tmp$ cat c.c
> > #include <stdio.h>
> > #include <stdlib.h>
> >
> > int
> > main(void)
> > {
> > printf("%p\n", calloc(0, 42));
> > perror("calloc(0, 42)");
> >
> > printf("%p\n", calloc(42, 0));
> > perror("calloc(42, 0)");
> >
> > printf("%p\n", calloc(0, 0));
> > perror("calloc(0, 0)");
> > }
> > alx@debian:~/tmp$ gcc c.c
> > alx@debian:~/tmp$ ./a.out
> > 0x564b188332a0
> > calloc(0, 42): Success
> > 0x564b188338d0
> > calloc(42, 0): Success
> > 0x564b18833d00
> > calloc(0, 0): Success
> >
> > In any case, I've improved the wording to be more explicit about it.
> > See alx-0029r6.
>
> I think the wording still allows calloc (42, 0) to fail with EINVAL (or
> some other error code) because 0 is not a valid object size.
7.25.4.1 Memory management functions :: General
@@ p1
...
-If the size of the space requested is zero,
+If the total size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
That paragraph makes it very explicit what to do when the total size is
zero, which is the case with calloc(42,0).
Joseph, would you mind reviewing this?
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 16:17 ` Florian Weimer
2025-06-27 17:11 ` Alejandro Colomar
@ 2025-06-27 17:22 ` Rich Felker
2025-06-27 17:38 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: Rich Felker @ 2025-06-27 17:22 UTC (permalink / raw)
To: Florian Weimer
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Thorsten Glaser,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Fri, Jun 27, 2025 at 06:17:14PM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > I think everybody agrees that glibc's calloc(3) is conforming to C11,
> > and it behaves like my proposal suggests.
> >
> > alx@debian:~/tmp$ cat c.c
> > #include <stdio.h>
> > #include <stdlib.h>
> >
> > int
> > main(void)
> > {
> > printf("%p\n", calloc(0, 42));
> > perror("calloc(0, 42)");
> >
> > printf("%p\n", calloc(42, 0));
> > perror("calloc(42, 0)");
> >
> > printf("%p\n", calloc(0, 0));
> > perror("calloc(0, 0)");
> > }
> > alx@debian:~/tmp$ gcc c.c
> > alx@debian:~/tmp$ ./a.out
> > 0x564b188332a0
> > calloc(0, 42): Success
> > 0x564b188338d0
> > calloc(42, 0): Success
> > 0x564b18833d00
> > calloc(0, 0): Success
> >
> > In any case, I've improved the wording to be more explicit about it.
> > See alx-0029r6.
>
> I think the wording still allows calloc (42, 0) to fail with EINVAL (or
> some other error code) because 0 is not a valid object size.
Since this is also a matter of confusion, I'd like to see it improved.
In addition to zero being unclear to some readers, it's not clear to
some users (and even implementors) that it must fail if the
multiplication overflows. This is only implied by the impossibility of
meeting the requirements for the success condition if the
multiplication would overflow.
The simple unambiguous way to define calloc would be as-if it performs
malloc followed by memset of size*nmemb bytes, with a requirement to
fail if the mathematical value of size*nmemb is not representable in
type size_t (and would thereby be subject to modular reduction).
A non-normative note should say that the intent is that it can be used
to allocate an array of nmemb objects each of size size, but that
calloc(size,nmemb) and calloc(nmemb,size) are equivalent and to not
imply any obligation to access the object as such an array or that
either value be nonzero.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 17:22 ` [musl] " Rich Felker
@ 2025-06-27 17:38 ` Alejandro Colomar
2025-06-27 19:37 ` Rich Felker
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-27 17:38 UTC (permalink / raw)
To: Rich Felker
Cc: Florian Weimer, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Thorsten Glaser,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 2298 bytes --]
Hi Rich,
On Fri, Jun 27, 2025 at 01:22:17PM -0400, Rich Felker wrote:
> On Fri, Jun 27, 2025 at 06:17:14PM +0200, Florian Weimer wrote:
> > I think the wording still allows calloc (42, 0) to fail with EINVAL (or
> > some other error code) because 0 is not a valid object size.
>
> Since this is also a matter of confusion, I'd like to see it improved.
> In addition to zero being unclear to some readers,
I've slightly tweaked the wording in alx-0029r6. Please check. I've
asked Joseph to review that too.
The rules that prevent 0-sized objects don't trigger, because we never
attempt to create a 0-sized object. It works *as-if* the size was
non-zero, except that the user is not allowed to access the object.
> it's not clear to
> some users (and even implementors) that it must fail if the
> multiplication overflows.
This is off-topic for my proposal.
However, if that would be unclear, I'm willing to improve the wording in
a separate paper. However, I don't find the confusion. The current
wording for calloc(3), as of n3550, is:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3550.pdf#subsubsection.0.7.25.4.3>
7.25.4.3p3 (Returns):
The calloc function returns
either a pointer to the allocated space
or a null pointer
if the space cannot be allocated
or if the product nmemb * size would wraparound size_t.
So, if n*size would wrap around, it must return a null pointer. How is
it unclear?
Have a lovely day!
Alex
> This is only implied by the impossibility of
> meeting the requirements for the success condition if the
> multiplication would overflow.
>
> The simple unambiguous way to define calloc would be as-if it performs
> malloc followed by memset of size*nmemb bytes, with a requirement to
> fail if the mathematical value of size*nmemb is not representable in
> type size_t (and would thereby be subject to modular reduction).
>
> A non-normative note should say that the intent is that it can be used
> to allocate an array of nmemb objects each of size size, but that
> calloc(size,nmemb) and calloc(nmemb,size) are equivalent and to not
> imply any obligation to access the object as such an array or that
> either value be nonzero.
>
> Rich
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r5 - Restore the traditional realloc(3) specification
2025-06-27 17:38 ` Alejandro Colomar
@ 2025-06-27 19:37 ` Rich Felker
0 siblings, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-27 19:37 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Florian Weimer, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Laurent Bercot, Andreas Schwab, Thorsten Glaser,
Eric Blake, Vincent Lefevre, Mark Harris, Collin Funk,
Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Fri, Jun 27, 2025 at 07:38:44PM +0200, Alejandro Colomar wrote:
> Hi Rich,
>
> On Fri, Jun 27, 2025 at 01:22:17PM -0400, Rich Felker wrote:
> > On Fri, Jun 27, 2025 at 06:17:14PM +0200, Florian Weimer wrote:
> > > I think the wording still allows calloc (42, 0) to fail with EINVAL (or
> > > some other error code) because 0 is not a valid object size.
> >
> > Since this is also a matter of confusion, I'd like to see it improved.
> > In addition to zero being unclear to some readers,
>
> I've slightly tweaked the wording in alx-0029r6. Please check. I've
> asked Joseph to review that too.
>
> The rules that prevent 0-sized objects don't trigger, because we never
> attempt to create a 0-sized object. It works *as-if* the size was
> non-zero, except that the user is not allowed to access the object.
Yes, that's not what I'm talking about. That's all fine.
> > it's not clear to
> > some users (and even implementors) that it must fail if the
> > multiplication overflows.
>
> This is off-topic for my proposal.
I actually largely agree, but at the same time, folks are wrongly
raising this matter as if it were on-topic for your proposal, because
the poor wording of the way calloc is specified seems to generate
confusion that semantics about zero-sized objects are involved when
they're not.
I don't necessarily think fixing this needs to or should be part of
your proposal.
> However, if that would be unclear, I'm willing to improve the wording in
> a separate paper. However, I don't find the confusion. The current
> wording for calloc(3), as of n3550, is:
>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3550.pdf#subsubsection.0.7.25.4.3>
> 7.25.4.3p3 (Returns):
>
> The calloc function returns
> either a pointer to the allocated space
> or a null pointer
> if the space cannot be allocated
> or if the product nmemb * size would wraparound size_t.
>
> So, if n*size would wrap around, it must return a null pointer. How is
> it unclear?
OK, I wasn't aware that it was finally clearly spelled out for the
folks who keep finding it surprising. But I was not so much proposing
adding a clarification as just making sure it would still be addressed
if calloc were just specified with malloc+memset(size*nmemb) without
talking about array-like usage.
Sorry if this has been a distraction. I know we have way too many of
those already...
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r6 - Restore the traditional realloc(3) specification
2025-06-27 14:01 ` alx-0029r6 " Alejandro Colomar
@ 2025-06-29 16:25 ` H. Peter Anvin
2025-06-29 16:35 ` Alejandro Colomar
2025-06-29 17:05 ` [musl] " Rich Felker
2025-06-30 2:27 ` Alejandro Colomar
1 sibling, 2 replies; 143+ messages in thread
From: H. Peter Anvin @ 2025-06-29 16:25 UTC (permalink / raw)
To: Alejandro Colomar, libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
On 2025-06-27 07:01, Alejandro Colomar wrote:
> Hi!
>
> Here's a new revision of the proposal, addressing some points raised by
> Mark, plus clarifying that the paragraph about when size is zero refers
> to the total size, as Florian was concerned that it might not be
> symmetric.
>
I don't know if it would be useful, but proposing a new interface of the
form:
reallocp(&ptr, size)
... to separate the status return from the pointer might be a really
good idea. This would hopefully eliminate users doing the "obvious":
ptr = realloc(ptr, size)
Not that this resolves anything in the short or even medium term, but
perhaps can help avoid problems in the more distant time frame. It is
also an interface which is mostly trivial to infill.
-hpa
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r6 - Restore the traditional realloc(3) specification
2025-06-29 16:25 ` H. Peter Anvin
@ 2025-06-29 16:35 ` Alejandro Colomar
2025-06-29 17:05 ` [musl] " Rich Felker
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-29 16:35 UTC (permalink / raw)
To: H. Peter Anvin
Cc: libc-alpha, bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 1877 bytes --]
Hi H. Peter,
On Sun, Jun 29, 2025 at 09:25:13AM -0700, H. Peter Anvin wrote:
> On 2025-06-27 07:01, Alejandro Colomar wrote:
> > Hi!
> >
> > Here's a new revision of the proposal, addressing some points raised by
> > Mark, plus clarifying that the paragraph about when size is zero refers
> > to the total size, as Florian was concerned that it might not be
> > symmetric.
>
> I don't know if it would be useful, but proposing a new interface of the
> form:
>
> reallocp(&ptr, size)
>
> ... to separate the status return from the pointer might be a really good
> idea. This would hopefully eliminate users doing the "obvious":
>
> ptr = realloc(ptr, size)
This is resolved with the reallocf(3) wrapper that FreeBSD provides,
which I pretend to standardize *after* fixing realloc(3). This is
provided in Linux (and other POSIX) systems by the libbsd library,
present in many distros.
reallocf(3) is a realloc(3) wrapper that unconditionally frees the
input pointer, even on error. So, it is designed to be used exactly as
p = reallocf(p, size);
if (p == NULL)
goto err;
As simple as it gets.
> Not that this resolves anything in the short or even medium term, but
> perhaps can help avoid problems in the more distant time frame. It is also
> an interface which is mostly trivial to infill.
No, this would make static analysis a lot harder. Currently, realloc(3)
is specified as ending the lifetime of the input object, and creating a
new one, and a static analyzer could easily understand that with
attributes like the existing [[gnu::malloc(free)]] --although for some
reason, there's no similar existing attribute for realloc(3)--.
However, if the new object is created in the parameter, you'll have a
very hard time tracking that.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: alx-0029r6 - Restore the traditional realloc(3) specification
2025-06-29 16:25 ` H. Peter Anvin
2025-06-29 16:35 ` Alejandro Colomar
@ 2025-06-29 17:05 ` Rich Felker
1 sibling, 0 replies; 143+ messages in thread
From: Rich Felker @ 2025-06-29 17:05 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Alejandro Colomar, libc-alpha, bug-gnulib, musl,
наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Adhemerval Zanella Netto,
Joseph Myers, Florian Weimer, Laurent Bercot, Andreas Schwab,
Thorsten Glaser, Eric Blake, Vincent Lefevre, Mark Harris,
Collin Funk, Wilco Dijkstra, DJ Delorie, Cristian Rodríguez,
Siddhesh Poyarekar, Sam James, Mark Wielaard, Maciej W. Rozycki,
Martin Uecker, Christopher Bazley, eskil, Daniel Krügler,
Kees Cook, Valdis Klētnieks
On Sun, Jun 29, 2025 at 09:25:13AM -0700, H. Peter Anvin wrote:
> On 2025-06-27 07:01, Alejandro Colomar wrote:
> > Hi!
> >
> > Here's a new revision of the proposal, addressing some points raised by
> > Mark, plus clarifying that the paragraph about when size is zero refers
> > to the total size, as Florian was concerned that it might not be
> > symmetric.
> >
>
> I don't know if it would be useful, but proposing a new interface of the
> form:
>
> reallocp(&ptr, size)
>
> ... to separate the status return from the pointer might be a really good
> idea. This would hopefully eliminate users doing the "obvious":
No, please no. These interfaces (generic void* allocators that take a
void** argument to store the result in) are an *extremely bad
antipattern* that produces undefined behavior. What you end up with
are things of the form:
T *p;
reallocp((void **)&p, size);
which is obviously UB to us, but not to your average C programmer.
They just think it's what you're supposed to do when the compiler
tells you there's a type mismatch.
The only well-defined way to use such an interface is hideously ugly,
and no one does it:
T *p;
void *tmp = p;
reallocp(&tmp, size);
if (success) p = tmp;
Please note that this issue is not theoretical. I've encountered it in
the wild with posix_memalign, cudamalloc(), libavcoded allocation
functions, etc. Often folks build machinery on top of these where it's
impossible to extricate the UB without major architectural changes to
the code. The "double-pointer allocate" idiom just needs to be burned
with fire.
Rich
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r6 - Restore the traditional realloc(3) specification
2025-06-27 14:01 ` alx-0029r6 " Alejandro Colomar
2025-06-29 16:25 ` H. Peter Anvin
@ 2025-06-30 2:27 ` Alejandro Colomar
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-30 2:27 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Thorsten Glaser, Eric Blake,
Vincent Lefevre, Mark Harris, Collin Funk, Wilco Dijkstra,
DJ Delorie, Cristian Rodríguez, Siddhesh Poyarekar,
Sam James, Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks
[-- Attachment #1: Type: text/plain, Size: 21138 bytes --]
Hi all,
This paper is now submitted to the C Commitee:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3621.txt>
Have a lovely day!
Alex
On Fri, Jun 27, 2025 at 04:01:54PM +0200, Alejandro Colomar wrote:
> Hi!
>
> Here's a new revision of the proposal, addressing some points raised by
> Mark, plus clarifying that the paragraph about when size is zero refers
> to the total size, as Florian was concerned that it might not be
> symmetric.
>
>
> Have a lovely day!
> Alex
>
> ---
> Name
> alx-0029r6 - Restore the traditional realloc(3) specification
>
> Principles
> - Uphold the character of the language
> - Keep the language small and simple
> - Facilitate portability
> - Avoid ambiguities
> - Pay attention to performance
> - Codify existing practice to address evident deficiencies.
> - Do not prefer any implementation over others
> - Ease migration to newer language editions
> - Avoid quiet changes
> - Enable secure programming
>
> Category
> Remove UB.
>
> Author
> Alejandro Colomar <alx@kernel.org>
>
> Cc: <bug-gnulib@gnu.org>
> Cc: <musl@lists.openwall.com>
> Cc: <libc-alpha@sourceware.org>
> Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Cc: Paul Eggert <eggert@cs.ucla.edu>
> Cc: Robert Seacord <rcseacord@gmail.com>
> Cc: Elliott Hughes <enh@google.com>
> Cc: Bruno Haible <bruno@clisp.org>
> Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> Cc: Joseph Myers <josmyers@redhat.com>
> Cc: Florian Weimer <fweimer@redhat.com>
> Cc: Andreas Schwab <schwab@suse.de>
> Cc: Thorsten Glaser <tg@mirbsd.de>
> Cc: Eric Blake <eblake@redhat.com>
> Cc: Vincent Lefevre <vincent@vinc17.net>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: Collin Funk <collin.funk1@gmail.com>
> Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
> Cc: DJ Delorie <dj@redhat.com>
> Cc: Cristian Rodríguez <cristian@rodriguez.im>
> Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
> Cc: Sam James <sam@gentoo.org>
> Cc: Mark Wielaard <mark@klomp.org>
> Cc: "Maciej W. Rozycki" <macro@redhat.com>
> Cc: Martin Uecker <ma.uecker@gmail.com>
> Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
> Cc: <eskil@obsession.se>
> Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
>
> History
> <https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
>
> r0 (2025-06-17):
> - Initial draft.
>
> r1 (2025-06-20):
> - Full rewrite after the recent glibc discussion.
>
> r2 (2025-06-21):
> - Remove CC. Add CC.
> - wfix.
> - Drop quote.
> - Add a few more principles
> - Clarify why ENOMEM is used in this proposal, and make it
> optional.
> - Mention exceptional leak in code checking (size != 0).
> - Clarify that part of the description of realloc can be
> editorially removed after this change.
>
> r3 (2025-06-23):
> - Fix diff missing line.
> - Remove ENOMEM from the proposal.
> - Clarify that ENOMEM should be retained by platforms already
> using it.
> - Add mention that LLVM's address sanitizer will catch the leak
> mentioned in r2.
> - Add links to real bugs (including an RCE bug).
>
> r4 (2025-06-24):
> - Use a better link for the Whatsapp RCE.
> - s/Description/Rationale/
> - wfix
> - Mention that glibc <2.1.1 had the BSD behavior.
> - Add footnote that realloc(3) may fail while shrinking.
>
> r5 (2025-06-26):
> - It was glibc 2.1.1 that broke it, not glibc 2.2.
> - wfix
> - Mention in the footnote that the pointer may change.
> - Document why not go the other way around. It was explained
> several times during discussion, but people keep suggesting
> it.
>
> r6 (2025-06-27):
> - Clarify that the paragraph about what happens when the size
> is zero refers to when the total size is zero (for calloc(3)
> that is nmemb*size).
> - s/Unix V7/V7 Unix/
> - tfix.
> - wfix.
>
> See also
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
> <https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
> <https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
> <https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
> <https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
> <https://github.com/llvm/llvm-project/issues/113065>
> <https://www.austingroupbugs.net/view.php?id=400>
> <https://www.austingroupbugs.net/view.php?id=526>
> <https://www.austingroupbugs.net/view.php?id=688>
> <https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
> <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
> <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
> <https://gbhackers.com/whatsapp-double-free-vulnerability/>
>
> Rationale
> The specification of realloc(3) has been problematic since the
> very first standards, even before ISO C. The wording has
> changed significantly, trying to forcedly permit implementations
> to return a null pointer when the requested size is zero. This
> originated from the intent of banning zero-sized objects from
> the language in C89, but that never worked well in
> retrospective, as we can see from the fallout.
>
> None of the specifications have been good, and C23 finally gave
> up and made it undefined behavior.
>
> The problem is not only theoretical. Programmers don't know how
> to use realloc(3) correctly, and have written weird code in
> their attempts. This has resulted in a lot of non-sensical code
> in configure scripts[1], and even bugs in actual programs[2].
>
> [1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
> [2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
>
> In some cases, this non-sensical code has resulted in RCEs[3].
>
> [3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in V7 Unix, inherited by
> the BSDs, and currently available in a range of systems,
> including musl libc, doesn't have any issues regarding zero-size
> allocations. glibc --which uses an independent implementation
> rather than a Unix derivative-- also had this behavior
> originally; it changed to the current behavior in 1999
> (glibc 2.1.1), only for compatibility with C89, even though
> ironically C99 was released soon after and removed the text that
> glibc was trying to comply with, and introduced some new text
> that was very confusing, and one of its interpretations would
> make the new glibc behavior non-conforming.
>
> Code written for platforms returning a null pointer can be
> migrated to platforms returning non-null, without significant
> issues.
>
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
>
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
>
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem. Part of the reason is also that
> returning a non-null pointer with zero bytes is the natural
> extension of the behavior, which is what programmers intuitively
> expect from libc; that is, if realloc(p,3) allocates 3 bytes,
> r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
> natural by induction to expect that r(p,0) will allocate zero
> bytes. Most algorithms naturally extend to 0 just fine, and
> special casing 0 is artificial.
>
> If the realloc(3) specification were changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory (and assuming the implementations
> will continue setting errno to ENOMEM), then code written for
> AIX or glibc would continue working just fine, since the errno
> check would be redundant with the null check. Simply, the
> conditional (errno == ENOMEM) would always be true when
> (new == NULL).
>
> Then, there are non-POSIX platforms that don't set ENOMEM. In
> those platforms, code might do this:
>
> new = realloc(old, size);
> if (new == NULL) {
> if (size != 0)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> That code would continue working with this proposal, except for
> a very rare corner case, in which it would leak. In the normal
> case, (size != 0) would never be true under (new == NULL),
> because a reallocation of 0 bytes would almost always succeed,
> and thus not return a null pointer under this proposal.
> However, in some cases, the system might not find space even for
> the small metadata needed for a 0-byte allocation. In such
> case, the (size != 0) conditional would prevent deallocating
> 'old', and thus cause a memory leak. This case is exceptional
> enough that it shouldn't stop us from fixing realloc(3).
> Anyway, on an out-of-memory case, the program is likely to
> terminate rather soon, so the issue is even less likely to have
> an impact on any existing programs. Also, LLVM's address
> sanitizer will soon able to catch such a leak:
> <https://github.com/llvm/llvm-project/issues/113065>
>
> This proposal makes handling of realloc(3) as straightforward as
> one would expect, with only two states: success or error. There
> are no in-between states.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return a null pointer on error, and use consistent
> wording.
>
> Why not go the other way around?
> Some people keep asking why not go the other way around: why not
> force the BSDs and musl to return a null pointer if size is 0.
> This would result in double-free and use-after-free bugs, which
> can result in RCE vulnerabilities (remote code execution), which
> is clearly unacceptable.
>
> Consider this code, which is the usual code for calling
> realloc(3) in such systems:
>
> new = realloc(old, size);
> if (new == NULL) {
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> If realloc(p,0) would return a null pointer and free the old
> block, then the third line would be a double-free bug.
>
> Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> V7 Unix, BSD
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in V7 Unix,
> and also present in the BSDs.
>
> glibc <= 2.1
> glibc was implemented originally to return non-null. It was
> only in 1999, and purely to comply with the standards --with no
> requests by users to do so--, that the glibc maintainers decided
> to switch to the current behavior.
>
> Design decisions
> This change needs two changes, which can be applied all at once,
> or in separate steps.
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have. This
> would require changes to (at least) the following
> implementations: glibc, Bionic, Windows.
>
> The second step would be to require that malloc(0) returns a
> non-null pointer. This would require changes to (at least) the
> following implementations: AIX.
>
> This proposal has merged all steps into a single proposal.
>
> Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes redundant several mentions of
> realloc(3) next to either free(3) or malloc(3) in the standard.
> We could remove them in this proposal, or clean up that in a
> separate (mostly editorial) proposal. Let's keep it for a
> future proposal for now.
>
> Caveats
> n?n:1
> Code written today should be careful, in case it can run on
> older systems that are not fixed to comply with this stricter
> specification. Thus, code written today should call realloc(3)
> similar to this:
>
> realloc(p, n?n:1);
>
> When all existing implementations are fixed to comply with this
> stricter specification, that workaround can be removed.
>
> ENOMEM
> Existing implementations that set errno to ENOMEM must continue
> doing so when the input pointer is not freed. If they didn't,
> code that is currently portable to all POSIX systems
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> would leak on error.
>
> Since it is currently impossible to write code today that is
> portable to arbitrary C17 systems, this is not an issue in
> ISO C.
>
> - New code written for C2y will only need to check for
> NULL to detect errors.
>
> - Code written for specific C17 and older platforms
> that don't set errno will continue to work for those
> specific platforms.
>
> - Code written for POSIX.1-2024 and older platforms
> will continue working on POSIX C2y platforms,
> assuming that POSIX will continue mandating ENOMEM.
>
> - Code written for POSIX.1-2024 and older will not be
> able to be run on non-POSIX C2y platforms, but that
> could be expected.
>
> The only important thing is that platforms that did set ENOMEM
> should continue setting it, to avoid introducing leaks.
>
> Proposed wording
> Based on N3550.
>
> 7.25.4.1 Memory management functions :: General
> @@ p1
> ...
> -If the size of the space requested is zero,
> +If the total size of the space requested is zero,
> -the behavior is implementation-defined:
> -either
> -a null pointer is returned to indicate the error,
> -or
> the behavior is as if the size were some nonzero value,
> except that the returned pointer shall not be used
> to access an object.
>
> 7.25.4.2 The aligned_alloc function
> @@ Returns, p3
> The <b>aligned_alloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.3 The calloc function
> @@ Returns, p3
> The <b>calloc</b> function returns
> -either
> a pointer to the allocated space
> +on success.
> -or a null pointer
> -if
> +If
> the space cannot be allocated
> or if the product <tt>nmemb * size</tt>
> -would wraparound <b>size_t</b>.
> +would wraparound <b>size_t</b>,
> +a null pointer is returned.
>
> 7.25.4.7 The malloc function
> @@ Returns, p3
> The <b>malloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.8 The realloc function
> @@ Description, p2
> The <b>realloc</b> function
> deallocates the old object pointed to by <tt>ptr</tt>
> +as if by a call to <b>free</b>,
> and returns a pointer to a new object
> -that has the size specified by <tt>size</tt>.
> +that has the size specified by <tt>size</tt>
> +as if by a call to <b>malloc</b>.
> The contents of the new object
> shall be the same as that of the old object prior to deallocation,
> up to the lesser of the new and old sizes.
> Any bytes in the new object
> beyond the size of the old object
> have unspecified values.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> ## We can probably remove all of the above, because of the
> ## behavior now being defined as-if by calls to malloc(3) and
> ## free(3). But let's do that editorially in a separate change.
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
> +XXX)
>
> @@ New footnote XXX
> +XXX)
> +While atypical,
> +<b>realloc</b> may fail
> +or return a different pointer
> +for a call that shrinks the block of memory.
>
> @@ Returns, p4
> The <b>realloc</b> function returns
> a pointer to the new object
> (which can have the same value
> -as a pointer to the old object),
> +as a pointer to the old object)
> +on success.
> -or
> +If
> +space cannot be allocated,
> a null pointer
> -if the new object has not been allocated.
> +is returned.
>
> --
> <https://www.alejandro-colomar.es/>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 23:37 ` Alejandro Colomar
` (4 preceding siblings ...)
2025-06-21 3:57 ` Sam James
@ 2025-07-04 23:31 ` Alejandro Colomar
2025-07-05 0:15 ` Collin Funk
` (2 more replies)
5 siblings, 3 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-07-04 23:31 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 1212 bytes --]
Hi Eric,
On Fri, Jun 20, 2025 at 01:38:14AM +0200, Alejandro Colomar wrote:
> > > - POSIX.1-2001
> >
> > This one defers to C89 anywhere that it is not explicitly documenting
> > with CX shading.
>
> Ahh, I had thought it would defer to C99 because it's older, but I guess
> it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
> corrected, and glibc conforms to POSIX.1-2001.
I was reading the memccpy(3) specification in POSIX.1-2004, and found
this:
Issue 6
The restrict keyword is added to the memccpy() prototype
for alignment with the ISO/IEC 9899:1999 standard.
So, Issue 6 aligned with ISO C99? Is this exceptional, or does then
POSIX.1-2001 not defer to ISO C89?
BTW, I was trying to find out the history of memccpy(3), and why it was
introduced in 4.4BSD. Does anyone know the history? I find it a weird
function that doesn't have any good use case, or I don't seem to see it.
Every use case I see, such as a poor-man's strlcpy(3), seems to be prone
to off-by-one errors, or have other APIs that would be more ergonomic.
What were the original uses in 4.4BSD?
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-07-04 23:31 ` Alejandro Colomar
@ 2025-07-05 0:15 ` Collin Funk
2025-07-05 0:43 ` Alejandro Colomar
2025-07-05 2:28 ` Alejandro Colomar
2025-07-23 22:03 ` Eric Blake
2 siblings, 1 reply; 143+ messages in thread
From: Collin Funk @ 2025-07-05 0:15 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
Alejandro Colomar <alx@kernel.org> writes:
> BTW, I was trying to find out the history of memccpy(3), and why it was
> introduced in 4.4BSD. Does anyone know the history? I find it a weird
> function that doesn't have any good use case, or I don't seem to see it.
> Every use case I see, such as a poor-man's strlcpy(3), seems to be prone
> to off-by-one errors, or have other APIs that would be more ergonomic.
> What were the original uses in 4.4BSD?
In the sources for 2.11 BSD you can find the following in
include/strings.h:
/* Routines described in memory(BA_LIB); System V compatibility */
char *memccpy(), *memchr(), *memcpy(), *memset(), *strchr(),
*strdup(), *strpbrk(), *strrchr(), *strsep(), *strtok();
The first time I can see the function defined is in Eigth Edition Unix.
You can look for yourself here, <https://www.tuhs.org/cgi-bin/utree.pl>.
Collin
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-07-05 0:15 ` Collin Funk
@ 2025-07-05 0:43 ` Alejandro Colomar
2025-07-05 1:45 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-07-05 0:43 UTC (permalink / raw)
To: Collin Funk
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 1794 bytes --]
Hi Collin,
On Fri, Jul 04, 2025 at 05:15:27PM -0700, Collin Funk wrote:
> Alejandro Colomar <alx@kernel.org> writes:
>
> > BTW, I was trying to find out the history of memccpy(3), and why it was
> > introduced in 4.4BSD. Does anyone know the history? I find it a weird
> > function that doesn't have any good use case, or I don't seem to see it.
> > Every use case I see, such as a poor-man's strlcpy(3), seems to be prone
> > to off-by-one errors, or have other APIs that would be more ergonomic.
> > What were the original uses in 4.4BSD?
>
> In the sources for 2.11 BSD you can find the following in
> include/strings.h:
>
> /* Routines described in memory(BA_LIB); System V compatibility */
> char *memccpy(), *memchr(), *memcpy(), *memset(), *strchr(),
> *strdup(), *strpbrk(), *strrchr(), *strsep(), *strtok();
>
> The first time I can see the function defined is in Eigth Edition Unix.
Hmmm, so the FreeBSD manual page seems incorrect:
$ find -type f | grep memccpy.3 | MANWIDTH=72 xargs man 2>/dev/null | sed -n '/HISTORY/,$p'
HISTORY
The memccpy() function first appeared in 4.4BSD and was first
specified in the . The restrict keyword was added to the proto‐
type in FreeBSD 5.0.0 in accordance with the updated specifica‐
tion of IEEE Std 1003.1-2004 (“POSIX.1”).
Debian December 5, 2023 MEMCCPY(3)
Okay, I'll look into V8 Unix. At least now I know where to search.
Thanks! :)
> You can look for yourself here, <https://www.tuhs.org/cgi-bin/utree.pl>.
>
Yup, thanks! I was thinking it would be in 4.4BSD, according to
FreeBSD. Unix should be easier to search.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-07-05 0:43 ` Alejandro Colomar
@ 2025-07-05 1:45 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-07-05 1:45 UTC (permalink / raw)
To: Collin Funk
Cc: Eric Blake, Rich Felker, enh, Florian Weimer,
Adhemerval Zanella Netto, musl, libc-alpha, Joseph Myers,
наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide
[-- Attachment #1: Type: text/plain, Size: 1014 bytes --]
Hi Collin,
On Sat, Jul 05, 2025 at 02:44:02AM +0200, Alejandro Colomar wrote:
> > You can look for yourself here, <https://www.tuhs.org/cgi-bin/utree.pl>.
Hmmm, I've only found definitions, but no uses at all. All definitions
seem to be there for compatibility with SysV. I wonder what SysV had
this API for. I guess it was for some networking code, as the function
seems to be for copying part of a string until a delimiter, and
appending a terminator after it (but including it in the new string).
Something like:
char uri[] = "https://www.example.es";
char scheme[countof(uri)];
p = memccpy(scheme, uri, ':', countof(uri));
if (p == NULL)
goto hell;
p = '\0';
// Here, scheme is "https:"
This would make sense, although it would be interesting to see the exact
use case they had for it. I'm pretty certain it was not the use case
that was envisioned by the C Committee when they standardized it in C23.
Cheers,
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-07-04 23:31 ` Alejandro Colomar
2025-07-05 0:15 ` Collin Funk
@ 2025-07-05 2:28 ` Alejandro Colomar
2025-07-23 22:03 ` Eric Blake
2 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-07-05 2:28 UTC (permalink / raw)
To: Eric Blake
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
[-- Attachment #1: Type: text/plain, Size: 1415 bytes --]
Hi Eric,
On Sat, Jul 05, 2025 at 01:31:38AM +0200, Alejandro Colomar wrote:
> > > > - POSIX.1-2001
> > >
> > > This one defers to C89 anywhere that it is not explicitly documenting
> > > with CX shading.
> >
> > Ahh, I had thought it would defer to C99 because it's older, but I guess
> > it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
> > corrected, and glibc conforms to POSIX.1-2001.
>
> I was reading the memccpy(3) specification in POSIX.1-2004, and found
> this:
>
> Issue 6
>
> The restrict keyword is added to the memccpy() prototype
> for alignment with the ISO/IEC 9899:1999 standard.
>
> So, Issue 6 aligned with ISO C99? Is this exceptional, or does then
> POSIX.1-2001 not defer to ISO C89?
POSIX.1-2004 certainly seems to be using deferring to C99, as it has the
c99(1) shell utility.
<https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap02.html#tag_02_01_04_02>
and has several references to ISO/IEC 9899:1999.
<https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap02.html#tag_02_02_01>
But I didn't find any to C89.
Which means, glibc didn't conform to POSIX.1-2004 (and much likely,
neither to POSIX.1-2001; but I don't have a link to that). Anyway, I
guess n3612 will solve our problems forever, hopefully. See you! :)
Cheers,
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: [musl] Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-07-04 23:31 ` Alejandro Colomar
2025-07-05 0:15 ` Collin Funk
2025-07-05 2:28 ` Alejandro Colomar
@ 2025-07-23 22:03 ` Eric Blake
2 siblings, 0 replies; 143+ messages in thread
From: Eric Blake @ 2025-07-23 22:03 UTC (permalink / raw)
To: Alejandro Colomar
Cc: Rich Felker, enh, Florian Weimer, Adhemerval Zanella Netto, musl,
libc-alpha, Joseph Myers, наб,
Paul Eggert, Robert Seacord, Bruno Haible, bug-gnulib,
JeanHeyd Meneide, Thorsten Glaser
Sorry for the delayed response; I've been offline for a couple of
weeks.
On Sat, Jul 05, 2025 at 01:31:22AM +0200, Alejandro Colomar wrote:
> Hi Eric,
>
> On Fri, Jun 20, 2025 at 01:38:14AM +0200, Alejandro Colomar wrote:
> > > > - POSIX.1-2001
> > >
> > > This one defers to C89 anywhere that it is not explicitly documenting
> > > with CX shading.
> >
> > Ahh, I had thought it would defer to C99 because it's older, but I guess
> > it's like POSIX.1-2024 that doesn't defer to C23. Thanks! Then I stand
> > corrected, and glibc conforms to POSIX.1-2001.
>
> I was reading the memccpy(3) specification in POSIX.1-2004, and found
> this:
>
> Issue 6
>
> The restrict keyword is added to the memccpy() prototype
> for alignment with the ISO/IEC 9899:1999 standard.
>
> So, Issue 6 aligned with ISO C99? Is this exceptional, or does then
> POSIX.1-2001 not defer to ISO C89?
Hmm - I may have been too hasty in my original research, so I'm
redoing it now. Disclaimer - my contributions to POSIX started after
2004, so anything pre-dating that is based on what documents I can
access and not first-hand knowledge.
I have access to a document P1003.1b-1993.pdf; with the title
including "(POSIX@)-Part1: System Application Program Interface
(MI)-Amendment 1: Realtime Extension [C Language]", and it clearly
calls out (page xiii line 131) "This standard is written in terms of
the standard C language as specified in the C Standard {2)"; chasing
that reference finds section 1.2 (page 3 line 56) "ISOIIEC 9899: 1990,
Programming languages--C’." which is clearly C90 (and it pre-dates
C99, so that makes sense).
The next revision appears to be online at [1] which clearly states
"The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
Edition" in its preface; chasing links finds [2] which lists "ISO C
(1999) ISO/IEC 9899:1999, Programming Languages - C, including
Technical Corrigendum 1.", very obviously C99. And wikipedia confirms
that POSIX.1-2004 is merely a TC revision to POSIX.1-2001 (no way a TC
would have changed normative references to the C standard).
So I stand corrected - Issue 6 defers to C99, not C90.
[1] https://pubs.opengroup.org/onlinepubs/009696699/frontmatter/preface.html
[2] https://pubs.opengroup.org/onlinepubs/009696699/basedefs/xbd_chap01.html#tag_01_03
>
> BTW, I was trying to find out the history of memccpy(3), and why it was
> introduced in 4.4BSD. Does anyone know the history? I find it a weird
> function that doesn't have any good use case, or I don't seem to see it.
POSIX says "First released in Issue 1. Derived from Issue 1 of the
SVID."; I agree that I haven't seen it in much use, but it was
probably standardized merely because it was easy alongside all the
other mem* interfaces.
> Every use case I see, such as a poor-man's strlcpy(3), seems to be prone
> to off-by-one errors, or have other APIs that would be more ergonomic.
> What were the original uses in 4.4BSD?
>
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org
^ permalink raw reply [flat|nested] 143+ messages in thread
* alx-0029r8 - Restore the traditional realloc(3) specification
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
` (7 preceding siblings ...)
2025-06-27 14:01 ` alx-0029r6 " Alejandro Colomar
@ 2025-10-09 22:37 ` Alejandro Colomar
2025-10-09 23:20 ` H. Peter Anvin
8 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-10-09 22:37 UTC (permalink / raw)
To: libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, H. Peter Anvin, Daniel Krügler,
Kees Cook, Valdis Klētnieks, Carlos O'Donell,
Terence Kelly
[-- Attachment #1: Type: text/plain, Size: 21587 bytes --]
Hi!
The Austin Group (POSIX) seems to be in favour of this proposal.
They're waiting for the C Committee to also accept it, for copying the
specific wording, but they seem in favor of the proposal regardless, and
as Eric said some time ago, POSIX could follow through even if wg14
keeps it as undefined behavior.
<https://www.austingroupbugs.net/view.php?id=1949#c7286>
The Austin Group discussed this on 9 Oct 2025, and is in general
in favor of tightening the requirements on allocations of size 0
for Issue 9, to eliminate EINVAL for an unsupported size 0.
However, as Issue 9 will likely depend on C2Y, we would prefer
to delay wordsmithing and determination of which portions of the
text may still need <CX> shading until after C2Y has settled on
their parallel project of improving the specifications of
allocation behavior on a size of 0.
With glibc, malloc() and calloc() would already be in
compliance, realloc() would need to change behavior to match the
suggested wording.
Most BSD implementations would already be in compliance.
So, please fix glibc already. We seem to have POSIX support (and at
least partial wg14 support).
Have a lovely night!
Alex
---
Name
alx-0029r8 - Restore the traditional realloc(3) specification
Principles
- Uphold the character of the language
- Keep the language small and simple
- Facilitate portability
- Avoid ambiguities
- Pay attention to performance
- Codify existing practice to address evident deficiencies.
- Do not prefer any implementation over others
- Ease migration to newer language editions
- Avoid quiet changes
- Enable secure programming
Category
Remove UB.
Author
Alejandro Colomar <alx@kernel.org>
Cc: <bug-gnulib@gnu.org>
Cc: <musl@lists.openwall.com>
Cc: <libc-alpha@sourceware.org>
Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Robert Seacord <rcseacord@gmail.com>
Cc: Elliott Hughes <enh@google.com>
Cc: Bruno Haible <bruno@clisp.org>
Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
Cc: Rich Felker <dalias@libc.org>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Cc: Joseph Myers <josmyers@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Thorsten Glaser <tg@mirbsd.de>
Cc: Eric Blake <eblake@redhat.com>
Cc: Vincent Lefevre <vincent@vinc17.net>
Cc: Mark Harris <mark.hsj@gmail.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Cc: DJ Delorie <dj@redhat.com>
Cc: Cristian Rodríguez <cristian@rodriguez.im>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Sam James <sam@gentoo.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: "Maciej W. Rozycki" <macro@redhat.com>
Cc: Martin Uecker <ma.uecker@gmail.com>
Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
Cc: <eskil@obsession.se>
Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
History
<https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
r0 (2025-06-17):
- Initial draft.
r1 (2025-06-20):
- Full rewrite after the recent glibc discussion.
r2 (2025-06-21):
- Remove CC. Add CC.
- wfix.
- Drop quote.
- Add a few more principles
- Clarify why ENOMEM is used in this proposal, and make it
optional.
- Mention exceptional leak in code checking (size != 0).
- Clarify that part of the description of realloc can be
editorially removed after this change.
r3 (2025-06-23):
- Fix diff missing line.
- Remove ENOMEM from the proposal.
- Clarify that ENOMEM should be retained by platforms already
using it.
- Add mention that LLVM's address sanitizer will catch the leak
mentioned in r2.
- Add links to real bugs (including an RCE bug).
r4 (2025-06-24):
- Use a better link for the Whatsapp RCE.
- s/Description/Rationale/
- wfix
- Mention that glibc <2.1.1 had the BSD behavior.
- Add footnote that realloc(3) may fail while shrinking.
r5 (2025-06-26):
- It was glibc 2.1.1 that broke it, not glibc 2.2.
- wfix
- Mention in the footnote that the pointer may change.
- Document why not go the other way around. It was explained
several times during discussion, but people keep suggesting
it.
r6 (2025-06-27; n3621):
- Clarify that the paragraph about what happens when the size
is zero refers to when the total size is zero (for calloc(3)
that is nmemb*size).
- s/Unix V7/V7 Unix/
- tfix.
- wfix.
Brno meeting (2025-08-27):
- 9/13/6
- Along the lines: 21/1/5
- People recognized in the dinner after the meeting, and in the
reflector, and in corridor discussions, that they hadn't
understood the paper, and that it was more well thought than
they initially thought. They would change their vote to be
in favour with this proposal.
r7 (2025-09-21):
- Add link.
r8 (2025-10-09):
- POSIX wants this change.
See also
<https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
<https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
<https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
<https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
<https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
<https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
<https://github.com/llvm/llvm-project/issues/113065>
<https://www.austingroupbugs.net/view.php?id=400>
<https://www.austingroupbugs.net/view.php?id=526>
<https://www.austingroupbugs.net/view.php?id=688>
<https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
<https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
<https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
<https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
<https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
<https://gbhackers.com/whatsapp-double-free-vulnerability/>
<https://www.austingroupbugs.net/view.php?id=1949>
Rationale
The specification of realloc(3) has been problematic since the
very first standards, even before ISO C. The wording has
changed significantly, trying to forcedly permit implementations
to return a null pointer when the requested size is zero. This
originated from the intent of banning zero-sized objects from
the language in C89, but that never worked well in
retrospective, as we can see from the fallout.
None of the specifications have been good, and C23 finally gave
up and made it undefined behavior.
The problem is not only theoretical. Programmers don't know how
to use realloc(3) correctly, and have written weird code in
their attempts. This has resulted in a lot of non-sensical code
in configure scripts[1], and even bugs in actual programs[2].
[1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
[2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
In some cases, this non-sensical code has resulted in RCEs[3].
[3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
However, this doesn't need to be like that. The traditional
implementation of realloc(3), present in V7 Unix, inherited by
the BSDs, and currently available in a range of systems,
including musl libc, doesn't have any issues regarding zero-size
allocations. glibc --which uses an independent implementation
rather than a Unix derivative-- also had this behavior
originally; it changed to the current behavior in 1999
(glibc 2.1.1), only for compatibility with C89, even though
ironically C99 was released soon after and removed the text that
glibc was trying to comply with, and introduced some new text
that was very confusing, and one of its interpretations would
make the new glibc behavior non-conforming.
Code written for platforms returning a null pointer can be
migrated to platforms returning non-null, without significant
issues.
There are two kinds of code that call realloc(p,0). One
hard-codes the 0, and is used as a replacement of free(p). This
code ignores the return value, since it's unimportant. This
code currently produces a leak of 0 bytes plus associated
metadata on platforms such as musl libc, where it returns a
non-null pointer. However, assuming that there are programs
written with the knowledge that they won't ever be run on such
platforms, we should take care of that, and make sure they don't
leak. A way of accomplishing this would be to recommend
implementations to issue a diagnostic when realloc(3) is called
with a hardcoded zero. This is only an informal recommendation
made by this proposal, as this is a matter of QoI, and the
standard shouldn't say anything about it. This would prevent
this class of minor leaks.
Moreover, in glibc, realloc(p,0) may return non-null, in the
case where p is NULL, so code must already take that into
account, and thus code that simply takes realloc(p,0) as a
synonym of free(p) is already leaky, as free(NULL) is a no-op,
but realloc(NULL,0) allocates 0 bytes.
The other kind of code is in algorithms that realloc(3) an
arbitrary size, which might eventually be zero. This gets more
complex.
Here's the code that should be written for AIX or glibc:
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
Failing to check for ENOMEM in these platforms before freeing
the old pointer would result in a double-free. If the program
decides to continue using the old pointer instead of freeing it,
it would result in a use-after-free.
In the platforms where realloc(p,0) returns non-null, such as
the BSDs or musl libc, it is simpler to handle it:
new = realloc(old, size);
if (new == NULL) { // errno is ENOMEM
free(old);
goto fail;
}
...
free(new);
Whenever the result is a null pointer, these platforms are
reporting an ENOMEM error, and thus it is superfluous to check
errno there.
Most code is written in this way, even if run on platforms
returning a null pointer. This is because most programmers are
just unaware of this problem. Part of the reason is also that
returning a non-null pointer with zero bytes is the natural
extension of the behavior, which is what programmers intuitively
expect from libc; that is, if realloc(p,3) allocates 3 bytes,
r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
natural by induction to expect that r(p,0) will allocate zero
bytes. Most algorithms naturally extend to 0 just fine, and
special casing 0 is artificial.
If the realloc(3) specification were changed to require that
realloc(p,0) returns non-null on success, and that realloc(p,0)
only fails when out-of-memory (and assuming the implementations
will continue setting errno to ENOMEM), then code written for
AIX or glibc would continue working just fine, since the errno
check would be redundant with the null check. Simply, the
conditional (errno == ENOMEM) would always be true when
(new == NULL).
Then, there are non-POSIX platforms that don't set ENOMEM. In
those platforms, code might do this:
new = realloc(old, size);
if (new == NULL) {
if (size != 0)
free(old);
goto fail;
}
...
free(new);
That code would continue working with this proposal, except for
a very rare corner case, in which it would leak. In the normal
case, (size != 0) would never be true under (new == NULL),
because a reallocation of 0 bytes would almost always succeed,
and thus not return a null pointer under this proposal.
However, in some cases, the system might not find space even for
the small metadata needed for a 0-byte allocation. In such
case, the (size != 0) conditional would prevent deallocating
'old', and thus cause a memory leak. This case is exceptional
enough that it shouldn't stop us from fixing realloc(3).
Anyway, on an out-of-memory case, the program is likely to
terminate rather soon, so the issue is even less likely to have
an impact on any existing programs. Also, LLVM's address
sanitizer will soon able to catch such a leak:
<https://github.com/llvm/llvm-project/issues/113065>
This proposal makes handling of realloc(3) as straightforward as
one would expect, with only two states: success or error. There
are no in-between states.
The resulting wording in the standard is also much simpler, as
it doesn't need to define so many special cases.
For consistency, all the other allocation functions are updated
to both return a null pointer on error, and use consistent
wording.
Why not go the other way around?
Some people keep asking why not go the other way around: why not
force the BSDs and musl to return a null pointer if size is 0.
This would result in double-free and use-after-free bugs, which
can result in RCE vulnerabilities (remote code execution), which
is clearly unacceptable.
Consider this code, which is the usual code for calling
realloc(3) in such systems:
new = realloc(old, size);
if (new == NULL) {
free(old);
goto fail;
}
...
free(new);
If realloc(p,0) would return a null pointer and free the old
block, then the third line would be a double-free bug.
POSIX
POSIX is in favour of this proposal, and is waiting for the
C Committee to accept it, to copy the wording from C2y.
<https://www.austingroupbugs.net/view.php?id=1949#c7286>
Prior art
gnulib
gnulib provides the realloc-posix module, which aims to wrap the
system realloc(3) and reallocarray(3) functions so that they
behave in a POSIX-complying manner.
It previously behaved like glibc. After I reported that it was
non-conforming to POSIX, we discussed the best way forward,
which we agreed was the same direction that this paper is
proposing now for C2y. The implementation was changed in
gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
There have been no regression reports since then, as we
expected.
V7 Unix, BSD
The proposed behavior is the one endorsed by Doug McIlroy, the
author of the original implementation of realloc(3) in V7 Unix,
and also present in the BSDs.
glibc <= 2.1
glibc was implemented originally to return non-null. It was
only in 1999, and purely to comply with the standards --with no
requests by users to do so--, that the glibc maintainers decided
to switch to the current behavior.
Design decisions
This change needs two changes, which can be applied all at once,
or in separate steps.
The first step would make realloc(p,s) be consistent with
free(p) and malloc(s), including when p is a null pointer, when
s is zero, and also when both corner cases happen at the same
time. This change would already turn the implementations where
malloc(0) returns non-null into the end goal we have. This
would require changes to (at least) the following
implementations: glibc, Bionic, Windows.
The second step would be to require that malloc(0) returns a
non-null pointer. This would require changes to (at least) the
following implementations: AIX.
This proposal has merged all steps into a single proposal.
Future directions
This proposal, by specifying realloc(3) as-if by calling
free(3) and malloc(3), makes redundant several mentions of
realloc(3) next to either free(3) or malloc(3) in the standard.
We could remove them in this proposal, or clean up that in a
separate (mostly editorial) proposal. Let's keep it for a
future proposal for now.
Caveats
n?n:1
Code written today should be careful, in case it can run on
older systems that are not fixed to comply with this stricter
specification. Thus, code written today should call realloc(3)
similar to this:
realloc(p, n?n:1);
When all existing implementations are fixed to comply with this
stricter specification, that workaround can be removed.
ENOMEM
Existing implementations that set errno to ENOMEM must continue
doing so when the input pointer is not freed. If they didn't,
code that is currently portable to all POSIX systems
errno = 0;
new = realloc(old, size);
if (new == NULL) {
if (errno == ENOMEM)
free(old);
goto fail;
}
...
free(new);
would leak on error.
Since it is currently impossible to write code today that is
portable to arbitrary C17 systems, this is not an issue in
ISO C.
- New code written for C2y will only need to check for
NULL to detect errors.
- Code written for specific C17 and older platforms
that don't set errno will continue to work for those
specific platforms.
- Code written for POSIX.1-2024 and older platforms
will continue working on POSIX C2y platforms,
assuming that POSIX will continue mandating ENOMEM.
- Code written for POSIX.1-2024 and older will not be
able to be run on non-POSIX C2y platforms, but that
could be expected.
The only important thing is that platforms that did set ENOMEM
should continue setting it, to avoid introducing leaks.
Proposed wording
Based on N3550.
7.25.4.1 Memory management functions :: General
@@ p1
...
-If the size of the space requested is zero,
+If the total size of the space requested is zero,
-the behavior is implementation-defined:
-either
-a null pointer is returned to indicate the error,
-or
the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used
to access an object.
7.25.4.2 The aligned_alloc function
@@ Returns, p3
The <b>aligned_alloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.3 The calloc function
@@ Returns, p3
The <b>calloc</b> function returns
-either
a pointer to the allocated space
+on success.
-or a null pointer
-if
+If
the space cannot be allocated
or if the product <tt>nmemb * size</tt>
-would wraparound <b>size_t</b>.
+would wraparound <b>size_t</b>,
+a null pointer is returned.
7.25.4.7 The malloc function
@@ Returns, p3
The <b>malloc</b> function returns
-either
-a null pointer
-or
-a pointer to the allocated space.
+a pointer to the allocated space
+on success.
+If
+the space cannot be allocated,
+a null pointer is returned.
7.25.4.8 The realloc function
@@ Description, p2
The <b>realloc</b> function
deallocates the old object pointed to by <tt>ptr</tt>
+as if by a call to <b>free</b>,
and returns a pointer to a new object
-that has the size specified by <tt>size</tt>.
+that has the size specified by <tt>size</tt>
+as if by a call to <b>malloc</b>.
The contents of the new object
shall be the same as that of the old object prior to deallocation,
up to the lesser of the new and old sizes.
Any bytes in the new object
beyond the size of the old object
have unspecified values.
@@ p3
If <tt>ptr</tt> is a null pointer,
the <b>realloc</b> function behaves
like the <b>malloc</b> function for the specified size.
Otherwise,
if <tt>ptr</tt> does not match a pointer
earlier returned by a memory management function,
or
if the space has been deallocated
by a call to the <b>free</b> or <b>realloc</b> function,
## We can probably remove all of the above, because of the
## behavior now being defined as-if by calls to malloc(3) and
## free(3). But let's do that editorially in a separate change.
-or
-if the size is zero,
## We're defining the behavior.
the behavior is undefined.
If
-memory for the new object is not allocated,
+the space cannot be allocated,
## Editorial; for consistency with the wording of the other functions.
the old object is not deallocated
and its value is unchanged.
+XXX)
@@ New footnote XXX
+XXX)
+While atypical,
+<b>realloc</b> may fail
+or return a different pointer
+for a call that shrinks the block of memory.
@@ Returns, p4
The <b>realloc</b> function returns
a pointer to the new object
(which can have the same value
-as a pointer to the old object),
+as a pointer to the old object)
+on success.
-or
+If
+space cannot be allocated,
a null pointer
-if the new object has not been allocated.
+is returned.
--
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: alx-0029r8 - Restore the traditional realloc(3) specification
2025-10-09 22:37 ` alx-0029r8 " Alejandro Colomar
@ 2025-10-09 23:20 ` H. Peter Anvin
0 siblings, 0 replies; 143+ messages in thread
From: H. Peter Anvin @ 2025-10-09 23:20 UTC (permalink / raw)
To: Alejandro Colomar, libc-alpha
Cc: bug-gnulib, musl, наб,
Douglas McIlroy, Paul Eggert, Robert Seacord, Elliott Hughes,
Bruno Haible, JeanHeyd Meneide, Rich Felker,
Adhemerval Zanella Netto, Joseph Myers, Florian Weimer,
Laurent Bercot, Andreas Schwab, Eric Blake, Vincent Lefevre,
Mark Harris, Collin Funk, Wilco Dijkstra, DJ Delorie,
Cristian Rodríguez, Siddhesh Poyarekar, Sam James,
Mark Wielaard, Maciej W. Rozycki, Martin Uecker,
Christopher Bazley, eskil, Daniel Krügler, Kees Cook,
Valdis Klētnieks, Carlos O'Donell, Terence Kelly
On October 9, 2025 3:37:19 PM PDT, Alejandro Colomar <alx@kernel.org> wrote:
>Hi!
>
>The Austin Group (POSIX) seems to be in favour of this proposal.
>They're waiting for the C Committee to also accept it, for copying the
>specific wording, but they seem in favor of the proposal regardless, and
>as Eric said some time ago, POSIX could follow through even if wg14
>keeps it as undefined behavior.
>
><https://www.austingroupbugs.net/view.php?id=1949#c7286>
>
> The Austin Group discussed this on 9 Oct 2025, and is in general
> in favor of tightening the requirements on allocations of size 0
> for Issue 9, to eliminate EINVAL for an unsupported size 0.
> However, as Issue 9 will likely depend on C2Y, we would prefer
> to delay wordsmithing and determination of which portions of the
> text may still need <CX> shading until after C2Y has settled on
> their parallel project of improving the specifications of
> allocation behavior on a size of 0.
>
> With glibc, malloc() and calloc() would already be in
> compliance, realloc() would need to change behavior to match the
> suggested wording.
>
> Most BSD implementations would already be in compliance.
>
>So, please fix glibc already. We seem to have POSIX support (and at
>least partial wg14 support).
>
>
>Have a lovely night!
>Alex
>
>---
>Name
> alx-0029r8 - Restore the traditional realloc(3) specification
>
>Principles
> - Uphold the character of the language
> - Keep the language small and simple
> - Facilitate portability
> - Avoid ambiguities
> - Pay attention to performance
> - Codify existing practice to address evident deficiencies.
> - Do not prefer any implementation over others
> - Ease migration to newer language editions
> - Avoid quiet changes
> - Enable secure programming
>
>Category
> Remove UB.
>
>Author
> Alejandro Colomar <alx@kernel.org>
>
> Cc: <bug-gnulib@gnu.org>
> Cc: <musl@lists.openwall.com>
> Cc: <libc-alpha@sourceware.org>
> Cc: наб <nabijaczleweli@nabijaczleweli.xyz>
> Cc: Douglas McIlroy <douglas.mcilroy@dartmouth.edu>
> Cc: Paul Eggert <eggert@cs.ucla.edu>
> Cc: Robert Seacord <rcseacord@gmail.com>
> Cc: Elliott Hughes <enh@google.com>
> Cc: Bruno Haible <bruno@clisp.org>
> Cc: JeanHeyd Meneide <phdofthehouse@gmail.com>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> Cc: Joseph Myers <josmyers@redhat.com>
> Cc: Florian Weimer <fweimer@redhat.com>
> Cc: Andreas Schwab <schwab@suse.de>
> Cc: Thorsten Glaser <tg@mirbsd.de>
> Cc: Eric Blake <eblake@redhat.com>
> Cc: Vincent Lefevre <vincent@vinc17.net>
> Cc: Mark Harris <mark.hsj@gmail.com>
> Cc: Collin Funk <collin.funk1@gmail.com>
> Cc: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
> Cc: DJ Delorie <dj@redhat.com>
> Cc: Cristian Rodríguez <cristian@rodriguez.im>
> Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
> Cc: Sam James <sam@gentoo.org>
> Cc: Mark Wielaard <mark@klomp.org>
> Cc: "Maciej W. Rozycki" <macro@redhat.com>
> Cc: Martin Uecker <ma.uecker@gmail.com>
> Cc: Christopher Bazley <chris.bazley.wg14@gmail.com>
> Cc: <eskil@obsession.se>
> Cc: Daniel Krügler <daniel.kruegler@googlemail.com>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
>
>History
> <https://www.alejandro-colomar.es/src/alx/alx/wg14/alx-0029.git/>
>
> r0 (2025-06-17):
> - Initial draft.
>
> r1 (2025-06-20):
> - Full rewrite after the recent glibc discussion.
>
> r2 (2025-06-21):
> - Remove CC. Add CC.
> - wfix.
> - Drop quote.
> - Add a few more principles
> - Clarify why ENOMEM is used in this proposal, and make it
> optional.
> - Mention exceptional leak in code checking (size != 0).
> - Clarify that part of the description of realloc can be
> editorially removed after this change.
>
> r3 (2025-06-23):
> - Fix diff missing line.
> - Remove ENOMEM from the proposal.
> - Clarify that ENOMEM should be retained by platforms already
> using it.
> - Add mention that LLVM's address sanitizer will catch the leak
> mentioned in r2.
> - Add links to real bugs (including an RCE bug).
>
> r4 (2025-06-24):
> - Use a better link for the Whatsapp RCE.
> - s/Description/Rationale/
> - wfix
> - Mention that glibc <2.1.1 had the BSD behavior.
> - Add footnote that realloc(3) may fail while shrinking.
>
> r5 (2025-06-26):
> - It was glibc 2.1.1 that broke it, not glibc 2.2.
> - wfix
> - Mention in the footnote that the pointer may change.
> - Document why not go the other way around. It was explained
> several times during discussion, but people keep suggesting
> it.
>
> r6 (2025-06-27; n3621):
> - Clarify that the paragraph about what happens when the size
> is zero refers to when the total size is zero (for calloc(3)
> that is nmemb*size).
> - s/Unix V7/V7 Unix/
> - tfix.
> - wfix.
>
> Brno meeting (2025-08-27):
> - 9/13/6
> - Along the lines: 21/1/5
> - People recognized in the dinner after the meeting, and in the
> reflector, and in corridor discussions, that they hadn't
> understood the paper, and that it was more well thought than
> they initially thought. They would change their vote to be
> in favour with this proposal.
>
> r7 (2025-09-21):
> - Add link.
>
> r8 (2025-10-09):
> - POSIX wants this change.
>
>See also
> <https://nabijaczleweli.xyz/content/blogn_t/017-malloc0.html>
> <https://sourceware.org/pipermail/libc-alpha/1999-April/000956.html>
> <https://inbox.sourceware.org/libc-alpha/nbyurzcgzgd5rdybbi4no2kw5grrc32k63svf7oq73nfcbus5r@77gry66kpqfr/>
> <https://inbox.sourceware.org/libc-alpha/20241019014002.3684656-1-siddhesh@sourceware.org/T/#u>
> <https://inbox.sourceware.org/libc-alpha/qukfe5yxycbl5v7ooskvqdnm3au3orohbx4babfltegi47iyly@or6dgf7akeqv/T/#u>
> <https://github.com/bminor/glibc/commit/7c2b945e1fd64e0a5a4dbd6ae6592a7314dcd4b5>
> <https://github.com/llvm/llvm-project/issues/113065>
> <https://www.austingroupbugs.net/view.php?id=400>
> <https://www.austingroupbugs.net/view.php?id=526>
> <https://www.austingroupbugs.net/view.php?id=688>
> <https://sourceware.org/bugzilla/show_bug.cgi?id=12547>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n868.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2438.htm>
> <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2464.pdf>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/realloc.html>
> <https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/realloc.html>
> <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120744>
> <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
> <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
> <https://gbhackers.com/whatsapp-double-free-vulnerability/>
> <https://www.austingroupbugs.net/view.php?id=1949>
>
>Rationale
> The specification of realloc(3) has been problematic since the
> very first standards, even before ISO C. The wording has
> changed significantly, trying to forcedly permit implementations
> to return a null pointer when the requested size is zero. This
> originated from the intent of banning zero-sized objects from
> the language in C89, but that never worked well in
> retrospective, as we can see from the fallout.
>
> None of the specifications have been good, and C23 finally gave
> up and made it undefined behavior.
>
> The problem is not only theoretical. Programmers don't know how
> to use realloc(3) correctly, and have written weird code in
> their attempts. This has resulted in a lot of non-sensical code
> in configure scripts[1], and even bugs in actual programs[2].
>
> [1] <https://codesearch.debian.net/search?q=%5Cbrealloc%5B+%5Ct%5D*%5B%28%5D%5B%5E%2C%5D*%2C%5B+%5Ct%5D0%5B%29%5D&literal=0>
> [2] <https://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org/>
>
> In some cases, this non-sensical code has resulted in RCEs[3].
>
> [3] <https://awakened1712.github.io/hacking/hacking-whatsapp-gif-rce/>
>
> However, this doesn't need to be like that. The traditional
> implementation of realloc(3), present in V7 Unix, inherited by
> the BSDs, and currently available in a range of systems,
> including musl libc, doesn't have any issues regarding zero-size
> allocations. glibc --which uses an independent implementation
> rather than a Unix derivative-- also had this behavior
> originally; it changed to the current behavior in 1999
> (glibc 2.1.1), only for compatibility with C89, even though
> ironically C99 was released soon after and removed the text that
> glibc was trying to comply with, and introduced some new text
> that was very confusing, and one of its interpretations would
> make the new glibc behavior non-conforming.
>
> Code written for platforms returning a null pointer can be
> migrated to platforms returning non-null, without significant
> issues.
>
> There are two kinds of code that call realloc(p,0). One
> hard-codes the 0, and is used as a replacement of free(p). This
> code ignores the return value, since it's unimportant. This
> code currently produces a leak of 0 bytes plus associated
> metadata on platforms such as musl libc, where it returns a
> non-null pointer. However, assuming that there are programs
> written with the knowledge that they won't ever be run on such
> platforms, we should take care of that, and make sure they don't
> leak. A way of accomplishing this would be to recommend
> implementations to issue a diagnostic when realloc(3) is called
> with a hardcoded zero. This is only an informal recommendation
> made by this proposal, as this is a matter of QoI, and the
> standard shouldn't say anything about it. This would prevent
> this class of minor leaks.
>
> Moreover, in glibc, realloc(p,0) may return non-null, in the
> case where p is NULL, so code must already take that into
> account, and thus code that simply takes realloc(p,0) as a
> synonym of free(p) is already leaky, as free(NULL) is a no-op,
> but realloc(NULL,0) allocates 0 bytes.
>
> The other kind of code is in algorithms that realloc(3) an
> arbitrary size, which might eventually be zero. This gets more
> complex.
>
> Here's the code that should be written for AIX or glibc:
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Failing to check for ENOMEM in these platforms before freeing
> the old pointer would result in a double-free. If the program
> decides to continue using the old pointer instead of freeing it,
> it would result in a use-after-free.
>
> In the platforms where realloc(p,0) returns non-null, such as
> the BSDs or musl libc, it is simpler to handle it:
>
> new = realloc(old, size);
> if (new == NULL) { // errno is ENOMEM
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> Whenever the result is a null pointer, these platforms are
> reporting an ENOMEM error, and thus it is superfluous to check
> errno there.
>
> Most code is written in this way, even if run on platforms
> returning a null pointer. This is because most programmers are
> just unaware of this problem. Part of the reason is also that
> returning a non-null pointer with zero bytes is the natural
> extension of the behavior, which is what programmers intuitively
> expect from libc; that is, if realloc(p,3) allocates 3 bytes,
> r(p,2) allocates two bytes, and r(p,1) allocates one byte, it is
> natural by induction to expect that r(p,0) will allocate zero
> bytes. Most algorithms naturally extend to 0 just fine, and
> special casing 0 is artificial.
>
> If the realloc(3) specification were changed to require that
> realloc(p,0) returns non-null on success, and that realloc(p,0)
> only fails when out-of-memory (and assuming the implementations
> will continue setting errno to ENOMEM), then code written for
> AIX or glibc would continue working just fine, since the errno
> check would be redundant with the null check. Simply, the
> conditional (errno == ENOMEM) would always be true when
> (new == NULL).
>
> Then, there are non-POSIX platforms that don't set ENOMEM. In
> those platforms, code might do this:
>
> new = realloc(old, size);
> if (new == NULL) {
> if (size != 0)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> That code would continue working with this proposal, except for
> a very rare corner case, in which it would leak. In the normal
> case, (size != 0) would never be true under (new == NULL),
> because a reallocation of 0 bytes would almost always succeed,
> and thus not return a null pointer under this proposal.
> However, in some cases, the system might not find space even for
> the small metadata needed for a 0-byte allocation. In such
> case, the (size != 0) conditional would prevent deallocating
> 'old', and thus cause a memory leak. This case is exceptional
> enough that it shouldn't stop us from fixing realloc(3).
> Anyway, on an out-of-memory case, the program is likely to
> terminate rather soon, so the issue is even less likely to have
> an impact on any existing programs. Also, LLVM's address
> sanitizer will soon able to catch such a leak:
> <https://github.com/llvm/llvm-project/issues/113065>
>
> This proposal makes handling of realloc(3) as straightforward as
> one would expect, with only two states: success or error. There
> are no in-between states.
>
> The resulting wording in the standard is also much simpler, as
> it doesn't need to define so many special cases.
>
> For consistency, all the other allocation functions are updated
> to both return a null pointer on error, and use consistent
> wording.
>
> Why not go the other way around?
> Some people keep asking why not go the other way around: why not
> force the BSDs and musl to return a null pointer if size is 0.
> This would result in double-free and use-after-free bugs, which
> can result in RCE vulnerabilities (remote code execution), which
> is clearly unacceptable.
>
> Consider this code, which is the usual code for calling
> realloc(3) in such systems:
>
> new = realloc(old, size);
> if (new == NULL) {
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> If realloc(p,0) would return a null pointer and free the old
> block, then the third line would be a double-free bug.
>
> POSIX
> POSIX is in favour of this proposal, and is waiting for the
> C Committee to accept it, to copy the wording from C2y.
> <https://www.austingroupbugs.net/view.php?id=1949#c7286>
>
>Prior art
> gnulib
> gnulib provides the realloc-posix module, which aims to wrap the
> system realloc(3) and reallocarray(3) functions so that they
> behave in a POSIX-complying manner.
>
> It previously behaved like glibc. After I reported that it was
> non-conforming to POSIX, we discussed the best way forward,
> which we agreed was the same direction that this paper is
> proposing now for C2y. The implementation was changed in
>
> gnulib.git d884e6fc4a60 (2024-11-04; "realloc-posix: realloc (..., 0) now returns nonnull")
>
> There have been no regression reports since then, as we
> expected.
>
> V7 Unix, BSD
> The proposed behavior is the one endorsed by Doug McIlroy, the
> author of the original implementation of realloc(3) in V7 Unix,
> and also present in the BSDs.
>
> glibc <= 2.1
> glibc was implemented originally to return non-null. It was
> only in 1999, and purely to comply with the standards --with no
> requests by users to do so--, that the glibc maintainers decided
> to switch to the current behavior.
>
>Design decisions
> This change needs two changes, which can be applied all at once,
> or in separate steps.
>
> The first step would make realloc(p,s) be consistent with
> free(p) and malloc(s), including when p is a null pointer, when
> s is zero, and also when both corner cases happen at the same
> time. This change would already turn the implementations where
> malloc(0) returns non-null into the end goal we have. This
> would require changes to (at least) the following
> implementations: glibc, Bionic, Windows.
>
> The second step would be to require that malloc(0) returns a
> non-null pointer. This would require changes to (at least) the
> following implementations: AIX.
>
> This proposal has merged all steps into a single proposal.
>
>Future directions
> This proposal, by specifying realloc(3) as-if by calling
> free(3) and malloc(3), makes redundant several mentions of
> realloc(3) next to either free(3) or malloc(3) in the standard.
> We could remove them in this proposal, or clean up that in a
> separate (mostly editorial) proposal. Let's keep it for a
> future proposal for now.
>
>Caveats
> n?n:1
> Code written today should be careful, in case it can run on
> older systems that are not fixed to comply with this stricter
> specification. Thus, code written today should call realloc(3)
> similar to this:
>
> realloc(p, n?n:1);
>
> When all existing implementations are fixed to comply with this
> stricter specification, that workaround can be removed.
>
> ENOMEM
> Existing implementations that set errno to ENOMEM must continue
> doing so when the input pointer is not freed. If they didn't,
> code that is currently portable to all POSIX systems
>
> errno = 0;
> new = realloc(old, size);
> if (new == NULL) {
> if (errno == ENOMEM)
> free(old);
> goto fail;
> }
> ...
> free(new);
>
> would leak on error.
>
> Since it is currently impossible to write code today that is
> portable to arbitrary C17 systems, this is not an issue in
> ISO C.
>
> - New code written for C2y will only need to check for
> NULL to detect errors.
>
> - Code written for specific C17 and older platforms
> that don't set errno will continue to work for those
> specific platforms.
>
> - Code written for POSIX.1-2024 and older platforms
> will continue working on POSIX C2y platforms,
> assuming that POSIX will continue mandating ENOMEM.
>
> - Code written for POSIX.1-2024 and older will not be
> able to be run on non-POSIX C2y platforms, but that
> could be expected.
>
> The only important thing is that platforms that did set ENOMEM
> should continue setting it, to avoid introducing leaks.
>
>Proposed wording
> Based on N3550.
>
> 7.25.4.1 Memory management functions :: General
> @@ p1
> ...
> -If the size of the space requested is zero,
> +If the total size of the space requested is zero,
> -the behavior is implementation-defined:
> -either
> -a null pointer is returned to indicate the error,
> -or
> the behavior is as if the size were some nonzero value,
> except that the returned pointer shall not be used
> to access an object.
>
> 7.25.4.2 The aligned_alloc function
> @@ Returns, p3
> The <b>aligned_alloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.3 The calloc function
> @@ Returns, p3
> The <b>calloc</b> function returns
> -either
> a pointer to the allocated space
> +on success.
> -or a null pointer
> -if
> +If
> the space cannot be allocated
> or if the product <tt>nmemb * size</tt>
> -would wraparound <b>size_t</b>.
> +would wraparound <b>size_t</b>,
> +a null pointer is returned.
>
> 7.25.4.7 The malloc function
> @@ Returns, p3
> The <b>malloc</b> function returns
> -either
> -a null pointer
> -or
> -a pointer to the allocated space.
> +a pointer to the allocated space
> +on success.
> +If
> +the space cannot be allocated,
> +a null pointer is returned.
>
> 7.25.4.8 The realloc function
> @@ Description, p2
> The <b>realloc</b> function
> deallocates the old object pointed to by <tt>ptr</tt>
> +as if by a call to <b>free</b>,
> and returns a pointer to a new object
> -that has the size specified by <tt>size</tt>.
> +that has the size specified by <tt>size</tt>
> +as if by a call to <b>malloc</b>.
> The contents of the new object
> shall be the same as that of the old object prior to deallocation,
> up to the lesser of the new and old sizes.
> Any bytes in the new object
> beyond the size of the old object
> have unspecified values.
>
> @@ p3
> If <tt>ptr</tt> is a null pointer,
> the <b>realloc</b> function behaves
> like the <b>malloc</b> function for the specified size.
> Otherwise,
> if <tt>ptr</tt> does not match a pointer
> earlier returned by a memory management function,
> or
> if the space has been deallocated
> by a call to the <b>free</b> or <b>realloc</b> function,
> ## We can probably remove all of the above, because of the
> ## behavior now being defined as-if by calls to malloc(3) and
> ## free(3). But let's do that editorially in a separate change.
> -or
> -if the size is zero,
> ## We're defining the behavior.
> the behavior is undefined.
> If
> -memory for the new object is not allocated,
> +the space cannot be allocated,
> ## Editorial; for consistency with the wording of the other functions.
> the old object is not deallocated
> and its value is unchanged.
> +XXX)
>
> @@ New footnote XXX
> +XXX)
> +While atypical,
> +<b>realloc</b> may fail
> +or return a different pointer
> +for a call that shrinks the block of memory.
>
> @@ Returns, p4
> The <b>realloc</b> function returns
> a pointer to the new object
> (which can have the same value
> -as a pointer to the old object),
> +as a pointer to the old object)
> +on success.
> -or
> +If
> +space cannot be allocated,
> a null pointer
> -if the new object has not been allocated.
> +is returned.
>
Makes sense to me.
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 2:58 ` H. Peter Anvin
@ 2025-06-21 3:00 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 3:00 UTC (permalink / raw)
To: H. Peter Anvin; +Cc: Wilco Dijkstra, Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 463 bytes --]
On Fri, Jun 20, 2025 at 07:58:30PM -0700, H. Peter Anvin wrote:
> Either way, my main point was that the uniqueness requirement *can* be
> satisfied without backing it up with memory.
Yup, thanks! That'll be useful for when I write a paper.
>
> (And I have seen unique pointers being used as error sentinels, i.e. "null
> pointers with payload".)
That's indeed what Doug mentioned. :)
Cheers,
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 2:48 ` Alejandro Colomar
@ 2025-06-21 2:58 ` H. Peter Anvin
2025-06-21 3:00 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: H. Peter Anvin @ 2025-06-21 2:58 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Wilco Dijkstra, Libc Alpha
On 2025-06-20 19:48, Alejandro Colomar wrote:
>>
>>>>
>>>> If a non-NULL pointer is returned, from either malloc or realloc, it needs
>>>> to be unique, but there is no requirement that it needs to be *accessible.*
>>>
>>> Nope, it doesn't need to be unique. There's no requisite in the
>>> standard that the result of malloc(0) is unique.
>>>
>>
>> I think it is for C++, though.
>
> Hmm, could be. I was talking with Doug and we found it was removed in
> C99 (the requirement was present in C89). I'll ask why it was changed
> some day, but I don't want to change the attention from realloc(3) from
> now.
>
> Once we merge this, I'll start asking about uniqueness.
>
> Also, Doug told me he has a use case for unique pointers, so I might
> write a paper for restoring that requirement, if we can. But I'll do
> that after we finish with this.
>
Either way, my main point was that the uniqueness requirement *can* be
satisfied without backing it up with memory.
(And I have seen unique pointers being used as error sentinels, i.e.
"null pointers with payload".)
-hpa
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 2:30 ` H. Peter Anvin
@ 2025-06-21 2:48 ` Alejandro Colomar
2025-06-21 2:58 ` H. Peter Anvin
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 2:48 UTC (permalink / raw)
To: H. Peter Anvin; +Cc: Wilco Dijkstra, Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 1690 bytes --]
Hi H. Peter,
On Fri, Jun 20, 2025 at 07:30:50PM -0700, H. Peter Anvin wrote:
> On 2025-06-20 18:21, Alejandro Colomar wrote:
> >
> > On Fri, Jun 20, 2025 at 04:58:04PM -0700, H. Peter Anvin wrote:
> > > On 2025-06-17 04:52, Wilco Dijkstra wrote:
> > > > Hi Alejandro,
> > > >
> > > > > > The purpose of realloc(p, 0) is to free memory.
> > > > >
> > > > > No. The purpose of realloc(p,0) is to resize a block to size 0.
> > > >
> > > > That's arguing semantics. Whether you return the original block or a new
> > > > small block is an internal implementation detail. The goal is to free the memory.
> > > >
> > >
> > > There is actually a third option.
> >
> > This isn't actually a third option. This is the option I'm proposing.
> > :)
> >
>
> :)
>
> > >
> > > If a non-NULL pointer is returned, from either malloc or realloc, it needs
> > > to be unique, but there is no requirement that it needs to be *accessible.*
> >
> > Nope, it doesn't need to be unique. There's no requisite in the
> > standard that the result of malloc(0) is unique.
> >
>
> I think it is for C++, though.
Hmm, could be. I was talking with Doug and we found it was removed in
C99 (the requirement was present in C89). I'll ask why it was changed
some day, but I don't want to change the attention from realloc(3) from
now.
Once we merge this, I'll start asking about uniqueness.
Also, Doug told me he has a use case for unique pointers, so I might
write a paper for restoring that requirement, if we can. But I'll do
that after we finish with this.
Have a lovely day!
Alex
>
> -hpa
>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-21 1:21 ` Alejandro Colomar
@ 2025-06-21 2:30 ` H. Peter Anvin
2025-06-21 2:48 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: H. Peter Anvin @ 2025-06-21 2:30 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Wilco Dijkstra, Libc Alpha
On 2025-06-20 18:21, Alejandro Colomar wrote:
>
> On Fri, Jun 20, 2025 at 04:58:04PM -0700, H. Peter Anvin wrote:
>> On 2025-06-17 04:52, Wilco Dijkstra wrote:
>>> Hi Alejandro,
>>>
>>>>> The purpose of realloc(p, 0) is to free memory.
>>>>
>>>> No. The purpose of realloc(p,0) is to resize a block to size 0.
>>>
>>> That's arguing semantics. Whether you return the original block or a new
>>> small block is an internal implementation detail. The goal is to free the memory.
>>>
>>
>> There is actually a third option.
>
> This isn't actually a third option. This is the option I'm proposing.
> :)
>
:)
>>
>> If a non-NULL pointer is returned, from either malloc or realloc, it needs
>> to be unique, but there is no requirement that it needs to be *accessible.*
>
> Nope, it doesn't need to be unique. There's no requisite in the
> standard that the result of malloc(0) is unique.
>
I think it is for C++, though.
-hpa
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-20 23:58 ` H. Peter Anvin
@ 2025-06-21 1:21 ` Alejandro Colomar
2025-06-21 2:30 ` H. Peter Anvin
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-21 1:21 UTC (permalink / raw)
To: H. Peter Anvin; +Cc: Wilco Dijkstra, Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 4438 bytes --]
Hi H. Peter,
On Fri, Jun 20, 2025 at 04:58:04PM -0700, H. Peter Anvin wrote:
> On 2025-06-17 04:52, Wilco Dijkstra wrote:
> > Hi Alejandro,
> >
> > > > The purpose of realloc(p, 0) is to free memory.
> > >
> > > No. The purpose of realloc(p,0) is to resize a block to size 0.
> >
> > That's arguing semantics. Whether you return the original block or a new
> > small block is an internal implementation detail. The goal is to free the memory.
> >
>
> There is actually a third option.
This isn't actually a third option. This is the option I'm proposing.
:)
>
> If a non-NULL pointer is returned, from either malloc or realloc, it needs
> to be unique, but there is no requirement that it needs to be *accessible.*
Nope, it doesn't need to be unique. There's no requisite in the
standard that the result of malloc(0) is unique.
Let's consider a simpler case: an array of 0 elements. I know it's not
valid in ISO C, but let's consider it as an extension with the obvious
meaning, which, BTW, I plan to propose for inclusion in ISO C
eventually:
int
main(void)
{
int a[0], b[0];
return a==b;
}
Does this return necessarily false? Or can it return true? ISO C says
it can return true:
<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#subsection.6.5.10>
n3220::6.5.10p7:
Two pointers compare equal
if and only if both are null pointers,
both are pointers to the same object
(including a pointer to an object and a subobject at its beginning)
or function,
both are pointers to
one past the last element of the same array object,
or one is a pointer to one past the end of one array object
and the other is a pointer to the start of a different array object
that happens to immediately follow the first array object
in the address space. 108)
And then the referenced footnote:
108) Two objects can be adjacent in memory
because they are adjacent elements of a larger array
[...]
or because the implementation chose to place them so,
even though they are unrelated.
If prior invalid pointer operations
(such as accesses outside array bounds)
produced undefined behavior,
subsequent comparisons also produce undefined behavior.
Considering that 'a' is a pointer to one past the last element (because
that's all you have when you shrink an array that much), and that 'b' is
a pointer to the start of an array object that overlaps in memory, this
program can return true.
For similar reasons, malloc(0) is allowed to return the same pointer
always. malloc() could have something like this:
if (size == 0)
return MAGIC_POINTER_WITH_0_BYTES;
> In other words, glibc could reserve a chunk of address space using mmap(...,
> PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, ...) and simply allocate
> pointers out of that space. Accessing those pointers will SIGSEGV (or
> SIGBUS, on alignment error), as they should as that is not valid storage
> even for a single byte; system calls will return EFAULT.
No need for that. It only needs une pointer; no need for an entire
chunk of address space.
if (size == 0)
return (void *)0xdeadbeef;
(assuming 0xdeadbeef is not a valid address.)
On the other hand, it might be useful for other reasons to have distinct
pointers.
> This is BETTER than making a small allocation, since it will trap on any
> reference to the dummy pointer.
I never said it should allocate something small, IIRC. I think
malloc(0) should allocate 0 bytes.
> It *could* keep a bitmask of allocations, or it could simply keep a high
> water marker and ignore free() [and realloc() to a nonzero-sized object]
> entirely. Especially on 64-bit architectures that is definitely an option,
> since there is enough pointer space available that any realistic failure
> would be many centuries in the future.
>
> On many architectures (especially 64-bit architectures) it could also just
> point into invalid address space and not even bother with reservations; like
> non-canonical address space on x86-64, providing an for all practical
> purposes inexhaustible supply of invalid pointer addresses.
>
> -hpa
I think we agree.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 11:52 ` Wilco Dijkstra
2025-06-17 18:42 ` Alejandro Colomar
@ 2025-06-20 23:58 ` H. Peter Anvin
2025-06-21 1:21 ` Alejandro Colomar
1 sibling, 1 reply; 143+ messages in thread
From: H. Peter Anvin @ 2025-06-20 23:58 UTC (permalink / raw)
To: Wilco Dijkstra, Alejandro Colomar; +Cc: Libc Alpha
On 2025-06-17 04:52, Wilco Dijkstra wrote:
> Hi Alejandro,
>
>>> The purpose of realloc(p, 0) is to free memory.
>>
>> No. The purpose of realloc(p,0) is to resize a block to size 0.
>
> That's arguing semantics. Whether you return the original block or a new
> small block is an internal implementation detail. The goal is to free the memory.
>
There is actually a third option.
If a non-NULL pointer is returned, from either malloc or realloc, it
needs to be unique, but there is no requirement that it needs to be
*accessible.*
In other words, glibc could reserve a chunk of address space using
mmap(..., PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, ...) and
simply allocate pointers out of that space. Accessing those pointers
will SIGSEGV (or SIGBUS, on alignment error), as they should as that is
not valid storage even for a single byte; system calls will return EFAULT.
This is BETTER than making a small allocation, since it will trap on any
reference to the dummy pointer.
It *could* keep a bitmask of allocations, or it could simply keep a high
water marker and ignore free() [and realloc() to a nonzero-sized object]
entirely. Especially on 64-bit architectures that is definitely an
option, since there is enough pointer space available that any realistic
failure would be many centuries in the future.
On many architectures (especially 64-bit architectures) it could also
just point into invalid address space and not even bother with
reservations; like non-canonical address space on x86-64, providing an
for all practical purposes inexhaustible supply of invalid pointer
addresses.
-hpa
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 20:07 ` Wilco Dijkstra
@ 2025-06-19 23:56 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-19 23:56 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Paul Eggert, Libc Alpha, josmyers
[-- Attachment #1: Type: text/plain, Size: 802 bytes --]
Hi Paul,
On Thu, Jun 19, 2025 at 08:07:35PM +0000, Wilco Dijkstra wrote:
> Hi Paul,
>
> > 2. When p is not null, realloc(p,0) frees p, returns NULL, and sets
> > errno=EINVAL. This conforms to both POSIX.1-2017 and POSIX.1-2024.
>
> That's interesting, was POSIX getting the idea of setting errno from AIX perhaps?
>
> However POSIX.1-2024 actually requires that the old block must not be freed
> if errno is set...
Indeed, I have a hard time reading POSIX.1-2024 as allowing realloc(3)
to free the pointer on EINVAL. I don't find it clear, but I'd say it
doesn't allow it because it doesn't say it can do it.
At the very least, it's a very contorted reading of the standard.
Have a lovely day!
Alex
>
> Cheers,
> Wilco
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 19:17 ` Paul Eggert
@ 2025-06-19 20:07 ` Wilco Dijkstra
2025-06-19 23:56 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-19 20:07 UTC (permalink / raw)
To: Paul Eggert, Alejandro Colomar; +Cc: Libc Alpha, josmyers
Hi Paul,
> 2. When p is not null, realloc(p,0) frees p, returns NULL, and sets
> errno=EINVAL. This conforms to both POSIX.1-2017 and POSIX.1-2024.
That's interesting, was POSIX getting the idea of setting errno from AIX perhaps?
However POSIX.1-2024 actually requires that the old block must not be freed
if errno is set...
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 23:32 ` Alejandro Colomar
2025-06-19 9:20 ` Florian Weimer
@ 2025-06-19 19:17 ` Paul Eggert
2025-06-19 20:07 ` Wilco Dijkstra
1 sibling, 1 reply; 143+ messages in thread
From: Paul Eggert @ 2025-06-19 19:17 UTC (permalink / raw)
To: Alejandro Colomar, Wilco Dijkstra; +Cc: Libc Alpha, josmyers
On 2025-06-18 16:32, Alejandro Colomar wrote:
> For this, I need a two-step change:
>
> 1) Define realloc(p,n) to be equivalent to free(p) and malloc(n), plus
> moving the contents, plus not freeing on error.
>
> 2) Require that malloc(0) returns non-null.
>
> The only implementations that need to be fixed to comply with step 1 are
> glibc, Bionic, and Windows, as far as I know.
>
> There are existing implementations that need to be fixed to comply with
> step 2, and I don't know how many.
The only such implementation I know of is AIX, which has the following
properties:
1. malloc(0) and realloc(NULL,0) return NULL without setting errno. This
conforms to POSIX.1-2017 (which is all that AIX attempts to conform to)
but not to POSIX.1-2024.
2. When p is not null, realloc(p,0) frees p, returns NULL, and sets
errno=EINVAL. This conforms to both POSIX.1-2017 and POSIX.1-2024.
3. However, if you compile with GCC (I tested GCC 10.3 on AIX 7.3) and
use -D_LINUX_SOURCE_COMPAT, AIX behaves like glibc 2.1 and earlier,
namely, malloc(0) returns a nonnull pointer on success, and realloc(p,0)
is consistent with malloc(0). This conforms to both POSIX.1-2017 and
POSIX.1-2024.
4. However however, if you compile with IBM's compiler (I tested
ibm-clang 17.1.1 on AIX 7.3) and use -D_LINUX_SOURCE_COMPAT, malloc(0)
returns NULL without setting errno, and realloc(p,0) behaves like glibc
2.1 and earlier. As far as I know this behavior is unique among
operating systems: it disagrees with all versions of glibc and it does
not conform to either POSIX.1-2017 or POSIX.1-2024 (though of course AIX
does not promise conformance when _LINUX_SOURCE_COMPAT is defined).
5. There is no way in AIX to get glibc's current behavior, where
malloc(0) and realloc(NULL,0) return non-null on success and
realloc(p,0) succeeds and returns NULL.
I suspect that (4) is simply a bug, that IBM intended IBM's compiler to
behave like GCC, and that IBM hasn't bothered to fix the bug because
nobody has reported it, as application developers long ago gave up on
trying to use malloc(0) and realloc(p,0) on AIX.
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-19 9:20 ` Florian Weimer
@ 2025-06-19 10:09 ` Alejandro Colomar
0 siblings, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-19 10:09 UTC (permalink / raw)
To: Florian Weimer; +Cc: Wilco Dijkstra, Libc Alpha, josmyers
[-- Attachment #1: Type: text/plain, Size: 1565 bytes --]
Hi Florian,
On Thu, Jun 19, 2025 at 11:20:51AM +0200, Florian Weimer wrote:
> * Alejandro Colomar:
>
> > And yes, it means that realloc(p,0) can result in returning NULL without
> > setting errno, and thus passing the input pointer to free(3). However,
> > the only situation in which this can happen is if p is a null pointer
> > itself. This is explicitly said in the RETURN VALUE section, which is
> > normative.
> >
> > RETURN VALUE
> >
> > If size is 0,
> > ...
> > either:
> >
> > - A null pointer shall be returned and,
> > if ptr is not a null pointer, errno shall be set to EINVAL.
> >
> > - A pointer to the allocated space shall be returned, ...
> >
> > You're acting as if you didn't read that normative section, which is
> > very explicit in what can happen.
>
> The part you elided, it also says this:
>
> | Upon successful completion, realloc() and reallocarray() shall return
> | a pointer to the new object (which can have the same value as a
> | pointer to the old object), or a null pointer if the new object has
> | not been allocated.
>
> I don't see anything clearing stating that the “has not been allocated”
> case is restricted to error scenarios only.
You're trying to interpret some wording that does not clearly specify in
either direction. The paragraph I quoted is very explicit, and does not
leave room to interpretation. It doesn't contradict what you quoted,
but rather clarify it.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 23:32 ` Alejandro Colomar
@ 2025-06-19 9:20 ` Florian Weimer
2025-06-19 10:09 ` Alejandro Colomar
2025-06-19 19:17 ` Paul Eggert
1 sibling, 1 reply; 143+ messages in thread
From: Florian Weimer @ 2025-06-19 9:20 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Wilco Dijkstra, Libc Alpha, josmyers
* Alejandro Colomar:
> And yes, it means that realloc(p,0) can result in returning NULL without
> setting errno, and thus passing the input pointer to free(3). However,
> the only situation in which this can happen is if p is a null pointer
> itself. This is explicitly said in the RETURN VALUE section, which is
> normative.
>
> RETURN VALUE
>
> If size is 0,
> ...
> either:
>
> - A null pointer shall be returned and,
> if ptr is not a null pointer, errno shall be set to EINVAL.
>
> - A pointer to the allocated space shall be returned, ...
>
> You're acting as if you didn't read that normative section, which is
> very explicit in what can happen.
The part you elided, it also says this:
| Upon successful completion, realloc() and reallocarray() shall return
| a pointer to the new object (which can have the same value as a
| pointer to the old object), or a null pointer if the new object has
| not been allocated.
I don't see anything clearing stating that the “has not been allocated”
case is restricted to error scenarios only.
Thanks,
Florian
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 22:11 ` Wilco Dijkstra
2025-06-18 23:18 ` Vincent Lefevre
@ 2025-06-18 23:32 ` Alejandro Colomar
2025-06-19 9:20 ` Florian Weimer
2025-06-19 19:17 ` Paul Eggert
1 sibling, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-18 23:32 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha, josmyers
[-- Attachment #1: Type: text/plain, Size: 5418 bytes --]
[CC += Joseph]
Hi Wilco,
On Wed, Jun 18, 2025 at 10:11:01PM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > > And those are the 4 words that allow one to call free(p) AND return NULL.
> >
> > By 'one' you mean realloc(p,0), I guess.
> >
> > No. Anything that starts by "If size is non-zero" does NOT give any
> > allowance for what can happen if size is zero.
>
> Of course it does. It HAS to explicitly exclude zero here to allow that case,
No, it doesn't. Please ask Joseph Myers if you don't believe it. We
just talked about the same wording situation for a similar thing just
today.
It excludes zero, and later must specify what happens for zero.
If it didn't specify it later, it would be implicit Undefined Behavior.
> otherwise the zero case must do the same as for any other size, and then
> there would be no point in specifying all the errno crazyness.
The zero case behaves different, but it's not whatever you want to
interpret from implicitly not saying it. It must be (and is) explicitly
specified what happens for the case of size zero.
> Look at what it claims:
>
> "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."
This is APPLICATION USAGE from POSIX.1-2024, which is a non-normative
section. It's good to mention where your quotes come from.
And yes, it means that realloc(p,0) can result in returning NULL without
setting errno, and thus passing the input pointer to free(3). However,
the only situation in which this can happen is if p is a null pointer
itself. This is explicitly said in the RETURN VALUE section, which is
normative.
RETURN VALUE
If size is 0,
...
either:
- A null pointer shall be returned and,
if ptr is not a null pointer, errno shall be set to EINVAL.
- A pointer to the allocated space shall be returned, ...
You're acting as if you didn't read that normative section, which is
very explicit in what can happen.
> So if realloc (p, 0) returns NULL AND sets errno then it must not free the block.
> However if it doesn't set errno, then it must free the block.
Correct. But this is only allowed to happen if ptr is a null pointer,
in which case, freeing is a no-op.
> Basically POSIX requires you to write something like this for every realloc:
>
> errno = 0;
> newp = realloc (oldp, size);
> if (newp == NULL)
> {
> if (size == 0 && errno == EINVAL)
> free (oldp); // only free old block if errno set
> else if (size == 0)
> // do NOT free oldp (OK for current GLIBC, memory leak for other allocators)
> else if (size != 0)
> free (oldp); // oldp valid, so free it
> }
> else
> {
> free (newp);
> }
Nope. Plus, you forgot to handle errors?
Also, do you know any existing code that does this? Otherwise, you're
acknowledging that existing code is written per the semantics I'm
proposing, and thus changing the implementation is harmless.
The only code I've seen which would be affected by our proposal is code
that implements free() by calling realloc(p, 0). That code would result
in leaks of a few bytes, which is relatively unimportant.
> > And BTW, if it has to set errno to EINVAL, it means it should not free
> > the input pointer, so it clearly is non-conforming.
>
> Setting EINVAL and freeing it would be non-conforming. But that's not what
> GLIBC does!
>
> POSIX can be fixed by removing the "size is non-zero" part and the useless
> errno handling. Then the code above just becomes:
>
> newp = realloc (oldp, size);
> if (newp == NULL)
> {
> free (oldp); // oldp was not freed (this will crash GLIBC if size == 0)
> }
> else
> {
> free (newp);
> }
This is precisely the behavior I'm suggesting. But we need *you* to fix
glibc before that can be standardized.
So, my end goal is that code should be written like this:
old = malloc(n); // n may be zero
if (old == NULL)
exit(1);
new = realloc(old, m); // m may be zero
if (new == NULL) {
free(old);
exit(1);
}
free(new);
For this, I need a two-step change:
1) Define realloc(p,n) to be equivalent to free(p) and malloc(n), plus
moving the contents, plus not freeing on error.
2) Require that malloc(0) returns non-null.
The only implementations that need to be fixed to comply with step 1 are
glibc, Bionic, and Windows, as far as I know.
There are existing implementations that need to be fixed to comply with
step 2, and I don't know how many.
So, do you agree with this goal or do you oppose? It seems you're
opposed, but now you say you'd like that.
> > Which versions of each standard are you reading? ISO C23 makes r(p,0)
> > UB, so there's nothing similar to POSIX in ISO C.
>
> https://pubs.opengroup.org/onlinepubs/9799919799/functions/realloc.html
> The quote above says ISO C, I'm assuming it means the latest version.
POSIX.1-2024 is based on ISO C17, not ISO C23.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-18 22:11 ` Wilco Dijkstra
@ 2025-06-18 23:18 ` Vincent Lefevre
2025-06-18 23:32 ` Alejandro Colomar
1 sibling, 0 replies; 143+ messages in thread
From: Vincent Lefevre @ 2025-06-18 23:18 UTC (permalink / raw)
To: libc-alpha
On 2025-06-18 22:11:01 +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > > And those are the 4 words that allow one to call free(p) AND return NULL.
> >
> > By 'one' you mean realloc(p,0), I guess.
> >
> > No. Anything that starts by "If size is non-zero" does NOT give any
> > allowance for what can happen if size is zero.
>
> Of course it does. It HAS to explicitly exclude zero here to allow that case,
> otherwise the zero case must do the same as for any other size, and then
> there would be no point in specifying all the errno crazyness.
It has to explicitly exclude zero, but what is under
"If size is non-zero" applies only to the case where the size
is non-zero; so does not give any spec for the case where the
size is zero. This is what Alejandro meant, I suppose.
> Look at what it claims:
>
> "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."
In ISO C99 and C11: "If memory for the new object cannot be allocated,
the old object is not deallocated and its value is unchanged."
So the case "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" was not possible with the historical behavior. And currently
(C23), realloc(non-null, 0) is UB.
--
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / Pascaline project (LIP, ENS-Lyon)
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 22:21 ` Alejandro Colomar
@ 2025-06-18 22:11 ` Wilco Dijkstra
2025-06-18 23:18 ` Vincent Lefevre
2025-06-18 23:32 ` Alejandro Colomar
0 siblings, 2 replies; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-18 22:11 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Libc Alpha
Hi Alejandro,
> > And those are the 4 words that allow one to call free(p) AND return NULL.
>
> By 'one' you mean realloc(p,0), I guess.
>
> No. Anything that starts by "If size is non-zero" does NOT give any
> allowance for what can happen if size is zero.
Of course it does. It HAS to explicitly exclude zero here to allow that case,
otherwise the zero case must do the same as for any other size, and then
there would be no point in specifying all the errno crazyness.
Look at what it claims:
"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."
So if realloc (p, 0) returns NULL AND sets errno then it must not free the block.
However if it doesn't set errno, then it must free the block.
Basically POSIX requires you to write something like this for every realloc:
errno = 0;
newp = realloc (oldp, size);
if (newp == NULL)
{
if (size == 0 && errno == EINVAL)
free (oldp); // only free old block if errno set
else if (size == 0)
// do NOT free oldp (OK for current GLIBC, memory leak for other allocators)
else if (size != 0)
free (oldp); // oldp valid, so free it
}
else
{
free (newp);
}
> And BTW, if it has to set errno to EINVAL, it means it should not free
> the input pointer, so it clearly is non-conforming.
Setting EINVAL and freeing it would be non-conforming. But that's not what
GLIBC does!
POSIX can be fixed by removing the "size is non-zero" part and the useless
errno handling. Then the code above just becomes:
newp = realloc (oldp, size);
if (newp == NULL)
{
free (oldp); // oldp was not freed (this will crash GLIBC if size == 0)
}
else
{
free (newp);
}
> Which versions of each standard are you reading? ISO C23 makes r(p,0)
> UB, so there's nothing similar to POSIX in ISO C.
https://pubs.opengroup.org/onlinepubs/9799919799/functions/realloc.html
The quote above says ISO C, I'm assuming it means the latest version.
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 21:53 ` Alejandro Colomar
@ 2025-06-17 22:21 ` Alejandro Colomar
2025-06-18 22:11 ` Wilco Dijkstra
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 22:21 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 3627 bytes --]
On Tue, Jun 17, 2025 at 11:53:17PM +0200, Alejandro Colomar wrote:
> Hi Wilco,
>
> On Tue, Jun 17, 2025 at 07:10:56PM +0000, Wilco Dijkstra wrote:
> > Hi Alejandro,
> >
> > >> "If size is non-zero and memory for the new object is not allocated, the old object shall not be deallocated."
> > >
> > > Read the first 4 words of that sentence. "If size is non-zero". We're
> > > talking about realloc(p,0). That sentence you've quoted doesn't cover
> > > this scenario.
> >
> > And those are the 4 words that allow one to call free(p) AND return NULL.
>
> By 'one' you mean realloc(p,0), I guess.
>
> No. Anything that starts by "If size is non-zero" does NOT give any
> allowance for what can happen if size is zero.
>
> What happens if size is zero can only come from generic text that
> doesn't mention the value of size, or from text that specifically says
> if size is zero.
>
> In the case of realloc(p,0) in POSIX.1-2024, the place where it's
> specified is under RETURN VALUE:
>
> If size is 0,
> ...
> either:
>
> - A null pointer shall be returned
> and, if ptr is not a null pointer,
> errno shall be set to EINVAL.
>
> - A pointer to the allocated space shall be returned,
> and the memory object pointed to by ptr shall be freed.
> ...
>
> glibc does not have any of those two behaviors. glibc returns a null
> pointer, but it does not set errno to EINVAL.
And BTW, if it has to set errno to EINVAL, it means it should not free
the input pointer, so it clearly is non-conforming.
> Thus, it doesn't
> comply with the first alternative. And it returns a null pointer, so it
> clearly doesn't comply with the second alternative either.
>
> glibc in non-conforming to POSIX.1-2024.
>
> alx@debian:~/tmp$ cat r.c
> #include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
>
> int
> main(void)
> {
> void *p, *q;
>
> p = malloc(42);
> if (p == NULL)
> exit(1);
>
> q = realloc(p, 0);
> if (q == NULL)
> printf("realloc(p,0) == NULL; errno == %d\n", errno);
>
> free(p);
>
> exit(0);
> }
> alx@debian:~/tmp$ gcc -Wall -Wextra r.c
> r.c: In function ‘main’:
> r.c:18:9: warning: pointer ‘p’ used after ‘realloc’ [-Wuse-after-free]
> 18 | free(p);
> | ^~~~~~~
> r.c:14:13: note: call to ‘realloc’ here
> 14 | q = realloc(p, 0);
> | ^~~~~~~~~~~~~
> alx@debian:~/tmp$ ./a.out
> realloc(p,0) == NULL; errno == 0
> free(): double free detected in tcache 2
> Aborted
>
>
> > Without those 4 words it would demand for zero size that p "shall not be deallocated"
> > and thus make GLIBCs current behaviour non-conforming.
> >
> > > Current POSIX.1-2024 is fine. glibc is not.
> >
> > How is it fine if it makes GLIBCs implementation legal? It would have to be fixed too.
>
> glibc is not legal. Please see the program above, and how it
> contradicts what POSIX.1-2024 says under RETURN VALUE very explicitly.
>
> > The only difference between POSIX and ISO C is that POSIX defines a way to differentiate
> > between these cases by checking errno. But that's not really useful since not all allocators
> > set errno, and nobody resets and checks errno after each realloc.
>
> Which versions of each standard are you reading? ISO C23 makes r(p,0)
> UB, so there's nothing similar to POSIX in ISO C.
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 19:10 ` Wilco Dijkstra
@ 2025-06-17 21:53 ` Alejandro Colomar
2025-06-17 22:21 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 21:53 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
Hi Wilco,
On Tue, Jun 17, 2025 at 07:10:56PM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> >> "If size is non-zero and memory for the new object is not allocated, the old object shall not be deallocated."
> >
> > Read the first 4 words of that sentence. "If size is non-zero". We're
> > talking about realloc(p,0). That sentence you've quoted doesn't cover
> > this scenario.
>
> And those are the 4 words that allow one to call free(p) AND return NULL.
By 'one' you mean realloc(p,0), I guess.
No. Anything that starts by "If size is non-zero" does NOT give any
allowance for what can happen if size is zero.
What happens if size is zero can only come from generic text that
doesn't mention the value of size, or from text that specifically says
if size is zero.
In the case of realloc(p,0) in POSIX.1-2024, the place where it's
specified is under RETURN VALUE:
If size is 0,
...
either:
- A null pointer shall be returned
and, if ptr is not a null pointer,
errno shall be set to EINVAL.
- A pointer to the allocated space shall be returned,
and the memory object pointed to by ptr shall be freed.
...
glibc does not have any of those two behaviors. glibc returns a null
pointer, but it does not set errno to EINVAL. Thus, it doesn't
comply with the first alternative. And it returns a null pointer, so it
clearly doesn't comply with the second alternative either.
glibc in non-conforming to POSIX.1-2024.
alx@debian:~/tmp$ cat r.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
void *p, *q;
p = malloc(42);
if (p == NULL)
exit(1);
q = realloc(p, 0);
if (q == NULL)
printf("realloc(p,0) == NULL; errno == %d\n", errno);
free(p);
exit(0);
}
alx@debian:~/tmp$ gcc -Wall -Wextra r.c
r.c: In function ‘main’:
r.c:18:9: warning: pointer ‘p’ used after ‘realloc’ [-Wuse-after-free]
18 | free(p);
| ^~~~~~~
r.c:14:13: note: call to ‘realloc’ here
14 | q = realloc(p, 0);
| ^~~~~~~~~~~~~
alx@debian:~/tmp$ ./a.out
realloc(p,0) == NULL; errno == 0
free(): double free detected in tcache 2
Aborted
> Without those 4 words it would demand for zero size that p "shall not be deallocated"
> and thus make GLIBCs current behaviour non-conforming.
>
> > Current POSIX.1-2024 is fine. glibc is not.
>
> How is it fine if it makes GLIBCs implementation legal? It would have to be fixed too.
glibc is not legal. Please see the program above, and how it
contradicts what POSIX.1-2024 says under RETURN VALUE very explicitly.
> The only difference between POSIX and ISO C is that POSIX defines a way to differentiate
> between these cases by checking errno. But that's not really useful since not all allocators
> set errno, and nobody resets and checks errno after each realloc.
Which versions of each standard are you reading? ISO C23 makes r(p,0)
UB, so there's nothing similar to POSIX in ISO C.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 18:42 ` Alejandro Colomar
@ 2025-06-17 19:10 ` Wilco Dijkstra
2025-06-17 21:53 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-17 19:10 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Libc Alpha
Hi Alejandro,
>> "If size is non-zero and memory for the new object is not allocated, the old object shall not be deallocated."
>
> Read the first 4 words of that sentence. "If size is non-zero". We're
> talking about realloc(p,0). That sentence you've quoted doesn't cover
> this scenario.
And those are the 4 words that allow one to call free(p) AND return NULL.
Without those 4 words it would demand for zero size that p "shall not be deallocated"
and thus make GLIBCs current behaviour non-conforming.
> Current POSIX.1-2024 is fine. glibc is not.
How is it fine if it makes GLIBCs implementation legal? It would have to be fixed too.
The only difference between POSIX and ISO C is that POSIX defines a way to differentiate
between these cases by checking errno. But that's not really useful since not all allocators
set errno, and nobody resets and checks errno after each realloc.
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 11:52 ` Wilco Dijkstra
@ 2025-06-17 18:42 ` Alejandro Colomar
2025-06-17 19:10 ` Wilco Dijkstra
2025-06-20 23:58 ` H. Peter Anvin
1 sibling, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 18:42 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 2127 bytes --]
Hi Wilco,
On Tue, Jun 17, 2025 at 11:52:18AM +0000, Wilco Dijkstra wrote:
> > POSIX.1-2024 has a weird wording. Quoting POSIX.1-2024:
>
> | If size is 0,
> ...
> | either:
> | - A null pointer shall be returned
> | and, if ptr is not a null pointer, errno shall be set to EINVAL.
> | - A pointer to the allocated space shall be returned,
> | and the memory object pointed to by ptr shall be freed.
> ...
>
> > Indeed, glibc seems non-conforming to POSIX.1-2024. More reason to fix
> > the broken and non-conforming glibc implementation now.
>
> As far as I can tell, it does still allow the first case to free the memory:
>
> "If size is non-zero and memory for the new object is not allocated, the old object shall not be deallocated."
Read the first 4 words of that sentence. "If size is non-zero". We're
talking about realloc(p,0). That sentence you've quoted doesn't cover
this scenario.
You're mixing things.
> So my proposal would be to change it to something like:
>
> "If memory for the new object is not allocated, the old object shall not be deallocated."
>
> "A null pointer shall be returned if the memory object pointed to by ptr is not freed."
Current POSIX.1-2024 is fine. glibc is not.
> >> It's hard to know your implementation. You may write your own application,
> >> but when you upgrade your system you may get a different malloc.
> >
> > No? I bet malloc(0) is never going to return NULL on glibc or the BSDs.
> > That would break tons of existing code.
>
> Whether malloc(0) does or doesn't break code is a different argument from realloc.
> POSIX does allow malloc(0) to return NULL:
>
> "If the size of the space requested is 0, the behavior is implementation-defined:
> either a null pointer shall be returned, or the behavior shall be as if the size were some non-zero value, ..."
It does. And such systems are also hard to use for similar reasons.
Luckily, those systems are rare in POSIX land, and are dying.
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 3:14 ` Alejandro Colomar
2025-06-17 3:18 ` Alejandro Colomar
@ 2025-06-17 11:52 ` Wilco Dijkstra
2025-06-17 18:42 ` Alejandro Colomar
2025-06-20 23:58 ` H. Peter Anvin
1 sibling, 2 replies; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-17 11:52 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Libc Alpha
Hi Alejandro,
>> The purpose of realloc(p, 0) is to free memory.
>
> No. The purpose of realloc(p,0) is to resize a block to size 0.
That's arguing semantics. Whether you return the original block or a new
small block is an internal implementation detail. The goal is to free the memory.
> POSIX.1-2024 has a weird wording. Quoting POSIX.1-2024:
| If size is 0,
...
| either:
| - A null pointer shall be returned
| and, if ptr is not a null pointer, errno shall be set to EINVAL.
| - A pointer to the allocated space shall be returned,
| and the memory object pointed to by ptr shall be freed.
...
> Indeed, glibc seems non-conforming to POSIX.1-2024. More reason to fix
> the broken and non-conforming glibc implementation now.
As far as I can tell, it does still allow the first case to free the memory:
"If size is non-zero and memory for the new object is not allocated, the old object shall not be deallocated."
"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."
GLIBC is conforming with this.
So my proposal would be to change it to something like:
"If memory for the new object is not allocated, the old object shall not be deallocated."
"A null pointer shall be returned if the memory object pointed to by ptr is not freed."
That way you avoid forcing applications to check the size passed to realloc
as well as clearing and checking errno (which nobody does).
>> It's hard to know your implementation. You may write your own application,
>> but when you upgrade your system you may get a different malloc.
>
> No? I bet malloc(0) is never going to return NULL on glibc or the BSDs.
> That would break tons of existing code.
Whether malloc(0) does or doesn't break code is a different argument from realloc.
POSIX does allow malloc(0) to return NULL:
"If the size of the space requested is 0, the behavior is implementation-defined:
either a null pointer shall be returned, or the behavior shall be as if the size were some non-zero value, ..."
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 3:14 ` Alejandro Colomar
@ 2025-06-17 3:18 ` Alejandro Colomar
2025-06-17 11:52 ` Wilco Dijkstra
1 sibling, 0 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 3:18 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
On Tue, Jun 17, 2025 at 05:14:07AM +0200, Alejandro Colomar wrote:
> > So it is unrelated to whatever
> > malloc(0) returns - the requirement is to return NULL if the old block remains
> > valid or a non-NULL new block.
>
> No? glibc currently returns NULL and the old block is freed.
>
> alx@debian:~/tmp$ cat r.c
> #include <stdlib.h>
>
> int
> main(void)
> {
> void *p;
>
> p = malloc(42);
> if (p == NULL)
> exit(1);
>
> p = realloc(p, 0);
> if (p == NULL)
> exit(2);
>
> exit(0);
> }
> alx@debian:~/tmp$ gcc -Wall -Wextra r.c
> alx@debian:~/tmp$ ./a.out; echo $?
> 2
>
> And of course, realloc(p,0) has freed the original p.
>
> In fact, my proposal more closely resembles your model. Under my
> proposal, realloc(p,0) would return non-null, and the old block is
> invalid, which is exactly what you said.
>
I didn't show it, but the old block is certainly freed. Here's a
program that shows that:
alx@debian:~/tmp$ cat r.c
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
void *p, *q;
p = malloc(42);
if (p == NULL)
exit(1);
q = realloc(p, 0);
if (q == NULL)
printf("realloc(p,0) == NULL\n");
free(p);
exit(0);
}
alx@debian:~/tmp$ gcc -Wall -Wextra r.c
r.c: In function ‘main’:
r.c:17:9: warning: pointer ‘p’ used after ‘realloc’ [-Wuse-after-free]
17 | free(p);
| ^~~~~~~
r.c:13:13: note: call to ‘realloc’ here
13 | q = realloc(p, 0);
| ^~~~~~~~~~~~~
alx@debian:~/tmp$ ./a.out
realloc(p,0) == NULL
free(): double free detected in tcache 2
Aborted
--
<https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 1:58 ` Wilco Dijkstra
@ 2025-06-17 3:14 ` Alejandro Colomar
2025-06-17 3:18 ` Alejandro Colomar
2025-06-17 11:52 ` Wilco Dijkstra
0 siblings, 2 replies; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 3:14 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
Hi Wilco,
On Tue, Jun 17, 2025 at 01:58:41AM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > Any portable application cannot use malloc(0), so why would it want to
> > realloc(p,0)?
>
> The purpose of realloc(p, 0) is to free memory.
No. The purpose of realloc(p,0) is to resize a block to size 0.
Just like malloc(0)'s purpose is to allocate a block of size 0.
The purpose of realloc(p,0) being to free memory is a bad interpretation
of the standard in a way that was never true in any existing systems
when C89 came to existence. Only glibc and Bionic interpret it that
way, from the libc implementations. And then there might be allocators
that seek compatibility with glibc, or which have also been written by
people who have had the same bad reading of the standard.
The other libc implementations all treat it as resizing a block to size
0.
> So it is unrelated to whatever
> malloc(0) returns - the requirement is to return NULL if the old block remains
> valid or a non-NULL new block.
No? glibc currently returns NULL and the old block is freed.
alx@debian:~/tmp$ cat r.c
#include <stdlib.h>
int
main(void)
{
void *p;
p = malloc(42);
if (p == NULL)
exit(1);
p = realloc(p, 0);
if (p == NULL)
exit(2);
exit(0);
}
alx@debian:~/tmp$ gcc -Wall -Wextra r.c
alx@debian:~/tmp$ ./a.out; echo $?
2
And of course, realloc(p,0) has freed the original p.
In fact, my proposal more closely resembles your model. Under my
proposal, realloc(p,0) would return non-null, and the old block is
invalid, which is exactly what you said.
glibc is conforming to ISO C23. Or are you claiming glibc is
non-conforming to POSIX.1-2024? I think I agree with that, since
POSIX.1-2024 has a weird wording. Quoting POSIX.1-2024:
| If size is 0,
...
| either:
| - A null pointer shall be returned
| and, if ptr is not a null pointer, errno shall be set to EINVAL.
| - A pointer to the allocated space shall be returned,
| and the memory object pointed to by ptr shall be freed.
...
Indeed, glibc seems non-conforming to POSIX.1-2024. More reason to fix
the broken and non-conforming glibc implementation now.
> > After my change, it would become implementation-defined. If you know
> > your implementation returns non-null from malloc(0) --and all
> > implementations I care about behave like that--, then you can also rely
> > on realloc(p,0).
>
> It's hard to know your implementation. You may write your own application,
> but when you upgrade your system you may get a different malloc.
No? I bet malloc(0) is never going to return NULL on glibc or the BSDs.
That would break tons of existing code.
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es/>
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 0:50 ` Alejandro Colomar
@ 2025-06-17 1:58 ` Wilco Dijkstra
2025-06-17 3:14 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-17 1:58 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Libc Alpha
Hi Alejandro,
> Any portable application cannot use malloc(0), so why would it want to
> realloc(p,0)?
The purpose of realloc(p, 0) is to free memory. So it is unrelated to whatever
malloc(0) returns - the requirement is to return NULL if the old block remains
valid or a non-NULL new block.
Realloc (p, 0) could return the smallest non-NULL block, which might be malloc(1)
on systems that return NULL for malloc(0). A simple allocator could search for
the smallest available free block, or return the original block if there isn't any
or if the search takes too long.
> After my change, it would become implementation-defined. If you know
> your implementation returns non-null from malloc(0) --and all
> implementations I care about behave like that--, then you can also rely
> on realloc(p,0).
It's hard to know your implementation. You may write your own application,
but when you upgrade your system you may get a different malloc.
> That's enough for many programs. The BSDs, glibc, and musl is already
> enough portability for me. If we convince Bionic too, that's basically
> every modern POSIX system I care about.
There is a world outside of POSIX, and lots of people care about that too...
> Of course I want to fix malloc(0) after this. But if it's already hard
> to convince glibc to fix their broken behavior unilaterally to be
> consistent with everyone else, imagine convincing the C Committee that
> several implementations must be changed. That'll be harder, so please
> expect some year or two before I convince them.
Sure I don't expect it to be easy - there are lots of embedded mallocs around,
practically every RTOS has their own.
However if you want to fix realloc first, I strongly suggest to propose a definition
that actually works for all cases without relying on malloc(0) - that would make
it much more convincing and likely to be adopted.
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-17 0:23 ` Wilco Dijkstra
@ 2025-06-17 0:50 ` Alejandro Colomar
2025-06-17 1:58 ` Wilco Dijkstra
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-17 0:50 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 2112 bytes --]
Hi Wilco,
On Tue, Jun 17, 2025 at 12:23:59AM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> >> I would say that feels like the wrong way around. Firstly you need malloc (0)
> >> to be non-NULL, and only then can realloc (p, 0) be well defined.
> >
> > No. I can define realloc(p,0) to be equivalent to free(p) and malloc(0)
> > regardless of what malloc(0) does. The only libc implementations that
> > will become non-conforming are glibc and Bionic. The rest will remain
> > conforming and self-consistent.
>
> But as long as malloc(0) isn't defined, any portable application still cannot
> use realloc (p, 0) - it remains undefined even if we say it behaves like
> malloc+free . So there is no real benefit from
> changing the definition of realloc without also fixing malloc.
Any portable application cannot use malloc(0), so why would it want to
realloc(p,0)?
> I believe removing the "undefined" part from the spec is actually incorrect
> because the zero case really is undefined. An application can only use it if it
> can differentiate between failure and success.
After my change, it would become implementation-defined. If you know
your implementation returns non-null from malloc(0) --and all
implementations I care about behave like that--, then you can also rely
on realloc(p,0).
That's enough for many programs. The BSDs, glibc, and musl is already
enough portability for me. If we convince Bionic too, that's basically
every modern POSIX system I care about.
> And if we're not willing to fix malloc(0) then I think Josephs proposal of
> aborting the application might be a better solution.
Of course I want to fix malloc(0) after this. But if it's already hard
to convince glibc to fix their broken behavior unilaterally to be
consistent with everyone else, imagine convincing the C Committee that
several implementations must be changed. That'll be harder, so please
expect some year or two before I convince them.
But yes, I plan to work on that too.
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 23:13 ` Alejandro Colomar
@ 2025-06-17 0:23 ` Wilco Dijkstra
2025-06-17 0:50 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-17 0:23 UTC (permalink / raw)
To: Alejandro Colomar; +Cc: Libc Alpha
Hi Alejandro,
>> I would say that feels like the wrong way around. Firstly you need malloc (0)
>> to be non-NULL, and only then can realloc (p, 0) be well defined.
>
> No. I can define realloc(p,0) to be equivalent to free(p) and malloc(0)
> regardless of what malloc(0) does. The only libc implementations that
> will become non-conforming are glibc and Bionic. The rest will remain
> conforming and self-consistent.
But as long as malloc(0) isn't defined, any portable application still cannot
use realloc (p, 0) - it remains undefined even if we say it behaves like
malloc+free . So there is no real benefit from
changing the definition of realloc without also fixing malloc.
I believe removing the "undefined" part from the spec is actually incorrect
because the zero case really is undefined. An application can only use it if it
can differentiate between failure and success.
And if we're not willing to fix malloc(0) then I think Josephs proposal of
aborting the application might be a better solution.
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
* Re: BUG: realloc(p,0) should be consistent with malloc(0)
2025-06-16 22:50 BUG: realloc(p,0) should be consistent with malloc(0) Wilco Dijkstra
@ 2025-06-16 23:13 ` Alejandro Colomar
2025-06-17 0:23 ` Wilco Dijkstra
0 siblings, 1 reply; 143+ messages in thread
From: Alejandro Colomar @ 2025-06-16 23:13 UTC (permalink / raw)
To: Wilco Dijkstra; +Cc: Libc Alpha
[-- Attachment #1: Type: text/plain, Size: 2265 bytes --]
Hi Wilco,
On Mon, Jun 16, 2025 at 10:50:56PM +0000, Wilco Dijkstra wrote:
> Hi Alejandro,
>
> > I'm working on that. I have a proposal for mandating that malloc(0),
> > but I can't present it until realloc(p, 0) is fixed.
>
> Presumably you mean that malloc (0) is no longer implementation defined
> and defined to return a non-NULL pointer if it succeeds?
Yep.
> I would say that feels like the wrong way around. Firstly you need malloc (0)
> to be non-NULL, and only then can realloc (p, 0) be well defined.
No. I can define realloc(p,0) to be equivalent to free(p) and malloc(0)
regardless of what malloc(0) does. The only libc implementations that
will become non-conforming are glibc and Bionic. The rest will remain
conforming and self-consistent.
On the other hand, if I start by defining malloc(0) to return non-null,
I must then also specify realloc(p,0) at the same time, or I'd have a
very broken allocator where realloc(p,0) is inconsistent in several libc
implementations. At the moment it is only inconsistent in glibc (and
Bionic because it followed glibc). It's glibc's fault to have such a
broken realloc(p,0), and the other implementations shouldn't get broken
because of glibc's fault.
> Otherwise when realloc (p, 0) returns NULL, you can't differentiate between
> the malloc/free failing and malloc/free being successful (since both return
> NULL and we can't rely on errno being set).
That's already a problem in malloc(0). I don't see how realloc(p,0)
makes it worse. I suspect those implementations would set errno, but I
agree that returning NULL from malloc(0) is brain-damaged precisely
because of that.
> So do all implementations (including alternative allocators like jemalloc etc)
> already return a non-NULL malloc (0)?
I don't know, and I don't care. But I recommend everyone to stay away
from any allocator that returns NULL on malloc(0). The fact that
realloc(p,0) returns NULL there is the least of the problems.
Presumably, the only users of those allocators are programmers who don't
want to ever call malloc(0), so whatever.
In a moment, I'll present a draft of a proposal.
Have a lovely night!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 143+ messages in thread
* BUG: realloc(p,0) should be consistent with malloc(0)
@ 2025-06-16 22:50 Wilco Dijkstra
2025-06-16 23:13 ` Alejandro Colomar
0 siblings, 1 reply; 143+ messages in thread
From: Wilco Dijkstra @ 2025-06-16 22:50 UTC (permalink / raw)
To: alx; +Cc: Libc Alpha
Hi Alejandro,
> I'm working on that. I have a proposal for mandating that malloc(0),
> but I can't present it until realloc(p, 0) is fixed.
Presumably you mean that malloc (0) is no longer implementation defined
and defined to return a non-NULL pointer if it succeeds?
I would say that feels like the wrong way around. Firstly you need malloc (0)
to be non-NULL, and only then can realloc (p, 0) be well defined.
Otherwise when realloc (p, 0) returns NULL, you can't differentiate between
the malloc/free failing and malloc/free being successful (since both return
NULL and we can't rely on errno being set).
So do all implementations (including alternative allocators like jemalloc etc)
already return a non-NULL malloc (0)?
Cheers,
Wilco
^ permalink raw reply [flat|nested] 143+ messages in thread
end of thread, other threads:[~2025-10-09 23:24 UTC | newest]
Thread overview: 143+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-16 11:55 BUG: realloc(p,0) should be consistent with malloc(0) Alejandro Colomar
2025-06-16 12:24 ` [musl] " Rich Felker
2025-06-16 12:43 ` Alejandro Colomar
2025-06-16 17:35 ` Paul Eggert
2025-06-16 16:40 ` enh
2025-06-16 21:21 ` Alejandro Colomar
2025-06-16 18:20 ` Adhemerval Zanella Netto
2025-06-16 19:35 ` Florian Weimer
2025-06-16 20:59 ` Adhemerval Zanella Netto
2025-06-16 21:44 ` Alejandro Colomar
2025-06-16 23:20 ` Alejandro Colomar
2025-06-16 23:39 ` Joseph Myers
2025-06-16 23:54 ` Alejandro Colomar
2025-06-17 14:07 ` enh
2025-06-17 14:40 ` Florian Weimer
2025-06-17 14:51 ` [musl] " Rich Felker
2025-06-17 15:28 ` Paul Eggert
2025-06-17 16:13 ` enh
2025-06-17 16:56 ` Rich Felker
2025-06-17 17:38 ` Re[2]: " Laurent Bercot
2025-06-17 21:58 ` Alejandro Colomar
2025-06-18 15:20 ` enh
2025-06-18 15:37 ` Thorsten Glaser
2025-06-18 16:26 ` enh
2025-06-18 16:35 ` Rich Felker
2025-06-18 19:04 ` Alejandro Colomar
2025-06-19 18:31 ` Eric Blake
2025-06-19 23:37 ` Alejandro Colomar
2025-06-19 23:45 ` Alejandro Colomar
2025-06-19 23:53 ` Alejandro Colomar
2025-06-20 2:11 ` Vincent Lefevre
2025-06-20 11:58 ` Alejandro Colomar
2025-06-20 14:36 ` Vincent Lefevre
2025-06-20 14:56 ` Alejandro Colomar
2025-06-20 13:42 ` Mark Harris
2025-06-20 16:18 ` Joseph Myers
2025-06-20 16:30 ` Eric Blake
2025-06-20 18:03 ` Alejandro Colomar
2025-06-20 19:41 ` Paul Eggert
2025-06-21 3:57 ` Sam James
2025-06-21 12:08 ` Alejandro Colomar
2025-06-21 12:11 ` Sam James
2025-06-21 15:14 ` Adhemerval Zanella
2025-06-21 15:34 ` Alejandro Colomar
2025-07-04 23:31 ` Alejandro Colomar
2025-07-05 0:15 ` Collin Funk
2025-07-05 0:43 ` Alejandro Colomar
2025-07-05 1:45 ` Alejandro Colomar
2025-07-05 2:28 ` Alejandro Colomar
2025-07-23 22:03 ` Eric Blake
2025-06-20 5:55 ` Paul Eggert
2025-06-20 10:04 ` Vincent Lefevre
2025-06-18 15:42 ` Andreas Schwab
2025-06-18 18:05 ` Alejandro Colomar
2025-06-20 21:26 ` alx-0029r1 - Restore the traditional realloc(3) specification Alejandro Colomar
2025-06-20 21:44 ` Alejandro Colomar
2025-06-20 22:06 ` Thorsten Glaser
2025-06-21 1:06 ` Alejandro Colomar
2025-06-21 1:34 ` Paul Eggert
2025-06-21 2:42 ` Alejandro Colomar
2025-06-21 3:50 ` Paul Eggert
2025-06-21 11:59 ` Alejandro Colomar
2025-06-21 2:42 ` [musl] " Rich Felker
2025-06-21 2:55 ` Alejandro Colomar
2025-06-20 22:31 ` Christopher Bazley
2025-06-21 1:59 ` Alejandro Colomar
2025-06-20 22:58 ` Maciej W. Rozycki
2025-06-21 2:07 ` Alejandro Colomar
2025-06-21 14:49 ` [musl] " Markus Wichmann
2025-06-21 16:27 ` Rich Felker
2025-06-21 2:44 ` Rich Felker
2025-06-21 14:00 ` alx-0029r2 " Alejandro Colomar
2025-06-24 5:01 ` alx-0029r3 " Alejandro Colomar
2025-06-24 9:07 ` Florian Weimer
2025-06-24 9:54 ` Bruno Haible
2025-06-24 13:04 ` [musl] " Rich Felker
2025-06-24 14:18 ` Alejandro Colomar
2025-06-25 0:14 ` [musl] " Jeffrey Walton
2025-06-25 0:19 ` Rich Felker
2025-06-25 14:10 ` enh
2025-06-24 21:35 ` Eric Blake
2025-06-24 21:18 ` Eric Blake
2025-06-24 23:01 ` Alejandro Colomar
2025-06-25 1:58 ` alx-0029r4 " Alejandro Colomar
2025-06-25 12:58 ` Eric Blake
2025-06-25 13:15 ` Alejandro Colomar
2025-06-25 17:35 ` Alejandro Colomar
2025-06-25 17:47 ` Alejandro Colomar
2025-06-25 18:07 ` Wilco Dijkstra
2025-06-25 18:20 ` Eric Blake
2025-06-25 18:21 ` Alejandro Colomar
2025-06-25 20:27 ` [musl] " Rich Felker
2025-06-25 18:10 ` Eric Blake
2025-06-25 18:03 ` Eric Blake
2025-06-25 18:10 ` Alejandro Colomar
2025-06-26 22:00 ` alx-0029r5 " Alejandro Colomar
2025-06-27 0:44 ` Mark Harris
2025-06-27 1:51 ` Alejandro Colomar
2025-06-27 8:52 ` Florian Weimer
2025-06-27 12:54 ` Martin Uecker
2025-06-27 13:49 ` Alejandro Colomar
2025-06-27 13:26 ` Alejandro Colomar
2025-06-27 14:44 ` Florian Weimer
2025-06-27 15:04 ` Alejandro Colomar
2025-06-27 16:17 ` Florian Weimer
2025-06-27 17:11 ` Alejandro Colomar
2025-06-27 17:22 ` [musl] " Rich Felker
2025-06-27 17:38 ` Alejandro Colomar
2025-06-27 19:37 ` Rich Felker
2025-06-27 16:15 ` Joseph Myers
2025-06-27 14:01 ` alx-0029r6 " Alejandro Colomar
2025-06-29 16:25 ` H. Peter Anvin
2025-06-29 16:35 ` Alejandro Colomar
2025-06-29 17:05 ` [musl] " Rich Felker
2025-06-30 2:27 ` Alejandro Colomar
2025-10-09 22:37 ` alx-0029r8 " Alejandro Colomar
2025-10-09 23:20 ` H. Peter Anvin
2025-06-16 22:50 BUG: realloc(p,0) should be consistent with malloc(0) Wilco Dijkstra
2025-06-16 23:13 ` Alejandro Colomar
2025-06-17 0:23 ` Wilco Dijkstra
2025-06-17 0:50 ` Alejandro Colomar
2025-06-17 1:58 ` Wilco Dijkstra
2025-06-17 3:14 ` Alejandro Colomar
2025-06-17 3:18 ` Alejandro Colomar
2025-06-17 11:52 ` Wilco Dijkstra
2025-06-17 18:42 ` Alejandro Colomar
2025-06-17 19:10 ` Wilco Dijkstra
2025-06-17 21:53 ` Alejandro Colomar
2025-06-17 22:21 ` Alejandro Colomar
2025-06-18 22:11 ` Wilco Dijkstra
2025-06-18 23:18 ` Vincent Lefevre
2025-06-18 23:32 ` Alejandro Colomar
2025-06-19 9:20 ` Florian Weimer
2025-06-19 10:09 ` Alejandro Colomar
2025-06-19 19:17 ` Paul Eggert
2025-06-19 20:07 ` Wilco Dijkstra
2025-06-19 23:56 ` Alejandro Colomar
2025-06-20 23:58 ` H. Peter Anvin
2025-06-21 1:21 ` Alejandro Colomar
2025-06-21 2:30 ` H. Peter Anvin
2025-06-21 2:48 ` Alejandro Colomar
2025-06-21 2:58 ` H. Peter Anvin
2025-06-21 3:00 ` Alejandro Colomar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).