| 
  | 
Message-ID: <tzrznth5ng3qukc4dlym5woctbppcabjglsxgfnfvdrd45rr5d@573xvnl5twv6>
Date: Fri, 31 Oct 2025 14:43:36 +0100
From: Alejandro Colomar <alx@...nel.org>
To: libc-alpha@...rceware.org, musl@...ts.openwall.com, 
	"A. Wilcox" <AWilcox@...cox-tech.com>, Lénárd Szolnoki <cpp@...ardszolnoki.com>, 
	Thorsten Glaser <tg@...bsd.de>, Collin Funk <collin.funk1@...il.com>
Cc: Arthur O'Dwyer <arthur.j.odwyer@...il.com>, 
	Jonathan Wakely <jwakely@...hat.com>, Thiago Macieira <thiago@...ieira.org>
Subject: Re: realloci(): A realloc() variant that works in-place
Hi,
I didn't receive replies that didn't CC me; I've learnt from them from
reading the archives.  Please keep me in CC, as I'm not subscribed to
musl@ nor libc-alpha@.  For those that don't have the address, here it
is:
	Cc: alx@...nel.org
I'll try to reply to the feedback I saw in the archives.
Thosten wrote:
> How is that supposed to work if you want to grow the
> allocation?
> 
> This seems like increasing burden on the implementation
> for everyone, just for niche corner use cases.
realloc(3) already does this sometimes.  If the memory has some empty
storage after the end of the existing allocation, it can just change the
metadata to adjust the allocated size.
Here's how it could be implemented (this is my current draft for
implementing in musl, which just copies part of the existing
realloc(3) implementation):
	+int realloci(void *p, size_t n)
	+{
	+       if (size_overflows(n)) return -1;
	+
	+       struct meta *g = get_meta(p);
	+       int idx = get_slot_index(p);
	+       size_t stride = get_stride(g);
	+       unsigned char *start = g->mem->storage + stride*idx;
	+       unsigned char *end = start + stride - IB;
	+       size_t avail_size = end-(unsigned char *)p;
	+
	+       // only resize in-place if size class matches
	+       if (n <= avail_size && n<MMAP_THRESHOLD
	+           && size_to_class(n)+1 >= g->sizeclass) {
	+               set_size(p, end, n);
	+               return 0;
	+       }
	+
	+       return -1;
	+}
On 30/10/2025 23:25, A. Wilcox wrote:
>> void *realloci(void *p, size_t size);
>>
>> -  It returns the input pointer on success, or a null pointer on
>>    error.  Usual code using it would look like this:
>>
>> if (realloci(p, size) == NULL)
>> goto fail;
>>
>>    without needing to store the return value anywhere, and it's
>>    just like fgets(3) where it's mainly useful for the null
>>    check.
> 
> 
> So, if it fails, does it free the pointer?
No.  In at least some cases (maybe most of them), the user will want to
fall back to realloc(3) if it failed, so we shouldn't free(3).
If one wants a reallocf(3) variant, as in the BSDs, it could be
reallocif().  However, I'd wait to see if anyone is actually interested
in that before even attempting to implement it; I suspect it could have
zero users.
BTW, I'm going to eventually propose reallocf(3) for standardization
(I want to first settle the realloc(p,0) discussion, as I'm worried that
 the C committee might be confused by more than one realloc(3)-like
 proposal at the same time).  I'll write a separate proposal mail to
introduce it in musl and glibc soon-ish.
> If it doesn’t, then you can’t use it in simple assignment.
If you implement your own reallocif(), then you can.
>>
>> -  'p' must be non-null.  This is because it doesn't make sense
>>    to keep in place a null pointer.
>>
>>    Forbidding null pointers here will also result in better
>>    static analysis: this function will never end any lifetime,
>>    and it will neither start any lifetime.  It's just a regular
>>    function, that happens to extend (or shrink) the storage of
>>    a block of memory.
>>
>> -  We could perfectly return int (0 for success, -1 for error),
>>    but returning the pointer makes it a drop-in replacement for
>>    realloc(3), and also allows using it in chained code
>>
>> foo(realloci(p, size));
> 
> 
> This is never safe if `realloci` can return `NULL`, IMO.
Unless foo() exit(3)s if it receives a null pointer.
See what we do in shadow utils:
	$ grepc -h XREALLOC .
	#define XREALLOC(p, n, T)  exit_if_null(REALLOC(p, n, T))
	$ grepc -h REALLOC .
	#define REALLOC(p, n, T)                                             \
	(                                                                    \
		_Generic(p, T *: (T *) reallocarray(p, (n) ?: 1, sizeof(T))) \
	)
	$ grepc -h exit_if_null .
	#define exit_if_null(p)                                               \
	({                                                                    \
		__auto_type  p_ = p;                                          \
									      \
		exit_if_null_(p_);                                            \
		p_;                                                           \
	})
	$ grepc -htfd exit_if_null_ .
	inline void
	exit_if_null_(void *p)
	{
		if (p == NULL) {
			fprintf(log_get_logfd(), "%s: %s\n",
				log_get_progname(), strerror(errno));
			exit(13);
		}
	}
However, after thinking a bit more, I don't think this would be useful.
It would be rare to want to exit on a realloci() failure, I think.
Normally, one would then try with realloc(3).
And one can always write something like this:
	void
	xrealloci(void *p, size_t n)
	{
		if (realloci(p, n) == -1)
			exit_if_null(NULL);
	}
>> About the name, I chose the 'i' because of sed(1) -i.  'i' seems to be
>> common for meaning in-place in several commands, so it would make sense
>> here, I think.
>>
>> I'd like to hear opinions from implementers about feasibility of this
>> API, before writing a standards proposal.  Please let me know any
>> feedback.
> 
> 
> Sigh.  The only safe way I can think to make this work is:
> 
> * It leaves the existing allocation alone if it can’t satisfy the
>     new size.
Yes.
> * It always returns the existing pointer, allowing for the above
>     example chain.
Let's forget about that.  I don't think it would be very useful.
Let's return an int.  That will also make it more obvious that it can't
possibly change the pointer at all, and that one can (has to) reuse old
pointers.
> * It is one of those dances where you need to zero out errno first,
>     and pay attention to *that* as the “side channel” return value, to
>     know whether the pointer actually points to memory of the desired
>     size or not.
I prefer returning an int.
> But all of that is complicated, easy to get wrong, and just introduces
> more foot guns to C.  Aren’t there enough already?
Let's try with
	int realloci(void *p, size_t size);
This is much less of a footgun, and is also not complicated at all.
> I like what you’ve done with `realloc`,
Thanks!
> but I think something like
> this belongs in a higher level language than C.
As Lénárd said, those higher level languages depend on libc for this.
Have a lovely day!
Alex
-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
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.