def debug_build(program):
program = os.path.basename(program)
name = os.path.splitext(program)[0]
- return name.endswith("_d")
+ return name.casefold().endswith("_d".casefold())
def remove_python_envvars():
if expected['stdio_errors'] is self.GET_DEFAULT_CONFIG:
expected['stdio_errors'] = 'surrogateescape'
- if sys.platform == 'win32':
+ if MS_WINDOWS:
default_executable = self.test_exe
elif expected['program_name'] is not self.GET_DEFAULT_CONFIG:
default_executable = os.path.abspath(expected['program_name'])
pre_config = dict(configs['pre_config'])
for key, value in list(expected.items()):
if value is self.IGNORE_CONFIG:
- del pre_config[key]
+ pre_config.pop(key, None)
del expected[key]
self.assertEqual(pre_config, expected)
config = dict(configs['config'])
for key, value in list(expected.items()):
if value is self.IGNORE_CONFIG:
- del config[key]
+ config.pop(key, None)
del expected[key]
self.assertEqual(config, expected)
self.check_pre_config(configs, expected_preconfig)
self.check_config(configs, expected_config)
self.check_global_config(configs)
+ return configs
def test_init_default_config(self):
self.check_all_configs("test_init_initialize_config", api=API_COMPAT)
}
self.default_program_name(config)
env = {'TESTPATH': os.path.pathsep.join(paths)}
+
self.check_all_configs("test_init_setpath", config,
api=API_COMPAT, env=env,
ignore_stderr=True)
# Copy pythonXY.dll (or pythonXY_d.dll)
ver = sys.version_info
dll = f'python{ver.major}{ver.minor}'
+ dll3 = f'python{ver.major}'
if debug_build(sys.executable):
dll += '_d'
+ dll3 += '_d'
dll += '.dll'
+ dll3 += '.dll'
dll = os.path.join(os.path.dirname(self.test_exe), dll)
+ dll3 = os.path.join(os.path.dirname(self.test_exe), dll3)
dll_copy = os.path.join(tmpdir, os.path.basename(dll))
+ dll3_copy = os.path.join(tmpdir, os.path.basename(dll3))
shutil.copyfile(dll, dll_copy)
+ shutil.copyfile(dll3, dll3_copy)
# Copy Python program
exec_copy = os.path.join(tmpdir, os.path.basename(self.test_exe))
config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home
env = self.copy_paths_by_env(config)
- self.check_all_configs("test_init_compat_config", config,
- api=API_COMPAT, env=env,
- ignore_stderr=True, cwd=tmpdir)
+ actual = self.check_all_configs("test_init_compat_config", config,
+ api=API_COMPAT, env=env,
+ ignore_stderr=True, cwd=tmpdir)
+ if MS_WINDOWS:
+ self.assertEqual(
+ actual['windows']['python3_dll'],
+ os.path.join(
+ tmpdir,
+ os.path.basename(self.EXPECTED_CONFIG['windows']['python3_dll'])
+ )
+ )
+
def test_global_pathconfig(self):
# Test C API functions getting the path configuration:
--- /dev/null
+Ensure :file:`python3.dll` is loaded from correct locations when Python is
+embedded (CVE-2020-15523).
#include "pycore_initconfig.h"
+#ifdef MS_WINDOWS
+#include <windows.h>
+
+static int
+_add_windows_config(PyObject *configs)
+{
+ HMODULE hPython3;
+ wchar_t py3path[MAX_PATH];
+ PyObject *dict = PyDict_New();
+ PyObject *obj = NULL;
+ if (!dict) {
+ return -1;
+ }
+
+ hPython3 = GetModuleHandleW(PY3_DLLNAME);
+ if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) {
+ obj = PyUnicode_FromWideChar(py3path, -1);
+ } else {
+ obj = Py_None;
+ Py_INCREF(obj);
+ }
+ if (obj &&
+ !PyDict_SetItemString(dict, "python3_dll", obj) &&
+ !PyDict_SetItemString(configs, "windows", dict)) {
+ Py_DECREF(obj);
+ Py_DECREF(dict);
+ return 0;
+ }
+ Py_DECREF(obj);
+ Py_DECREF(dict);
+ return -1;
+}
+#endif
+
+
static PyObject *
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
{
- return _Py_GetConfigsAsDict();
+ PyObject *dict = _Py_GetConfigsAsDict();
+#ifdef MS_WINDOWS
+ if (dict) {
+ if (_add_windows_config(dict) < 0) {
+ Py_CLEAR(dict);
+ }
+ }
+#endif
+ return dict;
}
wchar_t *machine_path; /* from HKEY_LOCAL_MACHINE */
wchar_t *user_path; /* from HKEY_CURRENT_USER */
- wchar_t *dll_path;
-
const wchar_t *pythonpath_env;
} PyCalculatePath;
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)) {
+ dest[0] = '\0';
+ return -1;
+ }
+ } else {
+ wchar_t *s = wcsrchr(dest, L'.');
+ if (s) {
+ s[0] = '\0';
+ }
}
- if (wcsncpy_s(dest, MAXPATHLEN+1, src, i) ||
- wcscat_s(dest, MAXPATHLEN+1, ext))
- {
+ if (wcscat_s(dest, MAXPATHLEN+1, ext)) {
dest[0] = '\0';
return -1;
}
}
+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.*/
#endif /* Py_ENABLE_SHARED */
-wchar_t*
-_Py_GetDLLPath(void)
-{
- 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
-
- return _PyMem_RawWcsdup(dll_path);
-}
-
-
static PyStatus
get_program_full_path(_PyPathConfig *pathconfig)
{
get_pth_filename(PyCalculatePath *calculate, wchar_t *filename,
const _PyPathConfig *pathconfig)
{
- if (calculate->dll_path[0]) {
- if (!change_ext(filename, calculate->dll_path, L"._pth") &&
- exists(filename))
- {
- return 1;
- }
+ if (get_dllpath(filename) &&
+ !change_ext(filename, filename, L"._pth") &&
+ exists(filename))
+ {
+ return 1;
}
- if (pathconfig->program_full_path[0]) {
- if (!change_ext(filename, pathconfig->program_full_path, L"._pth") &&
- exists(filename))
- {
- return 1;
- }
+ if (pathconfig->program_full_path[0] &&
+ !change_ext(filename, pathconfig->program_full_path, L"._pth") &&
+ exists(filename))
+ {
+ return 1;
}
return 0;
}
wchar_t zip_path[MAXPATHLEN+1];
memset(zip_path, 0, sizeof(zip_path));
- change_ext(zip_path,
- calculate->dll_path[0] ? calculate->dll_path : pathconfig->program_full_path,
- L".zip");
+ if (get_dllpath(zip_path) || change_ext(zip_path, zip_path, L".zip"))
+ {
+ if (change_ext(zip_path, pathconfig->program_full_path, L".zip")) {
+ zip_path[0] = L'\0';
+ }
+ }
calculate_home_prefix(calculate, argv0_path, zip_path, prefix);
calculate->home = pathconfig->home;
calculate->path_env = _wgetenv(L"PATH");
- calculate->dll_path = _Py_GetDLLPath();
- if (calculate->dll_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
-
calculate->pythonpath_env = config->pythonpath_env;
return _PyStatus_OK();
{
PyMem_RawFree(calculate->machine_path);
PyMem_RawFree(calculate->user_path);
- PyMem_RawFree(calculate->dll_path);
}
- PyConfig.pythonpath_env: PYTHONPATH environment variable
- _PyPathConfig.home: Py_SetPythonHome() or PYTHONHOME environment variable
- - DLL path: _Py_GetDLLPath()
- PATH environment variable
- __PYVENV_LAUNCHER__ environment variable
- GetModuleFileNameW(NULL): fully qualified path of the executable file of
_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 */
- if (_Py_dll_path != NULL) {
- wcscpy(py3path, _Py_dll_path);
- }
- else {
- wcscpy(py3path, L"");
- }
- 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_LIBRARY_SEARCH_DEFAULT_DIRS);
+ if (hPython3 != NULL) {
+ return 1;
+ }
}
- wcscpy(s, L"\\python3.dll");
- hPython3 = LoadLibraryExW(py3path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+ /* If we can locate python3.dll in our application dir,
+ use that DLL */
+ hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
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_LIBRARY_SEARCH_DEFAULT_DIRS);
+ }
return hPython3 != NULL;
}
<_PlatformPreprocessorDefinition>_WIN32;</_PlatformPreprocessorDefinition>
<_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64;</_PlatformPreprocessorDefinition>
<_PydPreprocessorDefinition Condition="$(TargetExt) == '.pyd'">Py_BUILD_CORE_MODULE;</_PydPreprocessorDefinition>
+ <_Py3NamePreprocessorDefinition>PY3_DLLNAME=L"$(Py3DllName)";</_Py3NamePreprocessorDefinition>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>WIN32;$(_Py3NamePreprocessorDefinition);$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<!-- The name of the resulting pythonXY.dll (without the extension) -->
<PyDllName>python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt)</PyDllName>
+ <!-- The name of the resulting pythonX.dll (without the extension) -->
+ <Py3DllName>python3$(PyDebugExt)</Py3DllName>
<!-- The version and platform tag to include in .pyd filenames -->
<PydTag Condition="$(ArchName) == 'win32'">.cp$(MajorVersionNumber)$(MinorVersionNumber)-win32</PydTag>
char funcname[258], *import_python;
const wchar_t *wpathname;
-#ifndef _DEBUG
_Py_CheckPython3();
-#endif
wpathname = _PyUnicode_AsUnicode(pathname);
if (wpathname == NULL)
_PyPathConfig _Py_path_config = _PyPathConfig_INIT;
-#ifdef MS_WINDOWS
-wchar_t *_Py_dll_path = NULL;
-#endif
static int
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
pathconfig_clear(&_Py_path_config);
-#ifdef MS_WINDOWS
- PyMem_RawFree(_Py_dll_path);
- _Py_dll_path = NULL;
-#endif
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
}
}
-#ifdef MS_WINDOWS
-/* Initialize _Py_dll_path on Windows. Do nothing on other platforms. */
-static PyStatus
-_PyPathConfig_InitDLLPath(void)
-{
- if (_Py_dll_path != NULL) {
- /* Already set: nothing to do */
- return _PyStatus_OK();
- }
-
- PyMemAllocatorEx old_alloc;
- _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-
- _Py_dll_path = _Py_GetDLLPath();
-
- PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-
- if (_Py_dll_path == NULL) {
- return _PyStatus_NO_MEMORY();
- }
- return _PyStatus_OK();
-}
-#endif
-
-
static PyStatus
pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
{
PyStatus
_PyConfig_WritePathConfig(const PyConfig *config)
{
-#ifdef MS_WINDOWS
- PyStatus status = _PyPathConfig_InitDLLPath();
- if (_PyStatus_EXCEPTION(status)) {
- return status;
- }
-#endif
-
return pathconfig_set_from_config(&_Py_path_config, config);
}
{
PyStatus status;
-#ifdef MS_WINDOWS
- status = _PyPathConfig_InitDLLPath();
- if (_PyStatus_EXCEPTION(status)) {
- Py_ExitStatusException(status);
- }
-#endif
-
if (_Py_path_config.module_search_path == NULL) {
status = pathconfig_global_read(&_Py_path_config);
if (_PyStatus_EXCEPTION(status)) {