CVE-2022-3352: Use After Free (CWE-416)
value: Use After Free in GitHub repository vim/vim prior to 9.0.0614.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 7.8
Published: 2022-09-29T12:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/buffer.c, src/spell.c, src/testdir/test_autocmd.vim, src/version.c
Commit date: 2022-09-28 11:48:30 +0100
Commit message: patch 9.0.0614: SpellFileMissing autocmd may delete buffer

Problem:    SpellFileMissing autocmd may delete buffer.
Solution:   Disallow deleting the current buffer to avoid using freed memory.

Diff: diff --git a/src/buffer.c b/src/buffer.c
index edf71b051337..be7f1fa8d416 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -466,11 +466,16 @@ can_unload_buffer(buf_T *buf)
 		can_unload = FALSE;
 		break;
 	    }
     }
     if (!can_unload)
-	semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str), buf->b_fname);
+    {
+	char_u *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname;
+
+	semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str),
+				fname != NULL ? fname : (char_u *)"[No Name]");
+    }
     return can_unload;
 }
 
 /*
  * Close the link to a buffer.
diff --git a/src/spell.c b/src/spell.c
index 628814fe6db3..975b5a6789a9 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -1566,10 +1566,14 @@ spell_load_lang(char_u *lang)
     // It's truncated when an error is detected.
     STRCPY(sl.sl_lang, lang);
     sl.sl_slang = NULL;
     sl.sl_nobreak = FALSE;
 
+    // Disallow deleting the current buffer.  Autocommands can do weird things
+    // and cause "lang" to be freed.
+    ++curbuf->b_locked;
+
     // We may retry when no spell file is found for the language, an
     // autocommand may load it then.
     for (round = 1; round <= 2; ++round)
     {
 	/*
@@ -1619,10 +1623,12 @@ spell_load_lang(char_u *lang)
     {
 	// At least one file was loaded, now load ALL the additions.
 	STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl");
 	do_in_runtimepath(fname_enc, DIP_ALL, spell_load_cb, &sl);
     }
+
+    --curbuf->b_locked;
 }
 
 /*
  * Return the encoding used for spell checking: Use 'encoding', except that we
  * use "latin1" for "latin9".  And limit to 60 characters (just in case).
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 86732f15dbe4..70f0f553a226 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -2878,10 +2878,20 @@ func Test_FileType_spell()
 
   au! crash
   setglobal spellfile=
 endfunc
 
+" this was wiping out the current buffer and using freed memory
+func Test_SpellFileMissing_bwipe()
+  next 0
+  au SpellFileMissing 0 bwipe
+  call assert_fails('set spell spelllang=0', 'E937:')
+
+  au! SpellFileMissing
+  bwipe
+endfunc
+
 " Test closing a window or editing another buffer from a FileChangedRO handler
 " in a readonly buffer
 func Test_FileChangedRO_winclose()
   call test_override('ui_delay', 10)
 
diff --git a/src/version.c b/src/version.c
index 1621c8141841..623fedaf1274 100644
--- a/src/version.c
+++ b/src/version.c
@@ -697,10 +697,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    614,
 /**/
     613,
 /**/
     612,
 /**/

Vulnerable code: 	semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str), buf->b_fname);
Fixed code:     {
	char_u *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname;

	semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str),
				fname != NULL ? fname : (char_u *)"[No Name]");
    }
    // Disallow deleting the current buffer.  Autocommands can do weird things
    // and cause "lang" to be freed.
    ++curbuf->b_locked;


    --curbuf->b_locked;
" this was wiping out the current buffer and using freed memory
func Test_SpellFileMissing_bwipe()
  next 0
  au SpellFileMissing 0 bwipe
  call assert_fails('set spell spelllang=0', 'E937:')

  au! SpellFileMissing
  bwipe
endfunc

/**/
    614,