changeset 32325:aa90a1f1a523 v9.0.1494

patch 9.0.1494: crash when recovering from corrupted swap file Commit: https://github.com/vim/vim/commit/bf1b7132021bac0fccefebb4a1c24a5f372bae4f Author: Bram Moolenaar <Bram@vim.org> Date: Thu Apr 27 21:13:12 2023 +0100 patch 9.0.1494: crash when recovering from corrupted swap file Problem: Crash when recovering from corrupted swap file. Solution: Bail out when the line index looks wrong. (closes https://github.com/vim/vim/issues/12276)
author Bram Moolenaar <Bram@vim.org>
date Thu, 27 Apr 2023 22:15:02 +0200
parents 9c7575089724
children fba0865977fe
files src/memline.c src/testdir/test_recover.vim src/version.c
diffstat 3 files changed, 46 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/memline.c
+++ b/src/memline.c
@@ -1637,14 +1637,13 @@ ml_recover(int checkext)
 		else
 		{
 		    /*
-		     * it is a data block
-		     * Append all the lines in this block
+		     * It is a data block.
+		     * Append all the lines in this block.
 		     */
 		    has_error = FALSE;
-			/*
-			 * check length of block
-			 * if wrong, use length in pointer block
-			 */
+
+		    // Check the length of the block.
+		    // If wrong, use the length given in the pointer block.
 		    if (page_count * mfp->mf_page_size != dp->db_txt_end)
 		    {
 			ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
@@ -1654,13 +1653,12 @@ ml_recover(int checkext)
 			dp->db_txt_end = page_count * mfp->mf_page_size;
 		    }
 
-			// make sure there is a NUL at the end of the block
+		    // Make sure there is a NUL at the end of the block so we
+		    // don't go over the end when copying text.
 		    *((char_u *)dp + dp->db_txt_end - 1) = NUL;
 
-			/*
-			 * check number of lines in block
-			 * if wrong, use count in data block
-			 */
+		    // Check the number of lines in the block.
+		    // If wrong, use the count in the data block.
 		    if (line_count != dp->db_line_count)
 		    {
 			ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
@@ -1669,17 +1667,36 @@ ml_recover(int checkext)
 			has_error = TRUE;
 		    }
 
+		    int did_questions = FALSE;
 		    for (i = 0; i < dp->db_line_count; ++i)
 		    {
+			if ((char_u *)&(dp->db_index[i])
+					    >= (char_u *)dp + dp->db_txt_start)
+			{
+			    // line count must be wrong
+			    ++error;
+			    ml_append(lnum++,
+				    (char_u *)_("??? lines may be missing"),
+							     (colnr_T)0, TRUE);
+			    break;
+			}
+
 			txt_start = (dp->db_index[i] & DB_INDEX_MASK);
 			if (txt_start <= (int)HEADER_SIZE
 					  || txt_start >= (int)dp->db_txt_end)
 			{
+			    ++error;
+			    // avoid lots of lines with "???"
+			    if (did_questions)
+				continue;
+			    did_questions = TRUE;
 			    p = (char_u *)"???";
-			    ++error;
 			}
 			else
+			{
+			    did_questions = FALSE;
 			    p = (char_u *)dp + txt_start;
+			}
 			ml_append(lnum++, p, (colnr_T)0, TRUE);
 		    }
 		    if (has_error)
--- a/src/testdir/test_recover.vim
+++ b/src/testdir/test_recover.vim
@@ -293,6 +293,21 @@ func Test_recover_corrupted_swap_file()
         \ '???END'], getline(1, '$'))
   bw!
 
+  " set the number of lines in the data block to a large value
+  let b = copy(save_b)
+  if system_64bit
+    let b[8208:8215] = 0z00FFFFFF.FFFFFF00
+  else
+    let b[8208:8211] = 0z00FFFF00
+  endif
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E312:')
+  call assert_equal('Xfile1', @%)
+  call assert_equal(['??? from here until ???END lines may have been inserted/deleted',
+        \ '', '???', '??? lines may be missing',
+        \ '???END'], getline(1, '$'))
+  bw!
+
   " use an invalid text start for the lines in a data block
   let b = copy(save_b)
   if system_64bit
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1494,
+/**/
     1493,
 /**/
     1492,