]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1802: 'nowrap' in a modeline may hide malicious code v9.1.1802
authorzeertzjq <zeertzjq@outlook.com>
Sun, 28 Sep 2025 17:29:19 +0000 (17:29 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 28 Sep 2025 17:37:58 +0000 (17:37 +0000)
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: #18214
related: #18399
closes: #18425

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
runtime/doc/version9.txt
src/drawline.c
src/option.c
src/structs.h
src/testdir/test_modeline.vim
src/version.c

index ebd7e53bca15905459fcfe7d5e49481e953b5aad..748b5ae0a176a69546cf323060921756bdefefff 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.1.  Last change: 2025 Sep 26
+*options.txt*  For Vim version 9.1.  Last change: 2025 Sep 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -10259,6 +10259,11 @@ A jump table for the options with a short description can be found at |Q_op|.
 <      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.
 
                                                *'wrapmargin'* *'wm'*
 'wrapmargin' 'wm'      number  (default 0)
index 5447b28c6a176f2fa53d99f7d689eb7ffd537af2..4ad3eb2a4a4486f0a47299729feb05132315a3f9 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Sep 26
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Sep 28
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41710,6 +41710,9 @@ Options: ~
 - 'rulerformat' now supports the |stl-%!| item
 - use 'smoothscroll' logic for CTRL-F / CTRL-B for pagewise scrolling
   and CTRL-D / CTRL-U for half-pagewise scrolling
+- Setting 'nowrap' in a modeline could cause long lines to be hidden
+  off-screen.  To make this visible, the listchars "extend" suboption is set
+  to ">" by default, indicating text that extends beyond the window width.
 
 Ex commands: ~
 - allow to specify a priority when defining a new sign |:sign-define|
index dc68c45c03efa39b2f9590d1aa9c99c802eb7526..40a57e785c54d5e20bbcaf59b6ad26e822324f38 100644 (file)
 
 #include "vim.h"
 
+/*
+ * Get the 'listchars' "extends" characters to use for "wp", or NUL if it
+ * shouldn't be used.
+ */
+    static int
+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 & P_INSECURE)
+       // If 'nowrap' was set from a modeline, forcibly use '>'.
+       return '>';
+    return wp->w_p_list ? wp->w_lcs_chars.ext : NUL;
+}
+
 #ifdef FEAT_SYN_HL
 /*
  * Advance **color_cols and return TRUE when there are columns to draw.
@@ -732,10 +748,7 @@ text_prop_position(
 
        // With 'nowrap' add one to show the "extends" character if needed (it
        // doesn't show if the text just fits).
-       if (!wp->w_p_wrap
-               && n_used < *n_extra
-               && wp->w_lcs_chars.ext != NUL
-               && wp->w_p_list)
+       if (n_used < *n_extra && get_lcs_ext(wp) != NUL)
            ++n_used;
 
        // add 1 for NUL, 2 for when '…' is used
@@ -3947,12 +3960,10 @@ win_line(
            }
        }
 
-       // Show "extends" character from 'listchars' if beyond the line end and
-       // 'list' is set.
-       if (wp->w_lcs_chars.ext != NUL
+       // Show "extends" character from 'listchars' if beyond the line end.
+       int lcs_ext = get_lcs_ext(wp);
+       if (lcs_ext != NUL
                && wlv.draw_state == WL_LINE
-               && wp->w_p_list
-               && !wp->w_p_wrap
 #ifdef FEAT_DIFF
                && wlv.filler_todo <= 0
 #endif
@@ -3970,7 +3981,7 @@ win_line(
 #endif
                   ))
        {
-           c = wp->w_lcs_chars.ext;
+           c = lcs_ext;
            wlv.char_attr = hl_combine_attr(wlv.win_attr, HL_ATTR(HLF_AT));
            mb_c = c;
            if (enc_utf8 && utf_char2len(c) > 1)
index 88da9c09959ed25968e404f7d7e49bfa51891aba..4a4d21b8813f5d59025a9d25acd15a2256d5de46 100644 (file)
@@ -3081,6 +3081,7 @@ insecure_flag(int opt_idx, int opt_flags)
     if (opt_flags & OPT_LOCAL)
        switch ((int)options[opt_idx].indir)
        {
+           case PV_WRAP:       return &curwin->w_p_wrap_flags;
 #ifdef FEAT_STL_OPT
            case PV_STL:        return &curwin->w_p_stl_flags;
 #endif
index 981db0e7cfc97cd6a38d9bfaf574ca960cf19494..72838bda2f5a3034ca8e20740cc8e6002ed2378c 100644 (file)
@@ -4214,6 +4214,7 @@ struct window_S
 #define GLOBAL_WO(p)   ((char *)(p) + sizeof(winopt_T))
 
     // A few options have local flags for P_INSECURE.
+    long_u     w_p_wrap_flags;     // flags for 'wrap'
 #ifdef FEAT_STL_OPT
     long_u     w_p_stl_flags;      // flags for 'statusline'
 #endif
index 1f8686328a5956126ee8787f261ec6605be85562..a5762f7f6b212ff31af30cac2f71749b410db35b 100644 (file)
@@ -361,4 +361,53 @@ func Test_modeline_disable()
   call assert_equal(2, &sw)
 endfunc
 
+" If 'nowrap' is set from a modeline, '>' is used forcibly as lcs-extends.
+func Test_modeline_nowrap_lcs_extends()
+  call writefile([
+        \ 'aaa',
+        \ 'bbb',
+        \ 'ccc                    evil',
+        \ 'ddd                    vim: nowrap',
+        \ ], 'Xmodeline_nowrap', 'D')
+  call NewWindow(10, 20)
+
+  setlocal nolist listchars=
+  edit Xmodeline_nowrap
+  let expect_insecure = [
+        \ 'aaa                 ',
+        \ 'bbb                 ',
+        \ 'ccc                >',
+        \ 'ddd                >',
+        \ '~                   ',
+        \ ]
+  call assert_equal(expect_insecure, ScreenLines([1, 5], 20))
+
+  setlocal nowrap
+  let expect_secure = [
+        \ 'aaa                 ',
+        \ 'bbb                 ',
+        \ 'ccc                 ',
+        \ 'ddd                 ',
+        \ '~                   ',
+        \ ]
+  call assert_equal(expect_secure, ScreenLines([1, 5], 20))
+
+  setlocal list listchars=extends:+
+  let expect_secure = [
+        \ 'aaa                 ',
+        \ 'bbb                 ',
+        \ 'ccc                +',
+        \ 'ddd                +',
+        \ '~                   ',
+        \ ]
+  call assert_equal(expect_secure, ScreenLines([1, 5], 20))
+
+  edit Xmodeline_nowrap
+  call assert_equal(expect_insecure, ScreenLines([1, 5], 20))
+  setlocal nowrap
+  call assert_equal(expect_secure, ScreenLines([1, 5], 20))
+
+  call CloseWindow()
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 47d461971d2117e62421448f6a5f566ea26ce1bb..4d03167a738712c5a57f9196e2c6b2e6768bb9fc 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1802,
 /**/
     1801,
 /**/