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