Improvements in freelist handling for Memcheck.  See #250065.

(Philippe Waroquiers, philippe.waroquiers@skynet.be)

This patch provides three improvements in the way the free list is 
handled in memcheck.

First improvement: a new command line option --freelist-big-blocks
(default 1000000) specifies the size of "free list big blocks". 
Such big blocks will be put on the free list, but will be re-cycled first
(i.e. in preference to block having a smaller size).
This fixes the bug https://bugs.kde.org/show_bug.cgi?id=250065.
Technically, the freed list is divided in two lists : small
and big blocks. Blocks are first released from the big block list.

Second improvement: the blocks of the freed list are re-cycled before
a new block is malloc-ed, not after a block is freed.
This gives better error messages for dangling pointer errors
when doing many frees without doing malloc between the frees.
(this does not uses more memory).

Third improvement: a block bigger than the free list volume will be
put in the free list (till a malloc is done, so as the needed memory
is not bigger than before) but will be put at the beginning of the
free list, rather than at the end. So, allocating then freeing such a
block does not cause any blocks in the free list to be released.

Results of the improvements above, with the new regression test
memcheck/test/big_blocks_freed_list: with the patch, 7 errors
are detected, 6 are giving the (correct) allocation stack.
Without the patch, only 6 errors are detected, 5 errors without
allocation stack, 1 with a (wrong) allocation stack.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12202 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index e9d7122..52582e3 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -62,6 +62,7 @@
 	badloop.stderr.exp badloop.vgtest \
 	badpoll.stderr.exp badpoll.vgtest \
 	badrw.stderr.exp badrw.vgtest badrw.stderr.exp-s390x-mvc \
+	big_blocks_freed_list.stderr.exp big_blocks_freed_list.vgtest \
 	brk2.stderr.exp brk2.vgtest \
 	buflen_check.stderr.exp buflen_check.vgtest buflen_check.stderr.exp-kfail \
 	calloc-overflow.stderr.exp calloc-overflow.vgtest\
@@ -214,6 +215,7 @@
 	badloop \
 	badpoll \
 	badrw \
+	big_blocks_freed_list \
 	brk2 \
 	buflen_check \
 	calloc-overflow \
diff --git a/memcheck/tests/big_blocks_freed_list.c b/memcheck/tests/big_blocks_freed_list.c
new file mode 100644
index 0000000..55b1ee1
--- /dev/null
+++ b/memcheck/tests/big_blocks_freed_list.c
@@ -0,0 +1,57 @@
+#include <stdlib.h>
+/* To be run with --freelist-vol=1000000 --freelist-big-blocks=50000 */
+static void jumped(void)
+{
+   ;
+}
+int main(int argc, char *argv[])
+{
+   char *semi_big = NULL;
+   char *big = NULL;
+   char *small = NULL;
+   char *other_small = NULL;
+   int i;
+   int j;
+
+   /* Verify that access via a dangling pointer to a big block bigger than
+      the free list is found by memcheck (still on the free list). */
+   semi_big = malloc (900000);
+   big = malloc (1000001);
+   free(semi_big);
+   free(big);
+   if (big[1000] > 0x0) jumped();
+   if (semi_big[1000] > 0x0) jumped();
+
+   /* Then verify that dangling pointers for small blocks is not hampered
+      by doing big alloc/free. */
+   small = malloc (10000);
+   free(small);
+
+   /* We should still have a nice error msg for the semi_big
+      but not for the big block, which has been removed from the free list
+      with the malloc of small above. */
+   if (big[2000] > 0x0) jumped();
+   if (semi_big[2000] > 0x0) jumped();
+
+   big = NULL;
+
+   {
+      big = malloc (1000001);
+      free(big);
+      if (small[10] > 0x0) jumped();
+      
+      /* Do not common up the below in a loop. We
+         want a different error/stack trace for each of
+         these. */
+      if (big[10] > 0x0) jumped();
+   }
+   
+   
+   for (i = 0; i < 100; i++) {
+      other_small = malloc(10000);
+      for (j = 0; j < 10000; j++)
+         other_small[j] = 0x1;
+   }
+   if (small[10] > 0x0) jumped();
+   return 0;
+}
diff --git a/memcheck/tests/big_blocks_freed_list.stderr.exp b/memcheck/tests/big_blocks_freed_list.stderr.exp
new file mode 100644
index 0000000..bedbb9a
--- /dev/null
+++ b/memcheck/tests/big_blocks_freed_list.stderr.exp
@@ -0,0 +1,50 @@
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:22)
+ Address 0x........ is 1,000 bytes inside a block of size 1,000,001 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:21)
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:23)
+ Address 0x........ is 1,000 bytes inside a block of size 900,000 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:20)
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:33)
+ Address 0x........ is not stack'd, malloc'd or (recently) free'd
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:34)
+ Address 0x........ is 2,000 bytes inside a block of size 900,000 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:20)
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:41)
+ Address 0x........ is 10 bytes inside a block of size 10,000 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:28)
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:46)
+ Address 0x........ is 10 bytes inside a block of size 1,000,001 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:40)
+
+Invalid read of size 1
+   at 0x........: main (big_blocks_freed_list.c:55)
+ Address 0x........ is 10 bytes inside a block of size 10,000 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (big_blocks_freed_list.c:28)
+
+
+HEAP SUMMARY:
+    in use at exit: 1,000,000 bytes in 100 blocks
+  total heap usage: 104 allocs, 4 frees, 3,910,002 bytes allocated
+
+For a detailed leak analysis, rerun with: --leak-check=full
+
+For counts of detected and suppressed errors, rerun with: -v
+ERROR SUMMARY: 7 errors from 7 contexts (suppressed: 0 from 0)
diff --git a/memcheck/tests/big_blocks_freed_list.vgtest b/memcheck/tests/big_blocks_freed_list.vgtest
new file mode 100644
index 0000000..54e4d17
--- /dev/null
+++ b/memcheck/tests/big_blocks_freed_list.vgtest
@@ -0,0 +1,2 @@
+prog: big_blocks_freed_list
+vgopts: --freelist-vol=1000000 --freelist-big-blocks=50000
diff --git a/memcheck/tests/memalign2.vgtest b/memcheck/tests/memalign2.vgtest
index d4e6ab8..3d4cc71 100644
--- a/memcheck/tests/memalign2.vgtest
+++ b/memcheck/tests/memalign2.vgtest
@@ -1,2 +1,2 @@
 prog: memalign2
-vgopts: -q --freelist-vol=100000
+vgopts: -q --freelist-vol=100000 --freelist-big-blocks=0