From 50bded71b23cb11a8b6c1b6eaf6e3abcc05a06c2 Mon Sep 17 00:00:00 2001 From: Paul Floyd Date: Fri, 10 Mar 2023 21:55:14 +0100 Subject: [PATCH] Bug 436413 - Warn about realloc of size zero 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. --- .gitignore | 2 + NEWS | 26 ++++++++ coregrind/m_main.c | 4 ++ .../m_replacemalloc/replacemalloc_core.c | 6 ++ coregrind/m_replacemalloc/vg_replace_malloc.c | 60 +++++++---------- coregrind/m_scheduler/scheduler.c | 1 + coregrind/pub_core_replacemalloc.h | 1 + dhat/dh_main.c | 7 +- docs/xml/manual-core.xml | 18 +++++ drd/drd_malloc_wrappers.c | 8 ++- drd/tests/memory_allocation.c | 7 +- helgrind/hg_main.c | 19 ++++++ include/pub_tool_replacemalloc.h | 2 + massif/ms_main.c | 14 ++++ massif/tests/realloc.post.exp | 2 +- massif/tests/realloc.vgtest | 3 +- memcheck/docs/mc-manual.xml | 27 ++++++++ memcheck/mc_errors.c | 65 +++++++++++++++---- memcheck/mc_include.h | 4 ++ memcheck/mc_main.c | 4 ++ memcheck/mc_malloc_wrappers.c | 19 ++++++ memcheck/tests/Makefile.am | 18 +++++ memcheck/tests/amd64-freebsd/reallocf.c | 1 + .../tests/amd64-freebsd/reallocf.stderr.exp | 11 +++- .../tests/freebsd/static_allocs.stderr.exp | 14 +++- memcheck/tests/realloc_size_zero.c | 39 +++++++++++ memcheck/tests/realloc_size_zero.stderr.exp | 7 ++ .../tests/realloc_size_zero.stdout.exp-glibc | 2 + .../tests/realloc_size_zero.stdout.exp-other | 2 + memcheck/tests/realloc_size_zero.supp | 7 ++ memcheck/tests/realloc_size_zero.vgtest | 2 + memcheck/tests/realloc_size_zero_mismatch.cpp | 41 ++++++++++++ .../realloc_size_zero_mismatch.stderr.exp | 28 ++++++++ .../realloc_size_zero_mismatch.stdout.exp | 2 + .../tests/realloc_size_zero_mismatch.vgtest | 2 + .../tests/realloc_size_zero_no.stderr.exp | 7 ++ .../tests/realloc_size_zero_no.stdout.exp | 2 + memcheck/tests/realloc_size_zero_no.vgtest | 2 + .../tests/realloc_size_zero_off.stderr.exp | 0 .../tests/realloc_size_zero_off.stdout.exp | 2 + memcheck/tests/realloc_size_zero_off.vgtest | 2 + .../tests/realloc_size_zero_supp.stderr.exp | 0 .../tests/realloc_size_zero_supp.stdout.exp | 2 + memcheck/tests/realloc_size_zero_supp.vgtest | 2 + .../tests/realloc_size_zero_yes.stderr.exp | 7 ++ .../tests/realloc_size_zero_yes.stdout.exp | 2 + memcheck/tests/realloc_size_zero_yes.vgtest | 2 + none/tests/cmdline1.stdout.exp | 4 ++ none/tests/cmdline1.stdout.exp-non-linux | 4 ++ none/tests/cmdline2.stdout.exp | 4 ++ none/tests/cmdline2.stdout.exp-non-linux | 4 ++ 51 files changed, 457 insertions(+), 64 deletions(-) create mode 100644 memcheck/tests/realloc_size_zero.c create mode 100644 memcheck/tests/realloc_size_zero.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero.stdout.exp-glibc create mode 100644 memcheck/tests/realloc_size_zero.stdout.exp-other create mode 100644 memcheck/tests/realloc_size_zero.supp create mode 100644 memcheck/tests/realloc_size_zero.vgtest create mode 100644 memcheck/tests/realloc_size_zero_mismatch.cpp create mode 100644 memcheck/tests/realloc_size_zero_mismatch.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero_mismatch.stdout.exp create mode 100644 memcheck/tests/realloc_size_zero_mismatch.vgtest create mode 100644 memcheck/tests/realloc_size_zero_no.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero_no.stdout.exp create mode 100644 memcheck/tests/realloc_size_zero_no.vgtest create mode 100644 memcheck/tests/realloc_size_zero_off.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero_off.stdout.exp create mode 100644 memcheck/tests/realloc_size_zero_off.vgtest create mode 100644 memcheck/tests/realloc_size_zero_supp.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero_supp.stdout.exp create mode 100644 memcheck/tests/realloc_size_zero_supp.vgtest create mode 100644 memcheck/tests/realloc_size_zero_yes.stderr.exp create mode 100644 memcheck/tests/realloc_size_zero_yes.stdout.exp create mode 100644 memcheck/tests/realloc_size_zero_yes.vgtest diff --git a/.gitignore b/.gitignore index d5e341063e..a88ab4dd43 100644 --- a/.gitignore +++ b/.gitignore @@ -947,6 +947,8 @@ /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 diff --git a/NEWS b/NEWS index bc3f4a363b..cd20f7773a 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,25 @@ AMD64/macOS 10.13 and nanoMIPS/Linux. $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: @@ -50,6 +69,13 @@ AMD64/macOS 10.13 and nanoMIPS/Linux. 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= allows to configure diff --git a/coregrind/m_main.c b/coregrind/m_main.c index b5501b5f31..0a7d81389e 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -243,6 +243,10 @@ static void usage_NORETURN ( int need_help ) " attempt to avoid expensive address-space-resync operations\n" " --max-threads= 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[] = diff --git a/coregrind/m_replacemalloc/replacemalloc_core.c b/coregrind/m_replacemalloc/replacemalloc_core.c index 4b7d8ae609..6c86a8ce17 100644 --- a/coregrind/m_replacemalloc/replacemalloc_core.c +++ b/coregrind/m_replacemalloc/replacemalloc_core.c @@ -45,6 +45,11 @@ /* 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. */ @@ -75,6 +80,7 @@ Bool VG_(replacement_malloc_process_cmd_line_option)(const HChar* arg) 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; diff --git a/coregrind/m_replacemalloc/vg_replace_malloc.c b/coregrind/m_replacemalloc/vg_replace_malloc.c index 3379aa96f7..c46e719c94 100644 --- a/coregrind/m_replacemalloc/vg_replace_malloc.c +++ b/coregrind/m_replacemalloc/vg_replace_malloc.c @@ -1460,20 +1460,16 @@ extern int *___errno (void) __attribute__((weak)); 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; \ } @@ -1487,21 +1483,16 @@ extern int *___errno (void) __attribute__((weak)); 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; \ } @@ -1514,24 +1505,17 @@ extern int *___errno (void) __attribute__((weak)); { \ 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; \ diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 788018c3e9..3062c1afc3 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -2109,6 +2109,7 @@ void do_client_request ( ThreadId tid ) 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 */ diff --git a/coregrind/pub_core_replacemalloc.h b/coregrind/pub_core_replacemalloc.h index cbf7f8ecdd..bfd137a8b2 100644 --- a/coregrind/pub_core_replacemalloc.h +++ b/coregrind/pub_core_replacemalloc.h @@ -55,6 +55,7 @@ struct vg_mallocfunc_info { 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 diff --git a/dhat/dh_main.c b/dhat/dh_main.c index 6f15ae82e3..57d94237c5 100644 --- a/dhat/dh_main.c +++ b/dhat/dh_main.c @@ -818,8 +818,11 @@ static void* dh_realloc ( ThreadId tid, void* p_old, SizeT new_szB ) 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); } diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 3a91c930fa..20886fe49e 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -1787,6 +1787,24 @@ that can report errors, e.g. Memcheck, but not Cachegrind. + + + + + + The behaviour of realloc() 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 + --realloc-zero-bytes-frees=no. + + + + diff --git a/drd/drd_malloc_wrappers.c b/drd/drd_malloc_wrappers.c index 991fb845a0..b1be605832 100644 --- a/drd/drd_malloc_wrappers.c +++ b/drd/drd_malloc_wrappers.c @@ -184,8 +184,12 @@ static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size) 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++; diff --git a/drd/tests/memory_allocation.c b/drd/tests/memory_allocation.c index d6d6388236..ec8578514d 100644 --- a/drd/tests/memory_allocation.c +++ b/drd/tests/memory_allocation.c @@ -25,13 +25,12 @@ int main() * 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; diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index 26a37ead5e..cebc2bd2a1 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -4323,12 +4323,31 @@ static void* hg_cli__realloc ( ThreadId tid, void* payloadV, SizeT new_size ) 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); diff --git a/include/pub_tool_replacemalloc.h b/include/pub_tool_replacemalloc.h index f9f39b7d64..d59027f3e2 100644 --- a/include/pub_tool_replacemalloc.h +++ b/include/pub_tool_replacemalloc.h @@ -67,6 +67,8 @@ extern Bool VG_(clo_trace_malloc); /* 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 ); diff --git a/massif/ms_main.c b/massif/ms_main.c index 1ebbe4f29f..f3500c367d 100644 --- a/massif/ms_main.c +++ b/massif/ms_main.c @@ -1268,6 +1268,20 @@ void* realloc_block ( ThreadId tid, void* p_old, SizeT new_req_szB ) 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) { diff --git a/massif/tests/realloc.post.exp b/massif/tests/realloc.post.exp index 779cfc16f7..c343c93b1b 100644 --- a/massif/tests/realloc.post.exp +++ b/massif/tests/realloc.post.exp @@ -1,6 +1,6 @@ -------------------------------------------------------------------------------- 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 -------------------------------------------------------------------------------- diff --git a/massif/tests/realloc.vgtest b/massif/tests/realloc.vgtest index bdb38d839e..01e06f0853 100644 --- a/massif/tests/realloc.vgtest +++ b/massif/tests/realloc.vgtest @@ -1,5 +1,6 @@ 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 diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml index e8f48d112d..414c3a2393 100644 --- a/memcheck/docs/mc-manual.xml +++ b/memcheck/docs/mc-manual.xml @@ -51,6 +51,11 @@ problems that are common in C and C++ programs. allocation function. + + Using a size value of 0 + with realloc. + + Memory leaks. @@ -431,6 +436,28 @@ as "silly arguments" and no back-trace was included. + +Realloc size zero + +The (ab)use or realloc to also do the job of free +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. + +For example: + + + + Memory leak detection diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c index ee5533a632..00d6ec301e 100644 --- a/memcheck/mc_errors.c +++ b/memcheck/mc_errors.c @@ -75,6 +75,7 @@ typedef Err_Leak, Err_IllegalMempool, Err_FishyValue, + Err_ReallocSizeZero, } MC_ErrorTag; @@ -159,6 +160,10 @@ struct _MC_Error { AddrInfo ai; } FreeMismatch; + struct { + AddrInfo ai; + } ReallocSizeZero; + // Call to strcpy, memcpy, etc, with overlapping blocks. struct { Addr src; // Source block @@ -714,6 +719,21 @@ void MC_(pp_Error) ( const Error* err ) } break; + case Err_ReallocSizeZero: + if (xml) { + emit( " ReallocSizeZero\n" ); + 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); + } 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)); @@ -868,6 +888,15 @@ void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc ) &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; @@ -1231,6 +1260,10 @@ UInt MC_(update_Error_extra)( const Error* err ) &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"); } @@ -1324,6 +1357,7 @@ typedef LeakSupp, // Something to be suppressed in a leak check. MempoolSupp, // Memory pool suppression. FishyValueSupp,// Fishy value suppression. + ReallocSizeZeroSupp, // realloc size 0 suppression } MC_SuppKind; @@ -1354,6 +1388,7 @@ Bool MC_(is_recognised_suppression) ( const HChar* name, Supp* su ) 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; @@ -1531,6 +1566,11 @@ Bool MC_(error_matches_suppression) ( const Error* err, const Supp* su ) supp_extra->argument_name); } + case ReallocSizeZeroSupp: { + + return (ekind == Err_ReallocSizeZero); + } + default: VG_(printf)("Error:\n" " unknown suppression type %d\n", @@ -1543,18 +1583,19 @@ Bool MC_(error_matches_suppression) ( const Error* err, const Supp* su ) 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 ) { diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index 30d0affdb2..3c1b1a7cd4 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -555,6 +555,7 @@ void MC_(record_jump_error) ( ThreadId tid, Addr a ); 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 ); @@ -727,6 +728,9 @@ extern Int MC_(clo_mc_level); /* 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 diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index 8efd7cb40c..3619dd1f92 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -6064,6 +6064,7 @@ Int MC_(clo_free_fill) = -1; 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; @@ -6256,6 +6257,8 @@ static Bool mc_process_cmd_line_options(const HChar* arg) 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) {} @@ -6320,6 +6323,7 @@ static void mc_print_usage(void) " --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" ); } diff --git a/memcheck/mc_malloc_wrappers.c b/memcheck/mc_malloc_wrappers.c index d6775bd1d3..87cf4d8f54 100644 --- a/memcheck/mc_malloc_wrappers.c +++ b/memcheck/mc_malloc_wrappers.c @@ -573,6 +573,25 @@ void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB ) 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; diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index a3b9332b53..7600981687 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -286,6 +286,21 @@ EXTRA_DIST = \ 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 \ @@ -442,6 +457,7 @@ check_PROGRAMS = \ pipe pointer-trace \ posix_memalign \ post-syscall \ + realloc_size_zero realloc_size_zero_mismatch \ realloc1 realloc2 realloc3 \ recursive-merge \ resvn_stack \ @@ -636,6 +652,8 @@ partial_load_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_USE_AFTER_FREE@ 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) diff --git a/memcheck/tests/amd64-freebsd/reallocf.c b/memcheck/tests/amd64-freebsd/reallocf.c index 043d0d6d7a..04ab4ccfce 100644 --- a/memcheck/tests/amd64-freebsd/reallocf.c +++ b/memcheck/tests/amd64-freebsd/reallocf.c @@ -10,6 +10,7 @@ int main(void) 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)); diff --git a/memcheck/tests/amd64-freebsd/reallocf.stderr.exp b/memcheck/tests/amd64-freebsd/reallocf.stderr.exp index b3e6658b78..4ec2f2ef01 100644 --- a/memcheck/tests/amd64-freebsd/reallocf.stderr.exp +++ b/memcheck/tests/amd64-freebsd/reallocf.stderr.exp @@ -10,6 +10,13 @@ LEAK SUMMARY: 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: @@ -26,9 +33,9 @@ All heap blocks were freed -- no leaks are possible 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) diff --git a/memcheck/tests/freebsd/static_allocs.stderr.exp b/memcheck/tests/freebsd/static_allocs.stderr.exp index 132976cbb6..b6e6d328d5 100644 --- a/memcheck/tests/freebsd/static_allocs.stderr.exp +++ b/memcheck/tests/freebsd/static_allocs.stderr.exp @@ -1,5 +1,15 @@ +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) diff --git a/memcheck/tests/realloc_size_zero.c b/memcheck/tests/realloc_size_zero.c new file mode 100644 index 0000000000..afe2a76680 --- /dev/null +++ b/memcheck/tests/realloc_size_zero.c @@ -0,0 +1,39 @@ +#include +#include +#include + +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); + } +} diff --git a/memcheck/tests/realloc_size_zero.stderr.exp b/memcheck/tests/realloc_size_zero.stderr.exp new file mode 100644 index 0000000000..6204deecdd --- /dev/null +++ b/memcheck/tests/realloc_size_zero.stderr.exp @@ -0,0 +1,7 @@ +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) + diff --git a/memcheck/tests/realloc_size_zero.stdout.exp-glibc b/memcheck/tests/realloc_size_zero.stdout.exp-glibc new file mode 100644 index 0000000000..ed2bcf0cd4 --- /dev/null +++ b/memcheck/tests/realloc_size_zero.stdout.exp-glibc @@ -0,0 +1,2 @@ +p is NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero.stdout.exp-other b/memcheck/tests/realloc_size_zero.stdout.exp-other new file mode 100644 index 0000000000..103f1d5ebc --- /dev/null +++ b/memcheck/tests/realloc_size_zero.stdout.exp-other @@ -0,0 +1,2 @@ +p not NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero.supp b/memcheck/tests/realloc_size_zero.supp new file mode 100644 index 0000000000..42e5f8d811 --- /dev/null +++ b/memcheck/tests/realloc_size_zero.supp @@ -0,0 +1,7 @@ +{ + Test for realoc zero suppression + Memcheck:ReallocZero + fun:realloc + fun:main +} + diff --git a/memcheck/tests/realloc_size_zero.vgtest b/memcheck/tests/realloc_size_zero.vgtest new file mode 100644 index 0000000000..e39d9c0725 --- /dev/null +++ b/memcheck/tests/realloc_size_zero.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero +vgopts: -q diff --git a/memcheck/tests/realloc_size_zero_mismatch.cpp b/memcheck/tests/realloc_size_zero_mismatch.cpp new file mode 100644 index 0000000000..13d1e58832 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_mismatch.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +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(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(realloc(const_cast(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; +} diff --git a/memcheck/tests/realloc_size_zero_mismatch.stderr.exp b/memcheck/tests/realloc_size_zero_mismatch.stderr.exp new file mode 100644 index 0000000000..f7c535574b --- /dev/null +++ b/memcheck/tests/realloc_size_zero_mismatch.stderr.exp @@ -0,0 +1,28 @@ +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) + diff --git a/memcheck/tests/realloc_size_zero_mismatch.stdout.exp b/memcheck/tests/realloc_size_zero_mismatch.stdout.exp new file mode 100644 index 0000000000..60b2ebd9e6 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_mismatch.stdout.exp @@ -0,0 +1,2 @@ +p not nullptr after realloc 0 +p2 not nullptr after realloc 0 diff --git a/memcheck/tests/realloc_size_zero_mismatch.vgtest b/memcheck/tests/realloc_size_zero_mismatch.vgtest new file mode 100644 index 0000000000..4aad69190e --- /dev/null +++ b/memcheck/tests/realloc_size_zero_mismatch.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero_mismatch +vgopts: -q --realloc-zero-bytes-frees=no diff --git a/memcheck/tests/realloc_size_zero_no.stderr.exp b/memcheck/tests/realloc_size_zero_no.stderr.exp new file mode 100644 index 0000000000..00ffada247 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_no.stderr.exp @@ -0,0 +1,7 @@ +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:...) + ... + diff --git a/memcheck/tests/realloc_size_zero_no.stdout.exp b/memcheck/tests/realloc_size_zero_no.stdout.exp new file mode 100644 index 0000000000..103f1d5ebc --- /dev/null +++ b/memcheck/tests/realloc_size_zero_no.stdout.exp @@ -0,0 +1,2 @@ +p not NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero_no.vgtest b/memcheck/tests/realloc_size_zero_no.vgtest new file mode 100644 index 0000000000..80bf9e53d6 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_no.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero +vgopts: -q --realloc-zero-bytes-frees=no diff --git a/memcheck/tests/realloc_size_zero_off.stderr.exp b/memcheck/tests/realloc_size_zero_off.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/memcheck/tests/realloc_size_zero_off.stdout.exp b/memcheck/tests/realloc_size_zero_off.stdout.exp new file mode 100644 index 0000000000..103f1d5ebc --- /dev/null +++ b/memcheck/tests/realloc_size_zero_off.stdout.exp @@ -0,0 +1,2 @@ +p not NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero_off.vgtest b/memcheck/tests/realloc_size_zero_off.vgtest new file mode 100644 index 0000000000..a376ce755f --- /dev/null +++ b/memcheck/tests/realloc_size_zero_off.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero +vgopts: -q --realloc-zero-bytes-frees=no --show-realloc-size-zero=no diff --git a/memcheck/tests/realloc_size_zero_supp.stderr.exp b/memcheck/tests/realloc_size_zero_supp.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/memcheck/tests/realloc_size_zero_supp.stdout.exp b/memcheck/tests/realloc_size_zero_supp.stdout.exp new file mode 100644 index 0000000000..103f1d5ebc --- /dev/null +++ b/memcheck/tests/realloc_size_zero_supp.stdout.exp @@ -0,0 +1,2 @@ +p not NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero_supp.vgtest b/memcheck/tests/realloc_size_zero_supp.vgtest new file mode 100644 index 0000000000..ea8886deb1 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_supp.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero +vgopts: -q --realloc-zero-bytes-frees=no --suppressions=realloc_size_zero.supp diff --git a/memcheck/tests/realloc_size_zero_yes.stderr.exp b/memcheck/tests/realloc_size_zero_yes.stderr.exp new file mode 100644 index 0000000000..00ffada247 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_yes.stderr.exp @@ -0,0 +1,7 @@ +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:...) + ... + diff --git a/memcheck/tests/realloc_size_zero_yes.stdout.exp b/memcheck/tests/realloc_size_zero_yes.stdout.exp new file mode 100644 index 0000000000..ed2bcf0cd4 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_yes.stdout.exp @@ -0,0 +1,2 @@ +p is NULL after realloc 0 +p2 not NULL after realloc 0 diff --git a/memcheck/tests/realloc_size_zero_yes.vgtest b/memcheck/tests/realloc_size_zero_yes.vgtest new file mode 100644 index 0000000000..16310f2a95 --- /dev/null +++ b/memcheck/tests/realloc_size_zero_yes.vgtest @@ -0,0 +1,2 @@ +prog: realloc_size_zero +vgopts: -q --realloc-zero-bytes-frees=yes diff --git a/none/tests/cmdline1.stdout.exp b/none/tests/cmdline1.stdout.exp index c3e91bfcde..3b6c116094 100644 --- a/none/tests/cmdline1.stdout.exp +++ b/none/tests/cmdline1.stdout.exp @@ -154,6 +154,10 @@ usage: valgrind [options] prog-and-args attempt to avoid expensive address-space-resync operations --max-threads= 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) diff --git a/none/tests/cmdline1.stdout.exp-non-linux b/none/tests/cmdline1.stdout.exp-non-linux index c0e71fa576..e67183efe4 100644 --- a/none/tests/cmdline1.stdout.exp-non-linux +++ b/none/tests/cmdline1.stdout.exp-non-linux @@ -152,6 +152,10 @@ usage: valgrind [options] prog-and-args attempt to avoid expensive address-space-resync operations --max-threads= 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) diff --git a/none/tests/cmdline2.stdout.exp b/none/tests/cmdline2.stdout.exp index 3158c267a9..241d33afa5 100644 --- a/none/tests/cmdline2.stdout.exp +++ b/none/tests/cmdline2.stdout.exp @@ -154,6 +154,10 @@ usage: valgrind [options] prog-and-args attempt to avoid expensive address-space-resync operations --max-threads= 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) diff --git a/none/tests/cmdline2.stdout.exp-non-linux b/none/tests/cmdline2.stdout.exp-non-linux index c19fa72597..63af17bf74 100644 --- a/none/tests/cmdline2.stdout.exp-non-linux +++ b/none/tests/cmdline2.stdout.exp-non-linux @@ -152,6 +152,10 @@ usage: valgrind [options] prog-and-args attempt to avoid expensive address-space-resync operations --max-threads= 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) -- 2.47.2