mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-03 17:24:29 +00:00 
			
		
		
		
	eval: Remove dictwatcher from watchers queue before freeing it
This fixes a use-after-free noticed by ASAN which would occur when a
dictwatcher was still active on a dictionary when the dictionary was
freed.
    fun! MakeWatch()
      let d = {'foo': 'bar'}
      call dictwatcheradd(d, 'foo', function('...'))
    endfun
Patch-by: oni-link
Closes #5930
			
			
This commit is contained in:
		@@ -6410,8 +6410,8 @@ static void dict_free_contents(dict_T *d) {
 | 
			
		||||
  while (!QUEUE_EMPTY(&d->watchers)) {
 | 
			
		||||
    QUEUE *w = QUEUE_HEAD(&d->watchers);
 | 
			
		||||
    DictWatcher *watcher = dictwatcher_node_data(w);
 | 
			
		||||
    dictwatcher_free(watcher);
 | 
			
		||||
    QUEUE_REMOVE(w);
 | 
			
		||||
    dictwatcher_free(watcher);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hash_clear(&d->dv_hashtab);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
 | 
			
		||||
local eq, next_msg = helpers.eq, helpers.next_message
 | 
			
		||||
local exc_exec = helpers.exc_exec
 | 
			
		||||
local command = helpers.command
 | 
			
		||||
local eval = helpers.eval
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('dictionary change notifications', function()
 | 
			
		||||
@@ -255,5 +256,21 @@ describe('dictionary change notifications', function()
 | 
			
		||||
      eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
 | 
			
		||||
 | 
			
		||||
    end)
 | 
			
		||||
 | 
			
		||||
    it('does not crash when freeing a watched dictionary', function()
 | 
			
		||||
      source([[
 | 
			
		||||
        function! Watcher(dict, key, value)
 | 
			
		||||
          echo a:key string(a:value)
 | 
			
		||||
        endfunction
 | 
			
		||||
 | 
			
		||||
        function! MakeWatch()
 | 
			
		||||
          let d = {'foo': 'bar'}
 | 
			
		||||
          call dictwatcheradd(d, 'foo', function('Watcher'))
 | 
			
		||||
        endfunction
 | 
			
		||||
      ]])
 | 
			
		||||
 | 
			
		||||
      command('call MakeWatch()')
 | 
			
		||||
      eq(2, eval('1+1')) -- Still alive?
 | 
			
		||||
    end)
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user