mirror of
https://github.com/neovim/neovim.git
synced 2026-06-16 00:31:16 +00:00
vim-patch:9.2.0461: Corrupted undofile causes use-after-free (#39707)
Problem: The four pointer-resolution loops in u_read_undo() lack
an i != j guard, so a header whose uh_next.seq equals
its own uh_seq resolves uh_next.ptr to itself. On
buffer close, u_freeheader() sees uhp->uh_next.ptr !=
NULL and skips updating b_u_oldhead, so u_blockfree()
dereferences the freed header on the next iteration.
The same pattern applies to uh_prev, uh_alt_next and
uh_alt_prev. A crafted .un~ file in the same directory
as a text file can trigger the use-after-free and
subsequent double-free when the buffer is closed.
(Daniel Cervera)
Solution: Add an i != j guard to each of the four resolution
loops, matching the guard already present in the
duplicate-detection loop above.
closes: vim/vim#20168
Supported by AI
4f610f07b7
Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 2d5f56c0aa)
This commit is contained in:
committed by
github-actions[bot]
parent
2902ec0541
commit
647b6be489
@@ -1565,36 +1565,48 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL
|
||||
&& uhp_table[j]->uh_seq == uhp->uh_next.seq) {
|
||||
uhp->uh_next.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
{
|
||||
const int seq = uhp->uh_next.seq;
|
||||
uhp->uh_next.ptr = NULL;
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL && i != j && uhp_table[j]->uh_seq == seq) {
|
||||
uhp->uh_next.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL
|
||||
&& uhp_table[j]->uh_seq == uhp->uh_prev.seq) {
|
||||
uhp->uh_prev.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
{
|
||||
const int seq = uhp->uh_prev.seq;
|
||||
uhp->uh_prev.ptr = NULL;
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL && i != j && uhp_table[j]->uh_seq == seq) {
|
||||
uhp->uh_prev.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL
|
||||
&& uhp_table[j]->uh_seq == uhp->uh_alt_next.seq) {
|
||||
uhp->uh_alt_next.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
{
|
||||
const int seq = uhp->uh_alt_next.seq;
|
||||
uhp->uh_alt_next.ptr = NULL;
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL && i != j && uhp_table[j]->uh_seq == seq) {
|
||||
uhp->uh_alt_next.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL
|
||||
&& uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq) {
|
||||
uhp->uh_alt_prev.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
{
|
||||
const int seq = uhp->uh_alt_prev.seq;
|
||||
uhp->uh_alt_prev.ptr = NULL;
|
||||
for (int j = 0; j < num_head; j++) {
|
||||
if (uhp_table[j] != NULL && i != j && uhp_table[j]->uh_seq == seq) {
|
||||
uhp->uh_alt_prev.ptr = uhp_table[j];
|
||||
SET_FLAG(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) {
|
||||
|
||||
@@ -924,4 +924,43 @@ func Test_restore_cursor_position_after_undo()
|
||||
endfunc
|
||||
|
||||
|
||||
" Corrupted undo file via cyclic cross-references caused
|
||||
" double free
|
||||
func Test_corrupted_undofile()
|
||||
CheckFeature persistent_undo
|
||||
let _uf = &undofile
|
||||
set undofile
|
||||
new
|
||||
call setline(1, 'hello')
|
||||
let b=eval('0z56696D9F556E446FE50002F3AEFE62965A91903610' ..
|
||||
\ 'F0E23CC8A69D5B87CEA6D28E75489B0D2CA02ED7993C' ..
|
||||
\ '00000001000000000000000000000000000000010000' ..
|
||||
\ '00010000000100000001000000010000000100000000' ..
|
||||
\ '3B9ACA00005FD0000000010000000000000000000000' ..
|
||||
\ '00000000010000000100000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '00000000000000000000000000000000000000000000' ..
|
||||
\ '000000000000000000000000000000000000003B9ACA' ..
|
||||
\ '00003581E7AA')
|
||||
" Nvim: convert undo file format
|
||||
let b = b->slice(0, -2) + 0z3581 + b->slice(-2) | let b[10] = 0x03
|
||||
call writefile(b, 'Xundo', 'bD')
|
||||
rundo Xundo
|
||||
bw!
|
||||
let &undofile = _uf
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
Reference in New Issue
Block a user