Skip to content

Commit f205f10

Browse files
authored
[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.
1 parent cac9ca8 commit f205f10

File tree

5 files changed

+92
-40
lines changed

5 files changed

+92
-40
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Ensure :file:`python3.dll` is loaded from correct locations when Python is
2+
embedded (CVE-2020-15523).

PC/getpathp.c

Lines changed: 86 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@
9393

9494
static wchar_t prefix[MAXPATHLEN+1];
9595
static wchar_t progpath[MAXPATHLEN+1];
96-
static wchar_t dllpath[MAXPATHLEN+1];
9796
static wchar_t *module_search_path = NULL;
9897

9998

@@ -122,6 +121,46 @@ reduce(wchar_t *dir)
122121
dir[i] = '\0';
123122
}
124123

124+
static int
125+
change_ext(wchar_t *dest, const wchar_t *src, const wchar_t *ext)
126+
{
127+
if (src && src != dest) {
128+
size_t src_len = wcsnlen_s(src, MAXPATHLEN+1);
129+
size_t i = src_len;
130+
if (i >= MAXPATHLEN+1) {
131+
Py_FatalError("buffer overflow in getpathp.c's reduce()");
132+
}
133+
134+
while (i > 0 && src[i] != '.' && !is_sep(src[i]))
135+
--i;
136+
137+
if (i == 0) {
138+
dest[0] = '\0';
139+
return -1;
140+
}
141+
142+
if (is_sep(src[i])) {
143+
i = src_len;
144+
}
145+
146+
if (wcsncpy_s(dest, MAXPATHLEN+1, src, i)) {
147+
dest[0] = '\0';
148+
return -1;
149+
}
150+
} else {
151+
wchar_t *s = wcsrchr(dest, L'.');
152+
if (s) {
153+
s[0] = '\0';
154+
}
155+
}
156+
157+
if (wcscat_s(dest, MAXPATHLEN+1, ext)) {
158+
dest[0] = '\0';
159+
return -1;
160+
}
161+
162+
return 0;
163+
}
125164

126165
static int
127166
exists(wchar_t *filename)
@@ -214,6 +253,20 @@ search_for_prefix(wchar_t *argv0_path, wchar_t *landmark)
214253
return 0;
215254
}
216255

256+
257+
static int
258+
get_dllpath(wchar_t *dllpath)
259+
{
260+
#ifdef Py_ENABLE_SHARED
261+
extern HANDLE PyWin_DLLhModule;
262+
if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, dllpath, MAXPATHLEN)) {
263+
return 0;
264+
}
265+
#endif
266+
return -1;
267+
}
268+
269+
217270
#ifdef MS_WINDOWS
218271
#ifdef Py_ENABLE_SHARED
219272

@@ -378,19 +431,8 @@ get_progpath(void)
378431
wchar_t *path = _wgetenv(L"PATH");
379432
wchar_t *prog = Py_GetProgramName();
380433

381-
#ifdef MS_WINDOWS
382-
#ifdef Py_ENABLE_SHARED
383-
extern HANDLE PyWin_DLLhModule;
384-
/* static init of progpath ensures final char remains \0 */
385-
if (PyWin_DLLhModule)
386-
if (!GetModuleFileNameW(PyWin_DLLhModule, dllpath, MAXPATHLEN))
387-
dllpath[0] = 0;
388-
#else
389-
dllpath[0] = 0;
390-
#endif
391434
if (GetModuleFileNameW(NULL, progpath, MAXPATHLEN))
392435
return;
393-
#endif
394436
if (prog == NULL || *prog == '\0')
395437
prog = L"python";
396438

@@ -578,14 +620,10 @@ calculate_path(void)
578620

579621
#ifdef MS_WINDOWS
580622
/* Calculate zip archive path from DLL or exe path */
581-
if (wcscpy_s(zip_path, MAXPATHLEN+1, dllpath[0] ? dllpath : progpath))
582-
/* exceeded buffer length - ignore zip_path */
583-
zip_path[0] = '\0';
584-
else {
585-
wchar_t *dot = wcsrchr(zip_path, '.');
586-
if (!dot || wcscpy_s(dot, MAXPATHLEN+1 - (dot - zip_path), L".zip"))
587-
/* exceeded buffer length - ignore zip_path */
588-
zip_path[0] = L'\0';
623+
if (!get_dllpath(zip_path)) {
624+
change_ext(zip_path, zip_path, L".zip");
625+
} else {
626+
change_ext(zip_path, progpath, L".zip");
589627
}
590628

591629
skiphome = pythonhome==NULL ? 0 : 1;
@@ -768,8 +806,6 @@ calculate_path(void)
768806
}
769807

770808

771-
/* External interface */
772-
773809
void
774810
Py_SetPath(const wchar_t *path)
775811
{
@@ -830,25 +866,39 @@ int
830866
_Py_CheckPython3()
831867
{
832868
wchar_t py3path[MAXPATHLEN+1];
833-
wchar_t *s;
834-
if (python3_checked)
869+
if (python3_checked) {
835870
return hPython3 != NULL;
871+
}
836872
python3_checked = 1;
837873

838874
/* If there is a python3.dll next to the python3y.dll,
839-
assume this is a build tree; use that DLL */
840-
wcscpy(py3path, dllpath);
841-
s = wcsrchr(py3path, L'\\');
842-
if (!s)
843-
s = py3path;
844-
wcscpy(s, L"\\python3.dll");
845-
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
846-
if (hPython3 != NULL)
847-
return 1;
875+
use that DLL */
876+
if (!get_dllpath(py3path)) {
877+
reduce(py3path);
878+
join(py3path, PY3_DLLNAME);
879+
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
880+
if (hPython3 != NULL) {
881+
return 1;
882+
}
883+
}
848884

849-
/* Check sys.prefix\DLLs\python3.dll */
885+
/* If we can locate python3.dll in our application dir,
886+
use that DLL */
850887
wcscpy(py3path, Py_GetPrefix());
851-
wcscat(py3path, L"\\DLLs\\python3.dll");
852-
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
888+
if (py3path[0]) {
889+
join(py3path, PY3_DLLNAME);
890+
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
891+
if (hPython3 != NULL) {
892+
return 1;
893+
}
894+
}
895+
896+
/* For back-compat, also search {sys.prefix}\DLLs, though
897+
that has not been a normal install layout for a while */
898+
wcscpy(py3path, Py_GetPrefix());
899+
if (py3path[0]) {
900+
join(py3path, L"DLLs\\" PY3_DLLNAME);
901+
hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
902+
}
853903
return hPython3 != NULL;
854904
}

PCbuild/pyproject.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
<_PlatformPreprocessorDefinition>_WIN32;</_PlatformPreprocessorDefinition>
2525
<_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64;</_PlatformPreprocessorDefinition>
2626
<_PydPreprocessorDefinition Condition="$(TargetExt) == '.pyd'">Py_BUILD_CORE_MODULE;</_PydPreprocessorDefinition>
27+
<_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)";</_Py3NamePreprocessorDefinition>
2728
</PropertyGroup>
2829
<ItemDefinitionGroup>
2930
<ClCompile>
3031
<AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
31-
<PreprocessorDefinitions>WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
32-
32+
<PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition)$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
3333
<Optimization>MaxSpeed</Optimization>
3434
<IntrinsicFunctions>true</IntrinsicFunctions>
3535
<StringPooling>true</StringPooling>

PCbuild/python.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@
152152

153153
<!-- The name of the resulting pythonXY.dll (without the extension) -->
154154
<PyDllName>python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt)</PyDllName>
155+
<!-- The name of the resulting pythonX.dll (without the extension) -->
156+
<Py3DllName>python3$(PyDebugExt)</Py3DllName>
155157

156158
<!-- The version and platform tag to include in .pyd filenames -->
157159
<PydTag Condition="$(ArchName) == 'win32'">.cp$(MajorVersionNumber)$(MinorVersionNumber)-win32</PydTag>

Python/dynload_win.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
194194
char funcname[258], *import_python;
195195
const wchar_t *wpathname;
196196

197-
#ifndef _DEBUG
198197
_Py_CheckPython3();
199-
#endif
200198

201199
wpathname = _PyUnicode_AsUnicode(pathname);
202200
if (wpathname == NULL)

0 commit comments

Comments
 (0)