refactor(path): more slash normalization #39426

Problem:
1. `vim_getenv` is followed by `TO_SLASH` when getting
path-related variables.
2. cmd exits when launched with forward slash.

Solution:
1. try calling `TO_SLASH` in `vim_getenv`.
2. pass fullpath via `lpApplicationName`, only include `cmd.exe`
in cmdline.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
This commit is contained in:
tao
2026-04-30 04:33:22 +08:00
committed by GitHub
parent 7cd0e40039
commit 040bdf0bc5
4 changed files with 41 additions and 11 deletions

View File

@@ -618,7 +618,6 @@ size_t expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen,
#endif
*var = NUL;
var = vim_getenv(dst);
TO_SLASH(var);
mustfree = true;
#ifdef UNIX
}
@@ -872,6 +871,18 @@ char *vim_getenv(const char *name)
#endif
char *kos_env_path = os_getenv(name);
#ifdef BACKSLASH_IN_FILENAME
if (striequal(name, "VIMRUNTIME")
|| striequal(name, "PATH")
|| striequal(name, "CDPATH")
|| striequal(name, "TMPDIR")
|| striequal(name, "TMP")
|| striequal(name, "TEMP")
|| striequal(name, "VIM")
|| striequal(name, "MYVIMRC")) {
TO_SLASH(kos_env_path);
}
#endif
if (kos_env_path != NULL) {
return kos_env_path;
}

View File

@@ -8,6 +8,7 @@
#include "nvim/log.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/os/fs.h"
#include "nvim/os/os.h"
#include "nvim/os/pty_conpty_win.h"
#include "nvim/os/pty_proc_win.h"
@@ -61,6 +62,7 @@ int pty_proc_spawn(PtyProc *ptyproc)
HANDLE proc_handle = NULL;
uv_connect_t *in_req = NULL;
uv_connect_t *out_req = NULL;
wchar_t *file = NULL;
wchar_t *cmd_line = NULL;
wchar_t *cwd = NULL;
wchar_t *env = NULL;
@@ -99,8 +101,28 @@ int pty_proc_spawn(PtyProc *ptyproc)
}
}
status = build_cmd_line(proc->argv, &cmd_line,
os_shell_is_cmdexe(proc->argv[0]));
// cmd.exe cant recognize forward slash in argv[0] during
// initialization, so we keep only cmd.exe in the cmd_line
// and pass fullpath via lpApplicationName to avoid hijacking.
// See https://www.microsoft.com/en-us/msrc/blog/2014/04/ms14-019-fixing-a-binary-hijacking-via-cmd-or-bat-file
bool is_cmdexe = os_shell_is_cmdexe(proc->argv[0]);
if (is_cmdexe) {
char *path = NULL;
// TODO(ntdiary): Could put the search logic in one place.
// See https://github.com/neovim/neovim/issues/36818#issuecomment-3977147445
if (!os_can_exe(proc->argv[0], &path, true)) {
status = UV_ENOENT;
emsg = "executable not found";
goto cleanup;
}
status = utf8_to_utf16(path, -1, &file);
if (status != 0) {
emsg = "utf8_to_utf16(proc->argv[0]) failed";
goto cleanup;
}
xfree(path);
}
status = build_cmd_line(proc->argv, &cmd_line, is_cmdexe);
if (status != 0) {
emsg = "build_cmd_line failed";
goto cleanup;
@@ -117,7 +139,7 @@ int pty_proc_spawn(PtyProc *ptyproc)
if (!os_conpty_spawn(conpty_object,
&proc_handle,
NULL,
file,
cmd_line,
cwd,
env)) {
@@ -163,6 +185,7 @@ cleanup:
}
xfree(in_req);
xfree(out_req);
xfree(file);
xfree(cmd_line);
xfree(env);
xfree(cwd);
@@ -261,10 +284,7 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe)
ArgNode *arg_node = xmalloc(sizeof(*arg_node));
arg_node->arg = xmalloc(buf_len);
if (is_cmdexe) {
xstrlcpy(arg_node->arg, *argv, buf_len);
if (argc == 0) {
TO_BACKSLASH(arg_node->arg);
}
xstrlcpy(arg_node->arg, argc == 0 ? "cmd.exe" : *argv, buf_len);
} else {
quote_cmd_arg(arg_node->arg, buf_len, *argv);
}

View File

@@ -1847,8 +1847,7 @@ char *runtimepath_default(bool clean_arg)
char *const libdir = get_lib_dir();
char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs);
char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
char *vimruntime = vim_getenv("VIMRUNTIME");
TO_SLASH(vimruntime);
char *const vimruntime = vim_getenv("VIMRUNTIME");
#define SITE_SIZE (sizeof("site") - 1)
#define AFTER_SIZE (sizeof("after") - 1)
size_t data_len = 0;

View File

@@ -35,7 +35,7 @@ describe('vim.fn.environ()', function()
local env = vim.fn.environ()
assert(vim.tbl_count(env) > 10, 'environ() should have some env vars!')
for k, v in pairs(env) do
if v ~= '' and vim.fn.getenv(k) ~= v then
if v ~= '' and vim.fn.getenv(k) ~= v and vim.fn.getenv(k) ~= v:gsub('\\', '/') then
error(('environ()[%q] = %q, but vim.fn.getenv(%q) = %q'):format(k, v, k, vim.fn.getenv(k)))
end
end