[Python-checkins] bpo-32457: Improves handling of denormalized executable path when launching Python (GH-5756) (#5818)

Steve Dower webhook-mailer at python.org
Thu Feb 22 15:33:19 EST 2018


https://github.com/python/cpython/commit/1d3c518c5ecbd78478738f068f4f035f81f035f9
commit: 1d3c518c5ecbd78478738f068f4f035f81f035f9
branch: 3.6
author: Steve Dower <steve.dower at microsoft.com>
committer: GitHub <noreply at github.com>
date: 2018-02-22T12:33:16-08:00
summary:

bpo-32457: Improves handling of denormalized executable path when launching Python (GH-5756) (#5818)

files:
A Misc/NEWS.d/next/Windows/2018-02-19-08-54-06.bpo-32457.vVP0Iz.rst
M Lib/test/test_cmd_line.py
M PC/getpathp.c

diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 32d2df9d6c26..38156b49265e 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -489,6 +489,18 @@ def test_isolatedmode(self):
                                           cwd=tmpdir)
             self.assertEqual(out.strip(), b"ok")
 
+    @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')
+
 
 def test_main():
     test.support.run_unittest(CmdLineTest)
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 000000000000..b55ec821e622
--- /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 e7be704a9a78..af4af766a70f 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -241,6 +241,36 @@ 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 void canonicalize(wchar_t *buffer, const wchar_t *path)
+{
+    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))) {
+            Py_FatalError("buffer overflow in getpathp.c's canonicalize()");
+        }
+    }
+    else {
+        if (!PathCanonicalizeW(buffer, path)) {
+            Py_FatalError("buffer overflow in getpathp.c's canonicalize()");
+        }
+    }
+}
+
 /* gotlandmark only called by search_for_prefix, which ensures
    'prefix' is null terminated in bounds.  join() ensures
    'landmark' can not overflow prefix if too long.
@@ -431,6 +461,7 @@ static void
 get_progpath(void)
 {
     extern wchar_t *Py_GetProgramName(void);
+    wchar_t modulepath[MAXPATHLEN];
     wchar_t *path = _wgetenv(L"PATH");
     wchar_t *prog = Py_GetProgramName();
 
@@ -443,8 +474,10 @@ get_progpath(void)
 #else
     dllpath[0] = 0;
 #endif
-    if (GetModuleFileNameW(NULL, progpath, MAXPATHLEN))
+    if (GetModuleFileNameW(NULL, modulepath, MAXPATHLEN)) {
+        canonicalize(progpath, modulepath);
         return;
+    }
     if (prog == NULL || *prog == '\0')
         prog = L"python";
 



More information about the Python-checkins mailing list