From: Christian Brabandt Date: Sat, 27 Jun 2026 08:39:43 +0000 (+0000) Subject: patch 9.2.0738: ml_recover() may write beyond block buffer X-Git-Tag: v9.2.0738^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=43939cf9ebf5302bb51c85d00ac83de7e01452d5;p=thirdparty%2Fvim.git patch 9.2.0738: ml_recover() may write beyond block buffer Problem: A crafted swap file can cause an out-of-bounds write during recovery when the same block is referenced twice with different pe_page_count values (cipher-creator) Solution: Check hp->bh_page_count against page_count after mf_get() and clamp page_count to the actual block size. closes: #20645 Signed-off-by: Christian Brabandt --- diff --git a/src/memline.c b/src/memline.c index 4ad09f43a5..2e0403d416 100644 --- a/src/memline.c +++ b/src/memline.c @@ -1680,6 +1680,20 @@ ml_recover(int checkext) */ has_error = FALSE; + // Verify the cached block's actual size matches the + // pointer entry's pe_page_count. mf_get() cache hits + // return the original block without resizing, so a + // crafted swap file referencing the same block twice + // with different pe_page_count values would cause an + // OOB write below. + if (hp->bh_page_count != page_count) + { + ++error; + ml_append(lnum++, (char_u *)_("??? BLOCK PAGE COUNT MISMATCH"), + (colnr_T)0, TRUE); + page_count = hp->bh_page_count; + } + // 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) diff --git a/src/po/vim.pot b/src/po/vim.pot index 131e52457f..4996b205ff 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-06-24 17:52+0000\n" +"POT-Creation-Date: 2026-06-27 08:41+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1978,6 +1978,9 @@ msgstr "" msgid "???BLOCK MISSING" msgstr "" +msgid "??? BLOCK PAGE COUNT MISMATCH" +msgstr "" + msgid "??? from here until ???END lines may be messed up" msgstr "" diff --git a/src/testdir/samples/recover-mismatch-pc.swp b/src/testdir/samples/recover-mismatch-pc.swp new file mode 100644 index 0000000000..dd5a1fa083 Binary files /dev/null and b/src/testdir/samples/recover-mismatch-pc.swp differ diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim index bb61e1995d..0c8e28196a 100644 --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -514,8 +514,8 @@ func Test_recover_corrupted_swap_file1() call assert_match('???ILLEGAL BLOCK NUMBER', content) call delete(target) bw! -" -" " Test 2: Segfault + + " Test 2: Segfault new let sample = 'samples/recover-crash2.swp' let target = 'Xpoc2.swp' @@ -531,6 +531,19 @@ func Test_recover_corrupted_swap_file1() call assert_match('???LINES MISSING', content) call delete(target) bw! + + " Test 3: wrong page_count header + new + let sample = 'samples/recover-mismatch-pc.swp' + let target = 'Xmismatch-pc.swp' + call writefile(readblob(sample), target, 'bD') + try + sil noa recover! Xmismatch-pc.swp + catch + endtry + " Verifies no crash occurs. The OOB write is only reliably triggered + " interactively due to memory pressure evicting blocks in the test runner. + bw! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index fa30c5e705..7cfc8de777 100644 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 738, /**/ 737, /**/