]> granicus.if.org Git - gc/commitdiff
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 d669afdfc2685726f77b6db28761041606ccbd23..5a23388424d0efc77f41e7bc4d75979a1a2989e4 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)));