[CVE-2020-15523]Ensure python3.dll is loaded from correct locations when Python is... 01/257501/1
authorJinWang An <jinwang.an@samsung.com>
Tue, 27 Apr 2021 04:35:55 +0000 (13:35 +0900)
committerJinWang An <jinwang.an@samsung.com>
Tue, 27 Apr 2021 04:35:55 +0000 (13:35 +0900)
In Python 3.6 through 3.6.10, 3.7 through 3.7.8,
3.8 through 3.8.4rc1, and 3.9 through 3.9.0b4 on Windows,
a Trojan horse python3.dll might be used in cases where
CPython is embedded in a native application. This occurs
because python3X.dll may use an invalid search path
for python3.dll loading (after Py_SetPath has been used).
NOTE: this issue CANNOT occur when using python.exe
from a standard (non-embedded) Python installation on Windows.

Change-Id: I4c07e791cfebf1b2bb4e5ccc3b54082eb5e813c2
Signed-off-by: JinWang An <jinwang.an@samsung.com>
PC/getpathp.c
PCbuild/pyproject.props
PCbuild/python.props
Python/dynload_win.c

index 0a1560c759f63006aeac55ff137d8af9f1519720..ada4020a099f33e40940a84ed8dca732f53e3f36 100644 (file)
@@ -162,32 +162,42 @@ reduce(wchar_t *dir)
 static int
 change_ext(wchar_t *dest, const wchar_t *src, const wchar_t *ext)
 {
-    size_t src_len = wcsnlen_s(src, MAXPATHLEN+1);
-    size_t i = src_len;
-    if (i >= MAXPATHLEN+1) {
-        Py_FatalError("buffer overflow in getpathp.c's reduce()");
-    }
-
-    while (i > 0 && src[i] != '.' && !is_sep(src[i]))
-        --i;
-
-    if (i == 0) {
-        dest[0] = '\0';
-        return -1;
-    }
-
-    if (is_sep(src[i])) {
-        i = src_len;
-    }
-
-    if (wcsncpy_s(dest, MAXPATHLEN+1, src, i) ||
-        wcscat_s(dest, MAXPATHLEN+1, ext))
-    {
-        dest[0] = '\0';
-        return -1;
-    }
-
-    return 0;
+       if (src && src != dest) {
+               size_t src_len = wcsnlen_s(src, MAXPATHLEN+1);
+               size_t i = src_len;
+               if (i >= MAXPATHLEN+1) {
+                       Py_FatalError("buffer overflow in getpathp.c's reduce()");
+               }
+
+               while (i > 0 && src[i] != '.' && !is_sep(src[i]))
+                       --i;
+
+               if (i == 0) {
+                       dest[0] = '\0';
+                       return -1;
+               }
+
+               if (is_sep(src[i])) {
+                       i = src_len;
+               }
+
+               if (wcsncpy_s(dest, MAXPATHLEN+1, src, i)) {
+                       dest[0] = '\0';
+                       return -1;
+               }
+       } else {
+               wchar_t *s = wcsrchr(dest, L'.');
+               if (s) {
+                       s[0] = '\0';
+               }
+       }
+
+       if (wcscat_s(dest, MAXPATHLEN+1, ext)) {
+               dest[0] = '\0';
+               return -1;
+       }
+
+       return 0;
 }
 
 
@@ -289,6 +299,17 @@ search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path, const wchar_t *lan
     return 0;
 }
 
+static int
+get_dllpath(wchar_t *dllpath)
+{
+#ifdef Py_ENABLE_SHARED
+    extern HANDLE PyWin_DLLhModule;
+    if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, dllpath, MAXPATHLEN)) {
+        return 0;
+    }
+#endif
+    return -1;
+}
 
 #ifdef Py_ENABLE_SHARED
 
@@ -462,31 +483,6 @@ done:
 #endif /* Py_ENABLE_SHARED */
 
 
-static _PyInitError
-get_dll_path(PyCalculatePath *calculate, _PyPathConfig *config)
-{
-    wchar_t dll_path[MAXPATHLEN+1];
-    memset(dll_path, 0, sizeof(dll_path));
-
-#ifdef Py_ENABLE_SHARED
-    extern HANDLE PyWin_DLLhModule;
-    if (PyWin_DLLhModule) {
-        if (!GetModuleFileNameW(PyWin_DLLhModule, dll_path, MAXPATHLEN)) {
-            dll_path[0] = 0;
-        }
-    }
-#else
-    dll_path[0] = 0;
-#endif
-
-    config->dll_path = _PyMem_RawWcsdup(dll_path);
-    if (config->dll_path == NULL) {
-        return _Py_INIT_NO_MEMORY();
-    }
-    return _Py_INIT_OK();
-}
-
-
 static _PyInitError
 get_program_full_path(const _PyCoreConfig *core_config,
                       PyCalculatePath *calculate, _PyPathConfig *config)
@@ -622,13 +618,12 @@ calculate_init(PyCalculatePath *calculate,
 static int
 get_pth_filename(wchar_t *spbuffer, _PyPathConfig *config)
 {
-    if (config->dll_path[0]) {
-        if (!change_ext(spbuffer, config->dll_path, L"._pth") &&
-            exists(spbuffer))
-        {
-            return 1;
-        }
-    }
+    if (get_dllpath(spbuffer) &&
+        !change_ext(spbuffer, spbuffer, L"._pth") &&
+        exists(spbuffer))
+    {
+        return 1;
+       }
     if (config->program_full_path[0]) {
         if (!change_ext(spbuffer, config->program_full_path, L"._pth") &&
             exists(spbuffer))
@@ -914,11 +909,6 @@ calculate_path_impl(const _PyCoreConfig *core_config,
 {
     _PyInitError err;
 
-    err = get_dll_path(calculate, config);
-    if (_Py_INIT_FAILED(err)) {
-        return err;
-    }
-
     err = get_program_full_path(core_config, calculate, config);
     if (_Py_INIT_FAILED(err)) {
         return err;
@@ -939,9 +929,13 @@ calculate_path_impl(const _PyCoreConfig *core_config,
     calculate_pyvenv_file(calculate);
 
     /* Calculate zip archive path from DLL or exe path */
-    change_ext(calculate->zip_path,
-               config->dll_path[0] ? config->dll_path : config->program_full_path,
-               L".zip");
+    if (get_dllpath(calculate->zip_path) ||
+        change_ext(calculate->zip_path, calculate->zip_path, L".zip"))
+    {
+        if (change_ext(calculate->zip_path, config->program_full_path, L".zip")) {
+            calculate->zip_path[0] = L'\0';
+        }
+    }
 
     calculate_home_prefix(calculate, prefix);
 
@@ -1001,28 +995,39 @@ int
 _Py_CheckPython3(void)
 {
     wchar_t py3path[MAXPATHLEN+1];
-    wchar_t *s;
     if (python3_checked) {
         return hPython3 != NULL;
     }
     python3_checked = 1;
 
     /* If there is a python3.dll next to the python3y.dll,
-       assume this is a build tree; use that DLL */
-    wcscpy(py3path, _Py_path_config.dll_path);
-    s = wcsrchr(py3path, L'\\');
-    if (!s) {
-        s = py3path;
+              use that DLL */
+    if (!get_dllpath(py3path)) {
+        reduce(py3path);
+        join(py3path, PY3_DLLNAME);
+        hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+        if (hPython3 != NULL) {
+            return 1;
+        }
     }
-    wcscpy(s, L"\\python3.dll");
-    hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
-    if (hPython3 != NULL) {
-        return 1;
+
+    /* If we can locate python3.dll in our application dir,
+       use that DLL */
+    wcscpy(py3path, Py_GetPrefix());
+    if (py3path[0]) {
+        join(py3path, PY3_DLLNAME);
+        hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+        if (hPython3 != NULL) {
+            return 1;
+        }
     }
 
-    /* Check sys.prefix\DLLs\python3.dll */
+    /* For back-compat, also search {sys.prefix}\DLLs, though
+       that has not been a normal install layout for a while */
     wcscpy(py3path, Py_GetPrefix());
-    wcscat(py3path, L"\\DLLs\\python3.dll");
-    hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+    if (py3path[0]) {
+        join(py3path, L"DLLs\\" PY3_DLLNAME);
+        hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+    }
     return hPython3 != NULL;
 }
index eaeb8b0d9975c441c591d12cdd04c6f6b4da38b2..9a4d0573fdcd18d9fe92bb8b3a5fc27f656544b4 100644 (file)
     <_PlatformPreprocessorDefinition>_WIN32;</_PlatformPreprocessorDefinition>\r
     <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64;</_PlatformPreprocessorDefinition>\r
     <_PydPreprocessorDefinition Condition="$(TargetExt) == '.pyd'">Py_BUILD_CORE_MODULE;</_PydPreprocessorDefinition>\r
+       <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)";</_Py3NamePreprocessorDefinition>
   </PropertyGroup>\r
   <ItemDefinitionGroup>\r
     <ClCompile>\r
       <AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
       <PreprocessorDefinitions>WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+         <PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition)$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
       \r
       <Optimization>MaxSpeed</Optimization>\r
       <IntrinsicFunctions>true</IntrinsicFunctions>\r
@@ -193,4 +195,4 @@ public override bool Execute() {
     <Error Text="Unable to locate signtool.exe. Set /p:SignToolPath and rebuild" Condition="'$(_SignCommand)' == ''" />\r
     <Exec Command='$(_SignCommand) "$(TargetPath)" || $(_SignCommand) "$(TargetPath)" || $(_SignCommand) "$(TargetPath)"' ContinueOnError="false" />\r
   </Target>\r
-</Project>
\ No newline at end of file
+</Project>
index 66533b696700b0daa08535f6b6960640d844aa33..16c59c65b11bac8d4c0e24e2c22372e25bfb6fc0 100644 (file)
     \r
     <!-- The name of the resulting pythonXY.dll (without the extension) -->\r
     <PyDllName>python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt)</PyDllName>\r
+    <!-- The name of the resulting pythonX.dll (without the extension) -->
+    <Py3DllName>python3$(PyDebugExt)</Py3DllName>
 \r
     <!-- The version and platform tag to include in .pyd filenames -->\r
     <PydTag Condition="$(ArchName) == 'win32'">.cp$(MajorVersionNumber)$(MinorVersionNumber)-win32</PydTag>\r
index 0fdf77f55280ca43c9696a8486d2e9350571d07c..a307188823cfe5fb28e6dbad99f6c8dac65b5807 100644 (file)
@@ -192,9 +192,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
     char funcname[258], *import_python;
     const wchar_t *wpathname;
 
-#ifndef _DEBUG
     _Py_CheckPython3();
-#endif
 
     wpathname = _PyUnicode_AsUnicode(pathname);
     if (wpathname == NULL)