CVE-2022-2980: NULL Pointer Dereference (CWE-476)
value: NULL Pointer Dereference in GitHub repository vim/vim prior to 9.0.0259.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2022-08-25T20:15Z
Repository: https://github.com/vim/vim
Language: C
Files changed: src/mouse.c, src/testdir/test_tabline.vim, src/version.c
Commit date: 2022-08-24 19:27:45 +0100
Commit message: patch 9.0.0259: crash with mouse click when not initialized

Problem:    Crash with mouse click when not initialized.
Solution:   Check TabPageIdxs[] is not NULL.

Diff: diff --git a/src/mouse.c b/src/mouse.c
index c39f6148b4f3..12895f8094b9 100644
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -469,78 +469,81 @@ do_mouse(
     if (!is_click)
 	jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
 
     start_visual.lnum = 0;
 
-    // Check for clicking in the tab page line.
-    if (mouse_row == 0 && firstwin->w_winrow > 0)
+    if (TabPageIdxs != NULL)  // only when initialized
     {
-	if (is_drag)
+	// Check for clicking in the tab page line.
+	if (mouse_row == 0 && firstwin->w_winrow > 0)
 	{
-	    if (in_tab_line)
+	    if (is_drag)
 	    {
-		c1 = TabPageIdxs[mouse_col];
-		tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
-								? c1 - 1 : c1);
+		if (in_tab_line)
+		{
+		    c1 = TabPageIdxs[mouse_col];
+		    tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
+								    ? c1 - 1 : c1);
+		}
+		return FALSE;
 	    }
-	    return FALSE;
-	}
 
-	// click in a tab selects that tab page
-	if (is_click
+	    // click in a tab selects that tab page
+	    if (is_click
 # ifdef FEAT_CMDWIN
-		&& cmdwin_type == 0
+		    && cmdwin_type == 0
 # endif
-		&& mouse_col < Columns)
-	{
-	    in_tab_line = TRUE;
-	    c1 = TabPageIdxs[mouse_col];
-	    if (c1 >= 0)
+		    && mouse_col < Columns)
 	    {
-		if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
-		{
-		    // double click opens new page
-		    end_visual_mode_keep_button();
-		    tabpage_new();
-		    tabpage_move(c1 == 0 ? 9999 : c1 - 1);
-		}
-		else
+		in_tab_line = TRUE;
+		c1 = TabPageIdxs[mouse_col];
+		if (c1 >= 0)
 		{
-		    // Go to specified tab page, or next one if not clicking
-		    // on a label.
-		    goto_tabpage(c1);
-
-		    // It's like clicking on the status line of a window.
-		    if (curwin != old_curwin)
+		    if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
+		    {
+			// double click opens new page
 			end_visual_mode_keep_button();
-		}
-	    }
-	    else
-	    {
-		tabpage_T	*tp;
+			tabpage_new();
+			tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+		    }
+		    else
+		    {
+			// Go to specified tab page, or next one if not clicking
+			// on a label.
+			goto_tabpage(c1);
 
-		// Close the current or specified tab page.
-		if (c1 == -999)
-		    tp = curtab;
+			// It's like clicking on the status line of a window.
+			if (curwin != old_curwin)
+			    end_visual_mode_keep_button();
+		    }
+		}
 		else
-		    tp = find_tabpage(-c1);
-		if (tp == curtab)
 		{
-		    if (first_tabpage->tp_next != NULL)
-			tabpage_close(FALSE);
+		    tabpage_T	*tp;
+
+		    // Close the current or specified tab page.
+		    if (c1 == -999)
+			tp = curtab;
+		    else
+			tp = find_tabpage(-c1);
+		    if (tp == curtab)
+		    {
+			if (first_tabpage->tp_next != NULL)
+			    tabpage_close(FALSE);
+		    }
+		    else if (tp != NULL)
+			tabpage_close_other(tp, FALSE);
 		}
-		else if (tp != NULL)
-		    tabpage_close_other(tp, FALSE);
 	    }
+	    return TRUE;
+	}
+	else if (is_drag && in_tab_line)
+	{
+	    c1 = TabPageIdxs[mouse_col];
+	    tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
+	    return FALSE;
 	}
-	return TRUE;
-    }
-    else if (is_drag && in_tab_line)
-    {
-	c1 = TabPageIdxs[mouse_col];
-	tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
-	return FALSE;
     }
 
     // When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
     // right button up   -> pop-up menu
     // shift-left button -> right button
diff --git a/src/testdir/test_tabline.vim b/src/testdir/test_tabline.vim
index e58a412c5a34..556b85931cb6 100644
--- a/src/testdir/test_tabline.vim
+++ b/src/testdir/test_tabline.vim
@@ -145,6 +145,20 @@ func Test_tabline_20_format_items_no_overrun()
   redrawtabline
 
   set showtabline& tabline&
 endfunc
 
+func Test_mouse_click_in_tab()
+  " This used to crash because TabPageIdxs[] was not initialized
+  let lines =<< trim END
+      tabnew
+      set mouse=a
+      exe "norm \<LeftMouse>"
+  END
+  call writefile(lines, 'Xclickscript')
+  call RunVim([], [], "-e -s -S Xclickscript -c qa")
+
+  call delete('Xclickscript')
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 474cbcb9caeb..b1943bb16157 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,10 +729,12 @@ static char *(features[]) =
 	NULL
 };
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    259,
 /**/
     258,
 /**/
     257,
 /**/

Vulnerable code:     // Check for clicking in the tab page line.
    if (mouse_row == 0 && firstwin->w_winrow > 0)
	if (is_drag)
	    if (in_tab_line)
		c1 = TabPageIdxs[mouse_col];
		tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
								? c1 - 1 : c1);
	    return FALSE;
	}
	// click in a tab selects that tab page
	if (is_click
		&& cmdwin_type == 0
		&& mouse_col < Columns)
	{
	    in_tab_line = TRUE;
	    c1 = TabPageIdxs[mouse_col];
	    if (c1 >= 0)
		if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
		{
		    // double click opens new page
		    end_visual_mode_keep_button();
		    tabpage_new();
		    tabpage_move(c1 == 0 ? 9999 : c1 - 1);
		}
		else
		    // Go to specified tab page, or next one if not clicking
		    // on a label.
		    goto_tabpage(c1);

		    // It's like clicking on the status line of a window.
		    if (curwin != old_curwin)
		}
	    }
	    else
	    {
		tabpage_T	*tp;
		// Close the current or specified tab page.
		if (c1 == -999)
		    tp = curtab;
		    tp = find_tabpage(-c1);
		if (tp == curtab)
		    if (first_tabpage->tp_next != NULL)
			tabpage_close(FALSE);
		else if (tp != NULL)
		    tabpage_close_other(tp, FALSE);
	return TRUE;
    }
    else if (is_drag && in_tab_line)
    {
	c1 = TabPageIdxs[mouse_col];
	tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
	return FALSE;
Fixed code:     if (TabPageIdxs != NULL)  // only when initialized
	// Check for clicking in the tab page line.
	if (mouse_row == 0 && firstwin->w_winrow > 0)
	    if (is_drag)
		if (in_tab_line)
		{
		    c1 = TabPageIdxs[mouse_col];
		    tabpage_move(c1 <= 0 ? 9999 : c1 < tabpage_index(curtab)
								    ? c1 - 1 : c1);
		}
		return FALSE;
	    // click in a tab selects that tab page
	    if (is_click
		    && cmdwin_type == 0
		    && mouse_col < Columns)
		in_tab_line = TRUE;
		c1 = TabPageIdxs[mouse_col];
		if (c1 >= 0)
		    if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)
		    {
			// double click opens new page
			tabpage_new();
			tabpage_move(c1 == 0 ? 9999 : c1 - 1);
		    }
		    else
		    {
			// Go to specified tab page, or next one if not clicking
			// on a label.
			goto_tabpage(c1);
			// It's like clicking on the status line of a window.
			if (curwin != old_curwin)
			    end_visual_mode_keep_button();
		    }
		}
		    tabpage_T	*tp;

		    // Close the current or specified tab page.
		    if (c1 == -999)
			tp = curtab;
		    else
			tp = find_tabpage(-c1);
		    if (tp == curtab)
		    {
			if (first_tabpage->tp_next != NULL)
			    tabpage_close(FALSE);
		    }
		    else if (tp != NULL)
			tabpage_close_other(tp, FALSE);
	    return TRUE;
	}
	else if (is_drag && in_tab_line)
	{
	    c1 = TabPageIdxs[mouse_col];
	    tabpage_move(c1 <= 0 ? 9999 : c1 - 1);
	    return FALSE;
func Test_mouse_click_in_tab()
  " This used to crash because TabPageIdxs[] was not initialized
  let lines =<< trim END
      tabnew
      set mouse=a
      exe "norm \<LeftMouse>"
  END
  call writefile(lines, 'Xclickscript')
  call RunVim([], [], "-e -s -S Xclickscript -c qa")

  call delete('Xclickscript')
endfunc


/**/
    259,