From: Yasuhiro Matsumoto Date: Sat, 18 Jul 2026 14:37:14 +0000 (+0000) Subject: patch 9.2.0802: Memory leak with list_append_dict/dict_add_list on alloc failure X-Git-Tag: v9.2.0802^0 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=74ddc96d412c5248a7bbfbf0a347143e7a44551f;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 9782993758..34edf25ef0 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 a29c434110..b38513693e 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 f2982bacaa..cfed85de33 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 e566ee5044..0833340c70 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 c7c546aa38..7ab7884246 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 709ac30495..6bd5ff8d90 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 861d787112..ddb381c62a 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 96fa7895a1..cd3fe80dab 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 5d22dc34a8..4ccb68b826 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 1d735de001..fc701d1ed6 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 5ec9b71a65..2a2932a884 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 6d5a108631..4d3c8d2d71 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 e5eaed8d96..c64e48f083 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, /**/