From 1f7838bcc0282d82bf51a25b194f8471574abce6 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 5 Oct 2000 10:54:45 +0000 Subject: [PATCH] Detect conflicting Python DLL on module import under Windows - as per [ Patch #101676 ] --- Python/dynload_win.c | 153 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 1 deletion(-) diff --git a/Python/dynload_win.c b/Python/dynload_win.c index a08e417aced..d5f712bab81 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -3,6 +3,7 @@ #include #include +#include #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); }