commit aed1f8c37730103a38f7248f22826892cd6f556e
parent 1629493f72e8b839cbe039cc99f5a49729e96e8a
Author: zeertzjq <zeertzjq@outlook.com>
Date: Mon, 12 Jan 2026 07:04:36 +0800
vim-patch:9.1.2079: use-after-free with 'qftf' wiping buffer (#37364)
Problem: use-after-free with 'quickfixtextfunc' wiping buffer
(henices)
Solution: Evaluate 'quickfixtextfunc' with textlock enabled.
closes: vim/vim#19142
https://github.com/vim/vim/commit/300ea1133fba310ae8acd7fadc3ab3cc24e8402f
Co-authored-by: Christian Brabandt <cb@256bit.org>
Diffstat:
5 files changed, 34 insertions(+), 0 deletions(-)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
@@ -4897,6 +4897,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|lambda| or a |Funcref|. See |option-value-function| for more
information.
+ It is not allowed to change text or jump to another window while
+ evaluating 'qftf' |textlock|.
+
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
@@ -5115,6 +5115,9 @@ vim.go.pyx = vim.go.pyxversion
--- `lambda` or a `Funcref`. See `option-value-function` for more
--- information.
---
+--- It is not allowed to change text or jump to another window while
+--- evaluating 'qftf' `textlock`.
+---
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
---
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
@@ -6729,6 +6729,9 @@ local options = {
|lambda| or a |Funcref|. See |option-value-function| for more
information.
+ It is not allowed to change text or jump to another window while
+ evaluating 'qftf' |textlock|.
+
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
]=],
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
@@ -4327,6 +4327,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, int start_idx, int e
args[0].v_type = VAR_DICT;
args[0].vval.v_dict = dict;
+ textlock++;
if (callback_call(cb, 1, args, &rettv)) {
if (rettv.v_type == VAR_LIST) {
qftf_list = rettv.vval.v_list;
@@ -4334,6 +4335,7 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, int start_idx, int e
}
tv_clear(&rettv);
}
+ textlock--;
tv_dict_unref(dict);
}
diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim
@@ -6991,4 +6991,27 @@ func Test_quickfix_restore_current_win()
bw! Xb
endfunc
+func Test_quickfixtextfunc_wipes_buffer()
+ let g:crash=""
+ new
+ fu QFexpr(dummy)
+ bw
+ endfu
+ try
+ set quickfixtextfunc=QFexpr
+ lad "['0:4:e']"
+ lw
+ catch /^Vim\%((\S\+)\)\=:E565:/
+ let g:crash='caught'
+ endtry
+ " close location list window
+ bw
+ delfunc QFexpr
+ set quickfixtextfunc=
+ call assert_equal('caught', g:crash)
+ unlet g:crash
+ " close the newly opened window
+ bw
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab