summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-06-07 16:08:08 +0200
committerBram Moolenaar <Bram@vim.org>2020-06-07 16:08:08 +0200
commit5b157fe2edfdce5f77080aeac2b4a03f39eb1c1a (patch)
treee2757dfeb60d92934e3cd0900d9eb43769389f56
parent673fc3e23f09095d17f0095c4323958041b2d0d2 (diff)
patch 8.2.0920: writing viminfo fails with a circular referencev8.2.0920
Problem: Writing viminfo fails with a circular reference. Solution: Use copyID to detect the cycle. (closes #6217)
-rw-r--r--src/testdir/test_viminfo.vim22
-rw-r--r--src/version.c2
-rw-r--r--src/viminfo.c30
3 files changed, 52 insertions, 2 deletions
diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim
index b4708f5f89..8650598216 100644
--- a/src/testdir/test_viminfo.vim
+++ b/src/testdir/test_viminfo.vim
@@ -91,6 +91,28 @@ func Test_global_vars()
set viminfo-=!
endfunc
+func Test_global_vars_with_circular_reference()
+ let g:MY_GLOBAL_LIST = []
+ call add(g:MY_GLOBAL_LIST, g:MY_GLOBAL_LIST)
+ let g:MY_GLOBAL_DICT = {}
+ let g:MY_GLOBAL_DICT['self'] = g:MY_GLOBAL_DICT
+
+ set viminfo='100,<50,s10,h,!,nviminfo
+ wv! Xviminfo
+ call assert_equal(v:errmsg, '')
+
+ unlet g:MY_GLOBAL_LIST
+ unlet g:MY_GLOBAL_DICT
+
+ rv! Xviminfo
+ call assert_equal(v:errmsg, '')
+ call assert_true(!exists('g:MY_GLOBAL_LIST'))
+ call assert_true(!exists('g:MY_GLOBAL_DICT'))
+
+ call delete('Xviminfo')
+ set viminfo-=!
+endfunc
+
func Test_cmdline_history()
call histdel(':')
call test_settime(11)
diff --git a/src/version.c b/src/version.c
index 36207450c2..c400df423f 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 920,
+/**/
919,
/**/
918,
diff --git a/src/viminfo.c b/src/viminfo.c
index b014d7f215..4f26348a22 100644
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -1337,8 +1337,34 @@ write_viminfo_varlist(FILE *fp)
case VAR_STRING: s = "STR"; break;
case VAR_NUMBER: s = "NUM"; break;
case VAR_FLOAT: s = "FLO"; break;
- case VAR_DICT: s = "DIC"; break;
- case VAR_LIST: s = "LIS"; break;
+ case VAR_DICT:
+ {
+ dict_T *di = this_var->di_tv.vval.v_dict;
+ int copyID = get_copyID();
+
+ s = "DIC";
+ if (di != NULL && !set_ref_in_ht(
+ &di->dv_hashtab, copyID, NULL)
+ && di->dv_copyID == copyID)
+ // has a circular reference, can't turn the
+ // value into a string
+ continue;
+ break;
+ }
+ case VAR_LIST:
+ {
+ list_T *l = this_var->di_tv.vval.v_list;
+ int copyID = get_copyID();
+
+ s = "LIS";
+ if (l != NULL && !set_ref_in_list_items(
+ l, copyID, NULL)
+ && l->lv_copyID == copyID)
+ // has a circular reference, can't turn the
+ // value into a string
+ continue;
+ break;
+ }
case VAR_BLOB: s = "BLO"; break;
case VAR_BOOL: s = "XPL"; break; // backwards compat.
case VAR_SPECIAL: s = "XPL"; break;