Mercurial > vim
view runtime/autoload/rustfmt.vim @ 33864:6e4c686b6b5b v9.0.2142
patch 9.0.2142: [security]: stack-buffer-overflow in option callback functions
Commit: https://github.com/vim/vim/commit/b39b240c386a5a29241415541f1c99e2e6b8ce47
Author: Christian Brabandt <cb@256bit.org>
Date: Wed Nov 29 11:34:05 2023 +0100
patch 9.0.2142: [security]: stack-buffer-overflow in option callback functions
Problem: [security]: stack-buffer-overflow in option callback functions
Solution: pass size of errbuf down the call stack, use snprintf()
instead of sprintf()
We pass the error buffer down to the option callback functions, but in
some parts of the code, we simply use sprintf(buf) to write into the error
buffer, which can overflow.
So let's pass down the length of the error buffer and use sprintf(buf, size)
instead.
Reported by @henices, thanks!
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sun, 10 Dec 2023 15:16:04 +0100 |
parents | d6dde6229b36 |
children |
line wrap: on
line source
" Author: Stephen Sugden <stephen@stephensugden.com> " Last Modified: 2023-09-11 " " Adapted from https://github.com/fatih/vim-go " For bugs, patches and license go to https://github.com/rust-lang/rust.vim if !exists("g:rustfmt_autosave") let g:rustfmt_autosave = 0 endif if !exists("g:rustfmt_command") let g:rustfmt_command = "rustfmt" endif if !exists("g:rustfmt_options") let g:rustfmt_options = "" endif if !exists("g:rustfmt_fail_silently") let g:rustfmt_fail_silently = 0 endif function! rustfmt#DetectVersion() " Save rustfmt '--help' for feature inspection silent let s:rustfmt_help = system(g:rustfmt_command . " --help") let s:rustfmt_unstable_features = s:rustfmt_help =~# "--unstable-features" " Build a comparable rustfmt version variable out of its `--version` output: silent let l:rustfmt_version_full = system(g:rustfmt_command . " --version") let l:rustfmt_version_list = matchlist(l:rustfmt_version_full, \ '\vrustfmt ([0-9]+[.][0-9]+[.][0-9]+)') if len(l:rustfmt_version_list) < 3 let s:rustfmt_version = "0" else let s:rustfmt_version = l:rustfmt_version_list[1] endif return s:rustfmt_version endfunction call rustfmt#DetectVersion() if !exists("g:rustfmt_emit_files") let g:rustfmt_emit_files = s:rustfmt_version >= "0.8.2" endif if !exists("g:rustfmt_file_lines") let g:rustfmt_file_lines = s:rustfmt_help =~# "--file-lines JSON" endif let s:got_fmt_error = 0 function! rustfmt#Load() " Utility call to get this script loaded, for debugging endfunction function! s:RustfmtWriteMode() if g:rustfmt_emit_files return "--emit=files" else return "--write-mode=overwrite" endif endfunction function! s:RustfmtConfigOptions() let l:rustfmt_toml = findfile('rustfmt.toml', expand('%:p:h') . ';') if l:rustfmt_toml !=# '' return '--config-path '.shellescape(fnamemodify(l:rustfmt_toml, ":p")) endif let l:_rustfmt_toml = findfile('.rustfmt.toml', expand('%:p:h') . ';') if l:_rustfmt_toml !=# '' return '--config-path '.shellescape(fnamemodify(l:_rustfmt_toml, ":p")) endif " Default to edition 2018 in case no rustfmt.toml was found. return '--edition 2018' endfunction function! s:RustfmtCommandRange(filename, line1, line2) if g:rustfmt_file_lines == 0 echo "--file-lines is not supported in the installed `rustfmt` executable" return endif let l:arg = {"file": shellescape(a:filename), "range": [a:line1, a:line2]} let l:write_mode = s:RustfmtWriteMode() let l:rustfmt_config = s:RustfmtConfigOptions() " FIXME: When --file-lines gets to be stable, add version range checking " accordingly. let l:unstable_features = s:rustfmt_unstable_features ? '--unstable-features' : '' let l:cmd = printf("%s %s %s %s %s --file-lines '[%s]' %s", g:rustfmt_command, \ l:write_mode, g:rustfmt_options, \ l:unstable_features, l:rustfmt_config, \ json_encode(l:arg), shellescape(a:filename)) return l:cmd endfunction function! s:RustfmtCommand() let write_mode = g:rustfmt_emit_files ? '--emit=stdout' : '--write-mode=display' let config = s:RustfmtConfigOptions() return join([g:rustfmt_command, write_mode, config, g:rustfmt_options]) endfunction function! s:DeleteLines(start, end) abort silent! execute a:start . ',' . a:end . 'delete _' endfunction function! s:RunRustfmt(command, tmpname, from_writepre) let l:view = winsaveview() let l:stderr_tmpname = tempname() call writefile([], l:stderr_tmpname) let l:command = a:command . ' 2> ' . l:stderr_tmpname if a:tmpname ==# '' " Rustfmt in stdin/stdout mode " chdir to the directory of the file let l:has_lcd = haslocaldir() let l:prev_cd = getcwd() execute 'lchdir! '.expand('%:h') let l:buffer = getline(1, '$') if exists("*systemlist") silent let out = systemlist(l:command, l:buffer) else silent let out = split(system(l:command, \ join(l:buffer, "\n")), '\r\?\n') endif else if exists("*systemlist") silent let out = systemlist(l:command) else silent let out = split(system(l:command), '\r\?\n') endif endif let l:stderr = readfile(l:stderr_tmpname) call delete(l:stderr_tmpname) let l:open_lwindow = 0 if v:shell_error == 0 if a:from_writepre " remove undo point caused via BufWritePre try | silent undojoin | catch | endtry endif if a:tmpname ==# '' let l:content = l:out else " take the tmpfile's content, this is better than rename " because it preserves file modes. let l:content = readfile(a:tmpname) endif call s:DeleteLines(len(l:content), line('$')) call setline(1, l:content) " only clear location list if it was previously filled to prevent " clobbering other additions if s:got_fmt_error let s:got_fmt_error = 0 call setloclist(0, []) let l:open_lwindow = 1 endif elseif g:rustfmt_fail_silently == 0 && !a:from_writepre " otherwise get the errors and put them in the location list let l:errors = [] let l:prev_line = "" for l:line in l:stderr " error: expected one of `;` or `as`, found `extern` " --> src/main.rs:2:1 let tokens = matchlist(l:line, '^\s\+-->\s\(.\{-}\):\(\d\+\):\(\d\+\)$') if !empty(tokens) call add(l:errors, {"filename": @%, \"lnum": tokens[2], \"col": tokens[3], \"text": l:prev_line}) endif let l:prev_line = l:line endfor if !empty(l:errors) call setloclist(0, l:errors, 'r') echohl Error | echomsg "rustfmt returned error" | echohl None else echo "rust.vim: was not able to parse rustfmt messages. Here is the raw output:" echo "\n" for l:line in l:stderr echo l:line endfor endif let s:got_fmt_error = 1 let l:open_lwindow = 1 endif " Restore the current directory if needed if a:tmpname ==# '' if l:has_lcd execute 'lchdir! '.l:prev_cd else execute 'chdir! '.l:prev_cd endif endif " Open lwindow after we have changed back to the previous directory if l:open_lwindow == 1 lwindow endif call winrestview(l:view) endfunction function! rustfmt#FormatRange(line1, line2) let l:tmpname = tempname() call writefile(getline(1, '$'), l:tmpname) let command = s:RustfmtCommandRange(l:tmpname, a:line1, a:line2) call s:RunRustfmt(command, l:tmpname, v:false) call delete(l:tmpname) endfunction function! rustfmt#Format() call s:RunRustfmt(s:RustfmtCommand(), '', v:false) endfunction function! rustfmt#Cmd() " Mainly for debugging return s:RustfmtCommand() endfunction function! rustfmt#PreWrite() if !filereadable(expand("%@")) return endif if rust#GetConfigVar('rustfmt_autosave_if_config_present', 0) if findfile('rustfmt.toml', '.;') !=# '' || findfile('.rustfmt.toml', '.;') !=# '' let b:rustfmt_autosave = 1 let b:_rustfmt_autosave_because_of_config = 1 endif else if has_key(b:, '_rustfmt_autosave_because_of_config') unlet b:_rustfmt_autosave_because_of_config unlet b:rustfmt_autosave endif endif if !rust#GetConfigVar("rustfmt_autosave", 0) return endif call s:RunRustfmt(s:RustfmtCommand(), '', v:true) endfunction " vim: set et sw=4 sts=4 ts=8: