admin管理员组文章数量:1122846
How realloc works passed 0 size as argument?
from man page: Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc(), or realloc().
Why it needs to be?
compile this with gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 with no options(flags)
this sample
#include <stdlib.h>
int main () {
int *p = malloc(0);
p = realloc(p, 0);
return 0;
}
this is working code, checking memory with valgrind show this:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==185872== Memcheck, a memory error detector
==185872== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==185872== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==185872== Command: ./test
==185872==
==185872==
==185872== HEAP SUMMARY:
==185872== in use at exit: 0 bytes in 0 blocks
==185872== total heap usage: 1 allocs, 1 frees, 0 bytes allocated
==185872==
==185872== All heap blocks were freed -- no leaks are possible
==185872==
==185872== For lists of detected and suppressed errors, rerun with: -s
==185872== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from
but then compile this(added flag -g):
#include <stdlib.h>
int main () {
int *p = NULL;
p = realloc(p, 0);
return 0;
}
valgrind output shows errors(memory leak):
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==186749== Memcheck, a memory error detector
==186749== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==186749== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==186749== Command: ./test
==186749==
==186749==
==186749== HEAP SUMMARY:
==186749== in use at exit: 0 bytes in 1 blocks
==186749== total heap usage: 1 allocs, 0 frees, 0 bytes allocated
==186749==
==186749== 0 bytes in 1 blocks are definitely lost in loss record 1 of 1
==186749== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==186749== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==186749== by 0x10916D: main (test1.c:5)
==186749==
==186749== LEAK SUMMARY:
==186749== definitely lost: 0 bytes in 1 blocks
==186749== indirectly lost: 0 bytes in 0 blocks
==186749== possibly lost: 0 bytes in 0 blocks
==186749== still reachable: 0 bytes in 0 blocks
==186749== suppressed: 0 bytes in 0 blocks
==186749==
==186749== For lists of detected and suppressed errors, rerun with: -s
==186749== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
why this is happening even i didnt allocate anything?
edits: compiled this with -std=c11 -g:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define ASSERT_ERROR_PREFIX "Assertion "
#define ASSERT_ERROR_SUFFIX " failed\n"
#define assert(x, num) { \
if(!(x)) { \
write(STDOUT_FILENO, ASSERT_ERROR_PREFIX, strlen(ASSERT_ERROR_PREFIX)); \
write(STDOUT_FILENO, num, sizeof(char)); \
write(STDOUT_FILENO, ASSERT_ERROR_SUFFIX, strlen(ASSERT_ERROR_SUFFIX)); \
} \
}
int main () {
int *p = NULL;
p = realloc(p, 0);
*p = '1';
assert((*p == '1'), "1");
assert((p == NULL), "2");
return 0;
}
output: Assertion 2 failed
the valgrind output:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==190041== Memcheck, a memory error detector
==190041== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==190041== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==190041== Command: ./test
==190041==
==190041== Invalid write of size 4
==190041== at 0x109196: main (test1.c:19)
==190041== Address 0x4a5f040 is 0 bytes after a block of size 0 alloc'd
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
==190041== Invalid read of size 4
==190041== at 0x1091A0: main (test1.c:20)
==190041== Address 0x4a5f040 is 0 bytes after a block of size 0 alloc'd
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
Assertion 2 failed
==190041==
==190041== HEAP SUMMARY:
==190041== in use at exit: 0 bytes in 1 blocks
==190041== total heap usage: 1 allocs, 0 frees, 0 bytes allocated
==190041==
==190041== 0 bytes in 1 blocks are definitely lost in loss record 1 of 1
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
==190041== LEAK SUMMARY:
==190041== definitely lost: 0 bytes in 1 blocks
==190041== indirectly lost: 0 bytes in 0 blocks
==190041== possibly lost: 0 bytes in 0 blocks
==190041== still reachable: 0 bytes in 0 blocks
==190041== suppressed: 0 bytes in 0 blocks
==190041==
==190041== For lists of detected and suppressed errors, rerun with: -s
==190041== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
How realloc works passed 0 size as argument?
from man page: Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc(), or realloc().
Why it needs to be?
compile this with gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 with no options(flags)
this sample
#include <stdlib.h>
int main () {
int *p = malloc(0);
p = realloc(p, 0);
return 0;
}
this is working code, checking memory with valgrind show this:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==185872== Memcheck, a memory error detector
==185872== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==185872== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==185872== Command: ./test
==185872==
==185872==
==185872== HEAP SUMMARY:
==185872== in use at exit: 0 bytes in 0 blocks
==185872== total heap usage: 1 allocs, 1 frees, 0 bytes allocated
==185872==
==185872== All heap blocks were freed -- no leaks are possible
==185872==
==185872== For lists of detected and suppressed errors, rerun with: -s
==185872== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from
but then compile this(added flag -g):
#include <stdlib.h>
int main () {
int *p = NULL;
p = realloc(p, 0);
return 0;
}
valgrind output shows errors(memory leak):
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==186749== Memcheck, a memory error detector
==186749== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==186749== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==186749== Command: ./test
==186749==
==186749==
==186749== HEAP SUMMARY:
==186749== in use at exit: 0 bytes in 1 blocks
==186749== total heap usage: 1 allocs, 0 frees, 0 bytes allocated
==186749==
==186749== 0 bytes in 1 blocks are definitely lost in loss record 1 of 1
==186749== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==186749== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==186749== by 0x10916D: main (test1.c:5)
==186749==
==186749== LEAK SUMMARY:
==186749== definitely lost: 0 bytes in 1 blocks
==186749== indirectly lost: 0 bytes in 0 blocks
==186749== possibly lost: 0 bytes in 0 blocks
==186749== still reachable: 0 bytes in 0 blocks
==186749== suppressed: 0 bytes in 0 blocks
==186749==
==186749== For lists of detected and suppressed errors, rerun with: -s
==186749== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
why this is happening even i didnt allocate anything?
edits: compiled this with -std=c11 -g:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define ASSERT_ERROR_PREFIX "Assertion "
#define ASSERT_ERROR_SUFFIX " failed\n"
#define assert(x, num) { \
if(!(x)) { \
write(STDOUT_FILENO, ASSERT_ERROR_PREFIX, strlen(ASSERT_ERROR_PREFIX)); \
write(STDOUT_FILENO, num, sizeof(char)); \
write(STDOUT_FILENO, ASSERT_ERROR_SUFFIX, strlen(ASSERT_ERROR_SUFFIX)); \
} \
}
int main () {
int *p = NULL;
p = realloc(p, 0);
*p = '1';
assert((*p == '1'), "1");
assert((p == NULL), "2");
return 0;
}
output: Assertion 2 failed
the valgrind output:
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test
==190041== Memcheck, a memory error detector
==190041== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==190041== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==190041== Command: ./test
==190041==
==190041== Invalid write of size 4
==190041== at 0x109196: main (test1.c:19)
==190041== Address 0x4a5f040 is 0 bytes after a block of size 0 alloc'd
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
==190041== Invalid read of size 4
==190041== at 0x1091A0: main (test1.c:20)
==190041== Address 0x4a5f040 is 0 bytes after a block of size 0 alloc'd
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
Assertion 2 failed
==190041==
==190041== HEAP SUMMARY:
==190041== in use at exit: 0 bytes in 1 blocks
==190041== total heap usage: 1 allocs, 0 frees, 0 bytes allocated
==190041==
==190041== 0 bytes in 1 blocks are definitely lost in loss record 1 of 1
==190041== at 0x483B723: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x483E017: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==190041== by 0x10918D: main (test1.c:18)
==190041==
==190041== LEAK SUMMARY:
==190041== definitely lost: 0 bytes in 1 blocks
==190041== indirectly lost: 0 bytes in 0 blocks
==190041== possibly lost: 0 bytes in 0 blocks
==190041== still reachable: 0 bytes in 0 blocks
==190041== suppressed: 0 bytes in 0 blocks
==190041==
==190041== For lists of detected and suppressed errors, rerun with: -s
==190041== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Share
Improve this question
edited yesterday
Force Security
asked yesterday
Force SecurityForce Security
11 silver badge1 bronze badge
New contributor
Force Security is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
5
|
1 Answer
Reset to default 2Update your Valgrind!
You should use a more recent version of Valgrind. Since version 3.21 it has included a check for realloc
of size 0.
You will now get errors like
==3891215== realloc() with size 0
==3891215== at 0x40440FB: realloc (vg_replace_malloc.c:1801)
==3891215== by 0x4011AE: main (realloc_size_zero.c:11)
==3891215== Address 0x4e08040 is 0 bytes inside a block of size 1,024 alloc'd
==3891215== at 0x403C7B2: malloc (vg_replace_malloc.c:446)
==3891215== by 0x401187: main (realloc_size_zero.c:8)
realloc size 0 is now UB
Since C23 realloc of size 0 has been made UB (undefined behaviour). Prior to that it was Implementation Defined (and still unsafe to use portably).
The problem with realloc
size 0 is that you can't easily tell what it is doing. Some implementations will free the memory, others may do nothing or free the memory then allocate a minimum sized block. Since realloc
is replaceable you might have a different behaviour between your development environment and the deployment environment. For instance, if you develop on Linux with GNU libc your users could use LD_PRELOAD with the snmalloc
library.
You can use the --show-realloc-size-zero=yes
option to turn off this check. If you do that then you may also need to use --realloc-zero-bytes-frees=yes
(or no) to try to get Valgrind to match the behaviour of the libc or allocator library that you are using.
What does a 0 size alloc do?
The next thing that you are missing is what the behaviour of malloc
of size 0 is (and also realloc
when ptr
is NULL). That's implementation defined. It will either return a NULL pointer or a pointer to allocate some memory that you are not supposed to access.
So, your first example
- allocates a small block
- frees it with
realloc
and your second example
- allocates a small block which leaks
and your third example
- allocates a small block which is inaccessible
- writes to it causing an error
- reads from it causing an error
- leaks the allocated block
Advice
Don't allow realloc
of size 0 in your code.
I know, 'cos I was there -- Max Boyce
本文标签: linuxHow realloc works passed 0 size as argumentStack Overflow
版权声明:本文标题:linux - How realloc works passed 0 size as argument - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736281364a1926294.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
new_size=0
is undefined behavior in C23 and even before that you might not get a NULL in which case I assume valgrind is assuming you need to free that zero-sized allocation. Maybe you can debug by printing out the results of therealloc
. – Paul Hankin Commented yesterdayrealloc
via a wrapper function that when the new size is 0, forces the old block (if any) to be freed and no new block to be allocated, e.g.void *my_realloc(void *p, size_t newsize) {
if (newsize) {
p = realloc(p, newsize);
} else {
free(p);
p = NULL;
}
return p;
}
. Caller should not attempt to use the old block if the wrapper returnsNULL
and the new size was 0, but can assume the wrapper freed the old block in that case. – Ian Abbott Commented 4 hours ago