RHEL-2159 vim: Heap Use After Free in function ins_compl_get_exp in vim/vim
Resolves: RHEL-2159
This commit is contained in:
parent
2bba9e8166
commit
99204e95ee
262
vim-CVE-2023-4752.patch
Normal file
262
vim-CVE-2023-4752.patch
Normal file
@ -0,0 +1,262 @@
|
||||
diff --git a/src/insexpand.c b/src/insexpand.c
|
||||
index 8d76c68..2ce675d 100644
|
||||
--- a/src/insexpand.c
|
||||
+++ b/src/insexpand.c
|
||||
@@ -2162,7 +2162,8 @@ ins_compl_next_buf(buf_T *buf, int flag)
|
||||
|
||||
if (flag == 'w') // just windows
|
||||
{
|
||||
- if (buf == curbuf || wp == NULL) // first call for this flag/expansion
|
||||
+ if (buf == curbuf || !win_valid(wp))
|
||||
+ // first call for this flag/expansion or window was closed
|
||||
wp = curwin;
|
||||
while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin
|
||||
&& wp->w_buffer->b_scanned)
|
||||
@@ -2675,10 +2676,12 @@ ins_compl_get_exp(pos_T *ini)
|
||||
{
|
||||
static pos_T first_match_pos;
|
||||
static pos_T last_match_pos;
|
||||
- static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete'
|
||||
+ static char_u *e_cpt; // curr. entry in 'complete'
|
||||
+ static char_u *e_cpt_copy; // copy of curr. entry in 'complete'
|
||||
static int found_all = FALSE; // Found all matches of a
|
||||
// certain type.
|
||||
static buf_T *ins_buf = NULL; // buffer being scanned
|
||||
+ static int st_cleared = FALSE;
|
||||
|
||||
pos_T *pos;
|
||||
char_u **matches;
|
||||
@@ -2697,12 +2700,34 @@ ins_compl_get_exp(pos_T *ini)
|
||||
|
||||
if (!compl_started)
|
||||
{
|
||||
- FOR_ALL_BUFFERS(ins_buf)
|
||||
- ins_buf->b_scanned = 0;
|
||||
+ buf_T *buf;
|
||||
+
|
||||
+ FOR_ALL_BUFFERS(buf)
|
||||
+ buf->b_scanned = 0;
|
||||
+ if (!st_cleared)
|
||||
+ {
|
||||
+ // this code does not match upstream commit, upstream clears struct `st`
|
||||
+ // which includes the members which are arguments of `CLEAR_FIELD()` macros
|
||||
+ // below. The code below should do the same thing as upstream `CLEAR_FIELD(st)`
|
||||
+ CLEAR_FIELD(e_cpt);
|
||||
+ CLEAR_FIELD(e_cpt_copy);
|
||||
+ CLEAR_FIELD(ins_buf);
|
||||
+ CLEAR_FIELD(pos);
|
||||
+ CLEAR_FIELD(set_match_pos);
|
||||
+ CLEAR_FIELD(first_match_pos);
|
||||
+ CLEAR_FIELD(last_match_pos);
|
||||
+ CLEAR_FIELD(found_all);
|
||||
+ CLEAR_FIELD(dict);
|
||||
+ CLEAR_FIELD(dict_f);
|
||||
+ st_cleared = TRUE;
|
||||
+ }
|
||||
found_all = FALSE;
|
||||
ins_buf = curbuf;
|
||||
- e_cpt = (compl_cont_status & CONT_LOCAL)
|
||||
- ? (char_u *)"." : curbuf->b_p_cpt;
|
||||
+ vim_free(e_cpt_copy);
|
||||
+ // Make a copy of 'complete', if case the buffer is wiped out.
|
||||
+ e_cpt_copy = vim_strsave((compl_cont_status & CONT_LOCAL)
|
||||
+ ? (char_u *)"." : curbuf->b_p_cpt);
|
||||
+ e_cpt = e_cpt_copy == NULL ? (char_u *)"" : e_cpt_copy;
|
||||
last_match_pos = first_match_pos = *ini;
|
||||
}
|
||||
else if (ins_buf != curbuf && !buf_valid(ins_buf))
|
||||
@@ -3100,7 +3125,7 @@ ins_compl_get_exp(pos_T *ini)
|
||||
else
|
||||
{
|
||||
// Mark a buffer scanned when it has been scanned completely
|
||||
- if (type == 0 || type == CTRL_X_PATH_PATTERNS)
|
||||
+ if (buf_valid(ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS))
|
||||
ins_buf->b_scanned = TRUE;
|
||||
|
||||
compl_started = FALSE;
|
||||
@@ -3210,6 +3235,7 @@ ins_compl_next(
|
||||
int found_end = FALSE;
|
||||
int advance;
|
||||
int started = compl_started;
|
||||
+ buf_T *orig_curbuf = curbuf;
|
||||
|
||||
// When user complete function return -1 for findstart which is next
|
||||
// time of 'always', compl_shown_match become NULL.
|
||||
@@ -3342,6 +3368,13 @@ ins_compl_next(
|
||||
}
|
||||
}
|
||||
|
||||
+ if (curbuf != orig_curbuf)
|
||||
+ {
|
||||
+ // In case some completion function switched buffer, don't want to
|
||||
+ // insert the completion elsewhere.
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
// Insert the text of the new completion, or the compl_leader.
|
||||
if (compl_no_insert && !started)
|
||||
{
|
||||
diff --git a/src/move.c b/src/move.c
|
||||
index 8ac1d0e..55cbd24 100644
|
||||
--- a/src/move.c
|
||||
+++ b/src/move.c
|
||||
@@ -649,6 +649,7 @@ cursor_valid(void)
|
||||
void
|
||||
validate_cursor(void)
|
||||
{
|
||||
+ check_cursor_lnum();
|
||||
check_cursor_moved(curwin);
|
||||
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
|
||||
curs_columns(TRUE);
|
||||
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
|
||||
index 108c528..e800864 100644
|
||||
--- a/src/testdir/Make_all.mak
|
||||
+++ b/src/testdir/Make_all.mak
|
||||
@@ -92,6 +92,7 @@ NEW_TESTS = \
|
||||
test_conceal \
|
||||
test_const \
|
||||
test_cpoptions \
|
||||
+ test_crash \
|
||||
test_crypt \
|
||||
test_cscope \
|
||||
test_cursor_func \
|
||||
@@ -348,6 +349,7 @@ NEW_TESTS_RES = \
|
||||
test_conceal.res \
|
||||
test_const.res \
|
||||
test_cpoptions.res \
|
||||
+ test_crash.res \
|
||||
test_crypt.res \
|
||||
test_cscope.res \
|
||||
test_cursor_func.res \
|
||||
diff --git a/src/testdir/crash/poc_tagfunc.vim b/src/testdir/crash/poc_tagfunc.vim
|
||||
new file mode 100644
|
||||
index 0000000..49d9b6f
|
||||
--- /dev/null
|
||||
+++ b/src/testdir/crash/poc_tagfunc.vim
|
||||
@@ -0,0 +1,6 @@
|
||||
+fu Tagfunc(t,f,o)
|
||||
+ bw
|
||||
+endf
|
||||
+set tagfunc=Tagfunc
|
||||
+n0
|
||||
+sil0norm0i
|
||||
diff --git a/src/testdir/term_util.vim b/src/testdir/term_util.vim
|
||||
index 8541ef3..3a06019 100644
|
||||
--- a/src/testdir/term_util.vim
|
||||
+++ b/src/testdir/term_util.vim
|
||||
@@ -54,6 +54,7 @@ endfunc
|
||||
" "cols" - width of the terminal window (max. 78)
|
||||
" "statusoff" - number of lines the status is offset from default
|
||||
" "wait_for_ruler" - if zero then don't wait for ruler to show
|
||||
+" "cmd" - run any other command, e.g. "xxd" (used in xxd test)
|
||||
func RunVimInTerminal(arguments, options)
|
||||
" If Vim doesn't exit a swap file remains, causing other tests to fail.
|
||||
" Remove it here.
|
||||
@@ -88,7 +89,11 @@ func RunVimInTerminal(arguments, options)
|
||||
let reset_u7 = ' --cmd "set t_u7=" '
|
||||
endif
|
||||
|
||||
- let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
|
||||
+ if empty(get(a:options, 'cmd', ''))
|
||||
+ let cmd = GetVimCommandCleanTerm() .. reset_u7 .. a:arguments
|
||||
+ else
|
||||
+ let cmd = get(a:options, 'cmd')
|
||||
+ endif
|
||||
|
||||
let options = #{curwin: 1}
|
||||
if &termwinsize == ''
|
||||
@@ -114,7 +119,7 @@ func RunVimInTerminal(arguments, options)
|
||||
|
||||
call TermWait(buf)
|
||||
|
||||
- if get(a:options, 'wait_for_ruler', 1)
|
||||
+ if get(a:options, 'wait_for_ruler', 1) && empty(get(a:options, 'cmd', ''))
|
||||
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
|
||||
" the status line of the last window. This can be quite slow (e.g. when
|
||||
" using valgrind).
|
||||
diff --git a/src/testdir/test_crash.vim b/src/testdir/test_crash.vim
|
||||
new file mode 100644
|
||||
index 0000000..4be1b49
|
||||
--- /dev/null
|
||||
+++ b/src/testdir/test_crash.vim
|
||||
@@ -0,0 +1,40 @@
|
||||
+" Some tests, that used to crash Vim
|
||||
+source check.vim
|
||||
+source screendump.vim
|
||||
+
|
||||
+CheckScreendump
|
||||
+
|
||||
+func Test_crash1()
|
||||
+ if !executable('sh')
|
||||
+ throw 'Skipped: sh not executable!'
|
||||
+ endif
|
||||
+ " The following used to crash Vim
|
||||
+ let opts = #{cmd: 'sh'}
|
||||
+ let vim = GetVimProg()
|
||||
+
|
||||
+ let buf = RunVimInTerminal('sh', opts)
|
||||
+
|
||||
+ let file = 'crash/poc_tagfunc.vim'
|
||||
+ let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
|
||||
+ let args = printf(cmn_args, vim, file)
|
||||
+ call term_sendkeys(buf, args ..
|
||||
+ \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt' .. "\<cr>")
|
||||
+
|
||||
+ call TermWait(buf, 100)
|
||||
+
|
||||
+ " clean up
|
||||
+ exe buf .. "bw!"
|
||||
+
|
||||
+ sp X_crash1_result.txt
|
||||
+
|
||||
+ let expected = [
|
||||
+ \ 'crash 5: [OK]',
|
||||
+ \ ]
|
||||
+
|
||||
+ call assert_equal(expected, getline(1, '$'))
|
||||
+ bw!
|
||||
+
|
||||
+ call delete('X_crash1_result.txt')
|
||||
+endfunc
|
||||
+
|
||||
+" vim: shiftwidth=2 sts=2 expandtab
|
||||
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
|
||||
index 613e952..24232d8 100644
|
||||
--- a/src/testdir/test_ins_complete.vim
|
||||
+++ b/src/testdir/test_ins_complete.vim
|
||||
@@ -496,9 +496,8 @@ func Test_pum_with_preview_win()
|
||||
|
||||
call writefile(lines, 'Xpreviewscript')
|
||||
let buf = RunVimInTerminal('-S Xpreviewscript', #{rows: 12})
|
||||
- call TermWait(buf, 50)
|
||||
call term_sendkeys(buf, "Gi\<C-X>\<C-O>")
|
||||
- call TermWait(buf, 100)
|
||||
+ call TermWait(buf, 200)
|
||||
call term_sendkeys(buf, "\<C-N>")
|
||||
call VerifyScreenDump(buf, 'Test_pum_with_preview_win', {})
|
||||
|
||||
@@ -724,7 +723,23 @@ func Test_z1_complete_no_history()
|
||||
exe "normal owh\<C-X>\<C-K>"
|
||||
exe "normal owh\<C-N>"
|
||||
call assert_equal(currmess, execute('messages'))
|
||||
- close!
|
||||
+ bwipe!
|
||||
+endfunc
|
||||
+
|
||||
+func s:Tagfunc(t,f,o)
|
||||
+ bwipe!
|
||||
+ return []
|
||||
+endfunc
|
||||
+
|
||||
+" This was using freed memory, since 'complete' was in a wiped out buffer.
|
||||
+" Also using a window that was closed.
|
||||
+func Test_tagfunc_wipes_out_buffer()
|
||||
+ new
|
||||
+ set complete=.,t,w,b,u,i
|
||||
+ se tagfunc=s:Tagfunc
|
||||
+ sil norm i␎
|
||||
+ bwipe!
|
||||
endfunc
|
||||
|
||||
+
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
15
vim.spec
15
vim.spec
@ -27,7 +27,7 @@ Summary: The VIM editor
|
||||
URL: http://www.vim.org/
|
||||
Name: vim
|
||||
Version: %{baseversion}.%{patchlevel}
|
||||
Release: 21%{?dist}
|
||||
Release: 22%{?dist}
|
||||
License: Vim and MIT
|
||||
Source0: ftp://ftp.vim.org/pub/vim/unix/vim-%{baseversion}-%{patchlevel}.tar.bz2
|
||||
Source1: virc
|
||||
@ -138,6 +138,15 @@ Patch3052: 0001-patch-8.2.5037-cursor-position-may-be-invalid-after-.patch
|
||||
Patch3053:0001-patch-9.0.0339-no-check-if-the-return-value-of-XChan.patch
|
||||
# RHEL-40602 CVE-2021-3903 vim: heap-based buffer overflow vulnerability
|
||||
Patch3054: 0001-patch-8.2.3564-invalid-memory-access-when-scrolling-.patch
|
||||
# RHEL-2159 vim: Heap Use After Free in function ins_compl_get_exp in vim/vim
|
||||
# combined patch of some parts of the following commits:
|
||||
# https://github.com/vim/vim/commit/e1dc9a6275
|
||||
# https://github.com/vim/vim/commit/ee9166eb3b
|
||||
# https://github.com/vim/vim/commit/0ff01835a4
|
||||
# https://github.com/vim/vim/commit/d4566c14e7
|
||||
# https://github.com/vim/vim/commit/d979d64fa2
|
||||
# https://github.com/vim/vim/commit/e2528ae111
|
||||
Patch3055: vim-CVE-2023-4752.patch
|
||||
|
||||
# gcc is no longer in buildroot by default
|
||||
BuildRequires: gcc
|
||||
@ -379,6 +388,7 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk
|
||||
%patch3052 -p1 -b .cve1927
|
||||
%patch3053 -p1 -b .cve47024
|
||||
%patch -P 3054 -p1 -b .cve2021-3903
|
||||
%patch -P 3055 -p1 -b .CVE-2023-4752
|
||||
|
||||
%build
|
||||
cd src
|
||||
@ -936,6 +946,9 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Tue Feb 25 2025 Zdenek Dohnal <zdohnal@redhat.com> - 2:8.2.2637-22
|
||||
- RHEL-2159 vim: Heap Use After Free in function ins_compl_get_exp in vim/vim
|
||||
|
||||
* Mon Aug 05 2024 Zdenek Dohnal <zdohnal@redhat.com> - 2:8.2.2637-21
|
||||
- RHEL-40602 CVE-2021-3903 vim: heap-based buffer overflow vulnerability
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user