patch 9.2.0802: Memory leak with list_append_dict/dict_add_list on alloc failure
Commit:
https://github.com/vim/vim/commit/74ddc96d412c5248a7bbfbf0a347143e7a44551f
Author: Yasuhiro Matsumoto <
matt...@gmail.com>
Date: Sat Jul 18 14:37:14 2026 +0000
patch 9.2.0802: Memory leak with list_append_dict/dict_add_list on alloc failure
Problem: Memory leak with list_append_dict()/dict_add_list() on alloc
failure
Solution: Free the list/dict when dict_add_list()/list_append_dict()
failed (Yasuhiro Matsumoto)
Various getinfo-style builtins ignored the return of list_append_dict()/
dict_add_list() and leaked the freshly allocated, GC-linked dict or list on
allocation failure. Check the return and unref on failure. Affects
getreginfo/setreg event (register.c), hlget (highlight.c), prop_list
(textprop.c), getmatches (match.c), menu_getinfo (menu.c),
popup_getoptions (popupwin.c), sign_getplaced (sign.c), job_info (job.c),
term_getcursor/term_scrape (terminal.c), diff() (diff.c), complete_info
(insexpand.c), cmdcomplete_info (cmdexpand.c).
related: #20668
related: #20745
Signed-off-by: Yasuhiro Matsumoto <
matt...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 978299375..34edf25ef 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -5042,6 +5042,8 @@ f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T *rettv)
if (li == NULL)
return;
ret = dict_add_list(retdict, "matches", li);
+ if (ret == FAIL)
+ list_unref(li);
for (idx = 0; ret == OK && idx < ccline->xpc->xp_numfiles; idx++)
list_append_string(li, ccline->xpc->xp_files[idx], -1);
}
diff --git a/src/diff.c b/src/diff.c
index a29c43411..b38513693 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -5140,7 +5140,8 @@ f_diff(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
if (hunk_dict == NULL)
goto done;
- list_append_dict(l, hunk_dict);
+ if (list_append_dict(l, hunk_dict) == FAIL)
+ dict_unref(hunk_dict);
}
}
else
diff --git a/src/highlight.c b/src/highlight.c
index f2982baca..cfed85de3 100644
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -5768,8 +5768,8 @@ f_hlget(typval_T *argvars, typval_T *rettv)
if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
{
dict = highlight_get_info(i, resolve_link);
- if (dict != NULL)
- list_append_dict(list, dict);
+ if (dict != NULL && list_append_dict(list, dict) == FAIL)
+ dict_unref(dict);
}
}
}
diff --git a/src/insexpand.c b/src/insexpand.c
index e566ee504..0833340c7 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -4176,6 +4176,8 @@ get_complete_info(list_T *what_list, dict_T *retdict)
return;
ret = dict_add_list(retdict, (has_matches && !has_items)
? "matches" : "items", li);
+ if (ret == FAIL)
+ list_unref(li);
}
if (ret == OK && what_flag & CI_WHAT_SELECTED)
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1)
@@ -4196,7 +4198,10 @@ get_complete_info(list_T *what_list, dict_T *retdict)
return;
ret = list_append_dict(li, di);
if (ret != OK)
+ {
+ dict_unref(di);
return;
+ }
fill_complete_info_dict(di, match, has_matches && has_items);
}
if (compl_curr_match != NULL
@@ -4219,6 +4224,8 @@ get_complete_info(list_T *what_list, dict_T *retdict)
return;
fill_complete_info_dict(di, compl_curr_match, FALSE);
ret = dict_add_dict(retdict, "completed", di);
+ if (ret == FAIL)
+ dict_unref(di);
}
}
}
diff --git a/src/job.c b/src/job.c
index c7c546aa3..7ab788424 100644
--- a/src/job.c
+++ b/src/job.c
@@ -1870,7 +1870,11 @@ job_info(job_T *job, dict_T *dict)
if (l == NULL)
return;
- dict_add_list(dict, "cmd", l);
+ if (dict_add_list(dict, "cmd", l) == FAIL)
+ {
+ list_unref(l);
+ return;
+ }
if (job->jv_argv != NULL)
for (i = 0; job->jv_argv[i] != NULL; i++)
list_append_string(l, (char_u *)job->jv_argv[i], -1);
diff --git a/src/match.c b/src/match.c
index 709ac3049..6bd5ff8d9 100644
--- a/src/match.c
+++ b/src/match.c
@@ -1035,7 +1035,8 @@ f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
list_append_number(l, (varnumber_T)llpos->len);
}
sprintf(buf, "pos%d", i + 1);
- dict_add_list(dict, buf, l);
+ if (dict_add_list(dict, buf, l) == FAIL)
+ list_unref(l);
}
}
else
@@ -1056,7 +1057,8 @@ f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
dict_add_string_len(dict, "conceal", (char_u *)&buf, buflen);
}
# endif
- list_append_dict(rettv->vval.v_list, dict);
+ if (list_append_dict(rettv->vval.v_list, dict) == FAIL)
+ dict_unref(dict);
cur = cur->mit_next;
}
# endif
diff --git a/src/menu.c b/src/menu.c
index 861d78711..ddb381c62 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -2862,7 +2862,11 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict)
if (l == NULL)
return FAIL;
- dict_add_list(dict, "submenus", l);
+ if (dict_add_list(dict, "submenus", l) == FAIL)
+ {
+ list_unref(l);
+ return FAIL;
+ }
// get all the children. Skip PopUp[nvoci].
for (topmenu = menu; topmenu != NULL; topmenu = topmenu->next)
if (!menu_is_hidden(topmenu->dname))
@@ -2950,7 +2954,11 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict)
if (l == NULL)
return FAIL;
- dict_add_list(dict, "submenus", l);
+ if (dict_add_list(dict, "submenus", l) == FAIL)
+ {
+ list_unref(l);
+ return FAIL;
+ }
child = menu->children;
while (child)
{
diff --git a/src/popupwin.c b/src/popupwin.c
index 96fa7895a..cd3fe80da 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -5344,7 +5344,11 @@ get_padding_border(dict_T *dict, int *array, char *name)
if (list == NULL)
return;
- dict_add_list(dict, name, list);
+ if (dict_add_list(dict, name, list) == FAIL)
+ {
+ list_unref(list);
+ return;
+ }
if (array[0] != 1 || array[1] != 1 || array[2] != 1 || array[3] != 1)
for (i = 0; i < 4; ++i)
list_append_number(list, array[i]);
@@ -5371,7 +5375,11 @@ get_borderhighlight(dict_T *dict, win_T *wp)
if (list == NULL)
return;
- dict_add_list(dict, "borderhighlight", list);
+ if (dict_add_list(dict, "borderhighlight", list) == FAIL)
+ {
+ list_unref(list);
+ return;
+ }
// When all highlights are NULL (cleared to empty list), return empty list.
if (i == 4)
return;
@@ -5400,7 +5408,11 @@ get_borderchars(dict_T *dict, win_T *wp)
if (list == NULL)
return;
- dict_add_list(dict, "borderchars", list);
+ if (dict_add_list(dict, "borderchars", list) == FAIL)
+ {
+ list_unref(list);
+ return;
+ }
for (i = 0; i < 8; ++i)
{
len = mb_char2bytes(wp->w_border_char[i], buf);
@@ -5419,16 +5431,24 @@ get_moved_list(dict_T *dict, win_T *wp)
list = list_alloc();
if (list != NULL)
{
- dict_add_list(dict, "moved", list);
- list_append_number(list, wp->w_popup_lnum);
- list_append_number(list, wp->w_popup_mincol);
- list_append_number(list, wp->w_popup_maxcol);
+ if (dict_add_list(dict, "moved", list) == FAIL)
+ list_unref(list);
+ else
+ {
+ list_append_number(list, wp->w_popup_lnum);
+ list_append_number(list, wp->w_popup_mincol);
+ list_append_number(list, wp->w_popup_maxcol);
+ }
}
list = list_alloc();
if (list == NULL)
return;
- dict_add_list(dict, "mousemoved", list);
+ if (dict_add_list(dict, "mousemoved", list) == FAIL)
+ {
+ list_unref(list);
+ return;
+ }
list_append_number(list, wp->w_popup_mouse_row);
list_append_number(list, wp->w_popup_mouse_mincol);
list_append_number(list, wp->w_popup_mouse_maxcol);
diff --git a/src/register.c b/src/register.c
index 5d22dc34a..4ccb68b82 100644
--- a/src/register.c
+++ b/src/register.c
@@ -1134,7 +1134,8 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
for (n = 0; n < reg->y_size; n++)
list_append_string(list, reg->y_array[n].string, (int)reg->y_array[n].length);
list->lv_lock = VAR_FIXED;
- (void)dict_add_list(v_event, "regcontents", list);
+ if (dict_add_list(v_event, "regcontents", list) == FAIL)
+ list_unref(list);
// register name or empty string for unnamed operation
buf[0] = (char_u)oap->regname;
@@ -1229,7 +1230,8 @@ put_do_autocmd(
}
list->lv_lock = VAR_FIXED;
- (void)dict_add_list(v_event, "regcontents", list);
+ if (dict_add_list(v_event, "regcontents", list) == FAIL)
+ list_unref(list);
// register name or empty string for unnamed operation
buf[0] = (char_u)regname;
diff --git a/src/sign.c b/src/sign.c
index 1d735de00..fc701d1ed 100644
--- a/src/sign.c
+++ b/src/sign.c
@@ -1860,8 +1860,8 @@ get_buffer_signs(buf_T *buf, list_T *l)
FOR_ALL_SIGNS_IN_BUF(buf, sign)
{
dict_T *d = sign_get_info(sign);
- if (d != NULL)
- list_append_dict(l, d);
+ if (d != NULL && list_append_dict(l, d) == FAIL)
+ dict_unref(d);
}
}
@@ -1879,7 +1879,11 @@ sign_get_placed_in_buf(buf_T *buf,
if (d == NULL)
return;
- list_append_dict(retlist, d);
+ if (list_append_dict(retlist, d) == FAIL)
+ {
+ dict_unref(d);
+ return;
+ }
dict_add_number(d, "bufnr", (long)buf->b_fnum);
@@ -1887,7 +1891,11 @@ sign_get_placed_in_buf(buf_T *buf,
if (l == NULL)
return;
- dict_add_list(d, "signs", l);
+ if (dict_add_list(d, "signs", l) == FAIL)
+ {
+ list_unref(l);
+ return;
+ }
sign_entry_T *sign = NULL;
FOR_ALL_SIGNS_IN_BUF(buf, sign)
@@ -1901,8 +1909,8 @@ sign_get_placed_in_buf(buf_T *buf,
(lnum == sign->se_lnum && sign_id == sign->se_id))
{
dict_T *sdict = sign_get_info(sign);
- if (sdict != NULL)
- list_append_dict(l, sdict);
+ if (sdict != NULL && list_append_dict(l, sdict) == FAIL)
+ dict_unref(sdict);
}
}
}
diff --git a/src/terminal.c b/src/terminal.c
index 5ec9b71a6..2a2932a88 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -6435,7 +6435,8 @@ f_term_getcursor(typval_T *argvars, typval_T *rettv)
? !term->tl_cursor_blink : term->tl_cursor_blink);
dict_add_number(d, "shape", term->tl_cursor_shape);
dict_add_string(d, "color", cursor_color_get(term->tl_cursor_color));
- list_append_dict(l, d);
+ if (list_append_dict(l, d) == FAIL)
+ dict_unref(d);
}
/*
@@ -6846,7 +6847,11 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
dcell = dict_alloc();
if (dcell == NULL)
break;
- list_append_dict(l, dcell);
+ if (list_append_dict(l, dcell) == FAIL)
+ {
+ dict_unref(dcell);
+ break;
+ }
dict_add_string_len(dcell, "chars", mbs, (int)mbslen);
diff --git a/src/textprop.c b/src/textprop.c
index 6d5a10863..4d3c8d2d7 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -2153,7 +2153,8 @@ get_props_in_line(
prop_fill_dict(d, &prop, buf);
if (add_lnum)
dict_add_number(d, "lnum", lnum);
- list_append_dict(retlist, d);
+ if (list_append_dict(retlist, d) == FAIL)
+ dict_unref(d);
}
}
}
diff --git a/src/version.c b/src/version.c
index e5eaed8d9..c64e48f08 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 802,
/**/
801,
/**/