summaryrefslogtreecommitdiff
path: root/Modules
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@microsoft.com>2019-03-29 16:37:16 -0700
committerGitHub <noreply@github.com>2019-03-29 16:37:16 -0700
commit2438cdf0e932a341c7613bf4323d06b91ae9f1f1 (patch)
tree231cdf3f22e1d5eb9f88fe7a511ab47e3cf8d225 /Modules
parent32119e10b792ad7ee4e5f951a2d89ddbaf111cc5 (diff)
downloadcpython-git-2438cdf0e932a341c7613bf4323d06b91ae9f1f1.tar.gz
bpo-36085: Enable better DLL resolution on Windows (GH-12302)
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ctypes/callproc.c34
-rw-r--r--Modules/clinic/posixmodule.c.h98
-rw-r--r--Modules/posixmodule.c133
3 files changed, 251 insertions, 14 deletions
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index 7c25e2e796..5a943d3c37 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -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;
}
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 55f2cbb91a..43f8ba6b4e 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -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]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 3f76018357..7c4e5f082b 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -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,
- errno,
+ 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;
}