mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.0678: [security]: use-after-free in alist_add()
Problem: [security]: use-after-free in alist_add() (SuyueGuo) Solution: Lock the current window, so that the reference to the argument list remains valid. This fixes CVE-2024-43374 Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
@@ -184,6 +184,8 @@ alist_set(
|
|||||||
/*
|
/*
|
||||||
* Add file "fname" to argument list "al".
|
* Add file "fname" to argument list "al".
|
||||||
* "fname" must have been allocated and "al" must have been checked for room.
|
* "fname" must have been allocated and "al" must have been checked for room.
|
||||||
|
*
|
||||||
|
* May trigger Buf* autocommands
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
alist_add(
|
alist_add(
|
||||||
@@ -196,6 +198,7 @@ alist_add(
|
|||||||
if (check_arglist_locked() == FAIL)
|
if (check_arglist_locked() == FAIL)
|
||||||
return;
|
return;
|
||||||
arglist_locked = TRUE;
|
arglist_locked = TRUE;
|
||||||
|
curwin->w_locked = TRUE;
|
||||||
|
|
||||||
#ifdef BACKSLASH_IN_FILENAME
|
#ifdef BACKSLASH_IN_FILENAME
|
||||||
slash_adjust(fname);
|
slash_adjust(fname);
|
||||||
@@ -207,6 +210,7 @@ alist_add(
|
|||||||
++al->al_ga.ga_len;
|
++al->al_ga.ga_len;
|
||||||
|
|
||||||
arglist_locked = FALSE;
|
arglist_locked = FALSE;
|
||||||
|
curwin->w_locked = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
|
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
|
||||||
@@ -365,6 +369,7 @@ alist_add_list(
|
|||||||
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
|
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
|
||||||
(ARGCOUNT - after) * sizeof(aentry_T));
|
(ARGCOUNT - after) * sizeof(aentry_T));
|
||||||
arglist_locked = TRUE;
|
arglist_locked = TRUE;
|
||||||
|
curwin->w_locked = TRUE;
|
||||||
for (i = 0; i < count; ++i)
|
for (i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
|
int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
|
||||||
@@ -373,6 +378,7 @@ alist_add_list(
|
|||||||
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
|
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
|
||||||
}
|
}
|
||||||
arglist_locked = FALSE;
|
arglist_locked = FALSE;
|
||||||
|
curwin->w_locked = FALSE;
|
||||||
ALIST(curwin)->al_ga.ga_len += count;
|
ALIST(curwin)->al_ga.ga_len += count;
|
||||||
if (old_argcount > 0 && curwin->w_arg_idx >= after)
|
if (old_argcount > 0 && curwin->w_arg_idx >= after)
|
||||||
curwin->w_arg_idx += count;
|
curwin->w_arg_idx += count;
|
||||||
|
@@ -1484,7 +1484,7 @@ do_buffer_ext(
|
|||||||
// (unless it's the only window). Repeat this so long as we end up in
|
// (unless it's the only window). Repeat this so long as we end up in
|
||||||
// a window with this buffer.
|
// a window with this buffer.
|
||||||
while (buf == curbuf
|
while (buf == curbuf
|
||||||
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
|
&& !(win_locked(curwin) || curwin->w_buffer->b_locked > 0)
|
||||||
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL))
|
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL))
|
||||||
{
|
{
|
||||||
if (win_close(curwin, FALSE) == FAIL)
|
if (win_close(curwin, FALSE) == FAIL)
|
||||||
@@ -5470,7 +5470,7 @@ ex_buffer_all(exarg_T *eap)
|
|||||||
: wp->w_width != Columns)
|
: wp->w_width != Columns)
|
||||||
|| (had_tab > 0 && wp != firstwin))
|
|| (had_tab > 0 && wp != firstwin))
|
||||||
&& !ONE_WINDOW
|
&& !ONE_WINDOW
|
||||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
|
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0)
|
||||||
&& !win_unlisted(wp))
|
&& !win_unlisted(wp))
|
||||||
{
|
{
|
||||||
if (win_close(wp, FALSE) == FAIL)
|
if (win_close(wp, FALSE) == FAIL)
|
||||||
|
@@ -2840,7 +2840,7 @@ do_ecmd(
|
|||||||
|
|
||||||
// Set the w_closing flag to avoid that autocommands close the
|
// Set the w_closing flag to avoid that autocommands close the
|
||||||
// window. And set b_locked for the same reason.
|
// window. And set b_locked for the same reason.
|
||||||
the_curwin->w_closing = TRUE;
|
the_curwin->w_locked = TRUE;
|
||||||
++buf->b_locked;
|
++buf->b_locked;
|
||||||
|
|
||||||
if (curbuf == old_curbuf.br_buf)
|
if (curbuf == old_curbuf.br_buf)
|
||||||
@@ -2854,7 +2854,7 @@ do_ecmd(
|
|||||||
|
|
||||||
// Autocommands may have closed the window.
|
// Autocommands may have closed the window.
|
||||||
if (win_valid(the_curwin))
|
if (win_valid(the_curwin))
|
||||||
the_curwin->w_closing = FALSE;
|
the_curwin->w_locked = FALSE;
|
||||||
--buf->b_locked;
|
--buf->b_locked;
|
||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
|
@@ -100,4 +100,5 @@ int get_win_number(win_T *wp, win_T *first_win);
|
|||||||
int get_tab_number(tabpage_T *tp);
|
int get_tab_number(tabpage_T *tp);
|
||||||
char *check_colorcolumn(win_T *wp);
|
char *check_colorcolumn(win_T *wp);
|
||||||
int get_last_winid(void);
|
int get_last_winid(void);
|
||||||
|
int win_locked(win_T *wp);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -3785,7 +3785,7 @@ struct window_S
|
|||||||
synblock_T *w_s; // for :ownsyntax
|
synblock_T *w_s; // for :ownsyntax
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int w_closing; // window is being closed, don't let
|
int w_locked; // window is being closed, don't let
|
||||||
// autocommands close it too.
|
// autocommands close it too.
|
||||||
|
|
||||||
frame_T *w_frame; // frame containing this window
|
frame_T *w_frame; // frame containing this window
|
||||||
|
@@ -3680,10 +3680,10 @@ term_after_channel_closed(term_T *term)
|
|||||||
if (is_aucmd_win(curwin))
|
if (is_aucmd_win(curwin))
|
||||||
do_set_w_closing = TRUE;
|
do_set_w_closing = TRUE;
|
||||||
if (do_set_w_closing)
|
if (do_set_w_closing)
|
||||||
curwin->w_closing = TRUE;
|
curwin->w_locked = TRUE;
|
||||||
do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
|
do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
|
||||||
if (do_set_w_closing)
|
if (do_set_w_closing)
|
||||||
curwin->w_closing = FALSE;
|
curwin->w_locked = FALSE;
|
||||||
aucmd_restbuf(&aco);
|
aucmd_restbuf(&aco);
|
||||||
}
|
}
|
||||||
#ifdef FEAT_PROP_POPUP
|
#ifdef FEAT_PROP_POPUP
|
||||||
|
@@ -359,6 +359,7 @@ func Test_argv()
|
|||||||
call assert_equal('', argv(1, 100))
|
call assert_equal('', argv(1, 100))
|
||||||
call assert_equal([], argv(-1, 100))
|
call assert_equal([], argv(-1, 100))
|
||||||
call assert_equal('', argv(10, -1))
|
call assert_equal('', argv(10, -1))
|
||||||
|
%argdelete
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for the :argedit command
|
" Test for the :argedit command
|
||||||
@@ -744,4 +745,26 @@ func Test_all_command()
|
|||||||
%bw!
|
%bw!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for deleting buffer when creating an arglist. This was accessing freed
|
||||||
|
" memory
|
||||||
|
func Test_crash_arglist_uaf()
|
||||||
|
"%argdelete
|
||||||
|
new one
|
||||||
|
au BufAdd XUAFlocal :bw
|
||||||
|
"call assert_fails(':arglocal XUAFlocal', 'E163:')
|
||||||
|
arglocal XUAFlocal
|
||||||
|
au! BufAdd
|
||||||
|
bw! XUAFlocal
|
||||||
|
|
||||||
|
au BufAdd XUAFlocal2 :bw
|
||||||
|
new two
|
||||||
|
new three
|
||||||
|
arglocal
|
||||||
|
argadd XUAFlocal2 Xfoobar
|
||||||
|
bw! XUAFlocal2
|
||||||
|
bw! two
|
||||||
|
|
||||||
|
au! BufAdd
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
678,
|
||||||
/**/
|
/**/
|
||||||
677,
|
677,
|
||||||
/**/
|
/**/
|
||||||
|
29
src/window.c
29
src/window.c
@@ -2511,7 +2511,7 @@ close_windows(
|
|||||||
for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
|
for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
|
||||||
{
|
{
|
||||||
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
|
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
|
||||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0))
|
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
|
||||||
{
|
{
|
||||||
if (win_close(wp, FALSE) == FAIL)
|
if (win_close(wp, FALSE) == FAIL)
|
||||||
// If closing the window fails give up, to avoid looping
|
// If closing the window fails give up, to avoid looping
|
||||||
@@ -2532,7 +2532,7 @@ close_windows(
|
|||||||
if (tp != curtab)
|
if (tp != curtab)
|
||||||
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
|
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
|
||||||
if (wp->w_buffer == buf
|
if (wp->w_buffer == buf
|
||||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0))
|
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
|
||||||
{
|
{
|
||||||
win_close_othertab(wp, FALSE, tp);
|
win_close_othertab(wp, FALSE, tp);
|
||||||
|
|
||||||
@@ -2654,10 +2654,10 @@ win_close_buffer(win_T *win, int action, int abort_if_last)
|
|||||||
bufref_T bufref;
|
bufref_T bufref;
|
||||||
|
|
||||||
set_bufref(&bufref, curbuf);
|
set_bufref(&bufref, curbuf);
|
||||||
win->w_closing = TRUE;
|
win->w_locked = TRUE;
|
||||||
close_buffer(win, win->w_buffer, action, abort_if_last, TRUE);
|
close_buffer(win, win->w_buffer, action, abort_if_last, TRUE);
|
||||||
if (win_valid_any_tab(win))
|
if (win_valid_any_tab(win))
|
||||||
win->w_closing = FALSE;
|
win->w_locked = FALSE;
|
||||||
// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
|
// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
|
||||||
// "wipe".
|
// "wipe".
|
||||||
if (!bufref_valid(&bufref))
|
if (!bufref_valid(&bufref))
|
||||||
@@ -2705,7 +2705,7 @@ win_close(win_T *win, int free_buf)
|
|||||||
if (window_layout_locked(CMD_close))
|
if (window_layout_locked(CMD_close))
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (win->w_closing || (win->w_buffer != NULL
|
if (win_locked(win) || (win->w_buffer != NULL
|
||||||
&& win->w_buffer->b_locked > 0))
|
&& win->w_buffer->b_locked > 0))
|
||||||
return FAIL; // window is already being closed
|
return FAIL; // window is already being closed
|
||||||
if (win_unlisted(win))
|
if (win_unlisted(win))
|
||||||
@@ -2754,19 +2754,19 @@ win_close(win_T *win, int free_buf)
|
|||||||
other_buffer = TRUE;
|
other_buffer = TRUE;
|
||||||
if (!win_valid(win))
|
if (!win_valid(win))
|
||||||
return FAIL;
|
return FAIL;
|
||||||
win->w_closing = TRUE;
|
win->w_locked = TRUE;
|
||||||
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
|
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
|
||||||
if (!win_valid(win))
|
if (!win_valid(win))
|
||||||
return FAIL;
|
return FAIL;
|
||||||
win->w_closing = FALSE;
|
win->w_locked = FALSE;
|
||||||
if (last_window())
|
if (last_window())
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
win->w_closing = TRUE;
|
win->w_locked = TRUE;
|
||||||
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
|
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
|
||||||
if (!win_valid(win))
|
if (!win_valid(win))
|
||||||
return FAIL;
|
return FAIL;
|
||||||
win->w_closing = FALSE;
|
win->w_locked = FALSE;
|
||||||
if (last_window())
|
if (last_window())
|
||||||
return FAIL;
|
return FAIL;
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
@@ -3346,7 +3346,7 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
|
|||||||
|
|
||||||
// Get here with win->w_buffer == NULL when win_close() detects the tab
|
// Get here with win->w_buffer == NULL when win_close() detects the tab
|
||||||
// page changed.
|
// page changed.
|
||||||
if (win->w_closing || (win->w_buffer != NULL
|
if (win_locked(win) || (win->w_buffer != NULL
|
||||||
&& win->w_buffer->b_locked > 0))
|
&& win->w_buffer->b_locked > 0))
|
||||||
return; // window is already being closed
|
return; // window is already being closed
|
||||||
|
|
||||||
@@ -8000,3 +8000,12 @@ get_last_winid(void)
|
|||||||
{
|
{
|
||||||
return last_win_id;
|
return last_win_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't let autocommands close the given window
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
win_locked(win_T *wp)
|
||||||
|
{
|
||||||
|
return wp->w_locked;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user