1ffe1d5
To: vim-dev@vim.org
1ffe1d5
Subject: Patch 7.0.135
1ffe1d5
Fcc: outbox
1ffe1d5
From: Bram Moolenaar <Bram@moolenaar.net>
1ffe1d5
Mime-Version: 1.0
1ffe1d5
Content-Type: text/plain; charset=ISO-8859-1
1ffe1d5
Content-Transfer-Encoding: 8bit
1ffe1d5
------------
1ffe1d5
1ffe1d5
Patch 7.0.135
1ffe1d5
Problem:    Crash when garbage collecting list or dict with loop.
1ffe1d5
Solution:   Don't use DEL_REFCOUNT but don't recurse into Lists and
1ffe1d5
	    Dictionaries when freeing them in the garbage collector.
1ffe1d5
	    Also add allocated Dictionaries to the list of Dictionaries to
1ffe1d5
	    avoid leaking memory.
1ffe1d5
Files:	    src/eval.c, src/proto/eval.pro, src/tag.c
1ffe1d5
1ffe1d5
1ffe1d5
*** ../vim-7.0.134/src/eval.c	Sun Oct 15 15:10:08 2006
1ffe1d5
--- src/eval.c	Sun Oct 15 22:30:09 2006
1ffe1d5
***************
1ffe1d5
*** 191,198 ****
1ffe1d5
  #define FC_RANGE    2		/* function accepts range */
1ffe1d5
  #define FC_DICT	    4		/* Dict function, uses "self" */
1ffe1d5
  
1ffe1d5
- #define DEL_REFCOUNT	999999	/* list/dict is being deleted */
1ffe1d5
- 
1ffe1d5
  /*
1ffe1d5
   * All user-defined functions are found in this hashtable.
1ffe1d5
   */
1ffe1d5
--- 191,196 ----
1ffe1d5
***************
1ffe1d5
*** 435,441 ****
1ffe1d5
  static void set_ref_in_list __ARGS((list_T *l, int copyID));
1ffe1d5
  static void set_ref_in_item __ARGS((typval_T *tv, int copyID));
1ffe1d5
  static void dict_unref __ARGS((dict_T *d));
1ffe1d5
! static void dict_free __ARGS((dict_T *d));
1ffe1d5
  static dictitem_T *dictitem_alloc __ARGS((char_u *key));
1ffe1d5
  static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
1ffe1d5
  static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
1ffe1d5
--- 433,439 ----
1ffe1d5
  static void set_ref_in_list __ARGS((list_T *l, int copyID));
1ffe1d5
  static void set_ref_in_item __ARGS((typval_T *tv, int copyID));
1ffe1d5
  static void dict_unref __ARGS((dict_T *d));
1ffe1d5
! static void dict_free __ARGS((dict_T *d, int recurse));
1ffe1d5
  static dictitem_T *dictitem_alloc __ARGS((char_u *key));
1ffe1d5
  static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
1ffe1d5
  static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
1ffe1d5
***************
1ffe1d5
*** 4899,4905 ****
1ffe1d5
  		    {
1ffe1d5
  			if (list_append_tv(l, &item->li_tv) == FAIL)
1ffe1d5
  			{
1ffe1d5
! 			    list_free(l);
1ffe1d5
  			    return FAIL;
1ffe1d5
  			}
1ffe1d5
  			item = item->li_next;
1ffe1d5
--- 4897,4903 ----
1ffe1d5
  		    {
1ffe1d5
  			if (list_append_tv(l, &item->li_tv) == FAIL)
1ffe1d5
  			{
1ffe1d5
! 			    list_free(l, TRUE);
1ffe1d5
  			    return FAIL;
1ffe1d5
  			}
1ffe1d5
  			item = item->li_next;
1ffe1d5
***************
1ffe1d5
*** 5299,5305 ****
1ffe1d5
  	EMSG2(_("E697: Missing end of List ']': %s"), *arg);
1ffe1d5
  failret:
1ffe1d5
  	if (evaluate)
1ffe1d5
! 	    list_free(l);
1ffe1d5
  	return FAIL;
1ffe1d5
      }
1ffe1d5
  
1ffe1d5
--- 5297,5303 ----
1ffe1d5
  	EMSG2(_("E697: Missing end of List ']': %s"), *arg);
1ffe1d5
  failret:
1ffe1d5
  	if (evaluate)
1ffe1d5
! 	    list_free(l, TRUE);
1ffe1d5
  	return FAIL;
1ffe1d5
      }
1ffe1d5
  
1ffe1d5
***************
1ffe1d5
*** 5363,5370 ****
1ffe1d5
  list_unref(l)
1ffe1d5
      list_T *l;
1ffe1d5
  {
1ffe1d5
!     if (l != NULL && l->lv_refcount != DEL_REFCOUNT && --l->lv_refcount <= 0)
1ffe1d5
! 	list_free(l);
1ffe1d5
  }
1ffe1d5
  
1ffe1d5
  /*
1ffe1d5
--- 5361,5368 ----
1ffe1d5
  list_unref(l)
1ffe1d5
      list_T *l;
1ffe1d5
  {
1ffe1d5
!     if (l != NULL && --l->lv_refcount <= 0)
1ffe1d5
! 	list_free(l, TRUE);
1ffe1d5
  }
1ffe1d5
  
1ffe1d5
  /*
1ffe1d5
***************
1ffe1d5
*** 5372,5385 ****
1ffe1d5
   * Ignores the reference count.
1ffe1d5
   */
1ffe1d5
      void
1ffe1d5
! list_free(l)
1ffe1d5
!     list_T *l;
1ffe1d5
  {
1ffe1d5
      listitem_T *item;
1ffe1d5
  
1ffe1d5
-     /* Avoid that recursive reference to the list frees us again. */
1ffe1d5
-     l->lv_refcount = DEL_REFCOUNT;
1ffe1d5
- 
1ffe1d5
      /* Remove the list from the list of lists for garbage collection. */
1ffe1d5
      if (l->lv_used_prev == NULL)
1ffe1d5
  	first_list = l->lv_used_next;
1ffe1d5
--- 5370,5381 ----
1ffe1d5
   * Ignores the reference count.
1ffe1d5
   */
1ffe1d5
      void
1ffe1d5
! list_free(l, recurse)
1ffe1d5
!     list_T  *l;
1ffe1d5
!     int	    recurse;	/* Free Lists and Dictionaries recursively. */
1ffe1d5
  {
1ffe1d5
      listitem_T *item;
1ffe1d5
  
1ffe1d5
      /* Remove the list from the list of lists for garbage collection. */
1ffe1d5
      if (l->lv_used_prev == NULL)
1ffe1d5
  	first_list = l->lv_used_next;
1ffe1d5
***************
1ffe1d5
*** 5392,5398 ****
1ffe1d5
      {
1ffe1d5
  	/* Remove the item before deleting it. */
1ffe1d5
  	l->lv_first = item->li_next;
1ffe1d5
! 	listitem_free(item);
1ffe1d5
      }
1ffe1d5
      vim_free(l);
1ffe1d5
  }
1ffe1d5
--- 5388,5397 ----
1ffe1d5
      {
1ffe1d5
  	/* Remove the item before deleting it. */
1ffe1d5
  	l->lv_first = item->li_next;
1ffe1d5
! 	if (recurse || (item->li_tv.v_type != VAR_LIST
1ffe1d5
! 					   && item->li_tv.v_type != VAR_DICT))
1ffe1d5
! 	    clear_tv(&item->li_tv);
1ffe1d5
! 	vim_free(item);
1ffe1d5
      }
1ffe1d5
      vim_free(l);
1ffe1d5
  }
1ffe1d5
***************
1ffe1d5
*** 6113,6119 ****
1ffe1d5
      for (dd = first_dict; dd != NULL; )
1ffe1d5
  	if (dd->dv_copyID != copyID)
1ffe1d5
  	{
1ffe1d5
! 	    dict_free(dd);
1ffe1d5
  	    did_free = TRUE;
1ffe1d5
  
1ffe1d5
  	    /* restart, next dict may also have been freed */
1ffe1d5
--- 6118,6127 ----
1ffe1d5
      for (dd = first_dict; dd != NULL; )
1ffe1d5
  	if (dd->dv_copyID != copyID)
1ffe1d5
  	{
1ffe1d5
! 	    /* Free the Dictionary and ordinary items it contains, but don't
1ffe1d5
! 	     * recurse into Lists and Dictionaries, they will be in the list
1ffe1d5
! 	     * of dicts or list of lists. */
1ffe1d5
! 	    dict_free(dd, FALSE);
1ffe1d5
  	    did_free = TRUE;
1ffe1d5
  
1ffe1d5
  	    /* restart, next dict may also have been freed */
1ffe1d5
***************
1ffe1d5
*** 6130,6136 ****
1ffe1d5
      for (ll = first_list; ll != NULL; )
1ffe1d5
  	if (ll->lv_copyID != copyID && ll->lv_watch == NULL)
1ffe1d5
  	{
1ffe1d5
! 	    list_free(ll);
1ffe1d5
  	    did_free = TRUE;
1ffe1d5
  
1ffe1d5
  	    /* restart, next list may also have been freed */
1ffe1d5
--- 6138,6147 ----
1ffe1d5
      for (ll = first_list; ll != NULL; )
1ffe1d5
  	if (ll->lv_copyID != copyID && ll->lv_watch == NULL)
1ffe1d5
  	{
1ffe1d5
! 	    /* Free the List and ordinary items it contains, but don't recurse
1ffe1d5
! 	     * into Lists and Dictionaries, they will be in the list of dicts
1ffe1d5
! 	     * or list of lists. */
1ffe1d5
! 	    list_free(ll, FALSE);
1ffe1d5
  	    did_free = TRUE;
1ffe1d5
  
1ffe1d5
  	    /* restart, next list may also have been freed */
1ffe1d5
***************
1ffe1d5
*** 6223,6233 ****
1ffe1d5
      d = (dict_T *)alloc(sizeof(dict_T));
1ffe1d5
      if (d != NULL)
1ffe1d5
      {
1ffe1d5
! 	/* Add the list to the hashtable for garbage collection. */
1ffe1d5
  	if (first_dict != NULL)
1ffe1d5
  	    first_dict->dv_used_prev = d;
1ffe1d5
  	d->dv_used_next = first_dict;
1ffe1d5
  	d->dv_used_prev = NULL;
1ffe1d5
  
1ffe1d5
  	hash_init(&d->dv_hashtab);
1ffe1d5
  	d->dv_lock = 0;
1ffe1d5
--- 6234,6245 ----
1ffe1d5
      d = (dict_T *)alloc(sizeof(dict_T));
1ffe1d5
      if (d != NULL)
1ffe1d5
      {
1ffe1d5
! 	/* Add the list to the list of dicts for garbage collection. */
1ffe1d5
  	if (first_dict != NULL)
1ffe1d5
  	    first_dict->dv_used_prev = d;
1ffe1d5
  	d->dv_used_next = first_dict;
1ffe1d5
  	d->dv_used_prev = NULL;
1ffe1d5
+ 	first_dict = d;
1ffe1d5
  
1ffe1d5
  	hash_init(&d->dv_hashtab);
1ffe1d5
  	d->dv_lock = 0;
1ffe1d5
***************
1ffe1d5
*** 6245,6252 ****
1ffe1d5
  dict_unref(d)
1ffe1d5
      dict_T *d;
1ffe1d5
  {
1ffe1d5
!     if (d != NULL && d->dv_refcount != DEL_REFCOUNT && --d->dv_refcount <= 0)
1ffe1d5
! 	dict_free(d);
1ffe1d5
  }
1ffe1d5
  
1ffe1d5
  /*
1ffe1d5
--- 6257,6264 ----
1ffe1d5
  dict_unref(d)
1ffe1d5
      dict_T *d;
1ffe1d5
  {
1ffe1d5
!     if (d != NULL && --d->dv_refcount <= 0)
1ffe1d5
! 	dict_free(d, TRUE);
1ffe1d5
  }
1ffe1d5
  
1ffe1d5
  /*
1ffe1d5
***************
1ffe1d5
*** 6254,6269 ****
1ffe1d5
   * Ignores the reference count.
1ffe1d5
   */
1ffe1d5
      static void
1ffe1d5
! dict_free(d)
1ffe1d5
!     dict_T *d;
1ffe1d5
  {
1ffe1d5
      int		todo;
1ffe1d5
      hashitem_T	*hi;
1ffe1d5
      dictitem_T	*di;
1ffe1d5
  
1ffe1d5
-     /* Avoid that recursive reference to the dict frees us again. */
1ffe1d5
-     d->dv_refcount = DEL_REFCOUNT;
1ffe1d5
- 
1ffe1d5
      /* Remove the dict from the list of dicts for garbage collection. */
1ffe1d5
      if (d->dv_used_prev == NULL)
1ffe1d5
  	first_dict = d->dv_used_next;
1ffe1d5
--- 6266,6279 ----
1ffe1d5
   * Ignores the reference count.
1ffe1d5
   */
1ffe1d5
      static void
1ffe1d5
! dict_free(d, recurse)
1ffe1d5
!     dict_T  *d;
1ffe1d5
!     int	    recurse;	/* Free Lists and Dictionaries recursively. */
1ffe1d5
  {
1ffe1d5
      int		todo;
1ffe1d5
      hashitem_T	*hi;
1ffe1d5
      dictitem_T	*di;
1ffe1d5
  
1ffe1d5
      /* Remove the dict from the list of dicts for garbage collection. */
1ffe1d5
      if (d->dv_used_prev == NULL)
1ffe1d5
  	first_dict = d->dv_used_next;
1ffe1d5
***************
1ffe1d5
*** 6283,6289 ****
1ffe1d5
  	     * something recursive causing trouble. */
1ffe1d5
  	    di = HI2DI(hi);
1ffe1d5
  	    hash_remove(&d->dv_hashtab, hi);
1ffe1d5
! 	    dictitem_free(di);
1ffe1d5
  	    --todo;
1ffe1d5
  	}
1ffe1d5
      }
1ffe1d5
--- 6293,6302 ----
1ffe1d5
  	     * something recursive causing trouble. */
1ffe1d5
  	    di = HI2DI(hi);
1ffe1d5
  	    hash_remove(&d->dv_hashtab, hi);
1ffe1d5
! 	    if (recurse || (di->di_tv.v_type != VAR_LIST
1ffe1d5
! 					     && di->di_tv.v_type != VAR_DICT))
1ffe1d5
! 		clear_tv(&di->di_tv);
1ffe1d5
! 	    vim_free(di);
1ffe1d5
  	    --todo;
1ffe1d5
  	}
1ffe1d5
      }
1ffe1d5
***************
1ffe1d5
*** 6734,6740 ****
1ffe1d5
  	EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
1ffe1d5
  failret:
1ffe1d5
  	if (evaluate)
1ffe1d5
! 	    dict_free(d);
1ffe1d5
  	return FAIL;
1ffe1d5
      }
1ffe1d5
  
1ffe1d5
--- 6747,6753 ----
1ffe1d5
  	EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
1ffe1d5
  failret:
1ffe1d5
  	if (evaluate)
1ffe1d5
! 	    dict_free(d, TRUE);
1ffe1d5
  	return FAIL;
1ffe1d5
      }
1ffe1d5
  
1ffe1d5
*** ../vim-7.0.134/src/proto/eval.pro	Fri Mar 24 23:16:28 2006
1ffe1d5
--- src/proto/eval.pro	Sun Oct 15 22:08:11 2006
1ffe1d5
***************
1ffe1d5
*** 44,50 ****
1ffe1d5
  extern char_u *get_user_var_name __ARGS((expand_T *xp, int idx));
1ffe1d5
  extern list_T *list_alloc __ARGS((void));
1ffe1d5
  extern void list_unref __ARGS((list_T *l));
1ffe1d5
! extern void list_free __ARGS((list_T *l));
1ffe1d5
  extern dictitem_T *dict_lookup __ARGS((hashitem_T *hi));
1ffe1d5
  extern int list_append_dict __ARGS((list_T *list, dict_T *dict));
1ffe1d5
  extern int garbage_collect __ARGS((void));
1ffe1d5
--- 44,50 ----
1ffe1d5
  extern char_u *get_user_var_name __ARGS((expand_T *xp, int idx));
1ffe1d5
  extern list_T *list_alloc __ARGS((void));
1ffe1d5
  extern void list_unref __ARGS((list_T *l));
1ffe1d5
! extern void list_free __ARGS((list_T *l, int recurse));
1ffe1d5
  extern dictitem_T *dict_lookup __ARGS((hashitem_T *hi));
1ffe1d5
  extern int list_append_dict __ARGS((list_T *list, dict_T *dict));
1ffe1d5
  extern int garbage_collect __ARGS((void));
1ffe1d5
*** ../vim-7.0.134/src/tag.c	Sun Sep 10 13:56:06 2006
1ffe1d5
--- src/tag.c	Sun Oct 15 21:44:56 2006
1ffe1d5
***************
1ffe1d5
*** 911,917 ****
1ffe1d5
  
1ffe1d5
  		set_errorlist(curwin, list, ' ');
1ffe1d5
  
1ffe1d5
! 		list_free(list);
1ffe1d5
  
1ffe1d5
  		cur_match = 0;		/* Jump to the first tag */
1ffe1d5
  	    }
1ffe1d5
--- 911,917 ----
1ffe1d5
  
1ffe1d5
  		set_errorlist(curwin, list, ' ');
1ffe1d5
  
1ffe1d5
! 		list_free(list, TRUE);
1ffe1d5
  
1ffe1d5
  		cur_match = 0;		/* Jump to the first tag */
1ffe1d5
  	    }
1ffe1d5
*** ../vim-7.0.134/src/version.c	Sun Oct 15 15:10:08 2006
1ffe1d5
--- src/version.c	Sun Oct 15 22:01:53 2006
1ffe1d5
***************
1ffe1d5
*** 668,669 ****
1ffe1d5
--- 668,671 ----
1ffe1d5
  {   /* Add new patch number below this line */
1ffe1d5
+ /**/
1ffe1d5
+     135,
1ffe1d5
  /**/
1ffe1d5
1ffe1d5
-- 
1ffe1d5
Well, you come from nothing, you go back to nothing...  What have you
1ffe1d5
lost?  Nothing!
1ffe1d5
				-- Monty Python: The life of Brian
1ffe1d5
1ffe1d5
 /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net   \\\
1ffe1d5
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
1ffe1d5
\\\        download, build and distribute -- http://www.A-A-P.org        ///
1ffe1d5
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///