Skip GC_DS_PER_OBJECT objects with negative descriptor in GC_mark_from
authorNiklas Therning <niklas@therning.org>
Wed, 17 Feb 2016 13:16:01 +0000 (14:16 +0100)
committerIvan Maidanski <ivmai@mail.ru>
Wed, 24 Feb 2016 07:32:39 +0000 (10:32 +0300)
Added a check in GC_mark_from() for GC_DS_PER_OBJECT objects with
negative descriptors to prevent mistaking the free list pointers in
free objects for being type descriptor pointers.  If the specified
descriptor offset was larger than the object size this could lead to
arbitrary data from allocated objects being misinterpreted as
descriptors and the process crashing.

* mark.c (GC_mark_from): In case of GC_DS_PER_OBJECT, skip objects
those descriptor is outside object.

mark.c

diff --git a/mark.c b/mark.c
index d669afd..5a23388 100644 (file)
--- a/mark.c
+++ b/mark.c
@@ -757,6 +757,28 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
                 mark_stack_top--;
                 continue;
             }
+            if ((GC_word)(type_descr) >= (GC_word)GC_least_plausible_heap_addr
+                    && (GC_word)(type_descr)
+                        <= (GC_word)GC_greatest_plausible_heap_addr) {
+                /* type_descr looks like a pointer into the heap.       */
+                /* It could still be the link pointer in a free list    */
+                /* though.  That's not a problem as long as the offset  */
+                /* of the actual descriptor in the pointed to object is */
+                /* within the same object.  In that case it will either */
+                /* point at the next free object in the list (if offset */
+                /* is 0) or be zeroed (which we check for below,        */
+                /* descr == 0).  If the offset is larger than the       */
+                /* objects in the block type_descr points to it cannot  */
+                /* be a proper pointer.                                 */
+                word offset = ~(descr + (GC_INDIR_PER_OBJ_BIAS
+                                         - GC_DS_PER_OBJECT - 1));
+                hdr *hhdr;
+                GET_HDR(type_descr, hhdr);
+                if (NULL == hhdr || hhdr->hb_sz - sizeof(word) < offset) {
+                    mark_stack_top--;
+                    continue;
+                }
+            }
             descr = *(word *)(type_descr
                               - (descr + (GC_INDIR_PER_OBJ_BIAS
                                           - GC_DS_PER_OBJECT)));