mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 09:44:31 +00:00 
			
		
		
		
	fix(terminal): block input when there is pending TermRequest (#27589)
This commit is contained in:
		@@ -163,6 +163,9 @@ struct terminal {
 | 
			
		||||
 | 
			
		||||
  bool color_set[16];
 | 
			
		||||
 | 
			
		||||
  // When there is a pending TermRequest autocommand, block and store input.
 | 
			
		||||
  StringBuilder *pending_send;
 | 
			
		||||
 | 
			
		||||
  size_t refcount;                  // reference count
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -178,11 +181,12 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
 | 
			
		||||
 | 
			
		||||
static Set(ptr_t) invalidated_terminals = SET_INIT;
 | 
			
		||||
 | 
			
		||||
static void emit_term_request(void **argv)
 | 
			
		||||
static void emit_termrequest(void **argv)
 | 
			
		||||
{
 | 
			
		||||
  char *payload = argv[0];
 | 
			
		||||
  size_t payload_length = (size_t)argv[1];
 | 
			
		||||
  Terminal *term = argv[2];
 | 
			
		||||
  Terminal *term = argv[0];
 | 
			
		||||
  char *payload = argv[1];
 | 
			
		||||
  size_t payload_length = (size_t)argv[2];
 | 
			
		||||
  StringBuilder *pending_send = argv[3];
 | 
			
		||||
 | 
			
		||||
  buf_T *buf = handle_get_buffer(term->buf_handle);
 | 
			
		||||
  String termrequest = { .data = payload, .size = payload_length };
 | 
			
		||||
@@ -190,6 +194,25 @@ static void emit_term_request(void **argv)
 | 
			
		||||
  set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length);
 | 
			
		||||
  apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data);
 | 
			
		||||
  xfree(payload);
 | 
			
		||||
 | 
			
		||||
  StringBuilder *term_pending_send = term->pending_send;
 | 
			
		||||
  term->pending_send = NULL;
 | 
			
		||||
  if (kv_size(*pending_send)) {
 | 
			
		||||
    terminal_send(term, pending_send->items, pending_send->size);
 | 
			
		||||
    kv_destroy(*pending_send);
 | 
			
		||||
  }
 | 
			
		||||
  if (term_pending_send != pending_send) {
 | 
			
		||||
    term->pending_send = term_pending_send;
 | 
			
		||||
  }
 | 
			
		||||
  xfree(pending_send);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length)
 | 
			
		||||
{
 | 
			
		||||
  term->pending_send = xmalloc(sizeof(StringBuilder));
 | 
			
		||||
  kv_init(*term->pending_send);
 | 
			
		||||
  multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length,
 | 
			
		||||
                 term->pending_send);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int on_osc(int command, VTermStringFragment frag, void *user)
 | 
			
		||||
@@ -197,24 +220,30 @@ static int on_osc(int command, VTermStringFragment frag, void *user)
 | 
			
		||||
  if (frag.str == NULL) {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (!has_event(EVENT_TERMREQUEST)) {
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  StringBuilder request = KV_INITIAL_VALUE;
 | 
			
		||||
  kv_printf(request, "\x1b]%d;", command);
 | 
			
		||||
  kv_concat_len(request, frag.str, frag.len);
 | 
			
		||||
  multiqueue_put(main_loop.events, emit_term_request, request.items, (void *)request.size, user);
 | 
			
		||||
  schedule_termrequest(user, request.items, request.size);
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
 | 
			
		||||
{
 | 
			
		||||
  if ((command == NULL) || (frag.str == NULL)) {
 | 
			
		||||
  if (command == NULL || frag.str == NULL) {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (!has_event(EVENT_TERMREQUEST)) {
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  StringBuilder request = KV_INITIAL_VALUE;
 | 
			
		||||
  kv_printf(request, "\x1bP%*s", (int)commandlen, command);
 | 
			
		||||
  kv_concat_len(request, frag.str, frag.len);
 | 
			
		||||
  multiqueue_put(main_loop.events, emit_term_request, request.items, (void *)request.size, user);
 | 
			
		||||
  schedule_termrequest(user, request.items, request.size);
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -747,6 +776,10 @@ static void terminal_send(Terminal *term, const char *data, size_t size)
 | 
			
		||||
  if (term->closed) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (term->pending_send) {
 | 
			
		||||
    kv_concat_len(*term->pending_send, data, size);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  term->opts.write_cb(data, size, term->opts.data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -319,8 +319,7 @@ describe(':terminal buffer', function()
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('emits TermRequest events #26972', function()
 | 
			
		||||
    command('split')
 | 
			
		||||
    command('enew')
 | 
			
		||||
    command('new')
 | 
			
		||||
    local term = api.nvim_open_term(0, {})
 | 
			
		||||
    local termbuf = api.nvim_get_current_buf()
 | 
			
		||||
 | 
			
		||||
@@ -336,6 +335,35 @@ describe(':terminal buffer', function()
 | 
			
		||||
    eq(expected, eval('v:termrequest'))
 | 
			
		||||
    eq(termbuf, eval('g:termbuf'))
 | 
			
		||||
  end)
 | 
			
		||||
 | 
			
		||||
  it('TermReqeust synchronization #27572', function()
 | 
			
		||||
    command('new')
 | 
			
		||||
    command('autocmd! nvim_terminal TermRequest')
 | 
			
		||||
    local term = exec_lua([[
 | 
			
		||||
      _G.input = {}
 | 
			
		||||
      local term = vim.api.nvim_open_term(0, {
 | 
			
		||||
        on_input = function(_, _, _, data)
 | 
			
		||||
          table.insert(_G.input, data)
 | 
			
		||||
        end,
 | 
			
		||||
        force_crlf = false,
 | 
			
		||||
      })
 | 
			
		||||
      vim.api.nvim_create_autocmd('TermRequest', {
 | 
			
		||||
        callback = function(args)
 | 
			
		||||
          if args.data == '\027]11;?' then
 | 
			
		||||
            table.insert(_G.input, '\027]11;rgb:0000/0000/0000\027\\')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      })
 | 
			
		||||
      return term
 | 
			
		||||
    ]])
 | 
			
		||||
    api.nvim_chan_send(term, '\027]11;?\007\027[5n\027]11;?\007\027[5n')
 | 
			
		||||
    eq({
 | 
			
		||||
      '\027]11;rgb:0000/0000/0000\027\\',
 | 
			
		||||
      '\027[0n',
 | 
			
		||||
      '\027]11;rgb:0000/0000/0000\027\\',
 | 
			
		||||
      '\027[0n',
 | 
			
		||||
    }, exec_lua('return _G.input'))
 | 
			
		||||
  end)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
describe('No heap-buffer-overflow when using', function()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user