]> granicus.if.org Git - vim/commitdiff
patch 9.0.1052: using freed memory on exit when EXITFREE is defined v9.0.1052
authorBram Moolenaar <Bram@vim.org>
Tue, 13 Dec 2022 13:42:37 +0000 (13:42 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 13 Dec 2022 13:42:37 +0000 (13:42 +0000)
Problem:    Using freed memory on exit when EXITFREE is defined.
Solution:   Make a deep copy of the type.  Make sure TTFLAG_STATIC is not set
            in the copy.

src/version.c
src/vim9type.c

index 5248bcf503a944f261a7e046d1c1387c958f9cc5..362cd43a34ec44dbe60aa93c5ff965e4a8aa93e4 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1052,
 /**/
     1051,
 /**/
index 5d37ac5dce0518850226a84fcfdd6c88e4f10f81..2d55cf224219ae74a10d1f178c89b7c6536a1e75 100644 (file)
@@ -57,6 +57,7 @@ copy_type(type_T *type, garray_T *type_gap)
     if (copy == NULL)
        return type;
     *copy = *type;
+    copy->tt_flags &= ~TTFLAG_STATIC;
 
     if (type->tt_args != NULL
           && func_type_add_arg_types(copy, type->tt_argcount, type_gap) == OK)
@@ -66,6 +67,54 @@ copy_type(type_T *type, garray_T *type_gap)
     return copy;
 }
 
+/*
+ * Inner part of copy_type_deep().
+ * When allocation fails returns "type".
+ */
+    static type_T *
+copy_type_deep_rec(type_T *type, garray_T *type_gap, garray_T *seen_types)
+{
+    for (int i = 0; i < seen_types->ga_len; ++i)
+       if (((type_T **)seen_types->ga_data)[i * 2] == type)
+           // seen this type before, return the copy we made
+           return ((type_T **)seen_types->ga_data)[i * 2 + 1];
+
+    type_T *copy = copy_type(type, type_gap);
+    if (ga_grow(seen_types, 1) == FAIL)
+       return copy;
+    ((type_T **)seen_types->ga_data)[seen_types->ga_len * 2] = type;
+    ((type_T **)seen_types->ga_data)[seen_types->ga_len * 2 + 1] = copy;
+    ++seen_types->ga_len;
+
+    if (copy->tt_member != NULL)
+       copy->tt_member = copy_type_deep_rec(copy->tt_member,
+                                                        type_gap, seen_types);
+
+    if (type->tt_args != NULL)
+       for (int i = 0; i < type->tt_argcount; ++i)
+           copy->tt_args[i] = copy_type_deep_rec(copy->tt_args[i],
+                                                        type_gap, seen_types);
+
+    return copy;
+}
+
+/*
+ * Make a deep copy of "type".
+ * When allocation fails returns "type".
+ */
+    static type_T *
+copy_type_deep(type_T *type, garray_T *type_gap)
+{
+    garray_T seen_types;
+    // stores type pairs : a type we have seen and the copy used
+    ga_init2(&seen_types, sizeof(type_T *) * 2, 20);
+
+    type_T *res = copy_type_deep_rec(type, type_gap, &seen_types);
+
+    ga_clear(&seen_types);
+    return res;
+}
+
     void
 clear_type_list(garray_T *gap)
 {
@@ -404,7 +453,7 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int flags)
                                           || (flags & TVTT_MORE_SPECIFIC) == 0
                                           || l->lv_type->tt_member != &t_any))
            // make a copy, lv_type may be freed if the list is freed
-           return copy_type(l->lv_type, type_gap);
+           return copy_type_deep(l->lv_type, type_gap);
        if (l->lv_first == &range_list_item)
            return &t_list_number;
        if (l->lv_copyID == copyID)