summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Hammond <mhammond@skippinet.com.au>2000-10-05 10:54:45 (GMT)
committerMark Hammond <mhammond@skippinet.com.au>2000-10-05 10:54:45 (GMT)
commit1f7838bcc0282d82bf51a25b194f8471574abce6 (patch)
tree643467e5f9ae7c92eb993cab07000ff8a492bf5b
parent85788edca4ed651d9c83742329e5b395c81e62be (diff)
downloadcpython-1f7838bcc0282d82bf51a25b194f8471574abce6.zip
cpython-1f7838bcc0282d82bf51a25b194f8471574abce6.tar.gz
cpython-1f7838bcc0282d82bf51a25b194f8471574abce6.tar.bz2
Detect conflicting Python DLL on module import under Windows - as per [ Patch #101676 ]
-rw-r--r--Python/dynload_win.c153
1 files changed, 152 insertions, 1 deletions
diff --git a/Python/dynload_win.c b/Python/dynload_win.c
index a08e417..d5f712b 100644
--- a/Python/dynload_win.c
+++ b/Python/dynload_win.c
@@ -3,6 +3,7 @@
#include <windows.h>
#include <direct.h>
+#include <ctype.h>
#include "Python.h"
#include "importdl.h"
@@ -19,11 +20,144 @@ const struct filedescr _PyImport_DynLoadFiletab[] = {
};
+#ifdef MS_WIN32
+
+/* Case insensitive string compare, to avoid any dependencies on particular
+ C RTL implementations */
+
+static int strcasecmp (char *string1, char *string2)
+{
+ int first, second;
+
+ do {
+ first = tolower(*string1);
+ second = tolower(*string2);
+ string1++;
+ string2++;
+ } while (first && first == second);
+
+ return (first - second);
+}
+
+
+/* Function to return the name of the "python" DLL that the supplied module
+ directly imports. Looks through the list of imported modules and
+ returns the first entry that starts with "python" (case sensitive) and
+ is followed by nothing but numbers until the separator (period).
+
+ Returns a pointer to the import name, or NULL if no matching name was
+ located.
+
+ This function parses through the PE header for the module as loaded in
+ memory by the system loader. The PE header is accessed as documented by
+ Microsoft in the MSDN PE and COFF specification (2/99), and handles
+ both PE32 and PE32+. It only worries about the direct import table and
+ not the delay load import table since it's unlikely an extension is
+ going to be delay loading Python (after all, it's already loaded).
+
+ If any magic values are not found (e.g., the PE header or optional
+ header magic), then this function simply returns NULL. */
+
+#define DWORD_AT(mem) (*(DWORD *)(mem))
+#define WORD_AT(mem) (*(WORD *)(mem))
+
+static char *GetPythonImport (HINSTANCE hModule)
+{
+ unsigned char *dllbase, *import_data, *import_name;
+ DWORD pe_offset, opt_offset;
+ WORD opt_magic;
+ int num_dict_off, import_off;
+
+ /* Safety check input */
+ if (hModule == NULL) {
+ return NULL;
+ }
+
+ /* Module instance is also the base load address. First portion of
+ memory is the MS-DOS loader, which holds the offset to the PE
+ header (from the load base) at 0x3C */
+ dllbase = (unsigned char *)hModule;
+ pe_offset = DWORD_AT(dllbase + 0x3C);
+
+ /* The PE signature must be "PE\0\0" */
+ if (memcmp(dllbase+pe_offset,"PE\0\0",4)) {
+ return NULL;
+ }
+
+ /* Following the PE signature is the standard COFF header (20
+ bytes) and then the optional header. The optional header starts
+ with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+
+ uses 64-bits for some fields). It might also be 0x107 for a ROM
+ image, but we don't process that here.
+
+ The optional header ends with a data dictionary that directly
+ points to certain types of data, among them the import entries
+ (in the second table entry). Based on the header type, we
+ determine offsets for the data dictionary count and the entry
+ within the dictionary pointing to the imports. */
+
+ opt_offset = pe_offset + 4 + 20;
+ opt_magic = WORD_AT(dllbase+opt_offset);
+ if (opt_magic == 0x10B) {
+ /* PE32 */
+ num_dict_off = 92;
+ import_off = 104;
+ } else if (opt_magic == 0x20B) {
+ /* PE32+ */
+ num_dict_off = 108;
+ import_off = 120;
+ } else {
+ /* Unsupported */
+ return NULL;
+ }
+
+ /* Now if an import table exists, offset to it and walk the list of
+ imports. The import table is an array (ending when an entry has
+ empty values) of structures (20 bytes each), which contains (at
+ offset 12) a relative address (to the module base) at which a
+ string constant holding the import name is located. */
+
+ if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) {
+ import_data = dllbase + DWORD_AT(dllbase +
+ opt_offset +
+ import_off);
+ while (DWORD_AT(import_data)) {
+ import_name = dllbase + DWORD_AT(import_data+12);
+ if (strlen(import_name) >= 6 &&
+ !strncmp(import_name,"python",6)) {
+ char *pch;
+
+ /* Ensure python prefix is followed only
+ by numbers to the end of the basename */
+ pch = import_name + 6;
+ while (*pch && *pch != '.') {
+ if (*pch >= '0' && *pch <= '9') {
+ pch++;
+ } else {
+ pch = NULL;
+ break;
+ }
+ }
+
+ if (pch) {
+ /* Found it - return the name */
+ return import_name;
+ }
+ }
+ import_data += 20;
+ }
+ }
+
+ return NULL;
+}
+#endif /* MS_WIN32 */
+
+
dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
const char *pathname, FILE *fp)
{
dl_funcptr p;
- char funcname[258];
+ char funcname[258], *import_python;
sprintf(funcname, "init%.200s", shortname);
@@ -91,6 +225,23 @@ dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname,
}
PyErr_SetString(PyExc_ImportError, errBuf);
return NULL;
+ } else {
+ char buffer[256];
+
+ sprintf(buffer,"python%d%d.dll",
+ PY_MAJOR_VERSION,PY_MINOR_VERSION);
+ import_python = GetPythonImport(hDLL);
+
+ if (import_python &&
+ strcasecmp(buffer,import_python)) {
+ sprintf(buffer,
+ "Module use of %s conflicts "
+ "with this version of Python.",
+ import_python);
+ PyErr_SetString(PyExc_ImportError,buffer);
+ FreeLibrary(hDLL);
+ return NULL;
+ }
}
p = GetProcAddress(hDLL, funcname);
}