malloc: Do not free or realloc chunks in corrupt arenas
diff mbox

Message ID 563CDB4C.1010806@redhat.com
State New
Headers show

Commit Message

Florian Weimer Nov. 6, 2015, 4:54 p.m. UTC
I am not sure if this is an actual bug in the malloc backtrace patch, or
if this is can only happen once there are additional sources of corrupt
arenas besides the malloc backtrace path

This was found through code inspection.  I do not have a test case at
the moment.

Florian

Patch
diff mbox

2015-11-06  Florian Weimer  <fweimer@redhat.com>

	* malloc/malloc.c (__libc_free): Do not free chunks in corrupt
	arenas.
	(realloc_from_corrupt): New function.
	(__libc_realloc): Call it for corrupt arenas.

diff --git a/malloc/malloc.c b/malloc/malloc.c
index 839263e..bce94da 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2966,10 +2966,38 @@  __libc_free (void *mem)
     }
 
   ar_ptr = arena_for_chunk (p);
+
+  if (__glibc_unlikely (arena_is_corrupt (ar_ptr)))
+    /* Do nothing if the arena is known to be corrupt.  */
+    return;
+
   _int_free (ar_ptr, p, 0);
 }
 libc_hidden_def (__libc_free)
 
+/* Always make a copy to enlarge a chunk in a corrupt arena.  The
+   chunk must not have been allocated with mmap.  */
+static void *
+realloc_from_corrupt (void *oldmem, size_t bytes,
+		      INTERNAL_SIZE_T oldsize)
+{
+  /* Size adjustment for the non-mmap case.  */
+  oldsize -= SIZE_SZ;
+
+  /* Reuse the old chunk if possible.  */
+  if (oldsize >= bytes)
+    return oldmem;
+
+  void *new_mem = __libc_malloc (bytes);
+  if (__glibc_unlikely (new_mem == NULL))
+    return NULL;
+  memcpy (new_mem, old_mem, oldsize);
+
+  /* Do not free the old object because the arena is known to be
+     corrupt.  */
+  return new_mem;
+}
+
 void *
 __libc_realloc (void *oldmem, size_t bytes)
 {
@@ -3041,6 +3069,9 @@  __libc_realloc (void *oldmem, size_t bytes)
       return newmem;
     }
 
+  if (__glibc_unlikely (arena_is_corrupt (ar_ptr)))
+    return realloc_from_corrupt (oldmem, bytes, oldsize);
+
   (void) mutex_lock (&ar_ptr->mutex);
 
   newp = _int_realloc (ar_ptr, oldp, oldsize, nb);