changeset 33879:d418c82f02a4 v9.0.2149

patch 9.0.2149: [security]: use-after-free in exec_instructions() Commit: https://github.com/vim/vim/commit/5dd41d4b6370b7b7d09d691f9252b3899c66102a Author: Christian Brabandt <cb@256bit.org> Date: Mon Dec 4 22:52:23 2023 +0100 patch 9.0.2149: [security]: use-after-free in exec_instructions() Problem: [security]: use-after-free in exec_instructions() Solution: get tv pointer again [security]: use-after-free in exec_instructions() exec_instructions may access freed memory, if the GA_GROWS_FAILS() re-allocates memory. When this happens, the typval tv may still point to now already freed memory. So let's get that pointer again and compare it with tv. If those two pointers differ, tv is now invalid and we have to refresh the tv pointer. closes: #13621 Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sun, 10 Dec 2023 15:16:17 +0100
parents 4037e95bb7d7
children f39f120aa33e
files src/testdir/crash/poc_uaf_exec_instructions src/testdir/test_crash.vim src/version.c src/vim9execute.c
diffstat 4 files changed, 41 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..49ae8577ff5bf7c47f86a3691525957b0eb57433
GIT binary patch
literal 69
zc$`Z~O;ZTc&}86BfiZvtSD8LnYF-LZh>NROKSotuQ$NNQBn}o-;L2y<Dl6tPC{8Y7
KNZ~5ZX8-_6G7%L3
--- a/src/testdir/test_crash.vim
+++ b/src/testdir/test_crash.vim
@@ -113,6 +113,7 @@ endfunc
 func Test_crash1_2()
   CheckNotBSD
   CheckExecutable dash
+  let g:test_is_flaky = 1
 
   " The following used to crash Vim
   let opts = #{cmd: 'sh'}
@@ -149,22 +150,9 @@ func Test_crash1_2()
     \ ' ; echo "crash 4: [OK]" >> '.. result .. "\<cr>")
   call TermWait(buf, 150)
 
-  let file = 'crash/poc_ex_substitute'
-  let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
-  let args = printf(cmn_args, vim, file)
-  " just make sure it runs, we don't care about the resulting echo
-  call term_sendkeys(buf, args .. "\<cr>")
-  " There is no output generated in Github CI for the asan clang build.
-  " so just skip generating the ouput.
-  " call term_sendkeys(buf, args ..
-  "   \ ' &&  echo "crash 5: [OK]" >> '.. result .. "\<cr>")
-  call TermWait(buf, 150)
-
   " clean up
   exe buf .. "bw!"
-
   exe "sp " .. result
-
   let expected = [
       \ 'crash 1: [OK]',
       \ 'crash 2: [OK]',
@@ -174,8 +162,31 @@ func Test_crash1_2()
 
   call assert_equal(expected, getline(1, '$'))
   bw!
+  call delete(result)
+endfunc
 
-  call delete(result)
+" This test just runs various scripts, that caused issues before.
+" We are not really asserting anything here, it's just important
+" that ASAN does not detect any issues.
+func Test_crash1_3()
+  let vim  = GetVimProg()
+  let buf = RunVimInTerminal('sh', #{cmd: 'sh'})
+
+  let file = 'crash/poc_ex_substitute'
+  let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>"
+  let args = printf(cmn_args, vim, file)
+  call term_sendkeys(buf, args)
+  call TermWait(buf, 150)
+
+  let file = 'crash/poc_uaf_exec_instructions'
+  let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>"
+  let args = printf(cmn_args, vim, file)
+  call term_sendkeys(buf, args)
+  call TermWait(buf, 150)
+
+  " clean up
+  exe buf .. "bw!"
+  bw!
 endfunc
 
 func Test_crash2()
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2149,
+/**/
     2148,
 /**/
     2147,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -4123,8 +4123,22 @@ exec_instructions(ectx_T *ectx)
 				      + iptr->isn_arg.outer.outer_idx;
 		    if (iptr->isn_type == ISN_LOADOUTER)
 		    {
+			typval_T *copy;
 			if (GA_GROW_FAILS(&ectx->ec_stack, 1))
 			    goto theend;
+			// careful: ga_grow_inner may re-alloc the stack
+			if (depth < 0)
+			    copy = ((typval_T *)outer->out_loop[-depth - 1]
+								   .stack->ga_data)
+					      + outer->out_loop[-depth - 1].var_idx
+					      + iptr->isn_arg.outer.outer_idx;
+			else
+			    copy = ((typval_T *)outer->out_stack->ga_data)
+					  + outer->out_frame_idx + STACK_FRAME_SIZE
+					  + iptr->isn_arg.outer.outer_idx;
+			// memory was freed, get tv again
+			if (copy != tv)
+			    tv = copy;
 			copy_tv(tv, STACK_TV_BOT(0));
 			++ectx->ec_stack.ga_len;
 		    }