fix(ui): avoid recursiveness and invalid memory access #28578

Problem:  Calling :redraw from vim.ui_attach() callback results in
          recursive cmdline/message events.
Solution: Avoid recursiveness where possible and replace global "call_buf"
          with separate, temporary buffers for each event so that when a Lua
          callback for one event fires another event, that does not result
          in invalid memory access.
This commit is contained in:
luukvbaal
2024-05-01 22:51:06 +02:00
committed by GitHub
parent 631d5189ad
commit e778e01161
9 changed files with 92 additions and 65 deletions

View File

@@ -71,8 +71,6 @@ static bool has_mouse = false;
static int pending_has_mouse = -1;
static bool pending_default_colors = false;
static Array call_buf = ARRAY_DICT_INIT;
#ifdef NVIM_LOG_DEBUG
static size_t uilog_seen = 0;
static const char *uilog_last_event = NULL;
@@ -128,14 +126,11 @@ void ui_init(void)
default_grid.handle = 1;
msg_grid_adj.target = &default_grid;
ui_comp_init();
kv_ensure_space(call_buf, 16);
}
#ifdef EXITFREE
void ui_free_all_mem(void)
{
kv_destroy(call_buf);
UIEventCallback *event_cb;
map_foreach_value(&ui_event_cbs, event_cb, {
free_ui_event_callback(event_cb);
@@ -197,11 +192,9 @@ void ui_refresh(void)
int width = INT_MAX;
int height = INT_MAX;
bool ext_widgets[kUIExtCount];
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
ext_widgets[i] = ui_active();
}
bool inclusive = ui_override();
memset(ext_widgets, ui_active(), ARRAY_SIZE(ext_widgets));
for (size_t i = 0; i < ui_count; i++) {
RemoteUI *ui = uis[i];
width = MIN(ui->width, width);
@@ -218,7 +211,7 @@ void ui_refresh(void)
if (i < kUIGlobalCount) {
ext_widgets[i] |= ui_cb_ext[i];
}
// Set 'cmdheight' to zero when ext_messages becomes active for all tabpages.
// Set 'cmdheight' to zero for all tabpages when ext_messages becomes active.
if (i == kUIMessages && !ui_ext[i] && ext_widgets[i]) {
set_option_value(kOptCmdheight, NUMBER_OPTVAL(0), 0);
command_height();
@@ -722,6 +715,9 @@ void ui_call_event(char *name, Array args)
map_foreach_value(&ui_event_cbs, event_cb, {
Error err = ERROR_INIT;
Object res = nlua_call_ref(event_cb->cb, name, args, kRetNilBool, NULL, &err);
// TODO(bfredl/luukvbaal): should this be documented or reconsidered?
// Why does truthy return from Lua callback mean remote UI should not receive
// the event.
if (LUARET_TRUTHY(res)) {
handled = true;
}