]> granicus.if.org Git - python/commitdiff
bpo-32457: Improves handling of denormalized executable path when launching Python...
authorSteve Dower <steve.dower@microsoft.com>
Thu, 22 Feb 2018 18:39:26 +0000 (10:39 -0800)
committerGitHub <noreply@github.com>
Thu, 22 Feb 2018 18:39:26 +0000 (10:39 -0800)
Lib/test/test_cmd_line.py
Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst [new file with mode: 0644]
PC/getpathp.c

index fe89e3c0ee342495d2039618457fadbbce72b208..d8a96c49ce969970874a7279e6230f6bdb81c4de 100644 (file)
@@ -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 (file)
index 0000000..b55ec82
--- /dev/null
@@ -0,0 +1 @@
+Improves handling of denormalized executable path when launching Python.
index e90a643ab82aed0b1d9b718372a0ba0de9a754c8..93828432ae3c4656b152617abcc72d479a14d75c 100644 (file)
@@ -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);
 }