diff options
author | Bram Moolenaar <Bram@vim.org> | 2023-04-22 21:14:26 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2023-04-22 21:14:26 +0100 |
commit | b67ba03d3ef2e6c5f207d508e85fc6906f938028 (patch) | |
tree | ec9c376833880f135b7391e2db774e5509996788 | |
parent | e7f05a8780426dc7af247419c6d02d5f1e896689 (diff) | |
download | vim-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.h | 2 | ||||
-rw-r--r-- | src/memfile.c | 20 | ||||
-rw-r--r-- | src/memline.c | 21 | ||||
-rw-r--r-- | src/testdir/test_recover.vim | 8 | ||||
-rw-r--r-- | src/version.c | 2 |
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, |