CVE-2022-1725: NULL Pointer Dereference (CWE-476)
value: NULL Pointer Dereference in GitHub repository vim/vim prior to 8.2.4959.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2022-09-29T03:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/buffer.c, src/testdir/test_buffer.vim, src/version.c
Commit date: 2022-05-15 14:50:12 +0100
Commit message: patch 8.2.4959: using NULL regexp program

Problem:    Using NULL regexp program.
Solution:   Check for regexp program becoming NULL in more places.

Diff: diff --git a/src/buffer.c b/src/buffer.c
index dfdb0c430d95..3234138aebb5 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2640,17 +2640,19 @@ buflist_findpat(
 		    *patend = (attempt < 2) ? NUL : '$'; // add/remove '$'
 		p = pat;
 		if (*p == '^' && !(attempt & 1))	 // add/remove '^'
 		    ++p;
 		regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
-		if (regmatch.regprog == NULL)
-		{
-		    vim_free(pat);
-		    return -1;
-		}
 
 		FOR_ALL_BUFS_FROM_LAST(buf)
+		{
+		    if (regmatch.regprog == NULL)
+		    {
+			// invalid pattern, possibly after switching engine
+			vim_free(pat);
+			return -1;
+		    }
 		    if (buf->b_p_bl == find_listed
 #ifdef FEAT_DIFF
 			    && (!diffmode || diff_mode_buf(buf))
 #endif
 			    && buflist_match(&regmatch, buf, FALSE) != NULL)
@@ -2672,10 +2674,11 @@ buflist_findpat(
 			    match = -2;
 			    break;
 			}
 			match = buf->b_fnum;	// remember first match
 		    }
+		}
 
 		vim_regfree(regmatch.regprog);
 		if (match >= 0)			// found one match
 		    break;
 	    }
@@ -2764,16 +2767,10 @@ ExpandBufnames(
 	if (!fuzzy)
 	{
 	    if (attempt > 0 && patc == pat)
 		break;	// there was no anchor, no need to try again
 	    regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
-	    if (regmatch.regprog == NULL)
-	    {
-		if (patc != pat)
-		    vim_free(patc);
-		return FAIL;
-	    }
 	}
 
 	// round == 1: Count the matches.
 	// round == 2: Build the array to keep the matches.
 	for (round = 1; round <= 2; ++round)
@@ -2790,11 +2787,20 @@ ExpandBufnames(
 		    if (buf == curbuf || !diff_mode_buf(buf))
 			continue;
 #endif
 
 		if (!fuzzy)
+		{
+		    if (regmatch.regprog == NULL)
+		    {
+			// invalid pattern, possibly after recompiling
+			if (patc != pat)
+			    vim_free(patc);
+			return FAIL;
+		    }
 		    p = buflist_match(&regmatch, buf, p_wic);
+		}
 		else
 		{
 		    p = NULL;
 		    // first try matching with the short file name
 		    if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0)
@@ -2919,10 +2925,11 @@ ExpandBufnames(
     return (count == 0 ? FAIL : OK);
 }
 
 /*
  * Check for a match on the file name for buffer "buf" with regprog "prog".
+ * Note that rmp->regprog may become NULL when switching regexp engine.
  */
     static char_u *
 buflist_match(
     regmatch_T	*rmp,
     buf_T	*buf,
@@ -2937,11 +2944,12 @@ buflist_match(
 
     return match;
 }
 
 /*
- * Try matching the regexp in "prog" with file name "name".
+ * Try matching the regexp in "rmp->regprog" with file name "name".
+ * Note that rmp->regprog may become NULL when switching regexp engine.
  * Return "name" when there is a match, NULL when not.
  */
     static char_u *
 fname_match(
     regmatch_T	*rmp,
@@ -2949,11 +2957,12 @@ fname_match(
     int		ignore_case)  // when TRUE ignore case, when FALSE use 'fic'
 {
     char_u	*match = NULL;
     char_u	*p;
 
-    if (name != NULL)
+    // extra check for valid arguments
+    if (name != NULL && rmp->regprog != NULL)
     {
 	// Ignore case when 'fileignorecase' or the argument is set.
 	rmp->rm_ic = p_fic || ignore_case;
 	if (vim_regexec(rmp, name, (colnr_T)0))
 	    match = name;
diff --git a/src/testdir/test_buffer.vim b/src/testdir/test_buffer.vim
index 55ef0fda7620..4dcfd2d8f766 100644
--- a/src/testdir/test_buffer.vim
+++ b/src/testdir/test_buffer.vim
@@ -417,10 +417,16 @@ func Test_buf_pattern_invalid()
   bwipe!
 
   vsplit 00000000000000000000000000
   silent! buf [0--]\&\zs*\zs*e
   bwipe!
+
+  " similar case with different code path
+  split 0
+  edit ÿ
+  silent! buf [0--]\&\zs*\zs*0
+  bwipe!
 endfunc
 
 " Test for the 'maxmem' and 'maxmemtot' options
 func Test_buffer_maxmem()
   " use 1KB per buffer and 2KB for all the buffers
diff --git a/src/version.c b/src/version.c
index 856922e1f512..dca2f92092c6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -744,10 +744,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4959,
 /**/
     4958,
 /**/
     4957,
 /**/

Vulnerable code: 		if (regmatch.regprog == NULL)
		{
		    vim_free(pat);
		    return -1;
		}
	    if (regmatch.regprog == NULL)
	    {
		if (patc != pat)
		    vim_free(patc);
		return FAIL;
	    }
 * Try matching the regexp in "prog" with file name "name".
    if (name != NULL)
Fixed code: 		{
		    if (regmatch.regprog == NULL)
		    {
			// invalid pattern, possibly after switching engine
			vim_free(pat);
			return -1;
		    }
		}
		{
		    if (regmatch.regprog == NULL)
		    {
			// invalid pattern, possibly after recompiling
			if (patc != pat)
			    vim_free(patc);
			return FAIL;
		    }
		}
 * Note that rmp->regprog may become NULL when switching regexp engine.
 * Try matching the regexp in "rmp->regprog" with file name "name".
 * Note that rmp->regprog may become NULL when switching regexp engine.
    // extra check for valid arguments
    if (name != NULL && rmp->regprog != NULL)

  " similar case with different code path
  split 0
  edit ÿ
  silent! buf [0--]\&\zs*\zs*0
  bwipe!
/**/
    4959,