Skip to content

Commit aa951b1

Browse files
authored
Merge pull request neovim#7751 from jamessan/vim-8.0.0590
[RFC] vim-patch:8.0.0590,8.0.0595,8.0.0597,8.0.0606
2 parents 53a530b + 190814b commit aa951b1

File tree

3 files changed

+161
-4
lines changed

3 files changed

+161
-4
lines changed

src/nvim/eval.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5173,6 +5173,8 @@ bool garbage_collect(bool testing)
51735173
ABORTING(set_ref_list)(sub.additional_elements, copyID);
51745174
}
51755175

5176+
ABORTING(set_ref_in_quickfix)(copyID);
5177+
51765178
bool did_free = false;
51775179
if (!abort) {
51785180
// 2. Free lists and dictionaries that are not referenced.

src/nvim/quickfix.c

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ typedef struct qf_list_S {
8585
int qf_nonevalid; // TRUE if not a single valid entry found
8686
char_u *qf_title; // title derived from the command that created
8787
// the error list
88+
typval_T *qf_ctx; // context set by setqflist/setloclist
8889
} qf_list_T;
8990

9091
struct qf_info_S {
@@ -800,7 +801,7 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen,
800801
fields->type = *regmatch.startp[i];
801802
}
802803
if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+
803-
if (linelen > fields->errmsglen) {
804+
if (linelen >= fields->errmsglen) {
804805
// linelen + null terminator
805806
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
806807
fields->errmsglen = linelen + 1;
@@ -811,7 +812,7 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen,
811812
continue;
812813
}
813814
len = (size_t)(regmatch.endp[i] - regmatch.startp[i]);
814-
if (len > fields->errmsglen) {
815+
if (len >= fields->errmsglen) {
815816
// len + null terminator
816817
fields->errmsg = xrealloc(fields->errmsg, len + 1);
817818
fields->errmsglen = len + 1;
@@ -888,7 +889,7 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen,
888889
fields->namebuf[0] = NUL; // no match found, remove file name
889890
fields->lnum = 0; // don't jump to this line
890891
fields->valid = false;
891-
if (linelen > fields->errmsglen) {
892+
if (linelen >= fields->errmsglen) {
892893
// linelen + null terminator
893894
fields->errmsg = xrealloc(fields->errmsg, linelen + 1);
894895
fields->errmsglen = linelen + 1;
@@ -1409,6 +1410,13 @@ void copy_loclist(win_T *from, win_T *to)
14091410
else
14101411
to_qfl->qf_title = NULL;
14111412

1413+
if (from_qfl->qf_ctx != NULL) {
1414+
to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T));
1415+
tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx);
1416+
} else {
1417+
to_qfl->qf_ctx = NULL;
1418+
}
1419+
14121420
if (from_qfl->qf_count) {
14131421
qfline_T *from_qfp;
14141422
qfline_T *prevp;
@@ -2410,6 +2418,8 @@ static void qf_free(qf_info_T *qi, int idx)
24102418
qi->qf_lists[idx].qf_start = NULL;
24112419
qi->qf_lists[idx].qf_ptr = NULL;
24122420
qi->qf_lists[idx].qf_title = NULL;
2421+
tv_free(qi->qf_lists[idx].qf_ctx);
2422+
qi->qf_lists[idx].qf_ctx = NULL;
24132423
qi->qf_lists[idx].qf_index = 0;
24142424
qi->qf_lists[idx].qf_start = NULL;
24152425
qi->qf_lists[idx].qf_last = NULL;
@@ -4060,6 +4070,7 @@ enum {
40604070
QF_GETLIST_ITEMS = 0x2,
40614071
QF_GETLIST_NR = 0x4,
40624072
QF_GETLIST_WINID = 0x8,
4073+
QF_GETLIST_CONTEXT = 0x10,
40634074
QF_GETLIST_ALL = 0xFF
40644075
};
40654076

@@ -4110,6 +4121,10 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
41104121
flags |= QF_GETLIST_WINID;
41114122
}
41124123

4124+
if (tv_dict_find(what, S_LEN("context")) != NULL) {
4125+
flags |= QF_GETLIST_CONTEXT;
4126+
}
4127+
41134128
if (flags & QF_GETLIST_TITLE) {
41144129
char_u *t = qi->qf_lists[qf_idx].qf_title;
41154130
if (t == NULL) {
@@ -4127,6 +4142,20 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
41274142
}
41284143
}
41294144

4145+
if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) {
4146+
if (qi->qf_lists[qf_idx].qf_ctx != NULL) {
4147+
di = tv_dict_item_alloc_len(S_LEN("context"));
4148+
if (di != NULL) {
4149+
tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv);
4150+
if (tv_dict_add(retdict, di) == FAIL) {
4151+
tv_dict_item_free(di);
4152+
}
4153+
}
4154+
} else {
4155+
status = tv_dict_add_str(retdict, S_LEN("context"), "");
4156+
}
4157+
}
4158+
41304159
return status;
41314160
}
41324161

@@ -4249,7 +4278,10 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)
42494278
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
42504279
// Use the specified quickfix/location list
42514280
if (di->di_tv.v_type == VAR_NUMBER) {
4252-
qf_idx = (int)di->di_tv.vval.v_number - 1;
4281+
// for zero use the current list
4282+
if (di->di_tv.vval.v_number != 0) {
4283+
qf_idx = (int)di->di_tv.vval.v_number - 1;
4284+
}
42534285
if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
42544286
return FAIL;
42554287
}
@@ -4276,6 +4308,14 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)
42764308
}
42774309
}
42784310

4311+
if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) {
4312+
tv_free(qi->qf_lists[qf_idx].qf_ctx);
4313+
4314+
typval_T *ctx = xcalloc(1, sizeof(typval_T));
4315+
tv_copy(&di->di_tv, ctx);
4316+
qi->qf_lists[qf_idx].qf_ctx = ctx;
4317+
}
4318+
42794319
return retval;
42804320
}
42814321

@@ -4362,6 +4402,42 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,
43624402
return retval;
43634403
}
43644404

4405+
static bool mark_quickfix_ctx(qf_info_T *qi, int copyID)
4406+
{
4407+
bool abort = false;
4408+
4409+
for (int i = 0; i < LISTCOUNT && !abort; i++) {
4410+
typval_T *ctx = qi->qf_lists[i].qf_ctx;
4411+
if (ctx != NULL && ctx->v_type != VAR_NUMBER
4412+
&& ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) {
4413+
abort = set_ref_in_item(ctx, copyID, NULL, NULL);
4414+
}
4415+
}
4416+
4417+
return abort;
4418+
}
4419+
4420+
/// Mark the context of the quickfix list and the location lists (if present) as
4421+
/// "in use". So that garabage collection doesn't free the context.
4422+
bool set_ref_in_quickfix(int copyID)
4423+
{
4424+
bool abort = mark_quickfix_ctx(&ql_info, copyID);
4425+
if (abort) {
4426+
return abort;
4427+
}
4428+
4429+
FOR_ALL_TAB_WINDOWS(tp, win) {
4430+
if (win->w_llist != NULL) {
4431+
abort = mark_quickfix_ctx(win->w_llist, copyID);
4432+
if (abort) {
4433+
return abort;
4434+
}
4435+
}
4436+
}
4437+
4438+
return abort;
4439+
}
4440+
43654441
/*
43664442
* ":[range]cbuffer [bufnr]" command.
43674443
* ":[range]caddbuffer [bufnr]" command.

src/nvim/testdir/test_quickfix.vim

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,6 +1754,69 @@ func Xproperty_tests(cchar)
17541754
if a:cchar == 'l'
17551755
call assert_equal({}, getloclist(99, {'title': 1}))
17561756
endif
1757+
1758+
" Context related tests
1759+
call g:Xsetlist([], 'a', {'context':[1,2,3]})
1760+
call test_garbagecollect_now()
1761+
let d = g:Xgetlist({'context':1})
1762+
call assert_equal([1,2,3], d.context)
1763+
call g:Xsetlist([], 'a', {'context':{'color':'green'}})
1764+
let d = g:Xgetlist({'context':1})
1765+
call assert_equal({'color':'green'}, d.context)
1766+
call g:Xsetlist([], 'a', {'context':"Context info"})
1767+
let d = g:Xgetlist({'context':1})
1768+
call assert_equal("Context info", d.context)
1769+
call g:Xsetlist([], 'a', {'context':246})
1770+
let d = g:Xgetlist({'context':1})
1771+
call assert_equal(246, d.context)
1772+
if a:cchar == 'l'
1773+
" Test for copying context across two different location lists
1774+
new | only
1775+
let w1_id = win_getid()
1776+
let l = [1]
1777+
call setloclist(0, [], 'a', {'context':l})
1778+
new
1779+
let w2_id = win_getid()
1780+
call add(l, 2)
1781+
call assert_equal([1, 2], getloclist(w1_id, {'context':1}).context)
1782+
call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
1783+
unlet! l
1784+
call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context)
1785+
only
1786+
call setloclist(0, [], 'f')
1787+
call assert_equal({}, getloclist(0, {'context':1}))
1788+
endif
1789+
1790+
" Test for changing the context of previous quickfix lists
1791+
call g:Xsetlist([], 'f')
1792+
Xexpr "One"
1793+
Xexpr "Two"
1794+
Xexpr "Three"
1795+
call g:Xsetlist([], ' ', {'context' : [1], 'nr' : 1})
1796+
call g:Xsetlist([], ' ', {'context' : [2], 'nr' : 2})
1797+
" Also, check for setting the context using quickfix list number zero.
1798+
call g:Xsetlist([], ' ', {'context' : [3], 'nr' : 0})
1799+
call test_garbagecollect_now()
1800+
let l = g:Xgetlist({'nr' : 1, 'context' : 1})
1801+
call assert_equal([1], l.context)
1802+
let l = g:Xgetlist({'nr' : 2, 'context' : 1})
1803+
call assert_equal([2], l.context)
1804+
let l = g:Xgetlist({'nr' : 3, 'context' : 1})
1805+
call assert_equal([3], l.context)
1806+
1807+
" Test for changing the context through reference and for garbage
1808+
" collection of quickfix context
1809+
let l = ["red"]
1810+
call g:Xsetlist([], ' ', {'context' : l})
1811+
call add(l, "blue")
1812+
let x = g:Xgetlist({'context' : 1})
1813+
call add(x.context, "green")
1814+
call assert_equal(["red", "blue", "green"], l)
1815+
call assert_equal(["red", "blue", "green"], x.context)
1816+
unlet l
1817+
call test_garbagecollect_now()
1818+
let m = g:Xgetlist({'context' : 1})
1819+
call assert_equal(["red", "blue", "green"], m.context)
17571820
endfunc
17581821

17591822
func Test_qf_property()
@@ -2023,3 +2086,19 @@ func Test_qf_free()
20232086
call XfreeTests('c')
20242087
call XfreeTests('l')
20252088
endfunc
2089+
2090+
" Test for buffer overflow when parsing lines and adding new entries to
2091+
" the quickfix list.
2092+
func Test_bufoverflow()
2093+
set efm=%f:%l:%m
2094+
cgetexpr ['File1:100:' . repeat('x', 1025)]
2095+
2096+
set efm=%+GCompiler:\ %.%#,%f:%l:%m
2097+
cgetexpr ['Compiler: ' . repeat('a', 1015), 'File1:10:Hello World']
2098+
2099+
set efm=%DEntering\ directory\ %f,%f:%l:%m
2100+
cgetexpr ['Entering directory ' . repeat('a', 1006),
2101+
\ 'File1:10:Hello World']
2102+
set efm&vim
2103+
endfunc
2104+

0 commit comments

Comments
 (0)