Fix for crash in eval.c

1 view
Skip to first unread message

Nico Weber

unread,
May 2, 2009, 7:06:13 PM5/2/09
to vim...@googlegroups.com
Hi,

Valgrind memory checker finds several errors in vim-7.2 (patches
1-148) with the reproduction steps described at http://groups.google.com/group/vim_mac/browse_thread/thread/4e0149ff4f84e3d3
:

==33469== Conditional jump or move depends on uninitialised value(s)
==33469== at 0x437EA: can_free_funccal (eval.c:21449)
==33469== by 0x2D213: garbage_collect (eval.c:6591)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469== by 0x9F674: main_loop (main.c:1255)
==33469== by 0x9F167: main (main.c:1002)
==33469==
==33469== Conditional jump or move depends on uninitialised value(s)
==33469== at 0x437F8: can_free_funccal (eval.c:21449)
==33469== by 0x2D213: garbage_collect (eval.c:6591)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469== by 0x9F674: main_loop (main.c:1255)
==33469== by 0x9F167: main (main.c:1002)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2D451: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x4382B: free_funccal (eval.c:21466)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== Address 0x7c290f0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid write of size 4
==33469== at 0x2D459: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x4382B: free_funccal (eval.c:21466)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== Address 0x7c290f0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2D45E: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x4382B: free_funccal (eval.c:21466)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== Address 0x7c290f0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2BFD3: list_unref (eval.c:5761)
==33469== by 0x3E4CD: clear_tv (eval.c:18554)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c34a58 is 8 bytes inside a block of size 48 free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2C0AE: list_free (eval.c:5793)
==33469== by 0x2D1D1: garbage_collect (eval.c:6579)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid write of size 4
==33469== at 0x2BFDC: list_unref (eval.c:5761)
==33469== by 0x3E4CD: clear_tv (eval.c:18554)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c34a58 is 8 bytes inside a block of size 48 free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2C0AE: list_free (eval.c:5793)
==33469== by 0x2D1D1: garbage_collect (eval.c:6579)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2BFE2: list_unref (eval.c:5761)
==33469== by 0x3E4CD: clear_tv (eval.c:18554)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c34a58 is 8 bytes inside a block of size 48 free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2C0AE: list_free (eval.c:5793)
==33469== by 0x2D1D1: garbage_collect (eval.c:6579)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2D451: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c33af0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid write of size 4
==33469== at 0x2D459: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c33af0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)
==33469==
==33469== Invalid read of size 4
==33469== at 0x2D45E: dict_unref (eval.c:6709)
==33469== by 0x3E4E7: clear_tv (eval.c:18558)
==33469== by 0x3F09B: vars_clear_ext (eval.c:18994)
==33469== by 0x3F02F: vars_clear (eval.c:18966)
==33469== by 0x4383B: free_funccal (eval.c:21469)
==33469== by 0x2D240: garbage_collect (eval.c:6595)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== Address 0x7c33af0 is 0 bytes inside a block of size 176
free'd
==33469== at 0x25661B: free (vg_replace_malloc.c:322)
==33469== by 0xCDBDC: vim_free (misc2.c:1638)
==33469== by 0x2D59B: dict_free (eval.c:6753)
==33469== by 0x2D176: garbage_collect (eval.c:6559)
==33469== by 0x8D92E: before_blocking (getchar.c:1473)
==33469== by 0x10764F: mch_inchar (os_unix.c:385)
==33469== by 0x176A06: ui_inchar (ui.c:193)
==33469== by 0x8FFD1: inchar (getchar.c:2959)
==33469== by 0x8FB64: vgetorpeek (getchar.c:2735)
==33469== by 0x8DAA3: vgetc (getchar.c:1552)
==33469== by 0x8E05D: safe_vgetc (getchar.c:1757)
==33469== by 0xDC89D: normal_cmd (normal.c:653)

On OS X, this leads to a crash. The problem was found by Meikel
Brandmeyer.

The attached patch fixes this.


There were two problems:

1. Without

dict->dv_copyID = 0;

the l_vars and l_avars dicts in funccall_T have no initialized
dv_copyID at the end of call_user_func, and hence valgrind complains
when garbage_collect walks the previous_funccal list

2. Double free. garbage_collect() frees dictionaries and lists,
including the ones belonging to functions in the previous_funccal
list. When the previou_funccal list is freed, these dictionaries and
lists are freed a second time.


Nico

eval_crash.patch

Dominique Pellé

unread,
May 3, 2009, 3:03:15 AM5/3/09
to vim...@googlegroups.com
Nico Weber wrote:

> Hi,
>
> Valgrind memory checker finds several errors in vim-7.2 (patches
> 1-148) with the reproduction steps described at http://groups.google.com/group/vim_mac/browse_thread/thread/4e0149ff4f84e3d3
>  :
>
> ==33469== Conditional jump or move depends on uninitialised value(s)
> ==33469==    at 0x437EA: can_free_funccal (eval.c:21449)
> ==33469==    by 0x2D213: garbage_collect (eval.c:6591)
> ==33469==    by 0x8D92E: before_blocking (getchar.c:1473)
> ==33469==    by 0x10764F: mch_inchar (os_unix.c:385)
> ==33469==    by 0x176A06: ui_inchar (ui.c:193)
> ==33469==    by 0x8FFD1: inchar (getchar.c:2959)
> ==33469==    by 0x8FB64: vgetorpeek (getchar.c:2735)
> ==33469==    by 0x8DAA3: vgetc (getchar.c:1552)
> ==33469==    by 0x8E05D: safe_vgetc (getchar.c:1757)
> ==33469==    by 0xDC89D: normal_cmd (normal.c:653)
> ==33469==    by 0x9F674: main_loop (main.c:1255)
> ==33469==    by 0x9F167: main (main.c:1002)

...snip...

> On OS X, this leads to a crash. The problem was found by Meikel
> Brandmeyer.
>
> The attached patch fixes this.
>
> There were two problems:
>
> 1. Without
>
>     dict->dv_copyID = 0;
>
> the l_vars and l_avars dicts in funccall_T have no initialized
> dv_copyID at the end of call_user_func, and hence valgrind complains
> when garbage_collect walks the previous_funccal list
>
> 2. Double free. garbage_collect() frees dictionaries and lists,
> including the ones belonging to functions in the previous_funccal
> list. When the previou_funccal list is freed, these dictionaries and
> lists are freed a second time.
>
> Nico


Thanks. I can confirm that Valgrind detects the same errors
on Linux x86 too using Vim-7.2.166. I did not try it earlier
because I don't follow the mailing list vim_mac.

So bug is not mac specific, even though it does not cause
a crash on Linux.

After applying your patch, there are no such errors anymore.

However, when exiting, I see that those blocks are not being
freed:

==16990== 217 bytes in 10 blocks are possibly lost in loss record 36 of 57
==16990== at 0x402603E: malloc (vg_replace_malloc.c:207)
==16990== by 0x81142FA: lalloc (misc2.c:866)
==16990== by 0x8114216: alloc (misc2.c:765)
==16990== by 0x807AD1D: dictitem_alloc (eval.c:6775)
==16990== by 0x8074FFD: set_var_lval (eval.c:2856)
==16990== by 0x80742F4: ex_let_one (eval.c:2414)
==16990== by 0x807329F: ex_let_vars (eval.c:1869)
==16990== by 0x8073250: ex_let (eval.c:1834)
==16990== by 0x80A6AA3: do_one_cmd (ex_docmd.c:2622)
==16990== by 0x80A4323: do_cmdline (ex_docmd.c:1096)
==16990== by 0x8090328: call_user_func (eval.c:21301)
==16990== by 0x807C4FE: call_func (eval.c:8079)
==16990== by 0x807C142: get_func_tv (eval.c:7925)
==16990== by 0x8075B83: ex_call (eval.c:3333)
==16990== by 0x80A6AA3: do_one_cmd (ex_docmd.c:2622)
==16990== by 0x80A4323: do_cmdline (ex_docmd.c:1096)
==16990== by 0x812A758: nv_colon (normal.c:5227)
==16990== by 0x8123DA2: normal_cmd (normal.c:1189)
==16990== by 0x80E6D49: main_loop (main.c:1180)
==16990== by 0x80E6896: main (main.c:939)

I built Vim with -DEXITFREE (i.e. uncommented line
PROFILE_CFLAGS = -DEXITFREE in src/Makefile)
so normally all blocks should be freed when exiting.

Regards
-- Dominique

Kana Natsuno

unread,
May 3, 2009, 3:37:13 AM5/3/09
to vim...@googlegroups.com
On Sun, 03 May 2009 08:06:13 +0900, Nico Weber <nicola...@gmx.de> wrote:
> Valgrind memory checker finds several errors in vim-7.2 (patches
> 1-148) with the reproduction steps described at http://groups.google.com/group/vim_mac/browse_thread/thread/4e0149ff4f84e3d3
> :

Thank you for the patch!
I encountered the same problem on Mac OS X and GNU/Linux since 7.2.70,
but I couldn't reproduce the problem, so that I couldn't send a report on it.
Now I should subscribe also vim_mac.


--
To Vim, or not to Vim.
http://whileimautomaton.net/

Nico Weber

unread,
May 3, 2009, 2:18:58 PM5/3/09
to vim...@googlegroups.com
Hi,

Thanks for checking.

I've attached an updated version of the patch that fixes some of the
leaks. The copy()d dicts in the script don't get freed, but I don't
(yet?) understand why.

Nico

eval_crash2.patch

Dominique Pellé

unread,
May 3, 2009, 2:56:40 PM5/3/09
to vim...@googlegroups.com
Nico Weber wrote:


I tried to fix the leak as well but without success so far.
It does not seem simple.

In any cases, it's a real leak, running the following leak.vim
script for example causes Vim memory usage to grow
continuously:

--- 8< --- cut here --- 8< --- cut here --- 8< ---
set nocp

" foo.vim is the script attached in the original bug submission
" at http://groups.google.com/group/vim_mac/browse_thread/thread/4e0149ff4f84e3d3
so foo.vim

let g:count_loops = 0
while 1
call foo#Buffer.New()
q
let g:count_loops = g:count_loops + 1
endwhile
--- 8< --- cut here --- 8< --- cut here --- 8< ---

I do:

$ vim -u NONE leak.vim
:so %

Then in another xterm, I can see with "top" that memory usage
of Vim keeps increasing.

If I run the leak.vim script with Valgrind...

valgrind --leak-resolution=high --leak-check=yes \
--num-callers=30 --track-fds=yes 2> vg.log \
./vim -u NONE leak.vim

Then do:
- :so %
- let the script execute for some time
- press CTRL-C to interrupt the infinite loop
- Look at how many times loop iterated with
:echo g:count_loops
(it prints 2774 for example)
- then quit vim: qa!

Then look for leaks in vg.log, and observe that some leaks
happen exactly 2774 times (same number as number as
g:loop_count):

==22962== 27,740 bytes in 2,774 blocks are possibly lost in loss
record 113 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x8114C16: vim_strsave (misc2.c:1177)
==22962== by 0x808C427: copy_tv (eval.c:19380)
==22962== by 0x808AE51: get_var_tv (eval.c:18452)
==22962== by 0x80785E7: eval7 (eval.c:5032)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8079667: get_list_tv (eval.c:5675)
==22962== by 0x807837D: eval7 (eval.c:4943)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962==
==22962==
==22962== 33,288 bytes in 5,548 blocks are possibly lost in loss
record 114 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x8114C16: vim_strsave (misc2.c:1177)
==22962== by 0x808C427: copy_tv (eval.c:19380)
==22962== by 0x807B117: dict_copy (eval.c:6961)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962== by 0x80A1F9B: ex_source (ex_cmds2.c:2714)
==22962==
==22962==
==22962== 49,932 bytes in 8,322 blocks are possibly lost in loss
record 115 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x8114C16: vim_strsave (misc2.c:1177)
==22962== by 0x808C427: copy_tv (eval.c:19380)
==22962== by 0x807B117: dict_copy (eval.c:6961)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x808AF05: handle_subscript (eval.c:18491)
==22962== by 0x8078640: eval7 (eval.c:5046)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962==
==22962==
==22962== 58,254 bytes in 2,774 blocks are possibly lost in loss
record 116 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AEFC: dictitem_alloc (eval.c:6855)
==22962== by 0x807507D: set_var_lval (eval.c:2864)
==22962== by 0x8074374: ex_let_one (eval.c:2422)
==22962== by 0x807331F: ex_let_vars (eval.c:1877)
==22962== by 0x80732D0: ex_let (eval.c:1842)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962== by 0x80A1F9B: ex_source (ex_cmds2.c:2714)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x812ACC4: nv_colon (normal.c:5227)
==22962== by 0x812430E: normal_cmd (normal.c:1189)
==22962== by 0x80E6F3D: main_loop (main.c:1180)
==22962==
==22962==
==22962== 77,672 bytes in 2,774 blocks are possibly lost in loss
record 117 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AEFC: dictitem_alloc (eval.c:6855)
==22962== by 0x807507D: set_var_lval (eval.c:2864)
==22962== by 0x8074374: ex_let_one (eval.c:2422)
==22962== by 0x807331F: ex_let_vars (eval.c:1877)
==22962== by 0x80732D0: ex_let (eval.c:1842)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x808AF05: handle_subscript (eval.c:18491)
==22962== by 0x8078640: eval7 (eval.c:5046)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962==
==22962==
==22962== 133,152 bytes in 5,548 blocks are possibly lost in loss
record 118 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AEFC: dictitem_alloc (eval.c:6855)
==22962== by 0x807B0B3: dict_copy (eval.c:6948)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962== by 0x80A1F9B: ex_source (ex_cmds2.c:2714)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962==
==22962==
==22962== 160,892 bytes in 5,548 blocks are possibly lost in loss
record 119 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AEFC: dictitem_alloc (eval.c:6855)
==22962== by 0x807507D: set_var_lval (eval.c:2864)
==22962== by 0x8074374: ex_let_one (eval.c:2422)
==22962== by 0x807331F: ex_let_vars (eval.c:1877)
==22962== by 0x80732D0: ex_let (eval.c:1842)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962== by 0x80A1F9B: ex_source (ex_cmds2.c:2714)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x812ACC4: nv_colon (normal.c:5227)
==22962== by 0x812430E: normal_cmd (normal.c:1189)
==22962== by 0x80E6F3D: main_loop (main.c:1180)
==22962== by 0x80E6A8A: main (main.c:939)
==22962==
==22962==
==22962== 213,598 bytes in 8,322 blocks are possibly lost in loss
record 120 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AEFC: dictitem_alloc (eval.c:6855)
==22962== by 0x807B0B3: dict_copy (eval.c:6948)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x808AF05: handle_subscript (eval.c:18491)
==22962== by 0x8078640: eval7 (eval.c:5046)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962==
==22962==
==22962== 288,496 bytes in 5,548 blocks are possibly lost in loss
record 121 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114723: alloc_clear (misc2.c:777)
==22962== by 0x80797D0: list_alloc (eval.c:5729)
==22962== by 0x807961D: get_list_tv (eval.c:5667)
==22962== by 0x807837D: eval7 (eval.c:4943)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962==
==22962==
==22962== 399,456 bytes in 16,644 blocks are possibly lost in loss
record 122 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807994D: listitem_alloc (eval.c:5810)
==22962== by 0x807967A: get_list_tv (eval.c:5679)
==22962== by 0x807837D: eval7 (eval.c:4943)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962==
==22962==
==22962== 499,320 bytes in 2,774 blocks are possibly lost in loss
record 123 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AD1E: dict_alloc (eval.c:6770)
==22962== by 0x807B6A8: get_dict_tv (eval.c:7216)
==22962== by 0x807839E: eval7 (eval.c:4949)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x808AF05: handle_subscript (eval.c:18491)
==22962== by 0x8078640: eval7 (eval.c:5046)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962==
==22962==
==22962== 499,320 bytes in 2,774 blocks are possibly lost in loss
record 124 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AD1E: dict_alloc (eval.c:6770)
==22962== by 0x807B040: dict_copy (eval.c:6933)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x808AF05: handle_subscript (eval.c:18491)
==22962== by 0x8078640: eval7 (eval.c:5046)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962==
==22962==
==22962== 499,320 bytes in 2,774 blocks are possibly lost in loss
record 125 of 125
==22962== at 0x402603E: malloc (vg_replace_malloc.c:207)
==22962== by 0x81147FD: lalloc (misc2.c:866)
==22962== by 0x8114708: alloc (misc2.c:765)
==22962== by 0x807AD1E: dict_alloc (eval.c:6770)
==22962== by 0x807B040: dict_copy (eval.c:6933)
==22962== by 0x808C662: item_copy (eval.c:19471)
==22962== by 0x807DB82: f_copy (eval.c:9141)
==22962== by 0x807C7DF: call_func (eval.c:8188)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x807859A: eval7 (eval.c:5018)
==22962== by 0x8077EA3: eval6 (eval.c:4685)
==22962== by 0x8077A8F: eval5 (eval.c:4501)
==22962== by 0x8076FE0: eval4 (eval.c:4196)
==22962== by 0x8076E38: eval3 (eval.c:4108)
==22962== by 0x8076CC4: eval2 (eval.c:4037)
==22962== by 0x8076AF4: eval1 (eval.c:3962)
==22962== by 0x8076A5B: eval0 (eval.c:3919)
==22962== by 0x807326C: ex_let (eval.c:1833)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x8090507: call_user_func (eval.c:21381)
==22962== by 0x807C6DD: call_func (eval.c:8159)
==22962== by 0x807C321: get_func_tv (eval.c:8005)
==22962== by 0x8075C03: ex_call (eval.c:3341)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)
==22962== by 0x80A450B: do_cmdline (ex_docmd.c:1096)
==22962== by 0x80A2793: do_source (ex_cmds2.c:3119)
==22962== by 0x80A2047: cmd_source (ex_cmds2.c:2741)
==22962== by 0x80A1F9B: ex_source (ex_cmds2.c:2714)
==22962== by 0x80A6C8B: do_one_cmd (ex_docmd.c:2622)

-- Dominique

Bram Moolenaar

unread,
May 5, 2009, 6:41:25 AM5/5/09
to Nico Weber, vim...@googlegroups.com

Nico Weber wrote:

I'm very glad you managed to pinpoint the problem and fix it.
I'll check the details and include the patch.
Thanks!

--
The Law of VIM:
For each member b of the possible behaviour space B of program P, there exists
a finite time t before which at least one user u in the total user space U of
program P will request b becomes a member of the allowed behaviour space B'
(B' <= B).
In other words: Sooner or later everyone wants everything as an option.
-- Vince Negri

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ download, build and distribute -- http://www.A-A-P.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///

Nico Weber

unread,
May 5, 2009, 10:11:16 AM5/5/09
to vim...@googlegroups.com
Hi Bram

On 05.05.2009, at 03:41, Bram Moolenaar wrote:

> Nico Weber wrote:
>
>> Valgrind memory checker finds several errors in vim-7.2 (patches
>> 1-148) with the reproduction steps described at http://groups.google.com/group/vim_mac/browse_thread/thread/4e0149ff4f84e3d3
>> :
>>

> I'm very glad you managed to pinpoint the problem and fix it.
> I'll check the details and include the patch.
> Thanks!

as Dominique pointed out, while this patch fixes a double free(), it
introduces a memory leak. The leak is smaller in the improved patch,
but it's still there. Now, a leak is arguably better than a crash, but
I wouldn't include this patch yet :-/

If you happen to know why the dicts copied by foo.vim are not freed
with the second version of my patch, that would be great. Else, I will
take another look next weekend.

Nico

Bram Moolenaar

unread,
May 22, 2009, 2:20:16 PM5/22/09
to Dominique Pellé, vim...@googlegroups.com

Dominique Pelle wrote:

Here is a new patch that hopefully fixes both the crash and the leaks.
It's a bit tricky, but I think this catches all situations.
Please verify.

Note that with your script one needs to wait a few moments before
exiting to give the garbage collector a chance.


*** ../vim-7.2.185/src/eval.c 2009-05-16 17:29:37.000000000 +0200
--- src/eval.c 2009-05-22 20:04:22.000000000 +0200
***************
*** 129,136 ****
--- 129,139 ----
/*
* When recursively copying lists and dicts we need to remember which ones we
* have done to avoid endless recursiveness. This unique ID is used for that.
+ * The last bit is used for previous_funccal, ignored when comparing.
*/
static int current_copyID = 0;
+ #define COPYID_INC 2
+ #define COPYID_MASK (~0x1)

/*
* Array to hold the hashtab with variables local to each sourced script.
***************
*** 439,444 ****
--- 442,448 ----
static void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2));
static char_u *list2string __ARGS((typval_T *tv, int copyID));
static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo, int copyID));
+ static int free_unref_items __ARGS((int copyID));
static void set_ref_in_ht __ARGS((hashtab_T *ht, int copyID));
static void set_ref_in_list __ARGS((list_T *l, int copyID));
static void set_ref_in_item __ARGS((typval_T *tv, int copyID));
***************
*** 6494,6507 ****
int
garbage_collect()
{
! dict_T *dd;
! list_T *ll;
! int copyID = ++current_copyID;
buf_T *buf;
win_T *wp;
int i;
funccall_T *fc, **pfc;
! int did_free = FALSE;
#ifdef FEAT_WINDOWS
tabpage_T *tp;
#endif
--- 6498,6510 ----
int
garbage_collect()
{
! int copyID;
buf_T *buf;
win_T *wp;
int i;
funccall_T *fc, **pfc;
! int did_free;
! int did_free_funccal = FALSE;
#ifdef FEAT_WINDOWS
tabpage_T *tp;
#endif
***************
*** 6511,6520 ****
--- 6514,6538 ----
may_garbage_collect = FALSE;
garbage_collect_at_exit = FALSE;

+ /* We advance by two because we add one for items referenced through
+ * previous_funccal. */
+ current_copyID += COPYID_INC;
+ copyID = current_copyID;
+
/*
* 1. Go through all accessible variables and mark all lists and dicts
* with copyID.
*/
+
+ /* Don't free variables in the previous_funccal list unless they are only
+ * referenced through previous_funccal. This must be first, because if
+ * the item is referenced elsewhere it must not be freed. */
+ for (fc = previous_funccal; fc != NULL; fc = fc->caller)
+ {
+ set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1);
+ set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1);
+ }
+
/* script-local variables */
for (i = 1; i <= ga_scripts.ga_len; ++i)
set_ref_in_ht(&SCRIPT_VARS(i), copyID);
***************
*** 6546,6556 ****
/* v: vars */
set_ref_in_ht(&vimvarht, copyID);

/*
! * 2. Go through the list of dicts and free items without the copyID.
*/
for (dd = first_dict; dd != NULL; )
! if (dd->dv_copyID != copyID)
{
/* Free the Dictionary and ordinary items it contains, but don't
* recurse into Lists and Dictionaries, they will be in the list
--- 6564,6610 ----
/* v: vars */
set_ref_in_ht(&vimvarht, copyID);

+ /* Free lists and dictionaries that are not referenced. */
+ did_free = free_unref_items(copyID);
+
+ /* check if any funccal can be freed now */
+ for (pfc = &previous_funccal; *pfc != NULL; )
+ {
+ if (can_free_funccal(*pfc, copyID))
+ {
+ fc = *pfc;
+ *pfc = fc->caller;
+ free_funccal(fc, TRUE);
+ did_free = TRUE;
+ did_free_funccal = TRUE;
+ }
+ else
+ pfc = &(*pfc)->caller;
+ }
+ if (did_free_funccal)
+ /* When a funccal was freed some more items might be garbage
+ * collected, so run again. */
+ (void)garbage_collect();
+
+ return did_free;
+ }
+
+ /*
+ * Free lists and dictionaries that are no longer referenced.
+ */
+ static int
+ free_unref_items(copyID)
+ int copyID;
+ {
+ dict_T *dd;
+ list_T *ll;
+ int did_free = FALSE;
+
/*
! * Go through the list of dicts and free items without the copyID.
*/
for (dd = first_dict; dd != NULL; )
! if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
{
/* Free the Dictionary and ordinary items it contains, but don't
* recurse into Lists and Dictionaries, they will be in the list
***************
*** 6565,6576 ****
dd = dd->dv_used_next;

/*
! * 3. Go through the list of lists and free items without the copyID.
! * But don't free a list that has a watcher (used in a for loop), these
! * are not referenced anywhere.
*/
for (ll = first_list; ll != NULL; )
! if (ll->lv_copyID != copyID && ll->lv_watch == NULL)
{
/* Free the List and ordinary items it contains, but don't recurse
* into Lists and Dictionaries, they will be in the list of dicts
--- 6619,6631 ----
dd = dd->dv_used_next;

/*
! * Go through the list of lists and free items without the copyID.
! * But don't free a list that has a watcher (used in a for loop), these
! * are not referenced anywhere.
*/
for (ll = first_list; ll != NULL; )
! if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
! && ll->lv_watch == NULL)
{
/* Free the List and ordinary items it contains, but don't recurse
* into Lists and Dictionaries, they will be in the list of dicts
***************
*** 6584,6603 ****
else
ll = ll->lv_used_next;

- /* check if any funccal can be freed now */
- for (pfc = &previous_funccal; *pfc != NULL; )
- {
- if (can_free_funccal(*pfc, copyID))
- {
- fc = *pfc;
- *pfc = fc->caller;
- free_funccal(fc, TRUE);
- did_free = TRUE;
- }
- else
- pfc = &(*pfc)->caller;
- }
-
return did_free;
}

--- 6639,6644 ----
***************
*** 18842,18847 ****
--- 18883,18889 ----
{
hash_init(&dict->dv_hashtab);
dict->dv_refcount = DO_NOT_FREE_CNT;
+ dict->dv_copyID = 0;
dict_var->di_tv.vval.v_dict = dict;
dict_var->di_tv.v_type = VAR_DICT;
dict_var->di_tv.v_lock = VAR_FIXED;
***************
*** 21294,21301 ****
current_funccal = fc->caller;
--depth;

! /* if the a:000 list and the a: dict are not referenced we can free the
! * funccall_T and what's in it. */
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
--- 21336,21343 ----
current_funccal = fc->caller;
--depth;

! /* If the a:000 list and the l: and a: dicts are not referenced we can
! * free the funccall_T and what's in it. */
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
***************
*** 21334,21340 ****

/*
* Return TRUE if items in "fc" do not have "copyID". That means they are not
! * referenced from anywhere.
*/
static int
can_free_funccal(fc, copyID)
--- 21376,21382 ----

/*
* Return TRUE if items in "fc" do not have "copyID". That means they are not
! * referenced from anywhere that is in use.
*/
static int
can_free_funccal(fc, copyID)


--
Why I like vim:
> I like VIM because, when I ask a question in this newsgroup, I get a
> one-line answer. With xemacs, I get a 1Kb lisp script with bugs in it ;-)

Nico Weber

unread,
May 23, 2009, 9:48:15 PM5/23/09
to vim...@googlegroups.com
On 22.05.2009, at 11:20, Bram Moolenaar wrote:

> Here is a new patch that hopefully fixes both the crash and the leaks.
> It's a bit tricky, but I think this catches all situations.
> Please verify.
>
> Note that with your script one needs to wait a few moments before
> exiting to give the garbage collector a chance.

LGTM. Thanks for taking a look at this.

I ran the offending script with and without this patch under valgrind,
the patch seems to remove the double free without introducing a memory
leak. Should there be a testcase for this in the test files?

Nit: garbage_collect() now contains a single step labeled "1." --
perhaps get rid of the number?

Nico

Bram Moolenaar

unread,
May 24, 2009, 9:13:36 AM5/24/09
to Nico Weber, vim...@googlegroups.com

Nico Weber wrote:

> On 22.05.2009, at 11:20, Bram Moolenaar wrote:
>
> > Here is a new patch that hopefully fixes both the crash and the leaks.
> > It's a bit tricky, but I think this catches all situations.
> > Please verify.
> >
> > Note that with your script one needs to wait a few moments before
> > exiting to give the garbage collector a chance.
>
> LGTM. Thanks for taking a look at this.
>
> I ran the offending script with and without this patch under valgrind,
> the patch seems to remove the double free without introducing a memory
> leak. Should there be a testcase for this in the test files?

There should be. Care to write one?

> Nit: garbage_collect() now contains a single step labeled "1." --
> perhaps get rid of the number?

I already sent out the patch. I'll adjust the comments and send it out
later.

--
ARTHUR: Be quiet! I order you to shut up.
OLD WOMAN: Order, eh -- who does he think he is?
ARTHUR: I am your king!
OLD WOMAN: Well, I didn't vote for you.
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

Kent Sibilev

unread,
May 27, 2009, 11:15:26 AM5/27/09
to vim...@googlegroups.com
2009/5/22 Bram Moolenaar <Br...@moolenaar.net>:


With this patch applied MacVim still crashes for me. Note that it
doesn't do that if I instead apply the second Nico's patch. These are
details of from gdb:

Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00000000000b7058
Crashed Thread: 0
(gdb) where
#0 0x0002afa1 in dict_free (d=0x35a42f0, recurse=1) at eval.c:6772
#1 0x0002b07a in dict_unref (d=0x35a42f0) at eval.c:6752
#2 0x0002a980 in clear_tv (varp=0x3413f60) at eval.c:18600
#3 0x0002c217 in vars_clear_ext (ht=0x8589c0, free_val=1) at eval.c:19037
#4 0x0002c25e in vars_clear (ht=0x34119f0) at eval.c:19009
#5 0x0002c285 in free_funccal (fc=0x858800, free_val=1) at eval.c:21512
#6 0x0002c6ee in garbage_collect () at eval.c:6579
#7 0x00075939 in before_blocking () at getchar.c:1473
#8 0x000dd53d in mch_inchar (buf=0x35a7001 "", maxlen=95, wtime=-1,
tb_change_cnt=21118) at os_unix.c:385
#9 0x00136ec5 in ui_inchar (buf=0x35a7001 "", maxlen=95, wtime=-1,
tb_change_cnt=21118) at ui.c:193
#10 0x00075c3a in inchar (buf=0x35a7001 "", maxlen=287, wait_time=-1,
tb_change_cnt=21118) at getchar.c:2959
#11 0x00078eda in vgetorpeek (advance=1) at getchar.c:2735
#12 0x00079441 in vgetc () at getchar.c:1552
#13 0x00079898 in safe_vgetc () at getchar.c:1757
#14 0x000c09ed in normal_cmd (oap=0xbffff350, toplevel=1) at normal.c:654
#15 0x00082d39 in main_loop (cmdwin=0, noexmode=0) at main.c:1255
#16 0x00086a87 in main (argc=54598128, argv=0x34119f0) at main.c:1002
(gdb) list
6767
6768 /* Remove the dict from the list of dicts for garbage collection. */
6769 if (d->dv_used_prev == NULL)
6770 first_dict = d->dv_used_next;
6771 else
6772 d->dv_used_prev->dv_used_next = d->dv_used_next;
6773 if (d->dv_used_next != NULL)
6774 d->dv_used_next->dv_used_prev = d->dv_used_prev;
6775
6776 /* Lock the hashtab, we don't want it to resize while
freeing items. */
(gdb) p *d
$44 = {
dv_refcount = -1073741822,
dv_hashtab = {
ht_mask = 3234897895,
ht_used = 11,
ht_filled = 18,
ht_locked = 1,
ht_error = 0,
ht_array = 0x85ee00,
ht_smallarray = {{
hi_hash = 1680060352,
hi_key = 0x34c692d "posStack"
}, {
hi_hash = 881893521,
hi_key = 0x347d13d "functionContainer"
}, {
hi_hash = 123778642,
hi_key = 0x342829d "wrap"
}, {
hi_hash = 1110333171,
hi_key = 0x3415ead "varPriority"
}, {
hi_hash = 122576644,
hi_key = 0x34bf01d "vars"
}, {
hi_hash = 2430892566,
hi_key = 0x34bde9d "wrapStartPos"
}, {
hi_hash = 1646883926,
hi_key = 0x34bd31d "tmplarr"
}, {
hi_hash = 2145903751,
hi_key = 0x34803ed "funcs"
}, {
hi_hash = 398141396,
hi_key = 0x34a1acd "lastPopup"
}, {
hi_hash = 2409478612,
hi_key = 0x34a295d "startColumn"
}, {
hi_hash = 3497526042,
hi_key = 0x341fc5d "stack"
}, {
hi_hash = 1528633632,
hi_key = 0x34a1c4d "wrappedNameTree"
}, {
hi_hash = 3594528268,
hi_key = 0x34bfb9d "tmpls"
}, {
hi_hash = 3360825129,
hi_key = 0x3487c5d "nameTree"
}, {
hi_hash = 0,
hi_key = 0x0
}, {
hi_hash = 2739356453,
hi_key = 0x3485b6d "keyword"
}}
},
dv_copyID = 1862,
dv_copydict = 0x3231203a,
dv_lock = 0 '\0',
dv_used_next = 0x34119f0,
dv_used_prev = 0xb6fb0
}

It looks like d->dv_refcount is invalid and d->dv_used_prev point is suspicious:

(gdb) p *d->dv_used_prev
$46 = {
dv_refcount = 533,
dv_hashtab = {
ht_mask = 552,
ht_used = 552,
ht_filled = 552,
ht_locked = 540,
ht_error = 547,
ht_array = 0x1f2,
ht_smallarray = {{
hi_hash = 96442,
hi_key = 0xba2feb00 <Address 0xba2feb00 out of bounds>
}, {
hi_hash = 8364,
hi_key = 0x60ba28eb <Address 0x60ba28eb out of bounds>
}, {
hi_hash = 3942645761,
hi_key = 0x161ba21 ""
}, {
hi_hash = 451608576,
hi_key = 0x17dba "\v#####"
}, {
hi_hash = 3121867520,
hi_key = 0x17e <Address 0x17e out of bounds>
}, {
hi_hash = 1387924715,
hi_key = 0xeb000001 <Address 0xeb000001 out of bounds>
}, {
hi_hash = 22264325,
hi_key = 0x4d8b0000 <Address 0x4d8b0000 out of bounds>
}, {
hi_hash = 608995784,
hi_key = 0x24148904 <Address 0x24148904 out of bounds>
}, {
hi_hash = 4293319400,
hi_key = 0xc84501ff <Address 0xc84501ff out of bounds>
}, {
hi_hash = 1003505151,
hi_key = 0x850fd075 <Address 0x850fd075 out of bounds>
}, {
hi_hash = 4294967067,
hi_key = 0xc6c87d8b <Address 0xc6c87d8b out of bounds>
}, {
hi_hash = 2105737223,
hi_key = 0x840f0010 <Address 0x840f0010 out of bounds>
}, {
hi_hash = 1409,
hi_key = 0x452bf889 <Address 0x452bf889 out of bounds>
}, {
hi_hash = 274041760,
hi_key = 0x72e90289 <Address 0x72e90289 out of bounds>
}, {
hi_hash = 2332033029,
hi_key = 0x1189144d <Address 0x1189144d out of bounds>
}, {
hi_hash = 2311355787,
hi_key = 0x2ce9ac7d <Address 0x2ce9ac7d out of bounds>
}}
},
dv_copyID = -1929379838,
dv_copydict = 0x4890146,
dv_lock = 36 '$',
dv_used_next = 0x4589ffff,
dv_used_prev = 0xfc085a0
}

I assume this is duble dealloc problem, but unfortunately I have no
idea how to track it down.
Hope it helps.

Kent

Bram Moolenaar

unread,
May 27, 2009, 3:38:37 PM5/27/09
to Kent Sibilev, vim...@googlegroups.com

Kent Sibilev wrote:

Do you have a reasonable simple way to reproduce this?

--
Managers are like cats in a litter box. They instinctively shuffle things
around to conceal what they've done.
(Scott Adams - The Dilbert principle)

Reply all
Reply to author
Forward
0 new messages