Mercurial > vim
changeset 32555:5c4c2d82d751 v9.0.1609
patch 9.0.1609: crash when an object indirectly references itself
Commit: https://github.com/vim/vim/commit/f7ca56f7193f8b383be43f1f6b3a6c6ca77b4233
Author: Bram Moolenaar <Bram@vim.org>
Date: Mon Jun 5 16:53:25 2023 +0100
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)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Mon, 05 Jun 2023 18:00:04 +0200 |
parents | 5178a7e13050 |
children | 6059fb171e40 |
files | src/testdir/test_vim9_class.vim src/version.c src/vim9class.c |
diffstat | 3 files changed, 41 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -925,6 +925,33 @@ func Test_class_garbagecollect() echo Point.pl Point.pd END 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 def Test_class_function()
--- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1609, +/**/ 1608, /**/ 1607,
--- a/src/vim9class.c +++ b/src/vim9class.c @@ -1497,6 +1497,9 @@ copy_object(typval_T *from, typval_T *to static void 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; // the member values are just after the object structure @@ -1619,6 +1622,8 @@ object_created(object_T *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. * 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; else if (first_object == obj) 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) { 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)) { // Free the object and items it contains. @@ -1654,6 +1662,7 @@ object_free_nonref(int copyID) } } + next_nonref_obj = NULL; return did_free; }