patch 9.2.0458: Crash with invalid shellredir/shellpipe value

Commit: 
https://github.com/vim/vim/commit/84ae09dd79b9888ba71dc2a28f9afcac3e7b8901
Author: Christian Brabandt <[email protected]>
Date:   Fri May 8 21:29:21 2026 +0000

    patch 9.2.0458: Crash with invalid shellredir/shellpipe value
    
    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 <[email protected]>

diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 969a17c16..c3753f197 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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)
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 4ad699ed5..3168235da 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -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*
diff --git a/src/errors.h b/src/errors.h
index 384dfaab9..3ba7eca8f 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -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"));
diff --git a/src/option.c b/src/option.c
index b44a3990a..426e58e29 100644
--- a/src/option.c
+++ b/src/option.c
@@ -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)
 /*
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 0a93b70f8..afcaf9631 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -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
diff --git a/src/po/vim.pot b/src/po/vim.pot
index 4dd6ba909..5db285038 100644
--- a/src/po/vim.pot
+++ b/src/po/vim.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Vim
"
 "Report-Msgid-Bugs-To: [email protected]
"
-"POT-Creation-Date: 2026-04-29 19:49+0000
"
+"POT-Creation-Date: 2026-05-07 19:25+0000
"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>
"
 "Language-Team: LANGUAGE <[email protected]>
"
@@ -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
diff --git a/src/proto/option.pro b/src/proto/option.pro
index ae586ea97..ad8c9043a 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -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);
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index fa2667525..340ca3641 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -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'],
diff --git a/src/testdir/util/gen_opt_test.vim 
b/src/testdir/util/gen_opt_test.vim
index 91ed2c80c..f5d71ad18 100644
--- a/src/testdir/util/gen_opt_test.vim
+++ b/src/testdir/util/gen_opt_test.vim
@@ -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',
diff --git a/src/version.c b/src/version.c
index 709f46b8d..2aefdf7e5 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    458,
 /**/
     457,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wLT0L-00Ci4p-GA%40256bit.org.

Raspunde prin e-mail lui