changeset 32290:9b0c304500cc v9.0.1477

patch 9.0.1477: crash when recovering from corrupted swap file Commit: https://github.com/vim/vim/commit/b67ba03d3ef2e6c5f207d508e85fc6906f938028 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Apr 22 21:14:26 2023 +0100 patch 9.0.1477: crash when recovering from corrupted swap file Problem: Crash when recovering from corrupted swap file. Solution: Check for a valid page count. (closes https://github.com/vim/vim/issues/12275)
author Bram Moolenaar <Bram@vim.org>
date Sat, 22 Apr 2023 22:15:04 +0200
parents 272eb607ad91
children 350996a79fc3
files src/errors.h src/memfile.c src/memline.c src/testdir/test_recover.vim src/version.c
diffstat 5 files changed, 41 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -3459,3 +3459,5 @@ EXTERN char e_cannot_use_non_null_object
 EXTERN char e_incomplete_type[]
 	INIT(= N_("E1363: Incomplete type"));
 #endif
+EXTERN char e_warning_pointer_block_corrupted[]
+	INIT(= N_("E1364: Warning: Pointer block corrupted"));
--- a/src/memfile.c
+++ b/src/memfile.c
@@ -431,7 +431,9 @@ mf_get(memfile_T *mfp, blocknr_T nr, int
 	 * If not, allocate a new block.
 	 */
 	hp = mf_release(mfp, page_count);
-	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
+	if (hp == NULL && page_count > 0)
+	    hp = mf_alloc_bhdr(mfp, page_count);
+	if (hp == NULL)
 	    return NULL;
 
 	hp->bh_bnum = nr;
@@ -812,9 +814,10 @@ mf_release(memfile_T *mfp, int page_coun
      */
     if (hp->bh_page_count != page_count)
     {
-	vim_free(hp->bh_data);
-	if ((hp->bh_data = alloc((size_t)mfp->mf_page_size * page_count))
-								       == NULL)
+	VIM_CLEAR(hp->bh_data);
+	if (page_count > 0)
+	    hp->bh_data = alloc((size_t)mfp->mf_page_size * page_count);
+	if (hp->bh_data == NULL)
 	{
 	    vim_free(hp);
 	    return NULL;
@@ -872,7 +875,7 @@ mf_release_all(void)
 }
 
 /*
- * Allocate a block header and a block of memory for it
+ * Allocate a block header and a block of memory for it.
  */
     static bhdr_T *
 mf_alloc_bhdr(memfile_T *mfp, int page_count)
@@ -882,8 +885,7 @@ mf_alloc_bhdr(memfile_T *mfp, int page_c
     if ((hp = ALLOC_ONE(bhdr_T)) == NULL)
 	return NULL;
 
-    if ((hp->bh_data = alloc((size_t)mfp->mf_page_size * page_count))
-	    == NULL)
+    if ((hp->bh_data = alloc((size_t)mfp->mf_page_size * page_count)) == NULL)
     {
 	vim_free(hp);	    // not enough memory
 	return NULL;
@@ -893,7 +895,7 @@ mf_alloc_bhdr(memfile_T *mfp, int page_c
 }
 
 /*
- * Free a block header and the block of memory for it
+ * Free a block header and the block of memory for it.
  */
     static void
 mf_free_bhdr(bhdr_T *hp)
@@ -903,7 +905,7 @@ mf_free_bhdr(bhdr_T *hp)
 }
 
 /*
- * insert entry *hp in the free list
+ * Insert entry *hp in the free list.
  */
     static void
 mf_ins_free(memfile_T *mfp, bhdr_T *hp)
--- a/src/memline.c
+++ b/src/memline.c
@@ -98,6 +98,9 @@ struct pointer_block
 				// followed by empty space until end of page
 };
 
+// Value for pb_count_max.
+#define PB_COUNT_MAX(mfp) (short_u)(((mfp)->mf_page_size - offsetof(PTR_BL, pb_pointer)) / sizeof(PTR_EN))
+
 /*
  * A data block is a leaf in the tree.
  *
@@ -1525,6 +1528,20 @@ ml_recover(int checkext)
 	    pp = (PTR_BL *)(hp->bh_data);
 	    if (pp->pb_id == PTR_ID)		// it is a pointer block
 	    {
+		int ptr_block_error = FALSE;
+		if (pp->pb_count_max != PB_COUNT_MAX(mfp))
+		{
+		    ptr_block_error = TRUE;
+		    pp->pb_count_max = PB_COUNT_MAX(mfp);
+		}
+		if (pp->pb_count > pp->pb_count_max)
+		{
+		    ptr_block_error = TRUE;
+		    pp->pb_count = pp->pb_count_max;
+		}
+		if (ptr_block_error)
+		    emsg(_(e_warning_pointer_block_corrupted));
+
 		// check line count when using pointer block first time
 		if (idx == 0 && line_count != 0)
 		{
@@ -4162,9 +4179,7 @@ ml_new_ptr(memfile_T *mfp)
     pp = (PTR_BL *)(hp->bh_data);
     pp->pb_id = PTR_ID;
     pp->pb_count = 0;
-    pp->pb_count_max =
-	(short_u)((mfp->mf_page_size - offsetof(PTR_BL, pb_pointer))
-							     / sizeof(PTR_EN));
+    pp->pb_count_max = PB_COUNT_MAX(mfp);
 
     return hp;
 }
--- a/src/testdir/test_recover.vim
+++ b/src/testdir/test_recover.vim
@@ -249,6 +249,14 @@ func Test_recover_corrupted_swap_file()
   call assert_equal(['???EMPTY BLOCK'], getline(1, '$'))
   bw!
 
+  " set the number of pointers in a pointer block to a large value
+  let b = copy(save_b)
+  let b[4098:4099] = 0zFFFF
+  call writefile(b, sn)
+  call assert_fails('recover Xfile1', 'E1364:')
+  call assert_equal('Xfile1', @%)
+  bw!
+
   " set the block number in a pointer entry to a negative number
   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 */
 /**/
+    1477,
+/**/
     1476,
 /**/
     1475,