diff options
| -rw-r--r-- | src/nvim/eval.c | 2 | ||||
| -rw-r--r-- | src/nvim/quickfix.c | 84 | ||||
| -rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 79 | 
3 files changed, 161 insertions, 4 deletions
| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1461ddb8f9..2355c8c813 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5173,6 +5173,8 @@ bool garbage_collect(bool testing)      ABORTING(set_ref_list)(sub.additional_elements, copyID);    } +  ABORTING(set_ref_in_quickfix)(copyID); +    bool did_free = false;    if (!abort) {      // 2. Free lists and dictionaries that are not referenced. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d77cec4fd8..c5d03e73f1 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -85,6 +85,7 @@ typedef struct qf_list_S {    int qf_nonevalid;             // TRUE if not a single valid entry found    char_u      *qf_title;        // title derived from the command that created                                  // the error list +  typval_T    *qf_ctx;          // context set by setqflist/setloclist  } qf_list_T;  struct qf_info_S { @@ -800,7 +801,7 @@ restofline:          fields->type = *regmatch.startp[i];        }        if (fmt_ptr->flags == '+' && !qi->qf_multiscan) {       // %+ -        if (linelen > fields->errmsglen) { +        if (linelen >= fields->errmsglen) {            // linelen + null terminator            fields->errmsg = xrealloc(fields->errmsg, linelen + 1);            fields->errmsglen = linelen + 1; @@ -811,7 +812,7 @@ restofline:            continue;          }          len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); -        if (len > fields->errmsglen) { +        if (len >= fields->errmsglen) {            // len + null terminator            fields->errmsg = xrealloc(fields->errmsg, len + 1);            fields->errmsglen = len + 1; @@ -888,7 +889,7 @@ restofline:      fields->namebuf[0] = NUL;                // no match found, remove file name      fields->lnum = 0;                        // don't jump to this line      fields->valid = false; -    if (linelen > fields->errmsglen) { +    if (linelen >= fields->errmsglen) {        // linelen + null terminator        fields->errmsg = xrealloc(fields->errmsg, linelen + 1);        fields->errmsglen = linelen + 1; @@ -1409,6 +1410,13 @@ void copy_loclist(win_T *from, win_T *to)      else        to_qfl->qf_title = NULL; +    if (from_qfl->qf_ctx != NULL) { +      to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T)); +      tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx); +    } else { +      to_qfl->qf_ctx = NULL; +    } +      if (from_qfl->qf_count) {        qfline_T    *from_qfp;        qfline_T    *prevp; @@ -2410,6 +2418,8 @@ static void qf_free(qf_info_T *qi, int idx)    qi->qf_lists[idx].qf_start = NULL;    qi->qf_lists[idx].qf_ptr = NULL;    qi->qf_lists[idx].qf_title = NULL; +  tv_free(qi->qf_lists[idx].qf_ctx); +  qi->qf_lists[idx].qf_ctx = NULL;    qi->qf_lists[idx].qf_index = 0;    qi->qf_lists[idx].qf_start = NULL;    qi->qf_lists[idx].qf_last = NULL; @@ -4060,6 +4070,7 @@ enum {    QF_GETLIST_ITEMS = 0x2,    QF_GETLIST_NR = 0x4,    QF_GETLIST_WINID = 0x8, +  QF_GETLIST_CONTEXT = 0x10,    QF_GETLIST_ALL = 0xFF  }; @@ -4110,6 +4121,10 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)      flags |= QF_GETLIST_WINID;    } +  if (tv_dict_find(what, S_LEN("context")) != NULL) { +    flags |= QF_GETLIST_CONTEXT; +  } +    if (flags & QF_GETLIST_TITLE) {      char_u *t = qi->qf_lists[qf_idx].qf_title;      if (t == NULL) { @@ -4127,6 +4142,20 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)      }    } +  if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { +    if (qi->qf_lists[qf_idx].qf_ctx != NULL) { +      di = tv_dict_item_alloc_len(S_LEN("context")); +      if (di != NULL) { +        tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv); +        if (tv_dict_add(retdict, di) == FAIL) { +          tv_dict_item_free(di); +        } +      } +    } else { +      status = tv_dict_add_str(retdict, S_LEN("context"), ""); +    } +  } +    return status;  } @@ -4249,7 +4278,10 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)    if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {      // Use the specified quickfix/location list      if (di->di_tv.v_type == VAR_NUMBER) { -      qf_idx = (int)di->di_tv.vval.v_number - 1; +      // for zero use the current list +      if (di->di_tv.vval.v_number != 0) { +        qf_idx = (int)di->di_tv.vval.v_number - 1; +      }        if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {          return FAIL;        } @@ -4276,6 +4308,14 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action)      }    } +  if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { +    tv_free(qi->qf_lists[qf_idx].qf_ctx); + +    typval_T *ctx = xcalloc(1, sizeof(typval_T)); +    tv_copy(&di->di_tv, ctx); +    qi->qf_lists[qf_idx].qf_ctx = ctx; +  } +    return retval;  } @@ -4362,6 +4402,42 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title,    return retval;  } +static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) +{ +  bool abort = false; + +  for (int i = 0; i < LISTCOUNT && !abort; i++) { +    typval_T *ctx = qi->qf_lists[i].qf_ctx; +    if (ctx != NULL && ctx->v_type != VAR_NUMBER +        && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) { +      abort = set_ref_in_item(ctx, copyID, NULL, NULL); +    } +  } + +  return abort; +} + +/// Mark the context of the quickfix list and the location lists (if present) as +/// "in use". So that garabage collection doesn't free the context. +bool set_ref_in_quickfix(int copyID) +{ +  bool abort = mark_quickfix_ctx(&ql_info, copyID); +  if (abort) { +    return abort; +  } + +  FOR_ALL_TAB_WINDOWS(tp, win) { +    if (win->w_llist != NULL) { +      abort = mark_quickfix_ctx(win->w_llist, copyID); +      if (abort) { +        return abort; +      } +    } +  } + +  return abort; +} +  /*   * ":[range]cbuffer [bufnr]" command.   * ":[range]caddbuffer [bufnr]" command. diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index dd64e37ce3..30023dddc9 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1754,6 +1754,69 @@ func Xproperty_tests(cchar)      if a:cchar == 'l'  	call assert_equal({}, getloclist(99, {'title': 1}))      endif + +    " Context related tests +    call g:Xsetlist([], 'a', {'context':[1,2,3]}) +    call test_garbagecollect_now() +    let d = g:Xgetlist({'context':1}) +    call assert_equal([1,2,3], d.context) +    call g:Xsetlist([], 'a', {'context':{'color':'green'}}) +    let d = g:Xgetlist({'context':1}) +    call assert_equal({'color':'green'}, d.context) +    call g:Xsetlist([], 'a', {'context':"Context info"}) +    let d = g:Xgetlist({'context':1}) +    call assert_equal("Context info", d.context) +    call g:Xsetlist([], 'a', {'context':246}) +    let d = g:Xgetlist({'context':1}) +    call assert_equal(246, d.context) +    if a:cchar == 'l' +	" Test for copying context across two different location lists +	new | only +	let w1_id = win_getid() +	let l = [1] +	call setloclist(0, [], 'a', {'context':l}) +	new +	let w2_id = win_getid() +	call add(l, 2) +	call assert_equal([1, 2], getloclist(w1_id, {'context':1}).context) +	call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context) +	unlet! l +	call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context) +	only +	call setloclist(0, [], 'f') +	call assert_equal({}, getloclist(0, {'context':1})) +    endif + +    " Test for changing the context of previous quickfix lists +    call g:Xsetlist([], 'f') +    Xexpr "One" +    Xexpr "Two" +    Xexpr "Three" +    call g:Xsetlist([], ' ', {'context' : [1], 'nr' : 1}) +    call g:Xsetlist([], ' ', {'context' : [2], 'nr' : 2}) +    " Also, check for setting the context using quickfix list number zero. +    call g:Xsetlist([], ' ', {'context' : [3], 'nr' : 0}) +    call test_garbagecollect_now() +    let l = g:Xgetlist({'nr' : 1, 'context' : 1}) +    call assert_equal([1], l.context) +    let l = g:Xgetlist({'nr' : 2, 'context' : 1}) +    call assert_equal([2], l.context) +    let l = g:Xgetlist({'nr' : 3, 'context' : 1}) +    call assert_equal([3], l.context) + +    " Test for changing the context through reference and for garbage +    " collection of quickfix context +    let l = ["red"] +    call g:Xsetlist([], ' ', {'context' : l}) +    call add(l, "blue") +    let x = g:Xgetlist({'context' : 1}) +    call add(x.context, "green") +    call assert_equal(["red", "blue", "green"], l) +    call assert_equal(["red", "blue", "green"], x.context) +    unlet l +    call test_garbagecollect_now() +    let m = g:Xgetlist({'context' : 1}) +    call assert_equal(["red", "blue", "green"], m.context)  endfunc  func Test_qf_property() @@ -2023,3 +2086,19 @@ func Test_qf_free()    call XfreeTests('c')    call XfreeTests('l')  endfunc + +" Test for buffer overflow when parsing lines and adding new entries to +" the quickfix list. +func Test_bufoverflow() +  set efm=%f:%l:%m +  cgetexpr ['File1:100:' . repeat('x', 1025)] + +  set efm=%+GCompiler:\ %.%#,%f:%l:%m +  cgetexpr ['Compiler: ' . repeat('a', 1015), 'File1:10:Hello World'] + +  set efm=%DEntering\ directory\ %f,%f:%l:%m +  cgetexpr ['Entering directory ' . repeat('a', 1006), +	      \ 'File1:10:Hello World'] +  set efm&vim +endfunc + | 
