summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@microsoft.com>2018-02-22 18:39:26 (GMT)
committerGitHub <noreply@github.com>2018-02-22 18:39:26 (GMT)
commit48e8c82fc63d2ddcddce8aa637a892839b551619 (patch)
tree37ad07f40dacd72bbd565fedbbefdd83d57cc4f7
parent23ad6d0d1a7a6145a01494f4f3913a63d1f0250c (diff)
downloadcpython-48e8c82fc63d2ddcddce8aa637a892839b551619.zip
cpython-48e8c82fc63d2ddcddce8aa637a892839b551619.tar.gz
cpython-48e8c82fc63d2ddcddce8aa637a892839b551619.tar.bz2
bpo-32457: Improves handling of denormalized executable path when launching Python (GH-5756)
-rw-r--r--Lib/test/test_cmd_line.py11
-rw-r--r--Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst1
-rw-r--r--PC/getpathp.c96
3 files changed, 54 insertions, 54 deletions
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index fe89e3c..d8a96c4 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -701,6 +701,17 @@ class CmdLineTest(unittest.TestCase):
self.assertEqual(proc.stdout.rstrip(), 'True')
self.assertEqual(proc.returncode, 0, proc)
+ @unittest.skipUnless(sys.platform == 'win32',
+ 'bpo-32457 only applies on Windows')
+ def test_argv0_normalization(self):
+ args = sys.executable, '-c', 'print(0)'
+ prefix, exe = os.path.split(sys.executable)
+ executable = prefix + '\\.\\.\\.\\' + exe
+
+ proc = subprocess.run(args, stdout=subprocess.PIPE,
+ executable=executable)
+ self.assertEqual(proc.returncode, 0, proc)
+ self.assertEqual(proc.stdout.strip(), b'0')
@unittest.skipIf(interpreter_requires_environment(),
'Cannot run -I tests when PYTHON env vars are required.')
diff --git a/Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst b/Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst
new file mode 100644
index 0000000..b55ec82
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst
@@ -0,0 +1 @@
+Improves handling of denormalized executable path when launching Python.
diff --git a/PC/getpathp.c b/PC/getpathp.c
index e90a643..9382843 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -266,6 +266,41 @@ join(wchar_t *buffer, const wchar_t *stuff)
}
}
+static int _PathCchCanonicalizeEx_Initialized = 0;
+typedef HRESULT(__stdcall *PPathCchCanonicalizeEx) (PWSTR pszPathOut, size_t cchPathOut,
+ PCWSTR pszPathIn, unsigned long dwFlags);
+static PPathCchCanonicalizeEx _PathCchCanonicalizeEx;
+
+static _PyInitError canonicalize(wchar_t *buffer, const wchar_t *path)
+{
+ if (buffer == NULL) {
+ return _Py_INIT_NO_MEMORY();
+ }
+
+ if (_PathCchCanonicalizeEx_Initialized == 0) {
+ HMODULE pathapi = LoadLibraryW(L"api-ms-win-core-path-l1-1-0.dll");
+ if (pathapi) {
+ _PathCchCanonicalizeEx = (PPathCchCanonicalizeEx)GetProcAddress(pathapi, "PathCchCanonicalizeEx");
+ }
+ else {
+ _PathCchCanonicalizeEx = NULL;
+ }
+ _PathCchCanonicalizeEx_Initialized = 1;
+ }
+
+ if (_PathCchCanonicalizeEx) {
+ if (FAILED(_PathCchCanonicalizeEx(buffer, MAXPATHLEN + 1, path, 0))) {
+ return _Py_INIT_ERR("buffer overflow in getpathp.c's canonicalize()");
+ }
+ }
+ else {
+ if (!PathCanonicalizeW(buffer, path)) {
+ return _Py_INIT_ERR("buffer overflow in getpathp.c's canonicalize()");
+ }
+ }
+ return _Py_INIT_OK();
+}
+
/* gotlandmark only called by search_for_prefix, which ensures
'prefix' is null terminated in bounds. join() ensures
@@ -504,63 +539,16 @@ get_program_full_path(const _PyCoreConfig *core_config,
wchar_t program_full_path[MAXPATHLEN+1];
memset(program_full_path, 0, sizeof(program_full_path));
- if (GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
- goto done;
- }
-
- /* If there is no slash in the argv0 path, then we have to
- * assume python is on the user's $PATH, since there's no
- * other way to find a directory to start the search from. If
- * $PATH isn't exported, you lose.
- */
-#ifdef ALTSEP
- if (wcschr(core_config->program_name, SEP) ||
- wcschr(core_config->program_name, ALTSEP))
-#else
- if (wcschr(core_config->program_name, SEP))
-#endif
- {
- wcsncpy(program_full_path, core_config->program_name, MAXPATHLEN);
+ if (!GetModuleFileNameW(NULL, program_full_path, MAXPATHLEN)) {
+ /* GetModuleFileName should never fail when passed NULL */
+ return _Py_INIT_ERR("Cannot determine program path");
}
- else if (calculate->path_env) {
- const wchar_t *path = calculate->path_env;
- while (1) {
- const wchar_t *delim = wcschr(path, DELIM);
-
- if (delim) {
- size_t len = delim - path;
- /* ensure we can't overwrite buffer */
- len = min(MAXPATHLEN,len);
- wcsncpy(program_full_path, path, len);
- program_full_path[len] = '\0';
- }
- else {
- wcsncpy(program_full_path, path, MAXPATHLEN);
- }
-
- /* join() is safe for MAXPATHLEN+1 size buffer */
- join(program_full_path, core_config->program_name);
- if (exists(program_full_path)) {
- break;
- }
- if (!delim) {
- program_full_path[0] = '\0';
- break;
- }
- path = delim + 1;
- }
- }
- else {
- program_full_path[0] = '\0';
- }
+ config->program_full_path = PyMem_RawMalloc(
+ sizeof(wchar_t) * (MAXPATHLEN + 1));
-done:
- config->program_full_path = _PyMem_RawWcsdup(program_full_path);
- if (config->program_full_path == NULL) {
- return _Py_INIT_NO_MEMORY();
- }
- return _Py_INIT_OK();
+ return canonicalize(config->program_full_path,
+ program_full_path);
}