• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
170     _Py_CheckPython3();
171 
172 #if USE_UNICODE_WCHAR_CACHE
173     const wchar_t *wpathname = _PyUnicode_AsUnicode(pathname);
174 #else /* USE_UNICODE_WCHAR_CACHE */
175     wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL);
176 #endif /* USE_UNICODE_WCHAR_CACHE */
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 #if !USE_UNICODE_WCHAR_CACHE
199         PyMem_Free(wpathname);
200 #endif /* USE_UNICODE_WCHAR_CACHE */
201 
202         /* restore old error mode settings */
203         SetErrorMode(old_mode);
204 
205         if (hDLL==NULL){
206             PyObject *message;
207             unsigned int errorCode;
208 
209             /* Get an error string from Win32 error code */
210             wchar_t theInfo[256]; /* Pointer to error text
211                                   from system */
212             int theLength; /* Length of error text */
213 
214             errorCode = GetLastError();
215 
216             theLength = FormatMessageW(
217                 FORMAT_MESSAGE_FROM_SYSTEM |
218                 FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
219                 NULL, /* message source */
220                 errorCode, /* the message (error) ID */
221                 MAKELANGID(LANG_NEUTRAL,
222                            SUBLANG_DEFAULT),
223                            /* Default language */
224                 theInfo, /* the buffer */
225                 sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
226                 NULL); /* no additional format args. */
227 
228             /* Problem: could not get the error message.
229                This should not happen if called correctly. */
230             if (theLength == 0) {
231                 message = PyUnicode_FromFormat(
232                     "DLL load failed with error code %u while importing %s",
233                     errorCode, shortname);
234             } else {
235                 /* For some reason a \r\n
236                    is appended to the text */
237                 if (theLength >= 2 &&
238                     theInfo[theLength-2] == '\r' &&
239                     theInfo[theLength-1] == '\n') {
240                     theLength -= 2;
241                     theInfo[theLength] = '\0';
242                 }
243                 message = PyUnicode_FromFormat(
244                     "DLL load failed while importing %s: ", shortname);
245 
246                 PyUnicode_AppendAndDel(&message,
247                     PyUnicode_FromWideChar(
248                         theInfo,
249                         theLength));
250             }
251             if (message != NULL) {
252                 PyObject *shortname_obj = PyUnicode_FromString(shortname);
253                 PyErr_SetImportError(message, shortname_obj, pathname);
254                 Py_XDECREF(shortname_obj);
255                 Py_DECREF(message);
256             }
257             return NULL;
258         } else {
259             char buffer[256];
260 
261             PyOS_snprintf(buffer, sizeof(buffer),
262 #ifdef _DEBUG
263                           "python%d%d_d.dll",
264 #else
265                           "python%d%d.dll",
266 #endif
267                           PY_MAJOR_VERSION,PY_MINOR_VERSION);
268             import_python = GetPythonImport(hDLL);
269 
270             if (import_python &&
271                 _stricmp(buffer,import_python)) {
272                 PyErr_Format(PyExc_ImportError,
273                              "Module use of %.150s conflicts "
274                              "with this version of Python.",
275                              import_python);
276                 Py_BEGIN_ALLOW_THREADS
277                 FreeLibrary(hDLL);
278                 Py_END_ALLOW_THREADS
279                 return NULL;
280             }
281         }
282         Py_BEGIN_ALLOW_THREADS
283         p = GetProcAddress(hDLL, funcname);
284         Py_END_ALLOW_THREADS
285     }
286 
287     return p;
288 }
289