0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 9.0.1609: crash when an object indirectly references itself

Problem:    Crash when an object indirectly references itself.
Solution:   Avoid clearing an object while it is already being cleared.
            (closes #12494)
This commit is contained in:
Bram Moolenaar
2023-06-05 16:53:25 +01:00
parent 5c606846b9
commit f7ca56f719
3 changed files with 41 additions and 3 deletions

View File

@@ -925,6 +925,33 @@ func Test_class_garbagecollect()
echo Point.pl Point.pd echo Point.pl Point.pd
END END
call v9.CheckScriptSuccess(lines) call v9.CheckScriptSuccess(lines)
let lines =<< trim END
vim9script
interface View
endinterface
class Widget
this.view: View
endclass
class MyView implements View
this.widget: Widget
def new()
# this will result in a circular reference to this object
this.widget = Widget.new(this)
enddef
endclass
var view = MyView.new()
# overwrite "view", will be garbage-collected next
view = MyView.new()
test_garbagecollect_now()
END
call v9.CheckScriptSuccess(lines)
endfunc endfunc
def Test_class_function() def Test_class_function()

View File

@@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1609,
/**/ /**/
1608, 1608,
/**/ /**/

View File

@@ -1497,6 +1497,9 @@ copy_object(typval_T *from, typval_T *to)
static void static void
object_clear(object_T *obj) object_clear(object_T *obj)
{ {
// Avoid a recursive call, it can happen if "obj" has a circular reference.
obj->obj_refcount = INT_MAX;
class_T *cl = obj->obj_class; class_T *cl = obj->obj_class;
// the member values are just after the object structure // the member values are just after the object structure
@@ -1619,6 +1622,8 @@ object_created(object_T *obj)
first_object = obj; first_object = obj;
} }
static object_T *next_nonref_obj = NULL;
/* /*
* Call this function when an object has been cleared and is about to be freed. * Call this function when an object has been cleared and is about to be freed.
* It is removed from the list headed by "first_object". * It is removed from the list headed by "first_object".
@@ -1632,6 +1637,10 @@ object_cleared(object_T *obj)
obj->obj_prev_used->obj_next_used = obj->obj_next_used; obj->obj_prev_used->obj_next_used = obj->obj_next_used;
else if (first_object == obj) else if (first_object == obj)
first_object = obj->obj_next_used; first_object = obj->obj_next_used;
// update the next object to check if needed
if (obj == next_nonref_obj)
next_nonref_obj = obj->obj_next_used;
} }
/* /*
@@ -1641,11 +1650,10 @@ object_cleared(object_T *obj)
object_free_nonref(int copyID) object_free_nonref(int copyID)
{ {
int did_free = FALSE; int did_free = FALSE;
object_T *next_obj;
for (object_T *obj = first_object; obj != NULL; obj = next_obj) for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
{ {
next_obj = obj->obj_next_used; next_nonref_obj = obj->obj_next_used;
if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
{ {
// Free the object and items it contains. // Free the object and items it contains.
@@ -1654,6 +1662,7 @@ object_free_nonref(int copyID)
} }
} }
next_nonref_obj = NULL;
return did_free; return did_free;
} }