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