From 65374097122f14c9bd947a3f3afa86b9db27f86e Mon Sep 17 00:00:00 2001 From: JinWang An Date: Tue, 27 Apr 2021 13:35:55 +0900 Subject: [PATCH] [CVE-2020-15523]Ensure python3.dll is loaded from correct locations when Python is embedded 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 --- PC/getpathp.c | 163 +++++++++++++++++++++------------------- PCbuild/pyproject.props | 4 +- PCbuild/python.props | 2 + Python/dynload_win.c | 2 - 4 files changed, 89 insertions(+), 82 deletions(-) diff --git a/PC/getpathp.c b/PC/getpathp.c index 0a1560c7..ada4020a 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -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; } diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props index eaeb8b0d..9a4d0573 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -24,11 +24,13 @@ <_PlatformPreprocessorDefinition>_WIN32; <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64; <_PydPreprocessorDefinition Condition="$(TargetExt) == '.pyd'">Py_BUILD_CORE_MODULE; + <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)"; $(PySourcePath)Include;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories) WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) + WIN32;$(_Py3NamePreprocessorDefinition)$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) MaxSpeed true @@ -193,4 +195,4 @@ public override bool Execute() { - \ No newline at end of file + diff --git a/PCbuild/python.props b/PCbuild/python.props index 66533b69..16c59c65 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -177,6 +177,8 @@ python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt) + + python3$(PyDebugExt) .cp$(MajorVersionNumber)$(MinorVersionNumber)-win32 diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 0fdf77f5..a3071888 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -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) -- 2.34.1