From f205f1000a2d7f8b044caf281041b3705f293480 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 4 Aug 2020 03:16:20 +0100 Subject: [3.5] bpo-29778: Ensure python3.dll is loaded from correct locations when Python is embedded (GH-21297) (#21377) bpo-29778: Ensure python3.dll is loaded from correct locations when Python is embedded. --- .../2020-07-03-17-21-37.bpo-29778.cR_fGS.rst | 2 + PC/getpathp.c | 122 +++++++++++++++------ PCbuild/pyproject.props | 4 +- PCbuild/python.props | 2 + Python/dynload_win.c | 2 - 5 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst diff --git a/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst b/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst new file mode 100644 index 0000000..998ffb1 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-07-03-17-21-37.bpo-29778.cR_fGS.rst @@ -0,0 +1,2 @@ +Ensure :file:`python3.dll` is loaded from correct locations when Python is +embedded (CVE-2020-15523). diff --git a/PC/getpathp.c b/PC/getpathp.c index 47a571e..282d3bc 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -93,7 +93,6 @@ static wchar_t prefix[MAXPATHLEN+1]; static wchar_t progpath[MAXPATHLEN+1]; -static wchar_t dllpath[MAXPATHLEN+1]; static wchar_t *module_search_path = NULL; @@ -122,6 +121,46 @@ reduce(wchar_t *dir) dir[i] = '\0'; } +static int +change_ext(wchar_t *dest, const wchar_t *src, const wchar_t *ext) +{ + 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; +} static int exists(wchar_t *filename) @@ -214,6 +253,20 @@ search_for_prefix(wchar_t *argv0_path, wchar_t *landmark) 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 MS_WINDOWS #ifdef Py_ENABLE_SHARED @@ -378,19 +431,8 @@ get_progpath(void) wchar_t *path = _wgetenv(L"PATH"); wchar_t *prog = Py_GetProgramName(); -#ifdef MS_WINDOWS -#ifdef Py_ENABLE_SHARED - extern HANDLE PyWin_DLLhModule; - /* static init of progpath ensures final char remains \0 */ - if (PyWin_DLLhModule) - if (!GetModuleFileNameW(PyWin_DLLhModule, dllpath, MAXPATHLEN)) - dllpath[0] = 0; -#else - dllpath[0] = 0; -#endif if (GetModuleFileNameW(NULL, progpath, MAXPATHLEN)) return; -#endif if (prog == NULL || *prog == '\0') prog = L"python"; @@ -578,14 +620,10 @@ calculate_path(void) #ifdef MS_WINDOWS /* Calculate zip archive path from DLL or exe path */ - if (wcscpy_s(zip_path, MAXPATHLEN+1, dllpath[0] ? dllpath : progpath)) - /* exceeded buffer length - ignore zip_path */ - zip_path[0] = '\0'; - else { - wchar_t *dot = wcsrchr(zip_path, '.'); - if (!dot || wcscpy_s(dot, MAXPATHLEN+1 - (dot - zip_path), L".zip")) - /* exceeded buffer length - ignore zip_path */ - zip_path[0] = L'\0'; + if (!get_dllpath(zip_path)) { + change_ext(zip_path, zip_path, L".zip"); + } else { + change_ext(zip_path, progpath, L".zip"); } skiphome = pythonhome==NULL ? 0 : 1; @@ -768,8 +806,6 @@ calculate_path(void) } -/* External interface */ - void Py_SetPath(const wchar_t *path) { @@ -830,25 +866,39 @@ int _Py_CheckPython3() { wchar_t py3path[MAXPATHLEN+1]; - wchar_t *s; - if (python3_checked) + 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, dllpath); - s = wcsrchr(py3path, L'\\'); - if (!s) - s = py3path; - wcscpy(s, L"\\python3.dll"); - hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (hPython3 != NULL) - return 1; + 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; + } + } - /* Check sys.prefix\DLLs\python3.dll */ + /* If we can locate python3.dll in our application dir, + use that DLL */ wcscpy(py3path, Py_GetPrefix()); - wcscat(py3path, L"\\DLLs\\python3.dll"); - hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (py3path[0]) { + join(py3path, PY3_DLLNAME); + hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (hPython3 != NULL) { + return 1; + } + } + + /* For back-compat, also search {sys.prefix}\DLLs, though + that has not been a normal install layout for a while */ + wcscpy(py3path, Py_GetPrefix()); + 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 d1ac998..03197fe 100644 --- a/PCbuild/pyproject.props +++ b/PCbuild/pyproject.props @@ -24,12 +24,12 @@ <_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 true diff --git a/PCbuild/python.props b/PCbuild/python.props index 0a6fd2a..f59bcea 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -152,6 +152,8 @@ python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt) + + python3$(PyDebugExt) .cp$(MajorVersionNumber)$(MinorVersionNumber)-win32 diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 3f533d1..1be876d 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -194,9 +194,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) -- cgit v0.12