vim-patch:9.1.2028: [security]: Buffer-overflow with incomplete multi-byte chars (#37133)

Problem:  Buffer overflow in buf_write() when converting incomplete
          multi-byte characters (Kevin Goodsell)
Solution: Make the buffer slightly larger

closes: vim/vim#19007

f99de42a9f

Co-authored-by: Christian Brabandt <cb@256bit.org>
(cherry picked from commit 444e1ffe3e)
This commit is contained in:
zeertzjq
2025-12-28 08:37:55 +08:00
committed by github-actions[bot]
parent 18f189a3f3
commit fda8d2c717
4 changed files with 44 additions and 2 deletions

View File

@@ -288,6 +288,17 @@ static int buf_write_convert(struct bw_info *ip, char **bufp, int *lenp)
c = n > 1 ? (unsigned)utf_ptr2char(*bufp + wlen) c = n > 1 ? (unsigned)utf_ptr2char(*bufp + wlen)
: (uint8_t)(*bufp)[wlen]; : (uint8_t)(*bufp)[wlen];
} }
// Check that there is enough space
if (!(flags & FIO_LATIN1)) {
size_t need = (flags & FIO_UCS4) ? 4 : 2;
if ((flags & FIO_UTF16) && c >= 0x10000) {
need = 4;
}
if ((size_t)(p - ip->bw_conv_buf) + need > ip->bw_conv_buflen) {
return FAIL;
}
}
if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) { if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) {
ip->bw_conv_error = true; ip->bw_conv_error = true;
@@ -1291,11 +1302,13 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (converted) { if (converted) {
wb_flags = get_fio_flags(fenc); wb_flags = get_fio_flags(fenc);
if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8)) { if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8)) {
// overallocate a bit, in case we read incomplete multi-byte chars
int size = bufsize + CONV_RESTLEN;
// Need to allocate a buffer to translate into. // Need to allocate a buffer to translate into.
if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) { if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) {
write_info.bw_conv_buflen = (size_t)bufsize * 2; write_info.bw_conv_buflen = (size_t)size * 2;
} else { // FIO_UCS4 } else { // FIO_UCS4
write_info.bw_conv_buflen = (size_t)bufsize * 4; write_info.bw_conv_buflen = (size_t)size * 4;
} }
write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen); write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen);
if (!write_info.bw_conv_buf) { if (!write_info.bw_conv_buf) {

View File

@@ -63,3 +63,13 @@ it('no crash when writing "Untitled" file fails', function()
eq('Vim(enew):E502: "Untitled" is a directory', pcall_err(command, 'confirm enew')) eq('Vim(enew):E502: "Untitled" is a directory', pcall_err(command, 'confirm enew'))
assert_alive() assert_alive()
end) end)
-- oldtest: Test_crash_bufwrite()
it('no crash when converting buffer with incomplete multibyte chars', function()
command('edit ++bin test/old/testdir/samples/buffer-test.txt')
finally(function()
os.remove('Xoutput')
end)
command('w! ++enc=ucs4 Xoutput')
assert_alive()
end)

File diff suppressed because one or more lines are too long

View File

@@ -231,4 +231,22 @@ func TearDown()
call delete('Untitled') call delete('Untitled')
endfunc endfunc
func Test_crash_bufwrite()
let lines =<< trim END
w! ++enc=ucs4 Xoutput
call writefile(['done'], 'Xbufwrite')
END
call writefile(lines, 'Xvimrc')
let opts = #{wait_for_ruler: 0, rows: 20}
let args = ' -u NONE -i NONE -b -S Xvimrc'
let buf = RunVimInTerminal(args .. ' samples/buffer-test.txt', opts)
call TermWait(buf, 1000)
call StopVimInTerminal(buf)
call WaitForAssert({-> assert_true(filereadable('Xbufwrite'))})
call assert_equal(['done'], readfile('Xbufwrite'))
call delete('Xbufwrite')
call delete('Xoutput')
call delete('Xvimrc')
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab