]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0738: ml_recover() may write beyond block buffer v9.2.0738
authorChristian Brabandt <cb@256bit.org>
Sat, 27 Jun 2026 08:39:43 +0000 (08:39 +0000)
committerChristian Brabandt <cb@256bit.org>
Sat, 27 Jun 2026 08:41:51 +0000 (08:41 +0000)
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 <cb@256bit.org>
src/memline.c
src/po/vim.pot
src/testdir/samples/recover-mismatch-pc.swp [new file with mode: 0644]
src/testdir/test_recover.vim
src/version.c

index 4ad09f43a58fd2f18558f7304c187923f0af30aa..2e0403d4167e7c50d0183b5a6040becd275fe887 100644 (file)
@@ -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)
index 131e52457fb7d9f2502dc074bee803d91299859a..4996b205ff817171a9bdd139b51170550e300dbd 100644 (file)
@@ -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 <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\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 (file)
index 0000000..dd5a1fa
Binary files /dev/null and b/src/testdir/samples/recover-mismatch-pc.swp differ
index bb61e1995d3a9f7c6600acb7ef3a2ddc026eb2bb..0c8e28196ab922a6f46f1a9e875803f73399e7fd 100644 (file)
@@ -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
index fa30c5e7057026494ba3279f1e48a3a11710c426..7cfc8de77729e9035211f0e3fb1a0ac31835c429 100644 (file)
@@ -759,6 +759,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    738,
 /**/
     737,
 /**/