]> 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, 16 Mar 2016 20:48:55 +0000 (23:48 +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 720e90eb273c0f182383b6a4725762b5dbff07a2..94829a52ee60c4fd0c8a784232a6699f9e6ac82b 100644 (file)
--- a/mark.c
+++ b/mark.c
@@ -767,6 +767,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)));