]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0458: Crash with invalid shellredir/shellpipe value v9.2.0458
authorChristian Brabandt <cb@256bit.org>
Fri, 8 May 2026 21:29:21 +0000 (21:29 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 8 May 2026 21:32:21 +0000 (21:32 +0000)
Problem:  Crash with invalid shellredir/shellpipe value
          (bfredl)
Solution: Validate the option and allow only a single "%s".

fixes:  #20157
closes: #20159

Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
runtime/doc/tags
src/errors.h
src/option.c
src/optiondefs.h
src/po/vim.pot
src/proto/option.pro
src/testdir/test_options.vim
src/testdir/util/gen_opt_test.vim
src/version.c

index 969a17c1649134fcf02f0174b5d7e4aa04ce3fa2..c3753f197fcda011aec8801ebfa9412013080f82 100644 (file)
@@ -7846,6 +7846,7 @@ A jump table for the options with a short description can be found at |Q_op|.
        Note: When using a pipe like "| tee", you'll lose the exit code of the
        shell command.  This might be configurable by your shell, look for
        the pipefail option (for bash and zsh, use ":set -o pipefail").
+       Only a single "%s" value is allowed.
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
 
@@ -7889,6 +7890,9 @@ A jump table for the options with a short description can be found at |Q_op|.
        become obsolete (at least for Unix).
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
+                                                               *E1577*
+       Only a single "%s" item is allowed in the option value.
+
 
                        *'shellslash'* *'ssl'* *'noshellslash'* *'nossl'*
 'shellslash' 'ssl'     boolean (default off)
index 4ad699ed55db93c10bb1b3c2713f1bd1c92a53dc..3168235dad021d321ac9967dcc850b1053ceaf96 100644 (file)
@@ -4779,6 +4779,7 @@ E1573     channel.txt     /*E1573*
 E1574  channel.txt     /*E1574*
 E1575  builtin.txt     /*E1575*
 E1576  tagsrch.txt     /*E1576*
+E1577  options.txt     /*E1577*
 E158   sign.txt        /*E158*
 E159   sign.txt        /*E159*
 E16    cmdline.txt     /*E16*
index 384dfaab9ca870786f0b9d20005badce4b6dc8b1..3ba7eca8f50d9284b287933fa016fa35f6502c94 100644 (file)
@@ -3811,3 +3811,5 @@ EXTERN char e_cannot_create_pipes[]
 #endif
 EXTERN char e_tag_file_entry_must_not_be_url[]
        INIT(= N_("E1576: Tag file entry must not be a URL"));
+EXTERN char e_invalid_format_string_single_percent_s[]
+       INIT(= N_("E1577: Invalid format string, only one \"%s\" is allowed"));
index b44a3990ad3a46e8f4d78e96d9a57b9e5266c3f9..426e58e29b6bc4c1d201b579e8a53f51da72c75a 100644 (file)
@@ -4527,6 +4527,43 @@ did_set_maxsearchcount(optset_T *args UNUSED)
 #undef MAX_SEARCH_COUNT
 }
 
+/*
+ * Validate 'shellpipe'/'shellredir' option.
+ */
+    char *
+did_set_shellpipe_redir(optset_T *args)
+{
+    char_u     *p;
+    bool       seen = false;
+
+    for (p = args->os_newval.string; *p != NUL; ++p)
+    {
+       if (*p != '%')
+           continue;
+
+       if (p[1] == NUL)
+           return e_invalid_format_string_single_percent_s;
+
+       if (p[1] == '%')
+       {
+           ++p;    // skip second %
+           continue;
+       }
+
+       if (p[1] == 's')
+       {
+           if (seen)
+               return e_invalid_format_string_single_percent_s;
+
+           seen = true;
+           ++p;    // consume 's'
+           continue;
+       }
+       return e_invalid_format_string_single_percent_s;
+    }
+    return NULL;
+}
+
 
 #if defined(BACKSLASH_IN_FILENAME)
 /*
index 0a93b70f8ef82be9c4dccfbc1d0e5b59c665ca0f..afcaf9631995fd1ea0798347dddea3b8a536277c 100644 (file)
@@ -2323,7 +2323,7 @@ static struct vimoption options[] =
                                (char_u *)0L} SCTX_INIT},
     {"shellpipe",   "sp",   P_STRING|P_VI_DEF|P_SECURE,
 #ifdef FEAT_QUICKFIX
-                           (char_u *)&p_sp, PV_NONE, NULL, NULL,
+                           (char_u *)&p_sp, PV_NONE, did_set_shellpipe_redir, NULL,
                            {
 # if defined(UNIX)
                            (char_u *)"| tee",
@@ -2340,7 +2340,7 @@ static struct vimoption options[] =
                            (char_u *)&p_shq, PV_NONE, NULL, NULL,
                            {(char_u *)"", (char_u *)0L} SCTX_INIT},
     {"shellredir",  "srr",  P_STRING|P_VI_DEF|P_SECURE,
-                           (char_u *)&p_srr, PV_NONE, NULL, NULL,
+                           (char_u *)&p_srr, PV_NONE, did_set_shellpipe_redir, NULL,
                            {(char_u *)">", (char_u *)0L} SCTX_INIT},
     {"shellslash",  "ssl",   P_BOOL|P_VI_DEF,
 #ifdef BACKSLASH_IN_FILENAME
index 4dd6ba9093d6586c1f71a77a12b1207375991940..5db285038994a986ba8eb43e7c1202f82cb2bcb3 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Vim\n"
 "Report-Msgid-Bugs-To: vim-dev@vim.org\n"
-"POT-Creation-Date: 2026-04-29 19:49+0000\n"
+"POT-Creation-Date: 2026-05-07 19:25+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -8860,6 +8860,10 @@ msgstr ""
 msgid "E1576: Tag file entry must not be a URL"
 msgstr ""
 
+#, c-format
+msgid "E1577: Invalid format string, only one \"%s\" is allowed"
+msgstr ""
+
 #. type of cmdline window or 0
 #. result of cmdline window or 0
 #. buffer of cmdline window or NULL
index ae586ea9787d813873b5681a0414d0bb9e2f8aa2..ad8c9043ab237c8e8fba77d8bedf5eed494774e6 100644 (file)
@@ -65,6 +65,7 @@ char *did_set_pyxversion(optset_T *args);
 char *did_set_readonly(optset_T *args);
 char *did_set_scrollbind(optset_T *args);
 char *did_set_maxsearchcount(optset_T *args);
+char *did_set_shellpipe_redir(optset_T *args);
 char *did_set_shellslash(optset_T *args);
 char *did_set_shiftwidth_tabstop(optset_T *args);
 char *did_set_showtabline(optset_T *args);
index fa2667525ff09397ef1fa22996401355807d55d1..340ca36419e786bce82ed65736102d4d2a835eb5 100644 (file)
@@ -2658,6 +2658,8 @@ func Test_string_option_revert_on_failure()
         \ ['selection', 'exclusive', 'a123'],
         \ ['selectmode', 'cmd', 'a123'],
         \ ['sessionoptions', 'options', 'a123'],
+        \ ['shellpipe', '>%s', "%s%s%s"],
+        \ ['shellredir', '>%s', "%s%s%s"],
         \ ['shortmess', 'w', '2'],
         \ ['showbreak', '>>', "\x01"],
         \ ['showcmdloc', 'statusline', 'a123'],
index 91ed2c80cd69031a6a846f72153932668e8ede4f..f5d71ad185965c7131798c07d7acab0c9843e161 100644 (file)
@@ -307,6 +307,10 @@ let test_values = {
       \ 'sessionoptions': [['', 'blank', 'curdir', 'sesdir',
       \                'help,options,slash'],
       \                ['xxx', 'curdir,sesdir']],
+      \ 'shellpipe': [[ '', '>', '>%s2>&1', '\|tee', '\|&tee', '2>&1\|tee', '%%'],
+      \                ['%s%s%s', '%s%p%d']],
+      \ 'shellredir': [[ '', '>', '>%s2>&1', '\|tee', '\|&tee', '2>&1\|tee', '%%'],
+      \                ['%s%s%s', '%s%p%d']],
       \ 'showcmdloc': [['', 'last', 'statusline', 'tabline'], ['xxx']],
       \ 'signcolumn': [['', 'auto', 'no', 'yes', 'number'], ['xxx', 'no,yes']],
       \ 'spellfile': [['', 'file.en.add', 'xxx.en.add,yyy.gb.add,zzz.ja.add',
index 709f46b8df78cfabc145dd41be0dc8627d211b58..2aefdf7e5b6decba2c9382174805a725ad17fbab 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    458,
 /**/
     457,
 /**/