Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
2438cdf0
Commit
2438cdf0
authored
Mar 29, 2019
by
Steve Dower
Committed by
GitHub
Mar 29, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-36085: Enable better DLL resolution on Windows (GH-12302)
parent
32119e10
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
492 additions
and
22 deletions
+492
-22
Doc/library/ctypes.rst
Doc/library/ctypes.rst
+14
-3
Doc/library/os.rst
Doc/library/os.rst
+30
-0
Doc/whatsnew/3.8.rst
Doc/whatsnew/3.8.rst
+30
-0
Lib/ctypes/__init__.py
Lib/ctypes/__init__.py
+11
-1
Lib/ctypes/test/test_loading.py
Lib/ctypes/test/test_loading.py
+63
-0
Lib/os.py
Lib/os.py
+37
-0
Lib/test/test_import/__init__.py
Lib/test/test_import/__init__.py
+48
-0
Misc/NEWS.d/next/Windows/2019-03-18-11-44-49.bpo-36085.mLfxfc.rst
...S.d/next/Windows/2019-03-18-11-44-49.bpo-36085.mLfxfc.rst
+2
-0
Modules/_ctypes/callproc.c
Modules/_ctypes/callproc.c
+25
-9
Modules/clinic/posixmodule.c.h
Modules/clinic/posixmodule.c.h
+97
-1
Modules/posixmodule.c
Modules/posixmodule.c
+129
-4
Python/dynload_win.c
Python/dynload_win.c
+6
-4
No files found.
Doc/library/ctypes.rst
View file @
2438cdf0
...
...
@@ -1322,14 +1322,14 @@ There are several ways to load shared libraries into the Python process. One
way is to instantiate one of the following classes:
.. class:: CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)
.. class:: CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False
, winmode=0
)
Instances of this class represent loaded shared libraries. Functions in these
libraries use the standard C calling convention, and are assumed to return
:c:type:`int`.
.. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)
.. class:: OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False
, winmode=0
)
Windows only: Instances of this class represent loaded shared libraries,
functions in these libraries use the ``stdcall`` calling convention, and are
...
...
@@ -1342,7 +1342,7 @@ way is to instantiate one of the following classes:
:exc:`WindowsError` used to be raised.
.. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)
.. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False
, winmode=0
)
Windows only: Instances of this class represent loaded shared libraries,
functions in these libraries use the ``stdcall`` calling convention, and are
...
...
@@ -1394,6 +1394,17 @@ the Windows error code which is managed by the :func:`GetLastError` and
:func:`ctypes.set_last_error` are used to request and change the ctypes private
copy of the windows error code.
The *winmode* parameter is used on Windows to specify how the library is loaded
(since *mode* is ignored). It takes any value that is valid for the Win32 API
``LoadLibraryEx`` flags parameter. When omitted, the default is to use the flags
that result in the most secure DLL load to avoiding issues such as DLL
hijacking. Passing the full path to the DLL is the safest way to ensure the
correct library and dependencies are loaded.
.. versionchanged:: 3.8
Added *winmode* parameter.
.. data:: RTLD_GLOBAL
:noindex:
...
...
Doc/library/os.rst
View file @
2438cdf0
...
...
@@ -3079,6 +3079,36 @@ to be ignored.
:func:`signal.signal`.
.. function:: add_dll_directory(path)
Add a path to the DLL search path.
This search path is used when resolving dependencies for imported
extension modules (the module itself is resolved through sys.path),
and also by :mod:`ctypes`.
Remove the directory by calling **close()** on the returned object
or using it in a :keyword:`with` statement.
See the `Microsoft documentation
<https://msdn.microsoft.com/44228cf2-6306-466c-8f16-f513cd3ba8b5>`_
for more information about how DLLs are loaded.
.. availability:: Windows.
.. versionadded:: 3.8
Previous versions of CPython would resolve DLLs using the default
behavior for the current process. This led to inconsistencies,
such as only sometimes searching :envvar:`PATH` or the current
working directory, and OS functions such as ``AddDllDirectory``
having no effect.
In 3.8, the two primary ways DLLs are loaded now explicitly
override the process-wide behavior to ensure consistency. See the
:ref:`porting notes <bpo-36085-whatsnew>` for information on
updating libraries.
.. function:: execl(path, arg0, arg1, ...)
execle(path, arg0, arg1, ..., env)
execlp(file, arg0, arg1, ...)
...
...
Doc/whatsnew/3.8.rst
View file @
2438cdf0
...
...
@@ -168,6 +168,16 @@ asyncio
On Windows, the default event loop is now :class:`~asyncio.ProactorEventLoop`.
ctypes
------
On Windows, :class:`~ctypes.CDLL` and subclasses now accept a *winmode* parameter
to specify flags for the underlying ``LoadLibraryEx`` call. The default flags are
set to only load DLL dependencies from trusted locations, including the path
where the DLL is stored (if a full or partial path is used to load the initial
DLL) and paths added by :func:`~os.add_dll_directory`.
gettext
-------
...
...
@@ -238,6 +248,13 @@ Added new function, :func:`math.prod`, as analogous function to :func:`sum`
that returns the product of a 'start' value (default: 1) times an iterable of
numbers. (Contributed by Pablo Galindo in :issue:`35606`)
os
--
Added new function :func:`~os.add_dll_directory` on Windows for providing
additional search paths for native dependencies when importing extension
modules or loading DLLs using :mod:`ctypes`.
os.path
-------
...
...
@@ -727,6 +744,19 @@ Changes in the Python API
environment variable and does not use :envvar:`HOME`, which is not normally
set for regular user accounts.
.. _bpo-36085-whatsnew:
* DLL dependencies for extension modules and DLLs loaded with :mod:`ctypes` on
Windows are now resolved more securely. Only the system paths, the directory
containing the DLL or PYD file, and directories added with
:func:`~os.add_dll_directory` are searched for load-time dependencies.
Specifically, :envvar:`PATH` and the current working directory are no longer
used, and modifications to these will no longer have any effect on normal DLL
resolution. If your application relies on these mechanisms, you should check
for :func:`~os.add_dll_directory` and if it exists, use it to add your DLLs
directory while loading your library.
(See :issue:`36085`.)
Changes in the C API
--------------------
...
...
Lib/ctypes/__init__.py
View file @
2438cdf0
...
...
@@ -326,7 +326,8 @@ class CDLL(object):
def
__init__
(
self
,
name
,
mode
=
DEFAULT_MODE
,
handle
=
None
,
use_errno
=
False
,
use_last_error
=
False
):
use_last_error
=
False
,
winmode
=
None
):
self
.
_name
=
name
flags
=
self
.
_func_flags_
if
use_errno
:
...
...
@@ -341,6 +342,15 @@ class CDLL(object):
"""
if
name
and
name
.
endswith
(
")"
)
and
".a("
in
name
:
mode
|=
(
_os
.
RTLD_MEMBER
|
_os
.
RTLD_NOW
)
if
_os
.
name
==
"nt"
:
if
winmode
is
not
None
:
mode
=
winmode
else
:
import
nt
mode
=
nt
.
_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
if
'/'
in
name
or
'
\
\
'
in
name
:
self
.
_name
=
nt
.
_getfullpathname
(
self
.
_name
)
mode
|=
nt
.
_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
class
_FuncPtr
(
_CFuncPtr
):
_flags_
=
flags
...
...
Lib/ctypes/test/test_loading.py
View file @
2438cdf0
from
ctypes
import
*
import
os
import
shutil
import
subprocess
import
sys
import
sysconfig
import
unittest
import
test.support
from
ctypes.util
import
find_library
...
...
@@ -112,5 +115,65 @@ class LoaderTest(unittest.TestCase):
# This is the real test: call the function via 'call_function'
self
.
assertEqual
(
0
,
call_function
(
proc
,
(
None
,)))
@
unittest
.
skipUnless
(
os
.
name
==
"nt"
,
'test specific to Windows'
)
def
test_load_dll_with_flags
(
self
):
_sqlite3
=
test
.
support
.
import_module
(
"_sqlite3"
)
src
=
_sqlite3
.
__file__
if
src
.
lower
().
endswith
(
"_d.pyd"
):
ext
=
"_d.dll"
else
:
ext
=
".dll"
with
test
.
support
.
temp_dir
()
as
tmp
:
# We copy two files and load _sqlite3.dll (formerly .pyd),
# which has a dependency on sqlite3.dll. Then we test
# loading it in subprocesses to avoid it starting in memory
# for each test.
target
=
os
.
path
.
join
(
tmp
,
"_sqlite3.dll"
)
shutil
.
copy
(
src
,
target
)
shutil
.
copy
(
os
.
path
.
join
(
os
.
path
.
dirname
(
src
),
"sqlite3"
+
ext
),
os
.
path
.
join
(
tmp
,
"sqlite3"
+
ext
))
def
should_pass
(
command
):
with
self
.
subTest
(
command
):
subprocess
.
check_output
(
[
sys
.
executable
,
"-c"
,
"from ctypes import *; import nt;"
+
command
],
cwd
=
tmp
)
def
should_fail
(
command
):
with
self
.
subTest
(
command
):
with
self
.
assertRaises
(
subprocess
.
CalledProcessError
):
subprocess
.
check_output
(
[
sys
.
executable
,
"-c"
,
"from ctypes import *; import nt;"
+
command
],
cwd
=
tmp
,
stderr
=
subprocess
.
STDOUT
,
)
# Default load should not find this in CWD
should_fail
(
"WinDLL('_sqlite3.dll')"
)
# Relative path (but not just filename) should succeed
should_pass
(
"WinDLL('./_sqlite3.dll')"
)
# Insecure load flags should succeed
should_pass
(
"WinDLL('_sqlite3.dll', winmode=0)"
)
# Full path load without DLL_LOAD_DIR shouldn't find dependency
should_fail
(
"WinDLL(nt._getfullpathname('_sqlite3.dll'), "
+
"winmode=nt._LOAD_LIBRARY_SEARCH_SYSTEM32)"
)
# Full path load with DLL_LOAD_DIR should succeed
should_pass
(
"WinDLL(nt._getfullpathname('_sqlite3.dll'), "
+
"winmode=nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR)"
)
# User-specified directory should succeed
should_pass
(
"import os; p = os.add_dll_directory(os.getcwd());"
+
"WinDLL('_sqlite3.dll'); p.close()"
)
if
__name__
==
"__main__"
:
unittest
.
main
()
Lib/os.py
View file @
2438cdf0
...
...
@@ -1070,3 +1070,40 @@ class PathLike(abc.ABC):
@
classmethod
def
__subclasshook__
(
cls
,
subclass
):
return
hasattr
(
subclass
,
'__fspath__'
)
if
name
==
'nt'
:
class
_AddedDllDirectory
:
def
__init__
(
self
,
path
,
cookie
,
remove_dll_directory
):
self
.
path
=
path
self
.
_cookie
=
cookie
self
.
_remove_dll_directory
=
remove_dll_directory
def
close
(
self
):
self
.
_remove_dll_directory
(
self
.
_cookie
)
self
.
path
=
None
def
__enter__
(
self
):
return
self
def
__exit__
(
self
,
*
args
):
self
.
close
()
def
__repr__
(
self
):
if
self
.
path
:
return
"<AddedDllDirectory({!r})>"
.
format
(
self
.
path
)
return
"<AddedDllDirectory()>"
def
add_dll_directory
(
path
):
"""Add a path to the DLL search path.
This search path is used when resolving dependencies for imported
extension modules (the module itself is resolved through sys.path),
and also by ctypes.
Remove the directory by calling close() on the returned object or
using it in a with statement.
"""
import
nt
cookie
=
nt
.
_add_dll_directory
(
path
)
return
_AddedDllDirectory
(
path
,
cookie
,
nt
.
_remove_dll_directory
)
Lib/test/test_import/__init__.py
View file @
2438cdf0
...
...
@@ -8,6 +8,8 @@ import os
import
platform
import
py_compile
import
random
import
shutil
import
subprocess
import
stat
import
sys
import
threading
...
...
@@ -17,6 +19,7 @@ import unittest.mock as mock
import
textwrap
import
errno
import
contextlib
import
glob
import
test.support
from
test.support
import
(
...
...
@@ -460,6 +463,51 @@ class ImportTests(unittest.TestCase):
finally
:
del
sys
.
path
[
0
]
@
unittest
.
skipUnless
(
sys
.
platform
==
"win32"
,
"Windows-specific"
)
def
test_dll_dependency_import
(
self
):
from
_winapi
import
GetModuleFileName
dllname
=
GetModuleFileName
(
sys
.
dllhandle
)
pydname
=
importlib
.
util
.
find_spec
(
"_sqlite3"
).
origin
depname
=
os
.
path
.
join
(
os
.
path
.
dirname
(
pydname
),
"sqlite3{}.dll"
.
format
(
"_d"
if
"_d"
in
pydname
else
""
))
with
test
.
support
.
temp_dir
()
as
tmp
:
tmp2
=
os
.
path
.
join
(
tmp
,
"DLLs"
)
os
.
mkdir
(
tmp2
)
pyexe
=
os
.
path
.
join
(
tmp
,
os
.
path
.
basename
(
sys
.
executable
))
shutil
.
copy
(
sys
.
executable
,
pyexe
)
shutil
.
copy
(
dllname
,
tmp
)
for
f
in
glob
.
glob
(
os
.
path
.
join
(
sys
.
prefix
,
"vcruntime*.dll"
)):
shutil
.
copy
(
f
,
tmp
)
shutil
.
copy
(
pydname
,
tmp2
)
env
=
None
env
=
{
k
.
upper
():
os
.
environ
[
k
]
for
k
in
os
.
environ
}
env
[
"PYTHONPATH"
]
=
tmp2
+
";"
+
os
.
path
.
dirname
(
os
.
__file__
)
# Test 1: import with added DLL directory
subprocess
.
check_call
([
pyexe
,
"-Sc"
,
";"
.
join
([
"import os"
,
"p = os.add_dll_directory({!r})"
.
format
(
os
.
path
.
dirname
(
depname
)),
"import _sqlite3"
,
"p.close"
])],
stderr
=
subprocess
.
STDOUT
,
env
=
env
,
cwd
=
os
.
path
.
dirname
(
pyexe
))
# Test 2: import with DLL adjacent to PYD
shutil
.
copy
(
depname
,
tmp2
)
subprocess
.
check_call
([
pyexe
,
"-Sc"
,
"import _sqlite3"
],
stderr
=
subprocess
.
STDOUT
,
env
=
env
,
cwd
=
os
.
path
.
dirname
(
pyexe
))
@
skip_if_dont_write_bytecode
class
FilePermissionTests
(
unittest
.
TestCase
):
...
...
Misc/NEWS.d/next/Windows/2019-03-18-11-44-49.bpo-36085.mLfxfc.rst
0 → 100644
View file @
2438cdf0
Enable better DLL resolution on Windows by using safe DLL search paths and
adding :func:`os.add_dll_directory`.
Modules/_ctypes/callproc.c
View file @
2438cdf0
...
...
@@ -1251,19 +1251,21 @@ static PyObject *format_error(PyObject *self, PyObject *args)
}
static
const
char
load_library_doc
[]
=
"LoadLibrary(name) -> handle
\n
\
"LoadLibrary(name
, load_flags
) -> handle
\n
\
\n
\
Load an executable (usually a DLL), and return a handle to it.
\n
\
The handle may be used to locate exported functions in this
\n
\
module.
\n
"
;
module. load_flags are as defined for LoadLibraryEx in the
\n
\
Windows API.
\n
"
;
static
PyObject
*
load_library
(
PyObject
*
self
,
PyObject
*
args
)
{
const
WCHAR
*
name
;
PyObject
*
nameobj
;
PyObject
*
ignored
;
int
load_flags
=
0
;
HMODULE
hMod
;
DWORD
err
;
if
(
!
PyArg_ParseTuple
(
args
,
"U|
O:LoadLibrary"
,
&
nameobj
,
&
ignored
))
if
(
!
PyArg_ParseTuple
(
args
,
"U|
i:LoadLibrary"
,
&
nameobj
,
&
load_flags
))
return
NULL
;
name
=
_PyUnicode_AsUnicode
(
nameobj
);
...
...
@@ -1271,11 +1273,22 @@ static PyObject *load_library(PyObject *self, PyObject *args)
return
NULL
;
Py_BEGIN_ALLOW_THREADS
hMod
=
LoadLibraryW
(
name
);
/* bpo-36085: Limit DLL search directories to avoid pre-loading
* attacks and enable use of the AddDllDirectory function.
*/
hMod
=
LoadLibraryExW
(
name
,
NULL
,
(
DWORD
)
load_flags
);
err
=
hMod
?
0
:
GetLastError
();
Py_END_ALLOW_THREADS
if
(
!
hMod
)
return
PyErr_SetFromWindowsErr
(
GetLastError
());
if
(
err
==
ERROR_MOD_NOT_FOUND
)
{
PyErr_Format
(
PyExc_FileNotFoundError
,
(
"Could not find module '%.500S'. Try using "
"the full path with constructor syntax."
),
nameobj
);
return
NULL
;
}
else
if
(
err
)
{
return
PyErr_SetFromWindowsErr
(
err
);
}
#ifdef _WIN64
return
PyLong_FromVoidPtr
(
hMod
);
#else
...
...
@@ -1291,15 +1304,18 @@ static PyObject *free_library(PyObject *self, PyObject *args)
{
void
*
hMod
;
BOOL
result
;
DWORD
err
;
if
(
!
PyArg_ParseTuple
(
args
,
"O&:FreeLibrary"
,
&
_parse_voidp
,
&
hMod
))
return
NULL
;
Py_BEGIN_ALLOW_THREADS
result
=
FreeLibrary
((
HMODULE
)
hMod
);
err
=
result
?
0
:
GetLastError
();
Py_END_ALLOW_THREADS
if
(
!
result
)
return
PyErr_SetFromWindowsErr
(
GetLastError
());
if
(
!
result
)
{
return
PyErr_SetFromWindowsErr
(
err
);
}
Py_RETURN_NONE
;
}
...
...
Modules/clinic/posixmodule.c.h
View file @
2438cdf0
...
...
@@ -7961,6 +7961,94 @@ exit:
#endif
/* defined(HAVE_GETRANDOM_SYSCALL) */
#if defined(MS_WINDOWS)
PyDoc_STRVAR
(
os__add_dll_directory__doc__
,
"_add_dll_directory($module, /, path)
\n
"
"--
\n
"
"
\n
"
"Add a path to the DLL search path.
\n
"
"
\n
"
"This search path is used when resolving dependencies for imported
\n
"
"extension modules (the module itself is resolved through sys.path),
\n
"
"and also by ctypes.
\n
"
"
\n
"
"Returns an opaque value that may be passed to os.remove_dll_directory
\n
"
"to remove this directory from the search path."
);
#define OS__ADD_DLL_DIRECTORY_METHODDEF \
{"_add_dll_directory", (PyCFunction)(void(*)(void))os__add_dll_directory, METH_FASTCALL|METH_KEYWORDS, os__add_dll_directory__doc__},
static
PyObject
*
os__add_dll_directory_impl
(
PyObject
*
module
,
path_t
*
path
);
static
PyObject
*
os__add_dll_directory
(
PyObject
*
module
,
PyObject
*
const
*
args
,
Py_ssize_t
nargs
,
PyObject
*
kwnames
)
{
PyObject
*
return_value
=
NULL
;
static
const
char
*
const
_keywords
[]
=
{
"path"
,
NULL
};
static
_PyArg_Parser
_parser
=
{
NULL
,
_keywords
,
"_add_dll_directory"
,
0
};
PyObject
*
argsbuf
[
1
];
path_t
path
=
PATH_T_INITIALIZE
(
"_add_dll_directory"
,
"path"
,
0
,
0
);
args
=
_PyArg_UnpackKeywords
(
args
,
nargs
,
NULL
,
kwnames
,
&
_parser
,
1
,
1
,
0
,
argsbuf
);
if
(
!
args
)
{
goto
exit
;
}
if
(
!
path_converter
(
args
[
0
],
&
path
))
{
goto
exit
;
}
return_value
=
os__add_dll_directory_impl
(
module
,
&
path
);
exit:
/* Cleanup for path */
path_cleanup
(
&
path
);
return
return_value
;
}
#endif
/* defined(MS_WINDOWS) */
#if defined(MS_WINDOWS)
PyDoc_STRVAR
(
os__remove_dll_directory__doc__
,
"_remove_dll_directory($module, /, cookie)
\n
"
"--
\n
"
"
\n
"
"Removes a path from the DLL search path.
\n
"
"
\n
"
"The parameter is an opaque value that was returned from
\n
"
"os.add_dll_directory. You can only remove directories that you added
\n
"
"yourself."
);
#define OS__REMOVE_DLL_DIRECTORY_METHODDEF \
{"_remove_dll_directory", (PyCFunction)(void(*)(void))os__remove_dll_directory, METH_FASTCALL|METH_KEYWORDS, os__remove_dll_directory__doc__},
static
PyObject
*
os__remove_dll_directory_impl
(
PyObject
*
module
,
PyObject
*
cookie
);
static
PyObject
*
os__remove_dll_directory
(
PyObject
*
module
,
PyObject
*
const
*
args
,
Py_ssize_t
nargs
,
PyObject
*
kwnames
)
{
PyObject
*
return_value
=
NULL
;
static
const
char
*
const
_keywords
[]
=
{
"cookie"
,
NULL
};
static
_PyArg_Parser
_parser
=
{
NULL
,
_keywords
,
"_remove_dll_directory"
,
0
};
PyObject
*
argsbuf
[
1
];
PyObject
*
cookie
;
args
=
_PyArg_UnpackKeywords
(
args
,
nargs
,
NULL
,
kwnames
,
&
_parser
,
1
,
1
,
0
,
argsbuf
);
if
(
!
args
)
{
goto
exit
;
}
cookie
=
args
[
0
];
return_value
=
os__remove_dll_directory_impl
(
module
,
cookie
);
exit:
return
return_value
;
}
#endif
/* defined(MS_WINDOWS) */
#ifndef OS_TTYNAME_METHODDEF
#define OS_TTYNAME_METHODDEF
#endif
/* !defined(OS_TTYNAME_METHODDEF) */
...
...
@@ -8480,4 +8568,12 @@ exit:
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif
/* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=1a9c62f5841221ae input=a9049054013a1b77]*/
#ifndef OS__ADD_DLL_DIRECTORY_METHODDEF
#define OS__ADD_DLL_DIRECTORY_METHODDEF
#endif
/* !defined(OS__ADD_DLL_DIRECTORY_METHODDEF) */
#ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF
#define OS__REMOVE_DLL_DIRECTORY_METHODDEF
#endif
/* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */
/*[clinic end generated code: output=ab36ec0376a422ae input=a9049054013a1b77]*/
Modules/posixmodule.c
View file @
2438cdf0
...
...
@@ -1442,17 +1442,23 @@ win32_error(const char* function, const char* filename)
}
static
PyObject
*
win32_error_object
(
const
char
*
function
,
PyObject
*
filename
)
win32_error_object
_err
(
const
char
*
function
,
PyObject
*
filename
,
DWORD
err
)
{
/* XXX - see win32_error for comments on 'function' */
errno
=
GetLastError
();
if
(
filename
)
return
PyErr_SetExcFromWindowsErrWithFilenameObject
(
PyExc_OSError
,
err
no
,
err
,
filename
);
else
return
PyErr_SetFromWindowsErr
(
errno
);
return
PyErr_SetFromWindowsErr
(
err
);
}
static
PyObject
*
win32_error_object
(
const
char
*
function
,
PyObject
*
filename
)
{
errno
=
GetLastError
();
return
win32_error_object_err
(
function
,
filename
,
errno
);
}
#endif
/* MS_WINDOWS */
...
...
@@ -13161,6 +13167,113 @@ error:
}
#endif
/* HAVE_GETRANDOM_SYSCALL */
#ifdef MS_WINDOWS
/* bpo-36085: Helper functions for managing DLL search directories
* on win32
*/
typedef
DLL_DIRECTORY_COOKIE
(
WINAPI
*
PAddDllDirectory
)(
PCWSTR
newDirectory
);
typedef
BOOL
(
WINAPI
*
PRemoveDllDirectory
)(
DLL_DIRECTORY_COOKIE
cookie
);
/*[clinic input]
os._add_dll_directory
path: path_t
Add a path to the DLL search path.
This search path is used when resolving dependencies for imported
extension modules (the module itself is resolved through sys.path),
and also by ctypes.
Returns an opaque value that may be passed to os.remove_dll_directory
to remove this directory from the search path.
[clinic start generated code]*/
static
PyObject
*
os__add_dll_directory_impl
(
PyObject
*
module
,
path_t
*
path
)
/*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/
{
HMODULE
hKernel32
;
PAddDllDirectory
AddDllDirectory
;
DLL_DIRECTORY_COOKIE
cookie
=
0
;
DWORD
err
=
0
;
/* For Windows 7, we have to load this. As this will be a fairly
infrequent operation, just do it each time. Kernel32 is always
loaded. */
Py_BEGIN_ALLOW_THREADS
if
(
!
(
hKernel32
=
GetModuleHandleW
(
L"kernel32"
))
||
!
(
AddDllDirectory
=
(
PAddDllDirectory
)
GetProcAddress
(
hKernel32
,
"AddDllDirectory"
))
||
!
(
cookie
=
(
*
AddDllDirectory
)(
path
->
wide
)))
{
err
=
GetLastError
();
}
Py_END_ALLOW_THREADS
if
(
err
)
{
return
win32_error_object_err
(
"add_dll_directory"
,
path
->
object
,
err
);
}
return
PyCapsule_New
(
cookie
,
"DLL directory cookie"
,
NULL
);
}
/*[clinic input]
os._remove_dll_directory
cookie: object
Removes a path from the DLL search path.
The parameter is an opaque value that was returned from
os.add_dll_directory. You can only remove directories that you added
yourself.
[clinic start generated code]*/
static
PyObject
*
os__remove_dll_directory_impl
(
PyObject
*
module
,
PyObject
*
cookie
)
/*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/
{
HMODULE
hKernel32
;
PRemoveDllDirectory
RemoveDllDirectory
;
DLL_DIRECTORY_COOKIE
cookieValue
;
DWORD
err
=
0
;
if
(
!
PyCapsule_IsValid
(
cookie
,
"DLL directory cookie"
))
{
PyErr_SetString
(
PyExc_TypeError
,
"Provided cookie was not returned from os.add_dll_directory"
);
return
NULL
;
}
cookieValue
=
(
DLL_DIRECTORY_COOKIE
)
PyCapsule_GetPointer
(
cookie
,
"DLL directory cookie"
);
/* For Windows 7, we have to load this. As this will be a fairly
infrequent operation, just do it each time. Kernel32 is always
loaded. */
Py_BEGIN_ALLOW_THREADS
if
(
!
(
hKernel32
=
GetModuleHandleW
(
L"kernel32"
))
||
!
(
RemoveDllDirectory
=
(
PRemoveDllDirectory
)
GetProcAddress
(
hKernel32
,
"RemoveDllDirectory"
))
||
!
(
*
RemoveDllDirectory
)(
cookieValue
))
{
err
=
GetLastError
();
}
Py_END_ALLOW_THREADS
if
(
err
)
{
return
win32_error_object_err
(
"remove_dll_directory"
,
NULL
,
err
);
}
if
(
PyCapsule_SetName
(
cookie
,
NULL
))
{
return
NULL
;
}
Py_RETURN_NONE
;
}
#endif
static
PyMethodDef
posix_methods
[]
=
{
...
...
@@ -13349,6 +13462,10 @@ static PyMethodDef posix_methods[] = {
OS_SCANDIR_METHODDEF
OS_FSPATH_METHODDEF
OS_GETRANDOM_METHODDEF
#ifdef MS_WINDOWS
OS__ADD_DLL_DIRECTORY_METHODDEF
OS__REMOVE_DLL_DIRECTORY_METHODDEF
#endif
{
NULL
,
NULL
}
/* Sentinel */
};
...
...
@@ -13826,6 +13943,14 @@ all_ins(PyObject *m)
if
(
PyModule_AddIntConstant
(
m
,
"_COPYFILE_DATA"
,
COPYFILE_DATA
))
return
-
1
;
#endif
#ifdef MS_WINDOWS
if
(
PyModule_AddIntConstant
(
m
,
"_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS"
,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
))
return
-
1
;
if
(
PyModule_AddIntConstant
(
m
,
"_LOAD_LIBRARY_SEARCH_APPLICATION_DIR"
,
LOAD_LIBRARY_SEARCH_APPLICATION_DIR
))
return
-
1
;
if
(
PyModule_AddIntConstant
(
m
,
"_LOAD_LIBRARY_SEARCH_SYSTEM32"
,
LOAD_LIBRARY_SEARCH_SYSTEM32
))
return
-
1
;
if
(
PyModule_AddIntConstant
(
m
,
"_LOAD_LIBRARY_SEARCH_USER_DIRS"
,
LOAD_LIBRARY_SEARCH_USER_DIRS
))
return
-
1
;
if
(
PyModule_AddIntConstant
(
m
,
"_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR"
,
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
))
return
-
1
;
#endif
return
0
;
}
...
...
Python/dynload_win.c
View file @
2438cdf0
...
...
@@ -215,12 +215,14 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
#if HAVE_SXS
cookie
=
_Py_ActivateActCtx
();
#endif
/* We use LoadLibraryEx so Windows looks for dependent DLLs
in directory of pathname first. */
/* XXX This call doesn't exist in Windows CE */
/* bpo-36085: We use LoadLibraryEx with restricted search paths
to avoid DLL preloading attacks and enable use of the
AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
ensure DLLs adjacent to the PYD are preferred. */
Py_BEGIN_ALLOW_THREADS
hDLL
=
LoadLibraryExW
(
wpathname
,
NULL
,
LOAD_WITH_ALTERED_SEARCH_PATH
);
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
|
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
);
Py_END_ALLOW_THREADS
#if HAVE_SXS
_Py_DeactivateActCtx
(
cookie
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment