077c9d
commit b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c
077c9d
Author: Istvan Kurucsai <pistukem@gmail.com>
077c9d
Date:   Tue Jan 16 14:54:32 2018 +0100
077c9d
077c9d
    malloc: Additional checks for unsorted bin integrity I.
077c9d
    
077c9d
    On Thu, Jan 11, 2018 at 3:50 PM, Florian Weimer <fweimer@redhat.com> wrote:
077c9d
    > On 11/07/2017 04:27 PM, Istvan Kurucsai wrote:
077c9d
    >>
077c9d
    >> +          next = chunk_at_offset (victim, size);
077c9d
    >
077c9d
    >
077c9d
    > For new code, we prefer declarations with initializers.
077c9d
    
077c9d
    Noted.
077c9d
    
077c9d
    >> +          if (__glibc_unlikely (chunksize_nomask (victim) <= 2 * SIZE_SZ)
077c9d
    >> +              || __glibc_unlikely (chunksize_nomask (victim) >
077c9d
    >> av->system_mem))
077c9d
    >> +            malloc_printerr("malloc(): invalid size (unsorted)");
077c9d
    >> +          if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)
077c9d
    >> +              || __glibc_unlikely (chunksize_nomask (next) >
077c9d
    >> av->system_mem))
077c9d
    >> +            malloc_printerr("malloc(): invalid next size (unsorted)");
077c9d
    >> +          if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) !=
077c9d
    >> size))
077c9d
    >> +            malloc_printerr("malloc(): mismatching next->prev_size
077c9d
    >> (unsorted)");
077c9d
    >
077c9d
    >
077c9d
    > I think this check is redundant because prev_size (next) and chunksize
077c9d
    > (victim) are loaded from the same memory location.
077c9d
    
077c9d
    I'm fairly certain that it compares mchunk_size of victim against
077c9d
    mchunk_prev_size of the next chunk, i.e. the size of victim in its
077c9d
    header and footer.
077c9d
    
077c9d
    >> +          if (__glibc_unlikely (bck->fd != victim)
077c9d
    >> +              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
077c9d
    >> +            malloc_printerr("malloc(): unsorted double linked list
077c9d
    >> corrupted");
077c9d
    >> +          if (__glibc_unlikely (prev_inuse(next)))
077c9d
    >> +            malloc_printerr("malloc(): invalid next->prev_inuse
077c9d
    >> (unsorted)");
077c9d
    >
077c9d
    >
077c9d
    > There's a missing space after malloc_printerr.
077c9d
    
077c9d
    Noted.
077c9d
    
077c9d
    > Why do you keep using chunksize_nomask?  We never investigated why the
077c9d
    > original code uses it.  It may have been an accident.
077c9d
    
077c9d
    You are right, I don't think it makes a difference in these checks. So
077c9d
    the size local can be reused for the checks against victim. For next,
077c9d
    leaving it as such avoids the masking operation.
077c9d
    
077c9d
    > Again, for non-main arenas, the checks against av->system_mem could be made
077c9d
    > tighter (against the heap size).  Maybe you could put the condition into a
077c9d
    > separate inline function?
077c9d
    
077c9d
    We could also do a chunk boundary check similar to what I proposed in
077c9d
    the thread for the first patch in the series to be even more strict.
077c9d
    I'll gladly try to implement either but believe that refining these
077c9d
    checks would bring less benefits than in the case of the top chunk.
077c9d
    Intra-arena or intra-heap overlaps would still be doable here with
077c9d
    unsorted chunks and I don't see any way to counter that besides more
077c9d
    generic measures like randomizing allocations and your metadata
077c9d
    encoding patches.
077c9d
    
077c9d
    I've attached a revised version with the above comments incorporated
077c9d
    but without the refined checks.
077c9d
    
077c9d
    Thanks,
077c9d
    Istvan
077c9d
    
077c9d
    From a12d5d40fd7aed5fa10fc444dcb819947b72b315 Mon Sep 17 00:00:00 2001
077c9d
    From: Istvan Kurucsai <pistukem@gmail.com>
077c9d
    Date: Tue, 16 Jan 2018 14:48:16 +0100
077c9d
    Subject: [PATCH v2 1/1] malloc: Additional checks for unsorted bin integrity
077c9d
     I.
077c9d
    
077c9d
    Ensure the following properties of chunks encountered during binning:
077c9d
    - victim chunk has reasonable size
077c9d
    - next chunk has reasonable size
077c9d
    - next->prev_size == victim->size
077c9d
    - valid double linked list
077c9d
    - PREV_INUSE of next chunk is unset
077c9d
    
077c9d
        * malloc/malloc.c (_int_malloc): Additional binning code checks.
077c9d
077c9d
diff --git a/malloc/malloc.c b/malloc/malloc.c
077c9d
index d8d4581a9dcea80a..dad0e73735789530 100644
077c9d
--- a/malloc/malloc.c
077c9d
+++ b/malloc/malloc.c
077c9d
@@ -3724,11 +3724,22 @@ _int_malloc (mstate av, size_t bytes)
077c9d
       while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
077c9d
         {
077c9d
           bck = victim->bk;
077c9d
-          if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
077c9d
-              || __builtin_expect (chunksize_nomask (victim)
077c9d
-				   > av->system_mem, 0))
077c9d
-            malloc_printerr ("malloc(): memory corruption");
077c9d
           size = chunksize (victim);
077c9d
+          mchunkptr next = chunk_at_offset (victim, size);
077c9d
+
077c9d
+          if (__glibc_unlikely (size <= 2 * SIZE_SZ)
077c9d
+              || __glibc_unlikely (size > av->system_mem))
077c9d
+            malloc_printerr ("malloc(): invalid size (unsorted)");
077c9d
+          if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)
077c9d
+              || __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
077c9d
+            malloc_printerr ("malloc(): invalid next size (unsorted)");
077c9d
+          if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
077c9d
+            malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
077c9d
+          if (__glibc_unlikely (bck->fd != victim)
077c9d
+              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
077c9d
+            malloc_printerr ("malloc(): unsorted double linked list corrupted");
077c9d
+          if (__glibc_unlikely (prev_inuse(next)))
077c9d
+            malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
077c9d
 
077c9d
           /*
077c9d
              If a small request, try to use last remainder if it is the