From: Christian Brabandt Date: Sun, 3 May 2026 17:47:50 +0000 (+0000) Subject: patch 9.2.0434: cscope: filename interpreted by /bin/sh X-Git-Tag: v9.2.0434^0 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fde5a56ecbf9101314ddcc572533e147a9fb11ff;p=thirdparty%2Fvim.git patch 9.2.0434: cscope: filename interpreted by /bin/sh Problem: cs_create_connection() builds the cscope command by interpolating csinfo[i].fname (and ppath, flags) into a string and lets the shell parse it. Shell metacharacters in a database filename are therefore evaluated by /bin/sh before cscope is exec'd, rather than being passed through as a literal path (q1uf3ng) Solution: Build argv directly and execvp() the cscope binary without an intervening shell. closes: #20119 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Christian Brabandt --- diff --git a/src/if_cscope.c b/src/if_cscope.c index 7962000777..54cae51f75 100644 --- a/src/if_cscope.c +++ b/src/if_cscope.c @@ -829,11 +829,11 @@ cs_create_connection(int i) int to_cs[2], from_cs[2]; # endif int cmdlen; - int len; char *prog, *cmd, *ppath = NULL; size_t proglen; # ifdef MSWIN int fd; + int len; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; @@ -872,7 +872,6 @@ err_closing: else if (csinfo[i].pid == 0) // child: run cscope. { char **argv = NULL; - int argc = 0; if (dup2(to_cs[0], STDIN_FILENO) == -1) PERROR("cs_create_connection 1"); @@ -956,48 +955,93 @@ err_closing: // run the cscope command # ifdef UNIX - vim_snprintf(cmd, cmdlen, "/bin/sh -c \"exec %s -dl -f %s", - prog, csinfo[i].fname); -# else - vim_snprintf(cmd, cmdlen, "%s -dl -f %s", prog, csinfo[i].fname); -# endif - if (csinfo[i].ppath != NULL) - { - len = (int)STRLEN(cmd); - vim_snprintf(cmd + len, cmdlen - len, " -P%s", csinfo[i].ppath); - } - if (csinfo[i].flags != NULL) { - len = (int)STRLEN(cmd); - vim_snprintf(cmd + len, cmdlen - len, " %s", csinfo[i].flags); - } -# ifdef UNIX - // terminate the -c command argument - STRCAT(cmd, "\""); + garray_T ga_argv; + char **tok = NULL; + int tokc = 0; - // on Win32 we still need prog - vim_free(prog); -# endif - vim_free(ppath); + ga_init2(&ga_argv, sizeof(char *), 8); + + // 'cscopeprg' may be multi-word (e.g. "cscope --foo"); + // split it into separate argv entries. + if (build_argv_from_string((char_u *)prog, &tok, &tokc) == FAIL) + exit(EXIT_FAILURE); + for (int k = 0; k < tokc; ++k) + { + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = tok[k]; + } + VIM_CLEAR(tok); + + // Literal arguments: "-dl", "-f", fname. + if (ga_grow(&ga_argv, 3) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)"-dl"); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)"-f"); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)csinfo[i].fname); + + // "-P" as a single argv entry, if set. + if (ppath != NULL) + { + size_t plen = STRLEN(ppath) + 3; + char *parg = alloc(plen); + + if (parg == NULL || ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + vim_snprintf(parg, plen, "-P%s", ppath); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = parg; + } + + // 'flags' is intended to be a sequence of cscope tokens + // like "-q -i somefile"; split it the same way as prog. + if (csinfo[i].flags != NULL) + { + if (build_argv_from_string((char_u *)csinfo[i].flags, + &tok, &tokc) == FAIL) + exit(EXIT_FAILURE); + for (int k = 0; k < tokc; ++k) + { + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = tok[k]; + } + vim_free(tok); + } + + // NULL-terminate argv. + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len] = NULL; + + vim_free(prog); + vim_free(ppath); + vim_free(cmd); -# if defined(UNIX) # if defined(HAVE_SETSID) || defined(HAVE_SETPGID) - // Change our process group to avoid cscope receiving SIGWINCH. + // Change our process group to avoid cscope receiving SIGWINCH. # if defined(HAVE_SETSID) - (void)setsid(); + (void)setsid(); # else - if (setpgid(0, 0) == -1) - PERROR(_("cs_create_connection setpgid failed")); + if (setpgid(0, 0) == -1) + PERROR(_("cs_create_connection setpgid failed")); # endif # endif - if (build_argv_from_string((char_u *)cmd, &argv, &argc) == FAIL) - exit(EXIT_FAILURE); - if (execvp(argv[0], argv) == -1) - PERROR(_("cs_create_connection exec failed")); + argv = (char **)ga_argv.ga_data; + if (argv[0] == NULL || execvp(argv[0], argv) == -1) + { + fprintf(stderr, "%s\n", + _("cs_create_connection exec failed")); + fflush(stderr); + } - exit(127); - // NOTREACHED + exit(127); + // NOTREACHED + } } else // parent. { @@ -1016,6 +1060,19 @@ err_closing: } # else // MSWIN + vim_snprintf(cmd, cmdlen, "%s -dl -f %s", prog, csinfo[i].fname); + if (csinfo[i].ppath != NULL) + { + len = (int)STRLEN(cmd); + vim_snprintf(cmd + len, cmdlen - len, " -P%s", csinfo[i].ppath); + } + if (csinfo[i].flags != NULL) + { + len = (int)STRLEN(cmd); + vim_snprintf(cmd + len, cmdlen - len, " %s", csinfo[i].flags); + } + vim_free(ppath); + // Create a new process to run cscope and use pipes to talk with it GetStartupInfo(&si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; diff --git a/src/testdir/test_cscope.vim b/src/testdir/test_cscope.vim index ddd4de0b89..f44a480ec0 100644 --- a/src/testdir/test_cscope.vim +++ b/src/testdir/test_cscope.vim @@ -288,6 +288,13 @@ func Test_cscopeWithCscopeConnections() call assert_equal(cscope_connection(5, 'out'), 0) call assert_equal(cscope_connection(-1, 'out'), 0) + cscope kill -1 + + " Test: cscope file with shell meta chars + call writefile([], 'Xcscope2.out`id>Xid`', 'D') + call assert_fails('cscope add Xcscope2.out`id>Xid`', 'E609:') + call assert_false(filereadable('Xid')) + cscope kill -1 call CscopeSetupOrClean(0) endfunc diff --git a/src/version.c b/src/version.c index 3a27d0c52b..6d8157e4aa 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 */ +/**/ + 434, /**/ 433, /**/