From b50504cbf3383845fb4a15a32f20a37908555f4e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 6 Jul 2020 18:24:59 +0100 Subject: [PATCH 1/2] bpo-29778: Ensure python3.dll is loaded from correct locations when Python is embedded (GH-21298) * bpo-29778: Ensure python3.dll is loaded from correct locations when Python is embedded. * Add CVE number --- .../2020-07-03-17-21-37.bpo-29778.cR_fGS.rst | 2 + PC/getpathp.c | 101 ++++++++++++------ PCbuild/pyproject.props | 4 +- PCbuild/python.props | 2 + Python/dynload_win.c | 2 - 5 files changed, 76 insertions(+), 35 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 00000000000000..998ffb1ee66677 --- /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 e86c376fb4d34e..38f4ff201e0753 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -150,24 +150,37 @@ 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()"); + 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; + while (i > 0 && src[i] != '.' && !is_sep(src[i])) + --i; - if (i == 0) { - dest[0] = '\0'; - return -1; - } + if (i == 0) { + dest[0] = '\0'; + return -1; + } - if (is_sep(src[i])) - i = src_len; + if (is_sep(src[i])) { + i = src_len; + } - if (wcsncpy_s(dest, MAXPATHLEN+1, src, i) || - wcscat_s(dest, MAXPATHLEN+1, ext)) { + 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; } @@ -304,6 +317,20 @@ search_for_prefix(wchar_t *argv0_path, const 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 Py_ENABLE_SHARED /* a string loaded from the DLL at startup.*/ @@ -693,7 +720,7 @@ calculate_path(void) { wchar_t spbuffer[MAXPATHLEN+1]; - if ((dllpath[0] && !change_ext(spbuffer, dllpath, L"._pth") && exists(spbuffer)) || + if ((!get_dllpath(spbuffer) && !change_ext(spbuffer, spbuffer, L"._pth") && exists(spbuffer)) || (progpath[0] && !change_ext(spbuffer, progpath, L"._pth") && exists(spbuffer))) { if (!read_pth_file(spbuffer, prefix, &Py_IsolatedFlag, &Py_NoSiteFlag)) { @@ -919,8 +946,6 @@ calculate_path(void) } -/* External interface */ - void Py_SetPath(const wchar_t *path) { @@ -981,25 +1006,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 1602ebf5ec9733..104a9d8e623a7b 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 3022ca50c247a2..b82425ea9710dc 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -175,6 +175,8 @@ python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt) + + python3$(PyDebugExt) .cp$(MajorVersionNumber)$(MinorVersionNumber)-win32 diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 0fdf77f55280ca..a307188823cfe5 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) From 2a2812d971daa63b127a1f48eb787cd37b88341e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 6 Jul 2020 19:35:21 +0100 Subject: [PATCH 2/2] Updates for 3.6 --- PC/getpathp.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/PC/getpathp.c b/PC/getpathp.c index 38f4ff201e0753..f6f362c7f9c1df 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -118,7 +118,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; @@ -494,15 +493,6 @@ get_progpath(void) wchar_t *path = _wgetenv(L"PATH"); wchar_t *prog = Py_GetProgramName(); -#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, modulepath, MAXPATHLEN)) { canonicalize(progpath, modulepath); return; @@ -764,7 +754,11 @@ calculate_path(void) } /* Calculate zip archive path from DLL or exe path */ - change_ext(zip_path, dllpath[0] ? dllpath : progpath, L".zip"); + if (!get_dllpath(zip_path)) { + change_ext(zip_path, zip_path, L".zip"); + } else { + change_ext(zip_path, progpath, L".zip"); + } if (pythonhome == NULL || *pythonhome == '\0') { if (zip_path[0] && exists(zip_path)) {