![]() |
|
Message-ID: <CAEHU8x9AGgaGUkAfoN-cTF93nn9WAOO6B4TLMyCvwPuXQTq9Uw@mail.gmail.com> Date: Fri, 20 Jun 2025 23:31:45 +0100 From: Christopher Bazley <chris.bazley.wg14@...il.com> To: Alejandro Colomar <alx@...nel.org> Cc: libc-alpha@...rceware.org, bug-gnulib@....org, musl@...ts.openwall.com, наб <nabijaczleweli@...ijaczleweli.xyz>, Douglas McIlroy <douglas.mcilroy@...tmouth.edu>, Paul Eggert <eggert@...ucla.edu>, Robert Seacord <rcseacord@...il.com>, Elliott Hughes <enh@...gle.com>, Bruno Haible <bruno@...sp.org>, JeanHeyd Meneide <phdofthehouse@...il.com>, Rich Felker <dalias@...c.org>, Adhemerval Zanella Netto <adhemerval.zanella@...aro.org>, Joseph Myers <josmyers@...hat.com>, Florian Weimer <fweimer@...hat.com>, Laurent Bercot <ska-dietlibc@...rnet.org>, Andreas Schwab <schwab@...e.de>, Thorsten Glaser <tg@...bsd.de>, Eric Blake <eblake@...hat.com>, Vincent Lefevre <vincent@...c17.net>, Mark Harris <mark.hsj@...il.com>, Collin Funk <collin.funk1@...il.com>, Wilco Dijkstra <Wilco.Dijkstra@....com>, DJ Delorie <dj@...hat.com>, Cristian Rodríguez <cristian@...riguez.im>, Siddhesh Poyarekar <siddhesh@...plt.org>, Sam James <sam@...too.org>, Mark Wielaard <mark@...mp.org>, "Maciej W. Rozycki" <macro@...hat.com>, Martin Uecker <ma.uecker@...il.com>, eskil@...ession.se Subject: Re: alx-0029r1 - Restore the traditional realloc(3) specification Hi Alex, On Fri, Jun 20, 2025 at 10:26 PM Alejandro Colomar <alx@...nel.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
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.