vim-patch:9.1.1802: 'nowrap' in a modeline may hide malicious code (#35946)

Problem:  'nowrap' in a modeline may hide malicious code.
Solution: Forcibly use '>' as 'listchars' "extends" if 'nowrap' was set
          from a modeline (zeertzjq).

Manual `:setlocal nowrap` disables this behavior.  There is a separate
problem with `:set nowrap` that also applies to some other options.

related: vim/vim#18214
related: vim/vim#18399
closes: vim/vim#18425

9d5208a931

Cherry-pick some test_modeline.vim changes from patches 9.0.{0363,0626}.
This commit is contained in:
zeertzjq
2025-09-29 07:48:46 +08:00
committed by GitHub
parent fcf752476a
commit 0fa0717d4e
7 changed files with 89 additions and 12 deletions

View File

@@ -1303,6 +1303,7 @@ struct window_S {
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
// A few options have local flags for kOptFlagInsecure.
uint32_t w_p_wrap_flags; // flags for 'wrap'
uint32_t w_p_stl_flags; // flags for 'statusline'
uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'

View File

@@ -153,6 +153,21 @@ void drawline_free_all_mem(void)
}
#endif
/// Get the 'listchars' "extends" characters to use for "wp", or NUL if it
/// shouldn't be used.
static schar_T get_lcs_ext(win_T *wp)
{
if (wp->w_p_wrap) {
// Line never continues beyond the right of the screen with 'wrap'.
return NUL;
}
if (wp->w_p_wrap_flags & kOptFlagInsecure) {
// If 'nowrap' was set from a modeline, forcibly use '>'.
return schar_from_ascii('>');
}
return wp->w_p_list ? wp->w_p_lcs_chars.ext : NUL;
}
/// Advance wlv->color_cols if not NULL
static void advance_color_col(winlinevars_T *wlv, int vcol)
{
@@ -2865,12 +2880,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
break;
}
// Show "extends" character from 'listchars' if beyond the line end and
// 'list' is set.
// Don't show this with 'wrap' as the line can't be scrolled horizontally.
if (wp->w_p_lcs_chars.ext != NUL
&& wp->w_p_list
&& !wp->w_p_wrap
// Show "extends" character from 'listchars' if beyond the line end.
const schar_T lcs_ext = get_lcs_ext(wp);
if (lcs_ext != NUL
&& wlv.filler_todo <= 0
&& wlv.col == view_width - 1
&& !has_foldtext) {
@@ -2882,7 +2894,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|| (lcs_eol > 0 && lcs_eol_todo)
|| (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|| (may_have_inline_virt && has_more_inline_virt(&wlv, ptr - line))) {
mb_schar = wp->w_p_lcs_chars.ext;
mb_schar = lcs_ext;
wlv.char_attr = win_hl_attr(wp, HLF_AT);
mb_c = schar_get_first_codepoint(mb_schar);
}

View File

@@ -1644,6 +1644,8 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags)
if (opt_flags & OPT_LOCAL) {
assert(wp != NULL);
switch (opt_idx) {
case kOptWrap:
return &wp->w_p_wrap_flags;
case kOptStatusline:
return &wp->w_p_stl_flags;
case kOptWinbar:

View File

@@ -10811,6 +10811,11 @@ local options = {
< See 'sidescroll', 'listchars' and |wrap-off|.
This option can't be set from a |modeline| when the 'diff' option is
on.
If 'nowrap' was set from a |modeline| or in the |sandbox|, '>' is used
as the |lcs-extends| character regardless of the value of the 'list'
and 'listchars' options. This is to prevent malicious code outside
the viewport from going unnoticed. Use `:setlocal nowrap` manually
afterwards to disable this behavior.
]=],
full_name = 'wrap',
redraw = { 'current_window' },