]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Bug 436413 - Warn about realloc of size zero
authorPaul Floyd <pjfloyd@wanadoo.fr>
Fri, 10 Mar 2023 20:55:14 +0000 (21:55 +0100)
committerPaul Floyd <pjfloyd@wanadoo.fr>
Fri, 10 Mar 2023 20:55:14 +0000 (21:55 +0100)
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.

51 files changed:
.gitignore
NEWS
coregrind/m_main.c
coregrind/m_replacemalloc/replacemalloc_core.c
coregrind/m_replacemalloc/vg_replace_malloc.c
coregrind/m_scheduler/scheduler.c
coregrind/pub_core_replacemalloc.h
dhat/dh_main.c
docs/xml/manual-core.xml
drd/drd_malloc_wrappers.c
drd/tests/memory_allocation.c
helgrind/hg_main.c
include/pub_tool_replacemalloc.h
massif/ms_main.c
massif/tests/realloc.post.exp
massif/tests/realloc.vgtest
memcheck/docs/mc-manual.xml
memcheck/mc_errors.c
memcheck/mc_include.h
memcheck/mc_main.c
memcheck/mc_malloc_wrappers.c
memcheck/tests/Makefile.am
memcheck/tests/amd64-freebsd/reallocf.c
memcheck/tests/amd64-freebsd/reallocf.stderr.exp
memcheck/tests/freebsd/static_allocs.stderr.exp
memcheck/tests/realloc_size_zero.c [new file with mode: 0644]
memcheck/tests/realloc_size_zero.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero.stdout.exp-glibc [new file with mode: 0644]
memcheck/tests/realloc_size_zero.stdout.exp-other [new file with mode: 0644]
memcheck/tests/realloc_size_zero.supp [new file with mode: 0644]
memcheck/tests/realloc_size_zero.vgtest [new file with mode: 0644]
memcheck/tests/realloc_size_zero_mismatch.cpp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_mismatch.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_mismatch.stdout.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_mismatch.vgtest [new file with mode: 0644]
memcheck/tests/realloc_size_zero_no.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_no.stdout.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_no.vgtest [new file with mode: 0644]
memcheck/tests/realloc_size_zero_off.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_off.stdout.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_off.vgtest [new file with mode: 0644]
memcheck/tests/realloc_size_zero_supp.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_supp.stdout.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_supp.vgtest [new file with mode: 0644]
memcheck/tests/realloc_size_zero_yes.stderr.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_yes.stdout.exp [new file with mode: 0644]
memcheck/tests/realloc_size_zero_yes.vgtest [new file with mode: 0644]
none/tests/cmdline1.stdout.exp
none/tests/cmdline1.stdout.exp-non-linux
none/tests/cmdline2.stdout.exp
none/tests/cmdline2.stdout.exp-non-linux

index d5e341063eab9dc99e8e70a1e037579179de2ce9..a88ab4dd43b5f9c60fb954721e2d352a0e254db7 100644 (file)
 /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 bc3f4a363b9ca8a6a96459169bc9c46c013b9e19..cd20f7773a0d3fc9c1a95bf347a5beff915c097b 100644 (file)
--- 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=<number> allows to configure
index b5501b5f313293318c49549f70d6baac2e2fa6ea..0a7d81389ebcad085de506a29dedb82d55a3c501 100644 (file)
@@ -243,6 +243,10 @@ static void usage_NORETURN ( int need_help )
 "              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[] =
index 4b7d8ae609e36a9d6a916fc0981d232931e97d1b..6c86a8ce1744f0ca45bc1cb2605628d94bd18799 100644 (file)
 
 /* 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;
 
index 3379aa96f785274a998878b4099be55b4a4cf1f1..c46e719c94ac1f362925f9d268a8f86ec1f70a09 100644 (file)
@@ -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; \
index 788018c3e957900668c69a836b9f164a4aa9d9d3..3062c1afc30b818347e836e9073a070bc89f30da 100644 (file)
@@ -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 */
 
index cbf7f8ecdd4a47de1a93735d57a5a94468309498..bfd137a8b229eff8339073f87a5370d0f8b2b41a 100644 (file)
@@ -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
index 6f15ae82e35bf2f1aa4f8ceba5573b80474bee80..57d94237c5b8261ac419abf1db90ba2ad8202b52 100644 (file)
@@ -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);
 }
index 3a91c930fa4917cc292ae1fea452af06493936db..20886fe49e538c95f84112065d9677effbcda006 100644 (file)
@@ -1787,6 +1787,24 @@ that can report errors, e.g. Memcheck, but not Cachegrind.</para>
     </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 -->
 
index 991fb845a02a559fb54e4b94f135615b5536f16c..b1be605832f0ebc5d85d96160656b80149467059 100644 (file)
@@ -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++;
index d6d63882363bf7a90e2a9522106c64888ee2d567..ec8578514d96c102b7d05723deb8442e8b3d6b1a 100644 (file)
@@ -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;
index 26a37ead5ec4f64a3eff74743265767da04c2f04..cebc2bd2a17f508a4789d79aa2a2107da961c0da 100644 (file)
@@ -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);
index f9f39b7d64dcbe0e973add046067f181e57df9cb..d59027f3e27f2d3ec7bc4f8160ea11a5efcaea99 100644 (file)
@@ -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 );
 
index 1ebbe4f29f3b5e8e107a03c379d9e33189fe8fa9..f3500c367d4adebdebbfa10d783fa43e29225790 100644 (file)
@@ -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) {
index 779cfc16f7502ab1f86d271638f673edfa1ea53e..c343c93b1b6e8dff4efa5e15246eac2abac5c2e3 100644 (file)
@@ -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
 --------------------------------------------------------------------------------
 
index bdb38d839e9a90f50a20600bcb8c7b0a937cbbc9..01e06f08539ed65fd9a5699cf61f6a426c0ab02d 100644 (file)
@@ -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
index e8f48d112d9767e086bb4b84d7ce0da642805668..414c3a2393615f42edf731de58fe86b484cc58dd 100644 (file)
@@ -51,6 +51,11 @@ problems that are common in C and C++ programs.</para>
     allocation function.</para>
   </listitem>
 
+  <listitem>
+    <para>Using a <computeroutput>size</computeroutput> value of 0
+    with realloc.</para>
+  </listitem>
+
   <listitem>
     <para>Memory leaks.</para>
   </listitem>
@@ -431,6 +436,28 @@ as "silly arguments" and no back-trace was included.
 
 </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>
index ee5533a632232589e8a8adaaea7d1f3c50f54212..00d6ec301eb1ee4cff2150cf2553aa4caf77e941 100644 (file)
@@ -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( "  <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));
@@ -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 ) {
index 30d0affdb2a73cb74d685106f219c9c08d7bf0ef..3c1b1a7cd410aaa92dad60ab94b3db35904d30c1 100644 (file)
@@ -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
index 8efd7cb40c6e867a89aa1c88a08f427266699b79..3619dd1f92026cc26c6a8e1132fec69f42a6cbe5 100644 (file)
@@ -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"
    );
 }
 
index d6775bd1d3815da9ce8d3eabc223fabd7068ea72..87cf4d8f54a1a825f11207e54eab3605158127de 100644 (file)
@@ -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;
index a3b9332b533114246f8b5ac775553310f0fbf011..7600981687e734b57d01c4d296beadcf50668a21 100644 (file)
@@ -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)
index 043d0d6d7a8b6a6c4f3825758ae8f09a767d5d38..04ab4ccfcee2fe7169c198d10dce5b03cfef2285 100644 (file)
@@ -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));
index b3e6658b783ff018f38cf87540da77d069f7c3c7..4ec2f2ef019369c4a26fc9b3bc0347e9fade9a3e 100644 (file)
@@ -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)
index 132976cbb6f9ac40f7cf640c872d5d3b04d588ed..b6e6d328d5f0e914213611a1e9dfc04c99239ee6 100644 (file)
@@ -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 (file)
index 0000000..afe2a76
--- /dev/null
@@ -0,0 +1,39 @@
+#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);
+   }
+}
diff --git a/memcheck/tests/realloc_size_zero.stderr.exp b/memcheck/tests/realloc_size_zero.stderr.exp
new file mode 100644 (file)
index 0000000..6204dee
--- /dev/null
@@ -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 (file)
index 0000000..ed2bcf0
--- /dev/null
@@ -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 (file)
index 0000000..103f1d5
--- /dev/null
@@ -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 (file)
index 0000000..42e5f8d
--- /dev/null
@@ -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 (file)
index 0000000..e39d9c0
--- /dev/null
@@ -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 (file)
index 0000000..13d1e58
--- /dev/null
@@ -0,0 +1,41 @@
+#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;
+}
diff --git a/memcheck/tests/realloc_size_zero_mismatch.stderr.exp b/memcheck/tests/realloc_size_zero_mismatch.stderr.exp
new file mode 100644 (file)
index 0000000..f7c5355
--- /dev/null
@@ -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 (file)
index 0000000..60b2ebd
--- /dev/null
@@ -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 (file)
index 0000000..4aad691
--- /dev/null
@@ -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 (file)
index 0000000..00ffada
--- /dev/null
@@ -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 (file)
index 0000000..103f1d5
--- /dev/null
@@ -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 (file)
index 0000000..80bf9e5
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/realloc_size_zero_off.stdout.exp b/memcheck/tests/realloc_size_zero_off.stdout.exp
new file mode 100644 (file)
index 0000000..103f1d5
--- /dev/null
@@ -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 (file)
index 0000000..a376ce7
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/realloc_size_zero_supp.stdout.exp b/memcheck/tests/realloc_size_zero_supp.stdout.exp
new file mode 100644 (file)
index 0000000..103f1d5
--- /dev/null
@@ -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 (file)
index 0000000..ea8886d
--- /dev/null
@@ -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 (file)
index 0000000..00ffada
--- /dev/null
@@ -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 (file)
index 0000000..ed2bcf0
--- /dev/null
@@ -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 (file)
index 0000000..16310f2
--- /dev/null
@@ -0,0 +1,2 @@
+prog: realloc_size_zero
+vgopts: -q --realloc-zero-bytes-frees=yes
index c3e91bfcdef05d1cc81072eed60895455bb2af03..3b6c1160941aa565d2d0f7be17c5dbf19adafdd9 100644 (file)
@@ -154,6 +154,10 @@ usage: valgrind [options] prog-and-args
               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)
index c0e71fa5760e93cdc5ded336a373b7ce1a505701..e67183efe40261f4c4e817b4ea41233ae985c452 100644 (file)
@@ -152,6 +152,10 @@ usage: valgrind [options] prog-and-args
               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)
index 3158c267a90b216bc32ccf7895a5bb11f312f9ac..241d33afa52fe802c65b6118d682e54dbba626ee 100644 (file)
@@ -154,6 +154,10 @@ usage: valgrind [options] prog-and-args
               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)
index c19fa7259739b7bde4bbca05c660b4e3d2f20f99..63af17bf74ab17aa89e6b0daaf8baf798a555c15 100644 (file)
@@ -152,6 +152,10 @@ usage: valgrind [options] prog-and-args
               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)