Zero is a number just like any other number
Zero is a number just like any other number
Posted Oct 24, 2024 19:26 UTC (Thu) by jem (subscriber, #24231)Parent article: realloc() and the oversize importance of zero-size objects
>His opinion, expressed in unambiguous terms, was that realloc() should behave in the same way as malloc() when passed a size of zero; it should, in other words, free the passed-in object and return a pointer to a zero-byte object.
In my opinion, realloc(ptr, 0) should logically behave in the same way as realloc(ptr, 1), except that in the second case there is a single byte left instead of zero bytes.
If malloc can take a size argument of zero, returning an object with a zero sized block that can be realloc'd, why do we have the asymmetry that a realloc(ptr, 0) can't return a zero sized block (that could be realloc'd back to some larger size)?
Using realloc(ptr, 0) as a substitute for free is an ugly hack. We already have the free function.
Posted Oct 25, 2024 7:47 UTC (Fri)
by taladar (subscriber, #68407)
[Link] (11 responses)
Posted Oct 25, 2024 9:53 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (7 responses)
But the behaviour of free is that the lifetime of the pointer outlives what it's pointing to ...
(And I don't understand your argument - realloc will destroy the contents then the assignment destroys the pointer - how is post-hoc destruction premature?)
"ptr = realloc(ptr, 0)" is the only syntax where the destruction both of the pointer, and its contents, APPEARS to be atomic from the PoV of the caller.
Otherwise it's multiple operations which can get forgotten, separated, {}-errored, etc etc.
Cheers,
Posted Oct 25, 2024 10:05 UTC (Fri)
by mb (subscriber, #50428)
[Link] (6 responses)
And the pattern ptr = realloc(ptr, ...) is dangerous (memory leaks), if your size can be non-zero. This pattern should not be encouraged.
The real solution is to avoid using C altogether and switch to a sane language with a sane allocator.
Posted Oct 25, 2024 11:12 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (4 responses)
How do you chop an atomic statement in half?
> And the pattern ptr = realloc(ptr, ...) is dangerous (memory leaks), if your size can be non-zero. This pattern should not be encouraged.
You should not encourage the CORRECT use of realloc? You should know whether you want to re-use ptr or not, and you should know what sort of object you're going to put there - if the answer is "nothing" then allocate zero space. You're advocating careless programming ... you'll say I'm exaggerating, but taking your argument to its extreme it sounds like "don't bother freeing ptr, you don't know if you're going to re-use it", which is probably worse on the memory leak front ...
> The real solution is to avoid using C altogether and switch to a sane language with a sane allocator.
Agreed :-)
Posted Oct 25, 2024 12:00 UTC (Fri)
by khim (subscriber, #9252)
[Link]
Easy: That approach was even tried in Go (where proper way to append something to the slice is It would have been “correct” if it was taught that way from the beginning (preferably in first edition of K&R). But, alas, in our world that haven't happened, thus it falls fully into “very non-standard and unusual non-portable code that some weirdos try to promote for no good reason”. Typical C programmer, in today's world, wouldn't even know that And you shouldn't do any other mistakes too. Which means that such code is useless for real programmers who are not taught to use it and it's also useless for imaginary “perfect” programmers who never do mistakes. Who could benefit from it, then? Nope. He (and me, too) advocate familiar programming that's based on the tools that we have today. You are imagining some alternate world, where C is different, C programmers are different and Sorry, but it's really too late to push for some new C and new set of C programmers and new C standard library that could, collectively, embrace that “brave new way” of dealing with memory allocations.
Posted Oct 25, 2024 12:40 UTC (Fri)
by mb (subscriber, #50428)
[Link] (2 responses)
It's not atomic.
> You should not encourage the CORRECT use of realloc?
No. It's usually incorrect to overwrite the pointer with the return value of realloc before checking for NULL return. If realloc fails to allocate, it does not free the pointer and returns NULL. If you overwrite your only pointer with the NULL return, you have leaked the original allocation.
Posted Oct 25, 2024 18:27 UTC (Fri)
by khim (subscriber, #9252)
[Link] (1 responses)
Sure, but if you want not to allocate memory, but to free it and that fails… what are the mitigations? At this point I would assume that allocator would just stop the program because it's probably the best response that it could do. And in some alternate reality where this behavior is mandated… and I would argue that even in that world it would have been bad design, but familiarity of the pattern would have made it justifiable. But inventing and pushing new convention like that in our world? That's just… I don't even know strong enough words to describe what I think about that idea.
Posted Oct 25, 2024 18:30 UTC (Fri)
by mb (subscriber, #50428)
[Link]
I commented on something else.
I was just saying that ptr = realloc(ptr, ...) was a bad pattern, because it's wrong for all cases *except* the free/zero case (maybe; implementation defined; if not UB).
Posted Oct 31, 2024 5:33 UTC (Thu)
by milesrout (subscriber, #126894)
[Link]
this has nothing to do with the fact p = realloc(p,...) is erroneous. Of course it is wrong! It is obviously nonsensical rubbish code with any allocator. If you cant get this basic stuff right then i dont think a "sane" allocator would save you from writing hundreds of other serious bugs in your program
Posted Oct 25, 2024 10:12 UTC (Fri)
by khim (subscriber, #9252)
[Link] (2 responses)
After call to
Posted Oct 25, 2024 11:04 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (1 responses)
main {
My C is rusty, but that will access what ptr USED TO point at, and (possibly) do a load of damage?
Whereas
The point is that, from the PoV of the calling function, realloc ATOMICally destroys BOTH what is pointed at, and what is doing the pointing. Absent multiple copies, you don't get dangling pointers lying around to cause "use after free" problems.
And it's easily enforced with a couple of rules, that I guess are easy to write in a linter - "don't use free; never ignore the return from realloc".
Cheers,
Posted Oct 25, 2024 11:43 UTC (Fri)
by khim (subscriber, #9252)
[Link]
It may or may not do that. In fact in a world where Which is still much more convoluted and contrived way that just “use I agree that in some alternate reality where C would have developed in some other direction than how it happened in our world Note that
Posted Oct 25, 2024 8:08 UTC (Fri)
by Wol (subscriber, #4433)
[Link] (1 responses)
But they're not identical. realloc is a function, free is a subroutine. From the point of view of the caller, wiping the pointer with realloc is an atomic operation, freeing then wiping is two actions which could get separated (or forgotten).
So what if realloc is a hack. It's also an aid to memory safety, and probably far easier to lint.
Cheers,
Posted Oct 25, 2024 10:31 UTC (Fri)
by khim (subscriber, #9252)
[Link]
As it's currently defined it's far from an aid to anything. Notice how it invalides some pointers that are not even arguments to The simples rule to enforce: flag any use of It may not be the most satisfying solution and in some alternate world You may never make different people agree on how these From safety POV the simplest resolution that hyphotetical C50 may do is to just replace all that mess with call to
Zero is a number just like any other number
Zero is a number just like any other number
Wol
Zero is a number just like any other number
Zero is a number just like any other number
> How do you chop an atomic statement in half?
Zero is a number just like any other number
p = realloc(q, 0);
. Bam: your pointer is no longer clobbered and can be happily reused.s = append(s, t);
and not just simply append(s, t);
or s.append(t);
) and causes nothing but grief.ptr = realloc(ptr, 0);
is supposed to free memory and would look, in vain, for free
, which would lead not to greater safety, but to greater confusion.realloc
is different, too!Zero is a number just like any other number
> If you overwrite your only pointer with the NULL return, you have leaked the original allocation.
Zero is a number just like any other number
realloc
was required to behave like that… and all C courses would have teached you to use that ability… yes, in such a world, use of realloc
would have been justified.Zero is a number just like any other number
Zero is a number just like any other number
> The existing behavior where realloc acts like free also means that the lifetime of the actual pointer ends prematurely which is a special case that I bet isn't checked
Zero is a number just like any other number
realloc
pointer is no longer usable in all cases, I don't see where do you see the special case.Zero is a number just like any other number
void *ptr;
ptrr = malloc(6);
free(ptr);
*ptr = 9.6;
}
ptr = realloc(ptr, 0);
using the "returns null" definition will in most circumstances trap and cause a run-time error "dereferencing null pointer"?
Wol
> using the "returns null" definition will in most circumstances trap and cause a run-time error "dereferencing null pointer"?
Zero is a number just like any other number
*ptr = 9.6;
means I solemnly swear that ptr
is not NULL
and ptr = realloc(ptr, 0);
means I solemnly swear that ptr
would be NULL
this could be “interpreted” by the compiler in a very radical manner with very non-obvious consequences.FREE
macro” that is defined as ({typeof(&p) addr_of_p = &p; free(*addr_of_p); *addr_of_p = NULL;})
.realloc
used in this fashion can aid safety, but in the mess that we have now, today? Nope, it would just make everything worse.free
is already defined as function that invalidates pointer and compiler can be taught to detect these situations statically in simple cases while in more complicated cases use-after-free comes not from pointer directly passed to free
but from entirely different pointer stashed in some different struct.Zero is a number just like any other number
Wol
> It's also an aid to memory safety
Zero is a number just like any other number
realloc
!realloc
and ask developer to stop doing it. Presto: problem solved.realloc
would behave differently but after the mess that was already created around it any attempt to use it for safety is a fools errand.realloc
-using programs have to behave.realloc
is undefined behavior. That would be shorter, simpler… and wouldn't affect all that many programs, in reality.