]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1477: crash when recovering from corrupted swap file v9.0.1477
authorBram Moolenaar <Bram@vim.org>
Sat, 22 Apr 2023 20:14:26 +0000 (21:14 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 22 Apr 2023 20:14:26 +0000 (21:14 +0100)
Problem:    Crash when recovering from corrupted swap file.
Solution:   Check for a valid page count. (closes #12275)

src/errors.h
src/memfile.c
src/memline.c
src/testdir/test_recover.vim
src/version.c

index f54adc70ff61d9f866fa3299cf2415339142ee72..8241dbf92a37aa90542f7813f8da356120f819d1 100644 (file)
@@ -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"));
index e8cbe3169602612a08e558b63e6edc536120f420..26890317752ee37999a63a0b47f92ddb8e820c0e 100644 (file)
@@ -431,7 +431,9 @@ mf_get(memfile_T *mfp, blocknr_T nr, int page_count)
         * 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_count)
      */
     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_count)
     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_count)
 }
 
 /*
- * 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)
index 7acea1346c67f8036e36ae9329444345d3b0f1e4..ea08d324ce8bfc9048a66ba5658384b8e04a141b 100644 (file)
@@ -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;
 }
index 395eb06ff59f628644ca7fc6999102454efd5a26..13437cd8ed6d5a404d53037e1a901f83e24de1d4 100644 (file)
@@ -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
index 1ffff638718595666af47d18f664e0bcdb6a8426..c94a503c57cfe2998c5b1896bbe7c4ede8215046 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1477,
 /**/
     1476,
 /**/