summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2023-04-22 21:14:26 +0100
committerBram Moolenaar <Bram@vim.org>2023-04-22 21:14:26 +0100
commitb67ba03d3ef2e6c5f207d508e85fc6906f938028 (patch)
treeec9c376833880f135b7391e2db774e5509996788
parente7f05a8780426dc7af247419c6d02d5f1e896689 (diff)
downloadvim-git-b67ba03d3ef2e6c5f207d508e85fc6906f938028.tar.gz
patch 9.0.1477: crash when recovering from corrupted swap filev9.0.1477
Problem: Crash when recovering from corrupted swap file. Solution: Check for a valid page count. (closes #12275)
-rw-r--r--src/errors.h2
-rw-r--r--src/memfile.c20
-rw-r--r--src/memline.c21
-rw-r--r--src/testdir/test_recover.vim8
-rw-r--r--src/version.c2
5 files changed, 41 insertions, 12 deletions
diff --git a/src/errors.h b/src/errors.h
index f54adc70f..8241dbf92 100644
--- 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"));
diff --git a/src/memfile.c b/src/memfile.c
index e8cbe3169..268903177 100644
--- a/src/memfile.c
+++ b/src/memfile.c
@@ -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)
diff --git a/src/memline.c b/src/memline.c
index 7acea1346..ea08d324c 100644
--- 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;
}
diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim
index 395eb06ff..13437cd8e 100644
--- 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
diff --git a/src/version.c b/src/version.c
index 1ffff6387..c94a503c5 100644
--- 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,