![]() |
|
Message-ID: <5pxe5f7s74pmrvw5fntv7llx46x6a4d6s4yt6tjrpkjcv54pny@fu27o3vwhw3u>
Date: Sat, 21 Jun 2025 21:10:58 +0200
From: Alejandro Colomar <alx@...nel.org>
To: libbsd@...ts.freedesktop.org, Guillem Jover <guillem@...rons.org>
Cc: musl@...ts.openwall.com, freebsd-bugs@...ebsd.org
Subject: Bug in reallocf(3) when calling realloc(3)
Bon dia Guillem,
I've just checked the implementation of reallocf(3bsd), and it looks
like it has a memory leak. Admittedly, a small one, but I just wanted
to report it. I'll suggest two proposals, plus keeping the status quo,
and explain the advantages of each of them.
Here's the current code in libbsd:
$ grepc -tfd reallocf .
./src/reallocf.c: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 that comment is false. realloc(nonnull,0) can fail
in systems in which realloc(nonnull,0) normally returns non-null, such
as the BSDs or musl. Let's say the system is unable to find enough
space for the metadata necessary for the new 0-sized block of memory.
It would return NULL and keep the old pointer intact.
I've CCed musl@ so that they can verify that this is true on their
implementation. I didn't see anything in their code suggesting that it
can't fail.
The same also seems to be true in FreeBSD, if I'm reading correctly.
I've also CCed them, so that they can confirm, and also because the
libbsd implementation is pasted from FreeBSD, so they have the same bug.
So, assuming that r(p,0) can indeed fail, there are two alternatives
that are portable to every POSIX system:
a) Check ENOMEM.
void *
reallocf(void *ptr, size_t size)
{
int e;
void *nptr;
e = errno;
errno = 0;
nptr = realloc(ptr, size);
if (nptr == NULL) {
if (errno == ENOMEM)
free(ptr);
return NULL;
}
errno = e;
return nptr;
}
This approach is very complex, although it is the most pedantically
correct approach (unless I introduced accidentally some bug, which
might happen, due to the difficulty of calling realloc(3) portably;
blame SysV).
b) Don't do that at all; pass a dummy size of 1.
void *
reallocf(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size ? size : 1);
if (nptr == NULL)
free(ptr);
return nptr;
}
This approach is the simplest, and it's portable to all realloc(3)s
that have ever existed. However, it allocates one byte too much,
which has a bad consequence: it can hide logic errors where the
programmer is accessing one byte after the requested size.
Sanitizers would think that the :1 was done on purpose, and thus be
silent.
c) You could keep the memory leak. It should be very rare, I guess.
But you never know. I think b) is simple enough and the drawback is
also rare enough (because a logic error that only manifests for a
size of 0 should be rare, even though not impossible).
BTW, I'm working on a proposal to restore the traditional specification
of realloc(3), the one that allocates a zero-sized object (returns
non-null), the one inherited from Unix V7. There's some support, and i
think it might pass in the C Committee and the Austin Group. But feel
free to chime in and voice your support if you're interested in it.
<https://inbox.sourceware.org/libc-alpha/limjtao3jpge6hrrjliko4w6p5t7cfpbc3m3etodqgsxayi3hw@gv4eml5icqb5/T/#u>
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.