Adds a new warning to memcheck when realloc is used with a size of 0.
For a long time this has been "implementation defined" and so
non-portable. With C23 it will become UB.
Also adds a switch to turn off the error generation and a
second switch to select between the most common
"implementation" behaviours. The defaults for this second
switch are baked in at build time.
/memcheck/tests/posix_memalign
/memcheck/tests/post-syscall
/memcheck/tests/reach_thread_register
+/memcheck/tests/realloc_size_zero
+/memcheck/tests/realloc_size_zero_mismatch
/memcheck/tests/realloc1
/memcheck/tests/realloc2
/memcheck/tests/realloc3
$3 = 40
(gdb) monitor who_point_at 0x1130a0 40
+* The behaviour of realloc with a size of zero can now
+ be changed for tools that intercept malloc. Those
+ tools are memcheck, helgrind, drd, massif and dhat.
+ Realloc implementations generally do one of two things
+ - free the memory like free() and return NULL
+ (GNU libc and ptmalloc).
+ - either free the memory and then allocate a
+ minumum siized block or just return the
+ original pointer. Return NULL if the
+ allocation of the minimum sized block fails
+ (jemalloc, musl, snmalloc, Solaris, macOS).
+ When Valgrind is configured and built it will
+ try to match the OS and libc behaviour. However
+ if you are using a non-default library to replace
+ malloc and family (e.g., musl on a glibc Linux or
+ tcmalloc on FreeBSD) then you can use a command line
+ option to change the behaviour of Valgrind:
+ --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]
+
* ==================== TOOL CHANGES ===================
* Memcheck:
the new loss records have a "new" marker.
- Valgrind now contains python code that defines GDB memcheck
front end monitor commands. See CORE CHANGES.
+ - Performs checks for the use of realloc with a size of zero.
+ This is non-portable and a source of errors. If memcheck
+ detects such a usage it will generate an error
+ realloc() with size 0
+ followed by the usual callstacks.
+ A switch has been added to allow this to be turned off:
+ --show-realloc-size-zero=yes|no [yes]
* Helgrind:
- The option ---history-backtrace-size=<number> allows to configure
" attempt to avoid expensive address-space-resync operations\n"
" --max-threads=<number> maximum number of threads that valgrind can\n"
" handle [%d]\n"
+" --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]\n"
+" should calls to realloc with a size of 0\n"
+" free memory and return NULL or\n"
+" allocate/resize and return non-NULL\n"
"\n";
const HChar usage2[] =
/* DEBUG: print malloc details? default: NO */
Bool VG_(clo_trace_malloc) = False;
+#if defined(VGO_linux) && !defined(MUSL_LIBC)
+Bool VG_(clo_realloc_zero_bytes_frees) = True;
+#else
+Bool VG_(clo_realloc_zero_bytes_frees) = False;
+#endif
/* Minimum alignment in functions that don't specify alignment explicitly.
default: 0, i.e. use VG_MIN_MALLOC_SZB. */
VG_(clo_xtree_compress_strings)) {}
else if VG_BOOL_CLO(arg, "--trace-malloc", VG_(clo_trace_malloc)) {}
+ else if VG_BOOL_CLO(arg, "--realloc-zero-bytes-frees", VG_(clo_realloc_zero_bytes_frees)) {}
else
return False;
void* v; \
\
DO_INIT; \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(ptrV); \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(new_size); \
MALLOC_TRACE("zone_realloc(%p,%p,%llu)", zone, ptrV, (ULong)new_size ); \
- \
- if (ptrV == NULL) \
- /* We need to call a malloc-like function; so let's use \
- one which we know exists. GrP fixme use zonemalloc instead? */ \
- return VG_REPLACE_FUNCTION_EZU(10010,VG_Z_LIBC_SONAME,malloc) \
- (new_size); \
- if (new_size <= 0) { \
- VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
- MALLOC_TRACE(" = 0\n"); \
- return NULL; \
- } \
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size ); \
MALLOC_TRACE(" = %p\n", v ); \
+ if (v == NULL) { \
+ if (!(new_size == 0U && info.clo_realloc_zero_bytes_frees == True)) {\
+ SET_ERRNO_ENOMEM; \
+ } \
+ } \
return v; \
}
void* v; \
\
DO_INIT; \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(ptrV); \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(new_size); \
MALLOC_TRACE("realloc(%p,%llu)", ptrV, (ULong)new_size ); \
- \
- if (ptrV == NULL) \
- /* We need to call a malloc-like function; so let's use \
- one which we know exists. */ \
- return VG_REPLACE_FUNCTION_EZU(10010,VG_Z_LIBC_SONAME,malloc) \
- (new_size); \
- if (new_size <= 0) { \
- VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
- MALLOC_TRACE(" = 0\n"); \
- return NULL; \
- } \
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size ); \
MALLOC_TRACE(" = %p\n", v ); \
- if (!v) SET_ERRNO_ENOMEM; \
+ if (v == NULL) { \
+ if (!(new_size == 0U && info.clo_realloc_zero_bytes_frees == True)) {\
+ SET_ERRNO_ENOMEM; \
+ } \
+ } \
return v; \
}
{ \
void* v; \
\
- if (!init_done) init(); \
+ DO_INIT; \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(ptrV); \
+ TRIGGER_MEMCHECK_ERROR_IF_UNDEFINED(new_size); \
MALLOC_TRACE("reallocf(%p,%llu)", ptrV, (ULong)new_size ); \
- \
- if (ptrV == NULL) \
- /* We need to call a malloc-like function; so let's use \
- one which we know exists. */ \
- return VG_REPLACE_FUNCTION_EZU(10010,VG_Z_LIBC_SONAME,malloc) \
- (new_size); \
- if (new_size == 0) { \
- VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
- MALLOC_TRACE(" = 0\n"); \
- return ptrV; \
- } \
v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size ); \
MALLOC_TRACE(" = %p\n", v ); \
- if (v == NULL) {\
- VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
- SET_ERRNO_ENOMEM; \
+ if (v == NULL) { \
+ if (!(new_size == 0U && info.clo_realloc_zero_bytes_frees == True)) {\
+ VG_REPLACE_FUNCTION_EZU(10050,VG_Z_LIBC_SONAME,free)(ptrV); \
+ SET_ERRNO_ENOMEM; \
+ } \
} \
MALLOC_TRACE(" = %p\n", v ); \
return v; \
info->mallinfo = VG_(mallinfo);
info->clo_trace_malloc = VG_(clo_trace_malloc);
+ info->clo_realloc_zero_bytes_frees = VG_(clo_realloc_zero_bytes_frees);
SET_CLREQ_RETVAL( tid, 0 ); /* return value is meaningless */
SizeT (*tl_malloc_usable_size) (ThreadId tid, void* payload);
void (*mallinfo) (ThreadId tid, struct vg_mallinfo* mi);
Bool clo_trace_malloc;
+ Bool clo_realloc_zero_bytes_frees;
};
#endif // __PUB_CORE_REPLACEMALLOC_H
return dh_malloc(tid, new_szB);
}
if (new_szB == 0) {
- dh_free(tid, p_old);
- return NULL;
+ if (VG_(clo_realloc_zero_bytes_frees) == True) {
+ dh_free(tid, p_old);
+ return NULL;
+ }
+ new_szB = 1;
}
return renew_block(tid, p_old, new_szB);
}
</listitem>
</varlistentry>
+ <varlistentry id="opt.realloc-zero-bytes-frees" xreflabel="--realloc-zero-bytes-frees">
+ <term>
+ <option><![CDATA[--realloc-zero-bytes-frees=yes|no [default: yes for glibc no otherwise] ]]></option>
+ </term>
+ <listitem>
+ <para>The behaviour of <computeroutput>realloc()</computeroutput> is
+ implementation defined (in C17, in C23 it is likely to become
+ undefined). Valgrind tries to work in the same way as the
+ underlying OS and C runtime library. However, if you use a
+ different C runtime library then this default may be wrong.
+ For instance, if you use Valgrind on Linux installed via a package
+ and use the musl C runtime or the JEMalloc library then
+ consider using
+ <computeroutput>--realloc-zero-bytes-frees=no</computeroutput>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
<!-- end of xi:include in the manpage -->
if (new_size == 0)
{
- drd_free(tid, p_old);
- return NULL;
+ if (VG_(clo_realloc_zero_bytes_frees) == True)
+ {
+ drd_free(tid, p_old);
+ return NULL;
+ }
+ new_size = 1;
}
s_cmalloc_n_mallocs++;
* glibc returns a NULL pointer when the size argument passed to realloc()
* is zero, while Darwin's C library returns a non-NULL pointer. Both are
* allowed by POSIX.
+ *
+ * Other platforms also tend not to free. To make things simpler just
+ * free it if it is not NULL.
*/
-#if defined(VGO_darwin)
if (p)
free(p);
-#else
- assert(! p);
-#endif
}
return 0;
if (((SSizeT)new_size) < 0) return NULL;
+ if (payloadV == NULL) {
+ return handle_alloc ( tid, new_size, VG_(clo_alignment),
+ /*is_zeroed*/False );
+ }
+
md = (MallocMeta*) VG_(HT_lookup)( hg_mallocmeta_table, (UWord)payload );
if (!md)
return NULL; /* apparently realloc-ing a bogus address. Oh well. */
tl_assert(md->payload == payload);
+ if (new_size == 0U ) {
+ if (VG_(clo_realloc_zero_bytes_frees) == True) {
+ md_tmp = VG_(HT_remove)( hg_mallocmeta_table, payload );
+ tl_assert(md_tmp);
+ tl_assert(md_tmp == md);
+
+ VG_(cli_free)((void*)md->payload);
+ delete_MallocMeta(md);
+
+ return NULL;
+ }
+ new_size = 1U;
+ }
+
if (md->szB == new_size) {
/* size unchanged */
md->where = VG_(record_ExeContext)(tid, 0);
/* Minimum alignment in functions that don't specify alignment explicitly.
default: VG_MIN_MALLOC_SZB */
extern UInt VG_(clo_alignment);
+/* Controls the behaviour of realloc(ptr, 0) */
+extern Bool VG_(clo_realloc_zero_bytes_frees);
extern Bool VG_(replacement_malloc_process_cmd_line_option) ( const HChar* arg );
Xecu old_where;
Bool is_ignored = False;
+ if (p_old == NULL) {
+ return alloc_and_record_block( tid, new_req_szB, VG_(clo_alignment), /*is_zeroed*/False );
+ }
+
+ if (new_req_szB == 0U) {
+ if (VG_(clo_realloc_zero_bytes_frees) == True) {
+ /* like ms_free */
+ unrecord_block(p_old, /*maybe_snapshot*/True, /*exclude_first_entry*/True);
+ VG_(cli_free)(p_old);
+ return NULL;
+ }
+ new_req_szB = 1U;
+ }
+
// Remove the old block
hc = VG_(HT_remove)(malloc_list, (UWord)p_old);
if (hc == NULL) {
--------------------------------------------------------------------------------
Command: ./realloc
-Massif arguments: --stacks=no --heap-admin=0 --time-unit=B --threshold=0 --massif-out-file=massif.out --ignore-fn=__part_load_locale --ignore-fn=__time_load_locale --ignore-fn=dwarf2_unwind_dyld_add_image_hook --ignore-fn=get_or_create_key_element
+Massif arguments: --stacks=no --heap-admin=0 --time-unit=B --threshold=0 --massif-out-file=massif.out --realloc-zero-bytes-frees=yes --ignore-fn=__part_load_locale --ignore-fn=__time_load_locale --ignore-fn=dwarf2_unwind_dyld_add_image_hook --ignore-fn=get_or_create_key_element
ms_print arguments: --threshold=0 massif.out
--------------------------------------------------------------------------------
prog: realloc
-vgopts: -v -v --stats=yes --stacks=no --heap-admin=0 --time-unit=B --threshold=0 --massif-out-file=massif.out
+# use --realloc-zero-bytes-frees=yes to get the same results on all platforms
+vgopts: -v -v --stats=yes --stacks=no --heap-admin=0 --time-unit=B --threshold=0 --massif-out-file=massif.out --realloc-zero-bytes-frees=yes
vgopts: --ignore-fn=__part_load_locale --ignore-fn=__time_load_locale --ignore-fn=dwarf2_unwind_dyld_add_image_hook --ignore-fn=get_or_create_key_element
stderr_filter: filter_verbose
post: perl ../../massif/ms_print --threshold=0 massif.out | ../../tests/filter_addresses
allocation function.</para>
</listitem>
+ <listitem>
+ <para>Using a <computeroutput>size</computeroutput> value of 0
+ with realloc.</para>
+ </listitem>
+
<listitem>
<para>Memory leaks.</para>
</listitem>
</sect2>
+<sect2 id="mc-manual.realocsizezero"
+ xreflabel="Realloc size zero">
+<title>Realloc size zero</title>
+
+<para>The (ab)use or realloc to also do the job of <function>free</function>
+has been poorly understood for a long time. In the C17 standard
+ISO/IEC 9899:2017] the behaviour of realloc when the size argument
+is zero is specified as implementation defined. Memcheck warns about
+the non-portable use or realloc.</para>
+
+<para>For example:</para>
+<programlisting><![CDATA[
+==77609== realloc() with size 0
+==77609== at 0x48502B8: realloc (vg_replace_malloc.c:1450)
+==77609== by 0x201989: main (realloczero.c:8)
+==77609== Address 0x5464040 is 0 bytes inside a block of size 4 alloc'd
+==77609== at 0x484CBB4: malloc (vg_replace_malloc.c:397)
+==77609== by 0x201978: main (realloczero.c:7)
+]]></programlisting>
+
+</sect2>
+
<sect2 id="mc-manual.leaks" xreflabel="Memory leak detection">
<title>Memory leak detection</title>
Err_Leak,
Err_IllegalMempool,
Err_FishyValue,
+ Err_ReallocSizeZero,
}
MC_ErrorTag;
AddrInfo ai;
} FreeMismatch;
+ struct {
+ AddrInfo ai;
+ } ReallocSizeZero;
+
// Call to strcpy, memcpy, etc, with overlapping blocks.
struct {
Addr src; // Source block
}
break;
+ case Err_ReallocSizeZero:
+ if (xml) {
+ emit( " <kind>ReallocSizeZero</kind>\n" );
+ emit( " <what>realloc() with size 0</what>\n" );
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
+ &extra->Err.ReallocSizeZero.ai, False);
+ } else {
+ emit( "realloc() with size 0\n" );
+ VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+ VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
+ &extra->Err.ReallocSizeZero.ai, False);
+ }
+ break;
+
default:
VG_(printf)("Error:\n unknown Memcheck error code %d\n",
VG_(get_error_kind)(err));
&extra );
}
+void MC_(record_realloc_size_zero) ( ThreadId tid, Addr a )
+{
+ MC_Error extra;
+ tl_assert(VG_INVALID_THREADID != tid);
+ extra.Err.ReallocSizeZero.ai.tag = Addr_Undescribed;
+ VG_(maybe_record_error)( tid, Err_ReallocSizeZero, a, /*s*/NULL, &extra );
+}
+
+
void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a )
{
MC_Error extra;
&extra->Err.FreeMismatch.ai );
return sizeof(MC_Error);
}
+ case Err_ReallocSizeZero:
+ describe_addr ( ep, VG_(get_error_address)(err),
+ &extra->Err.ReallocSizeZero.ai );
+ return sizeof(MC_Error);
default: VG_(tool_panic)("mc_update_extra: bad errkind");
}
LeakSupp, // Something to be suppressed in a leak check.
MempoolSupp, // Memory pool suppression.
FishyValueSupp,// Fishy value suppression.
+ ReallocSizeZeroSupp, // realloc size 0 suppression
}
MC_SuppKind;
else if (VG_STREQ(name, "Value16")) skind = Value16Supp;
else if (VG_STREQ(name, "Value32")) skind = Value32Supp;
else if (VG_STREQ(name, "FishyValue")) skind = FishyValueSupp;
+ else if (VG_STREQ(name, "ReallocZero")) skind = ReallocSizeZeroSupp;
else
return False;
supp_extra->argument_name);
}
+ case ReallocSizeZeroSupp: {
+
+ return (ekind == Err_ReallocSizeZero);
+ }
+
default:
VG_(printf)("Error:\n"
" unknown suppression type %d\n",
const HChar* MC_(get_error_name) ( const Error* err )
{
switch (VG_(get_error_kind)(err)) {
- case Err_RegParam: return "Param";
- case Err_MemParam: return "Param";
- case Err_User: return "User";
- case Err_FreeMismatch: return "Free";
- case Err_IllegalMempool: return "Mempool";
- case Err_Free: return "Free";
- case Err_Jump: return "Jump";
- case Err_CoreMem: return "CoreMem";
- case Err_Overlap: return "Overlap";
- case Err_Leak: return "Leak";
- case Err_Cond: return "Cond";
- case Err_FishyValue: return "FishyValue";
+ case Err_RegParam: return "Param";
+ case Err_MemParam: return "Param";
+ case Err_User: return "User";
+ case Err_FreeMismatch: return "Free";
+ case Err_IllegalMempool: return "Mempool";
+ case Err_Free: return "Free";
+ case Err_Jump: return "Jump";
+ case Err_CoreMem: return "CoreMem";
+ case Err_Overlap: return "Overlap";
+ case Err_Leak: return "Leak";
+ case Err_Cond: return "Cond";
+ case Err_FishyValue: return "FishyValue";
+ case Err_ReallocSizeZero: return "ReallocZero";
case Err_Addr: {
MC_Error* extra = VG_(get_error_extra)(err);
switch ( extra->Err.Addr.szB ) {
void MC_(record_free_error) ( ThreadId tid, Addr a );
void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a );
void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc );
+void MC_(record_realloc_size_zero) ( ThreadId tid, Addr a );
void MC_(record_overlap_error) ( ThreadId tid, const HChar* function,
Addr src, Addr dst, SizeT szB );
/* Should we show mismatched frees? Default: YES */
extern Bool MC_(clo_show_mismatched_frees);
+/* Should we warn about deprecated realloc() of size 0 ? Default : YES */
+extern Bool MC_(clo_show_realloc_size_zero);
+
/* Indicates the level of detail for Vbit tracking through integer add,
subtract, and some integer comparison operations. */
typedef
KeepStacktraces MC_(clo_keep_stacktraces) = KS_alloc_and_free;
Int MC_(clo_mc_level) = 2;
Bool MC_(clo_show_mismatched_frees) = True;
+Bool MC_(clo_show_realloc_size_zero) = True;
ExpensiveDefinednessChecks
MC_(clo_expensive_definedness_checks) = EdcAUTO;
else if VG_BOOL_CLOM(cloPD, arg, "--show-mismatched-frees",
MC_(clo_show_mismatched_frees)) {}
+ else if VG_BOOL_CLOM(cloPD, arg, "--show-realloc-size-zero",
+ MC_(clo_show_realloc_size_zero)) {}
else if VG_XACT_CLO(arg, "--expensive-definedness-checks=no",
MC_(clo_expensive_definedness_checks), EdcNO) {}
" --keep-stacktraces=alloc|free|alloc-and-free|alloc-then-free|none\n"
" stack trace(s) to keep for malloc'd/free'd areas [alloc-and-free]\n"
" --show-mismatched-frees=no|yes show frees that don't match the allocator? [yes]\n"
+" --show-realloc-size-zero=no|yes show realocs with a size of zero? [yes]\n"
);
}
if (MC_(record_fishy_value_error)(tid, "realloc", "size", new_szB))
return NULL;
+ if (p_old == NULL) {
+ return MC_(new_block) ( tid, 0, new_szB, VG_(clo_alignment),
+ /*is_zeroed*/False, MC_AllocMalloc, MC_(malloc_list));
+ }
+
+ if (new_szB == 0U) {
+ if (MC_(clo_show_realloc_size_zero)) {
+ MC_(record_realloc_size_zero)(tid, (Addr)p_old);
+ }
+
+ if (VG_(clo_realloc_zero_bytes_frees) == True) {
+ MC_(handle_free)(
+ tid, (Addr)p_old, MC_(Malloc_Redzone_SzB), MC_AllocMalloc );
+
+ return NULL;
+ }
+ new_szB = 1U;
+ }
+
cmalloc_n_frees ++;
cmalloc_n_mallocs ++;
cmalloc_bs_mallocd += (ULong)new_szB;
reach_thread_register.stderr.exp reach_thread_register.vgtest \
reach_thread_register.stderr.exp-mips32 \
reach_thread_register.stderr.exp-mips64 \
+ realloc_size_zero.stderr.exp realloc_size_zero.stdout.exp-glibc \
+ realloc_size_zero.stdout.exp-other \
+ realloc_size_zero.vgtest \
+ realloc_size_zero_yes.stderr.exp realloc_size_zero_yes.stdout.exp \
+ realloc_size_zero_yes.vgtest \
+ realloc_size_zero_no.stderr.exp realloc_size_zero_no.stdout.exp \
+ realloc_size_zero_no.vgtest \
+ realloc_size_zero_off.stderr.exp realloc_size_zero_off.stdout.exp \
+ realloc_size_zero_off.vgtest \
+ realloc_size_zero_mismatch.stderr.exp \
+ realloc_size_zero_mismatch.stdout.exp \
+ realloc_size_zero_mismatch.vgtest \
+ realloc_size_zero_supp.stderr.exp realloc_size_zero_supp.stdout.exp \
+ realloc_size_zero_supp.vgtest \
+ realloc_size_zero.supp \
realloc1.stderr.exp realloc1.vgtest \
realloc2.stderr.exp realloc2.vgtest \
realloc3.stderr.exp realloc3.vgtest \
pipe pointer-trace \
posix_memalign \
post-syscall \
+ realloc_size_zero realloc_size_zero_mismatch \
realloc1 realloc2 realloc3 \
recursive-merge \
resvn_stack \
reach_thread_register_CFLAGS = $(AM_CFLAGS) -O2
reach_thread_register_LDADD = -lpthread
+realloc_size_zero_mismatch_SOURCES = realloc_size_zero_mismatch.cpp
+
resvn_stack_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
sendmsg_CFLAGS = $(AM_CFLAGS)
pi = reallocf(pi, 10*sizeof(int));
VALGRIND_DO_ADDED_LEAK_CHECK;
pi = reallocf(pi, 0);
+ free(pi);
VALGRIND_DO_CHANGED_LEAK_CHECK;
pi = NULL;
pi = realloc(pi, 10*sizeof(int));
Reachable blocks (those to which a pointer was found) are not shown.
To see them, rerun with: --leak-check=full --show-leak-kinds=all
+realloc() with size 0
+ at 0x........: reallocf (vg_replace_malloc.c:...)
+ by 0x........: main (reallocf.c:12)
+ Address 0x........ is 0 bytes inside a block of size 40 alloc'd
+ at 0x........: reallocf (vg_replace_malloc.c:...)
+ by 0x........: main (reallocf.c:10)
+
All heap blocks were freed -- no leaks are possible
LEAK SUMMARY:
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
- total heap usage: 3 allocs, 3 frees, 562,949,953,421,392 bytes allocated
+ total heap usage: 4 allocs, 4 frees, 562,949,953,421,393 bytes allocated
For a detailed leak analysis, rerun with: --leak-check=full
For lists of detected and suppressed errors, rerun with: -s
-ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+realloc() with size 0
+ at 0x........: reallocf (vg_replace_malloc.c:...)
+ by 0x........: main (static_allocs.c:36)
+ Address 0x........ is 0 bytes inside a block of size 160 alloc'd
+ at 0x........: calloc (vg_replace_malloc.c:...)
+ by 0x........: main (static_allocs.c:35)
+
+1 bytes in 1 blocks are definitely lost in loss record ... of ...
+ at 0x........: reallocf (vg_replace_malloc.c:...)
+ by 0x........: main (static_allocs.c:36)
+
10 bytes in 1 blocks are definitely lost in loss record ... of ...
- at 0x........: malloc (vg_replace_malloc.c:...)
- by 0x........: realloc (vg_replace_malloc.c:...)
+ at 0x........: realloc (vg_replace_malloc.c:...)
by 0x........: main (static_allocs.c:34)
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int main(void)
+{
+ int i;
+ char* p = malloc(1024);
+ p[0] = '\0';
+ errno = 0;
+ p = realloc(p, 0);
+ if (p) {
+ printf("p not NULL after realloc 0\n");
+ } else {
+ printf("p is NULL after realloc 0\n");
+ }
+ if (errno) {
+ perror("realloc(something, 0):");
+ }
+ if (p) {
+ free(p);
+ }
+
+ errno = 0;
+ volatile void *ptr = NULL;
+ volatile size_t size = 0U;
+ char *p2 = realloc(ptr, size);
+ if (p2) {
+ printf("p2 not NULL after realloc 0\n");
+ } else {
+ printf("p2 is NULL after realloc 0\n");
+ }
+ if (errno) {
+ perror("realloc(NULL, 0):");
+ }
+ if (p2) {
+ free(p2);
+ }
+}
--- /dev/null
+realloc() with size 0
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero.c:11)
+ Address 0x........ is 0 bytes inside a block of size 1,024 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero.c:8)
+
--- /dev/null
+p is NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+p not NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+{
+ Test for realoc zero suppression
+ Memcheck:ReallocZero
+ fun:realloc
+ fun:main
+}
+
--- /dev/null
+prog: realloc_size_zero
+vgopts: -q
--- /dev/null
+#include <iostream>
+#include <cstdlib>
+#include <cerrno>
+
+using std::realloc;
+using std::cout;
+using std::perror;
+
+int main(void)
+{
+ char* p = new char[1024];
+ p[0] = '\0';
+ errno = 0;
+ // mismatch
+ p = static_cast<char *>(realloc(p, 0));
+ if (p) {
+ cout << "p not nullptr after realloc 0\n";
+ } else {
+ cout << "p is nullptr after realloc 0\n";
+ }
+ if (errno) {
+ perror("realloc(something, 0):");
+ }
+ // mismatch again
+ delete [] p;
+
+ errno = 0;
+ volatile void *ptr = NULL;
+ volatile size_t size = 0U;
+ char *p2 = static_cast<char *>(realloc(const_cast<void*>(ptr), size));
+ if (p2) {
+ cout << "p2 not nullptr after realloc 0\n";
+ } else {
+ cout << "p2 is nullptr after realloc 0\n";
+ }
+ if (errno) {
+ perror("realloc(NULL, 0):");
+ }
+ // mismatch
+ delete [] p2;
+}
--- /dev/null
+realloc() with size 0
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:15)
+ Address 0x........ is 0 bytes inside a block of size 1,024 alloc'd
+ at 0x........: ...operator new[]... (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:11)
+
+Mismatched free() / delete / delete []
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:15)
+ Address 0x........ is 0 bytes inside a block of size 1,024 alloc'd
+ at 0x........: ...operator new[]... (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:11)
+
+Mismatched free() / delete / delete []
+ at 0x........: ...operator delete[]... (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:25)
+ Address 0x........ is 0 bytes inside a block of size 1 alloc'd
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:15)
+
+Mismatched free() / delete / delete []
+ at 0x........: ...operator delete[]... (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:40)
+ Address 0x........ is 0 bytes after a block of size 0 alloc'd
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ by 0x........: main (realloc_size_zero_mismatch.cpp:30)
+
--- /dev/null
+p not nullptr after realloc 0
+p2 not nullptr after realloc 0
--- /dev/null
+prog: realloc_size_zero_mismatch
+vgopts: -q --realloc-zero-bytes-frees=no
--- /dev/null
+realloc() with size 0
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ ...
+ Address 0x........ is 0 bytes inside a block of size 1,024 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ ...
+
--- /dev/null
+p not NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+prog: realloc_size_zero
+vgopts: -q --realloc-zero-bytes-frees=no
--- /dev/null
+p not NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+prog: realloc_size_zero
+vgopts: -q --realloc-zero-bytes-frees=no --show-realloc-size-zero=no
--- /dev/null
+p not NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+prog: realloc_size_zero
+vgopts: -q --realloc-zero-bytes-frees=no --suppressions=realloc_size_zero.supp
--- /dev/null
+realloc() with size 0
+ at 0x........: realloc (vg_replace_malloc.c:...)
+ ...
+ Address 0x........ is 0 bytes inside a block of size 1,024 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ ...
+
--- /dev/null
+p is NULL after realloc 0
+p2 not NULL after realloc 0
--- /dev/null
+prog: realloc_size_zero
+vgopts: -q --realloc-zero-bytes-frees=yes
attempt to avoid expensive address-space-resync operations
--max-threads=<number> maximum number of threads that valgrind can
handle [500]
+ --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]
+ should calls to realloc with a size of 0
+ free memory and return NULL or
+ allocate/resize and return non-NULL
user options for Nulgrind:
(none)
attempt to avoid expensive address-space-resync operations
--max-threads=<number> maximum number of threads that valgrind can
handle [500]
+ --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]
+ should calls to realloc with a size of 0
+ free memory and return NULL or
+ allocate/resize and return non-NULL
user options for Nulgrind:
(none)
attempt to avoid expensive address-space-resync operations
--max-threads=<number> maximum number of threads that valgrind can
handle [500]
+ --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]
+ should calls to realloc with a size of 0
+ free memory and return NULL or
+ allocate/resize and return non-NULL
user options for Nulgrind:
(none)
attempt to avoid expensive address-space-resync operations
--max-threads=<number> maximum number of threads that valgrind can
handle [500]
+ --realloc-zero-bytes-frees=yes|no [yes on Linux glibc, no otherwise]
+ should calls to realloc with a size of 0
+ free memory and return NULL or
+ allocate/resize and return non-NULL
user options for Nulgrind:
(none)