1
2 /* Support for dynamic loading of extension modules */
3
4 #include "Python.h"
5
6 #ifdef HAVE_DIRECT_H
7 #include <direct.h>
8 #endif
9 #include <ctype.h>
10
11 #include "importdl.h"
12 #include "patchlevel.h"
13 #include <windows.h>
14
15 #ifdef _DEBUG
16 #define PYD_DEBUG_SUFFIX "_d"
17 #else
18 #define PYD_DEBUG_SUFFIX ""
19 #endif
20
21 #ifdef PYD_PLATFORM_TAG
22 #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) "-" PYD_PLATFORM_TAG ".pyd"
23 #else
24 #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) ".pyd"
25 #endif
26
27 #define PYD_UNTAGGED_SUFFIX PYD_DEBUG_SUFFIX ".pyd"
28
29 const char *_PyImport_DynLoadFiletab[] = {
30 PYD_TAGGED_SUFFIX,
31 PYD_UNTAGGED_SUFFIX,
32 NULL
33 };
34
35 /* Function to return the name of the "python" DLL that the supplied module
36 directly imports. Looks through the list of imported modules and
37 returns the first entry that starts with "python" (case sensitive) and
38 is followed by nothing but numbers until the separator (period).
39
40 Returns a pointer to the import name, or NULL if no matching name was
41 located.
42
43 This function parses through the PE header for the module as loaded in
44 memory by the system loader. The PE header is accessed as documented by
45 Microsoft in the MSDN PE and COFF specification (2/99), and handles
46 both PE32 and PE32+. It only worries about the direct import table and
47 not the delay load import table since it's unlikely an extension is
48 going to be delay loading Python (after all, it's already loaded).
49
50 If any magic values are not found (e.g., the PE header or optional
51 header magic), then this function simply returns NULL. */
52
53 #define DWORD_AT(mem) (*(DWORD *)(mem))
54 #define WORD_AT(mem) (*(WORD *)(mem))
55
GetPythonImport(HINSTANCE hModule)56 static char *GetPythonImport (HINSTANCE hModule)
57 {
58 unsigned char *dllbase, *import_data, *import_name;
59 DWORD pe_offset, opt_offset;
60 WORD opt_magic;
61 int num_dict_off, import_off;
62
63 /* Safety check input */
64 if (hModule == NULL) {
65 return NULL;
66 }
67
68 /* Module instance is also the base load address. First portion of
69 memory is the MS-DOS loader, which holds the offset to the PE
70 header (from the load base) at 0x3C */
71 dllbase = (unsigned char *)hModule;
72 pe_offset = DWORD_AT(dllbase + 0x3C);
73
74 /* The PE signature must be "PE\0\0" */
75 if (memcmp(dllbase+pe_offset,"PE\0\0",4)) {
76 return NULL;
77 }
78
79 /* Following the PE signature is the standard COFF header (20
80 bytes) and then the optional header. The optional header starts
81 with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+
82 uses 64-bits for some fields). It might also be 0x107 for a ROM
83 image, but we don't process that here.
84
85 The optional header ends with a data dictionary that directly
86 points to certain types of data, among them the import entries
87 (in the second table entry). Based on the header type, we
88 determine offsets for the data dictionary count and the entry
89 within the dictionary pointing to the imports. */
90
91 opt_offset = pe_offset + 4 + 20;
92 opt_magic = WORD_AT(dllbase+opt_offset);
93 if (opt_magic == 0x10B) {
94 /* PE32 */
95 num_dict_off = 92;
96 import_off = 104;
97 } else if (opt_magic == 0x20B) {
98 /* PE32+ */
99 num_dict_off = 108;
100 import_off = 120;
101 } else {
102 /* Unsupported */
103 return NULL;
104 }
105
106 /* Now if an import table exists, offset to it and walk the list of
107 imports. The import table is an array (ending when an entry has
108 empty values) of structures (20 bytes each), which contains (at
109 offset 12) a relative address (to the module base) at which a
110 string constant holding the import name is located. */
111
112 if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) {
113 /* We have at least 2 tables - the import table is the second
114 one. But still it may be that the table size is zero */
115 if (0 == DWORD_AT(dllbase + opt_offset + import_off + sizeof(DWORD)))
116 return NULL;
117 import_data = dllbase + DWORD_AT(dllbase +
118 opt_offset +
119 import_off);
120 while (DWORD_AT(import_data)) {
121 import_name = dllbase + DWORD_AT(import_data+12);
122 if (strlen(import_name) >= 6 &&
123 !strncmp(import_name,"python",6)) {
124 char *pch;
125
126 #ifndef _DEBUG
127 /* In a release version, don't claim that python3.dll is
128 a Python DLL. */
129 if (strcmp(import_name, "python3.dll") == 0) {
130 import_data += 20;
131 continue;
132 }
133 #endif
134
135 /* Ensure python prefix is followed only
136 by numbers to the end of the basename */
137 pch = import_name + 6;
138 #ifdef _DEBUG
139 while (*pch && pch[0] != '_' && pch[1] != 'd' && pch[2] != '.') {
140 #else
141 while (*pch && *pch != '.') {
142 #endif
143 if (*pch >= '0' && *pch <= '9') {
144 pch++;
145 } else {
146 pch = NULL;
147 break;
148 }
149 }
150
151 if (pch) {
152 /* Found it - return the name */
153 return import_name;
154 }
155 }
156 import_data += 20;
157 }
158 }
159
160 return NULL;
161 }
162
163 dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
164 const char *shortname,
165 PyObject *pathname, FILE *fp)
166 {
167 dl_funcptr p;
168 char funcname[258], *import_python;
169 const wchar_t *wpathname;
170
171 _Py_CheckPython3();
172
173 _Py_COMP_DIAG_PUSH
174 _Py_COMP_DIAG_IGNORE_DEPR_DECLS
175 wpathname = _PyUnicode_AsUnicode(pathname);
176 _Py_COMP_DIAG_POP
177 if (wpathname == NULL)
178 return NULL;
179
180 PyOS_snprintf(funcname, sizeof(funcname), "%.20s_%.200s", prefix, shortname);
181
182 {
183 HINSTANCE hDLL = NULL;
184 unsigned int old_mode;
185
186 /* Don't display a message box when Python can't load a DLL */
187 old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
188
189 /* bpo-36085: We use LoadLibraryEx with restricted search paths
190 to avoid DLL preloading attacks and enable use of the
191 AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
192 ensure DLLs adjacent to the PYD are preferred. */
193 Py_BEGIN_ALLOW_THREADS
194 hDLL = LoadLibraryExW(wpathname, NULL,
195 LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
196 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
197 Py_END_ALLOW_THREADS
198
199 /* restore old error mode settings */
200 SetErrorMode(old_mode);
201
202 if (hDLL==NULL){
203 PyObject *message;
204 unsigned int errorCode;
205
206 /* Get an error string from Win32 error code */
207 wchar_t theInfo[256]; /* Pointer to error text
208 from system */
209 int theLength; /* Length of error text */
210
211 errorCode = GetLastError();
212
213 theLength = FormatMessageW(
214 FORMAT_MESSAGE_FROM_SYSTEM |
215 FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
216 NULL, /* message source */
217 errorCode, /* the message (error) ID */
218 MAKELANGID(LANG_NEUTRAL,
219 SUBLANG_DEFAULT),
220 /* Default language */
221 theInfo, /* the buffer */
222 sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
223 NULL); /* no additional format args. */
224
225 /* Problem: could not get the error message.
226 This should not happen if called correctly. */
227 if (theLength == 0) {
228 message = PyUnicode_FromFormat(
229 "DLL load failed with error code %u while importing %s",
230 errorCode, shortname);
231 } else {
232 /* For some reason a \r\n
233 is appended to the text */
234 if (theLength >= 2 &&
235 theInfo[theLength-2] == '\r' &&
236 theInfo[theLength-1] == '\n') {
237 theLength -= 2;
238 theInfo[theLength] = '\0';
239 }
240 message = PyUnicode_FromFormat(
241 "DLL load failed while importing %s: ", shortname);
242
243 PyUnicode_AppendAndDel(&message,
244 PyUnicode_FromWideChar(
245 theInfo,
246 theLength));
247 }
248 if (message != NULL) {
249 PyObject *shortname_obj = PyUnicode_FromString(shortname);
250 PyErr_SetImportError(message, shortname_obj, pathname);
251 Py_XDECREF(shortname_obj);
252 Py_DECREF(message);
253 }
254 return NULL;
255 } else {
256 char buffer[256];
257
258 PyOS_snprintf(buffer, sizeof(buffer),
259 #ifdef _DEBUG
260 "python%d%d_d.dll",
261 #else
262 "python%d%d.dll",
263 #endif
264 PY_MAJOR_VERSION,PY_MINOR_VERSION);
265 import_python = GetPythonImport(hDLL);
266
267 if (import_python &&
268 _stricmp(buffer,import_python)) {
269 PyErr_Format(PyExc_ImportError,
270 "Module use of %.150s conflicts "
271 "with this version of Python.",
272 import_python);
273 Py_BEGIN_ALLOW_THREADS
274 FreeLibrary(hDLL);
275 Py_END_ALLOW_THREADS
276 return NULL;
277 }
278 }
279 Py_BEGIN_ALLOW_THREADS
280 p = GetProcAddress(hDLL, funcname);
281 Py_END_ALLOW_THREADS
282 }
283
284 return p;
285 }
286