Skip to content

Commit 0a6e57b

Browse files
committed
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>
1 parent 3b59be4 commit 0a6e57b

9 files changed

Lines changed: 58 additions & 17 deletions

File tree

src/arglist.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ alist_set(
184184
/*
185185
* Add file "fname" to argument list "al".
186186
* "fname" must have been allocated and "al" must have been checked for room.
187+
*
188+
* May trigger Buf* autocommands
187189
*/
188190
void
189191
alist_add(
@@ -196,6 +198,7 @@ alist_add(
196198
if (check_arglist_locked() == FAIL)
197199
return;
198200
arglist_locked = TRUE;
201+
curwin->w_locked = TRUE;
199202

200203
#ifdef BACKSLASH_IN_FILENAME
201204
slash_adjust(fname);
@@ -207,6 +210,7 @@ alist_add(
207210
++al->al_ga.ga_len;
208211

209212
arglist_locked = FALSE;
213+
curwin->w_locked = FALSE;
210214
}
211215

212216
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
@@ -365,6 +369,7 @@ alist_add_list(
365369
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
366370
(ARGCOUNT - after) * sizeof(aentry_T));
367371
arglist_locked = TRUE;
372+
curwin->w_locked = TRUE;
368373
for (i = 0; i < count; ++i)
369374
{
370375
int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
@@ -373,6 +378,7 @@ alist_add_list(
373378
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
374379
}
375380
arglist_locked = FALSE;
381+
curwin->w_locked = FALSE;
376382
ALIST(curwin)->al_ga.ga_len += count;
377383
if (old_argcount > 0 && curwin->w_arg_idx >= after)
378384
curwin->w_arg_idx += count;

src/buffer.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,7 @@ do_buffer_ext(
14841484
// (unless it's the only window). Repeat this so long as we end up in
14851485
// a window with this buffer.
14861486
while (buf == curbuf
1487-
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
1487+
&& !(win_locked(curwin) || curwin->w_buffer->b_locked > 0)
14881488
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL))
14891489
{
14901490
if (win_close(curwin, FALSE) == FAIL)
@@ -5470,7 +5470,7 @@ ex_buffer_all(exarg_T *eap)
54705470
: wp->w_width != Columns)
54715471
|| (had_tab > 0 && wp != firstwin))
54725472
&& !ONE_WINDOW
5473-
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
5473+
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0)
54745474
&& !win_unlisted(wp))
54755475
{
54765476
if (win_close(wp, FALSE) == FAIL)

src/ex_cmds.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,7 +2840,7 @@ do_ecmd(
28402840

28412841
// Set the w_closing flag to avoid that autocommands close the
28422842
// window. And set b_locked for the same reason.
2843-
the_curwin->w_closing = TRUE;
2843+
the_curwin->w_locked = TRUE;
28442844
++buf->b_locked;
28452845

28462846
if (curbuf == old_curbuf.br_buf)
@@ -2854,7 +2854,7 @@ do_ecmd(
28542854

28552855
// Autocommands may have closed the window.
28562856
if (win_valid(the_curwin))
2857-
the_curwin->w_closing = FALSE;
2857+
the_curwin->w_locked = FALSE;
28582858
--buf->b_locked;
28592859

28602860
#ifdef FEAT_EVAL

src/proto/window.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,5 @@ int get_win_number(win_T *wp, win_T *first_win);
100100
int get_tab_number(tabpage_T *tp);
101101
char *check_colorcolumn(win_T *wp);
102102
int get_last_winid(void);
103+
int win_locked(win_T *wp);
103104
/* vim: set ft=c : */

src/structs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3785,7 +3785,7 @@ struct window_S
37853785
synblock_T *w_s; // for :ownsyntax
37863786
#endif
37873787

3788-
int w_closing; // window is being closed, don't let
3788+
int w_locked; // window is being closed, don't let
37893789
// autocommands close it too.
37903790

37913791
frame_T *w_frame; // frame containing this window

src/terminal.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3680,10 +3680,10 @@ term_after_channel_closed(term_T *term)
36803680
if (is_aucmd_win(curwin))
36813681
do_set_w_closing = TRUE;
36823682
if (do_set_w_closing)
3683-
curwin->w_closing = TRUE;
3683+
curwin->w_locked = TRUE;
36843684
do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
36853685
if (do_set_w_closing)
3686-
curwin->w_closing = FALSE;
3686+
curwin->w_locked = FALSE;
36873687
aucmd_restbuf(&aco);
36883688
}
36893689
#ifdef FEAT_PROP_POPUP

src/testdir/test_arglist.vim

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ func Test_argv()
359359
call assert_equal('', argv(1, 100))
360360
call assert_equal([], argv(-1, 100))
361361
call assert_equal('', argv(10, -1))
362+
%argdelete
362363
endfunc
363364

364365
" Test for the :argedit command
@@ -744,4 +745,26 @@ func Test_all_command()
744745
%bw!
745746
endfunc
746747

748+
" Test for deleting buffer when creating an arglist. This was accessing freed
749+
" memory
750+
func Test_crash_arglist_uaf()
751+
"%argdelete
752+
new one
753+
au BufAdd XUAFlocal :bw
754+
"call assert_fails(':arglocal XUAFlocal', 'E163:')
755+
arglocal XUAFlocal
756+
au! BufAdd
757+
bw! XUAFlocal
758+
759+
au BufAdd XUAFlocal2 :bw
760+
new two
761+
new three
762+
arglocal
763+
argadd XUAFlocal2 Xfoobar
764+
bw! XUAFlocal2
765+
bw! two
766+
767+
au! BufAdd
768+
endfunc
769+
747770
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,8 @@ static char *(features[]) =
704704

705705
static int included_patches[] =
706706
{ /* Add new patch number below this line */
707+
/**/
708+
678,
707709
/**/
708710
677,
709711
/**/

src/window.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,7 @@ close_windows(
25112511
for (wp = firstwin; wp != NULL && !ONE_WINDOW; )
25122512
{
25132513
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
2514-
&& !(wp->w_closing || wp->w_buffer->b_locked > 0))
2514+
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
25152515
{
25162516
if (win_close(wp, FALSE) == FAIL)
25172517
// If closing the window fails give up, to avoid looping
@@ -2532,7 +2532,7 @@ close_windows(
25322532
if (tp != curtab)
25332533
FOR_ALL_WINDOWS_IN_TAB(tp, wp)
25342534
if (wp->w_buffer == buf
2535-
&& !(wp->w_closing || wp->w_buffer->b_locked > 0))
2535+
&& !(win_locked(wp) || wp->w_buffer->b_locked > 0))
25362536
{
25372537
win_close_othertab(wp, FALSE, tp);
25382538

@@ -2654,10 +2654,10 @@ win_close_buffer(win_T *win, int action, int abort_if_last)
26542654
bufref_T bufref;
26552655

26562656
set_bufref(&bufref, curbuf);
2657-
win->w_closing = TRUE;
2657+
win->w_locked = TRUE;
26582658
close_buffer(win, win->w_buffer, action, abort_if_last, TRUE);
26592659
if (win_valid_any_tab(win))
2660-
win->w_closing = FALSE;
2660+
win->w_locked = FALSE;
26612661
// Make sure curbuf is valid. It can become invalid if 'bufhidden' is
26622662
// "wipe".
26632663
if (!bufref_valid(&bufref))
@@ -2705,7 +2705,7 @@ win_close(win_T *win, int free_buf)
27052705
if (window_layout_locked(CMD_close))
27062706
return FAIL;
27072707

2708-
if (win->w_closing || (win->w_buffer != NULL
2708+
if (win_locked(win) || (win->w_buffer != NULL
27092709
&& win->w_buffer->b_locked > 0))
27102710
return FAIL; // window is already being closed
27112711
if (win_unlisted(win))
@@ -2754,19 +2754,19 @@ win_close(win_T *win, int free_buf)
27542754
other_buffer = TRUE;
27552755
if (!win_valid(win))
27562756
return FAIL;
2757-
win->w_closing = TRUE;
2757+
win->w_locked = TRUE;
27582758
apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
27592759
if (!win_valid(win))
27602760
return FAIL;
2761-
win->w_closing = FALSE;
2761+
win->w_locked = FALSE;
27622762
if (last_window())
27632763
return FAIL;
27642764
}
2765-
win->w_closing = TRUE;
2765+
win->w_locked = TRUE;
27662766
apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf);
27672767
if (!win_valid(win))
27682768
return FAIL;
2769-
win->w_closing = FALSE;
2769+
win->w_locked = FALSE;
27702770
if (last_window())
27712771
return FAIL;
27722772
#ifdef FEAT_EVAL
@@ -3346,7 +3346,7 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
33463346

33473347
// Get here with win->w_buffer == NULL when win_close() detects the tab
33483348
// page changed.
3349-
if (win->w_closing || (win->w_buffer != NULL
3349+
if (win_locked(win) || (win->w_buffer != NULL
33503350
&& win->w_buffer->b_locked > 0))
33513351
return; // window is already being closed
33523352

@@ -8000,3 +8000,12 @@ get_last_winid(void)
80008000
{
80018001
return last_win_id;
80028002
}
8003+
8004+
/*
8005+
* Don't let autocommands close the given window
8006+
*/
8007+
int
8008+
win_locked(win_T *wp)
8009+
{
8010+
return wp->w_locked;
8011+
}

0 commit comments

Comments
 (0)