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