Skip to content

Commit f7ca56f

Browse files
committed
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)
1 parent 5c60684 commit f7ca56f

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/testdir/test_vim9_class.vim

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,33 @@ func Test_class_garbagecollect()
925925
echo Point.pl Point.pd
926926
END
927927
call v9.CheckScriptSuccess(lines)
928+
929+
let lines =<< trim END
930+
vim9script
931+
932+
interface View
933+
endinterface
934+
935+
class Widget
936+
this.view: View
937+
endclass
938+
939+
class MyView implements View
940+
this.widget: Widget
941+
942+
def new()
943+
# this will result in a circular reference to this object
944+
this.widget = Widget.new(this)
945+
enddef
946+
endclass
947+
948+
var view = MyView.new()
949+
950+
# overwrite "view", will be garbage-collected next
951+
view = MyView.new()
952+
test_garbagecollect_now()
953+
END
954+
call v9.CheckScriptSuccess(lines)
928955
endfunc
929956

930957
def Test_class_function()

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1609,
698700
/**/
699701
1608,
700702
/**/

src/vim9class.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,9 @@ copy_object(typval_T *from, typval_T *to)
14971497
static void
14981498
object_clear(object_T *obj)
14991499
{
1500+
// Avoid a recursive call, it can happen if "obj" has a circular reference.
1501+
obj->obj_refcount = INT_MAX;
1502+
15001503
class_T *cl = obj->obj_class;
15011504

15021505
// the member values are just after the object structure
@@ -1619,6 +1622,8 @@ object_created(object_T *obj)
16191622
first_object = obj;
16201623
}
16211624

1625+
static object_T *next_nonref_obj = NULL;
1626+
16221627
/*
16231628
* Call this function when an object has been cleared and is about to be freed.
16241629
* It is removed from the list headed by "first_object".
@@ -1632,6 +1637,10 @@ object_cleared(object_T *obj)
16321637
obj->obj_prev_used->obj_next_used = obj->obj_next_used;
16331638
else if (first_object == obj)
16341639
first_object = obj->obj_next_used;
1640+
1641+
// update the next object to check if needed
1642+
if (obj == next_nonref_obj)
1643+
next_nonref_obj = obj->obj_next_used;
16351644
}
16361645

16371646
/*
@@ -1641,11 +1650,10 @@ object_cleared(object_T *obj)
16411650
object_free_nonref(int copyID)
16421651
{
16431652
int did_free = FALSE;
1644-
object_T *next_obj;
16451653

1646-
for (object_T *obj = first_object; obj != NULL; obj = next_obj)
1654+
for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
16471655
{
1648-
next_obj = obj->obj_next_used;
1656+
next_nonref_obj = obj->obj_next_used;
16491657
if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
16501658
{
16511659
// Free the object and items it contains.
@@ -1654,6 +1662,7 @@ object_free_nonref(int copyID)
16541662
}
16551663
}
16561664

1665+
next_nonref_obj = NULL;
16571666
return did_free;
16581667
}
16591668

0 commit comments

Comments
 (0)