• 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