Implement stricter extension compatibility check
authorAnatol Belski <ab@php.net>
Sun, 31 Mar 2019 12:01:36 +0000 (14:01 +0200)
committerAnatol Belski <ab@php.net>
Sun, 31 Mar 2019 12:26:00 +0000 (14:26 +0200)
This hardens the dynamic module loading by checking the linker compatibility
between the core and the dynamic module. This likely should be extended
for the CRT as well, as 2015, 2017 and 2019 versions of Visual Studio
all have same DLL name for the CRT.

ext/standard/dl.c
main/php_ini.c
win32/winutil.c
win32/winutil.h

index 226474efbfded86943056644ecf23b217476f1f7..ddc87a40d0061c5083894836fd51dfe97a90ff13 100644 (file)
@@ -167,6 +167,16 @@ PHPAPI int php_load_extension(char *filename, int type, int start_now)
                efree(err1);
        }
 
+#ifdef PHP_WIN32
+       if (!php_win32_image_compatible(libpath, NULL, &err1)) {
+                       php_error_docref(NULL, error_type, err1);
+                       efree(err1);
+                       efree(libpath);
+                       DL_UNLOAD(handle);
+                       return FAILURE;
+       }
+#endif
+
        efree(libpath);
 
        get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");
index 7d4e916432a5aa3e1e687e67b42c0d4a0bbfeeb6..f4c61820aeff1786c9d348f3525bd286aac59167 100644 (file)
@@ -339,6 +339,13 @@ static void php_load_zend_extension_cb(void *arg)
 #endif
 
        if (IS_ABSOLUTE_PATH(filename, length)) {
+#ifdef PHP_WIN32
+       char *err;
+       if (!php_win32_image_compatible(filename, NULL, &err)) {
+               php_error(E_CORE_WARNING, err);
+               return FAILURE;
+       }
+#endif
                zend_load_extension(filename);
        } else {
                DL_HANDLE handle;
@@ -384,6 +391,16 @@ static void php_load_zend_extension_cb(void *arg)
                        efree(err1);
                }
 
+#ifdef PHP_WIN32
+               if (!php_win32_image_compatible(libpath, NULL, &err1)) {
+                               php_error(E_CORE_WARNING, err1);
+                               efree(err1);
+                               efree(libpath);
+                               DL_UNLOAD(handle);
+                               return FAILURE;
+               }
+#endif
+
                zend_load_extension_handle(handle, libpath);
                efree(libpath);
        }
index 366a48bb11cd663a89c124cb56bf0bd34c340a20..18f890fb33e09cf403f39e41503ce097f4da4b21 100644 (file)
@@ -22,6 +22,8 @@
 #include "codepage.h"
 #include <bcrypt.h>
 #include <lmcons.h>
+#include <imagehlp.h>
+
 
 PHP_WINUTIL_API char *php_win32_error_to_msg(HRESULT error)
 {/*{{{*/
@@ -435,3 +437,38 @@ PHP_WINUTIL_API char *php_win32_get_username(void)
 
        return uname;
 }/*}}}*/
+
+PHP_WINUTIL_API BOOL php_win32_image_compatible(const char *name, const char *path, char **err)
+{/*{{{*/
+       PLOADED_IMAGE img = ImageLoad(name, NULL);
+
+       if (!img) {
+               DWORD _err = GetLastError();
+               char *err_txt = php_win32_error_to_msg(_err);
+               spprintf(err, 0, "Failed to load %s, %s", name, err_txt);
+               free(err_txt);
+               return FALSE;
+       }
+       
+       DWORD major = img->FileHeader->OptionalHeader.MajorLinkerVersion;
+       DWORD minor = img->FileHeader->OptionalHeader.MinorLinkerVersion;
+
+       /* VS 2015, 2017 and 2019 are binary compatible, but only forward compatible.
+               It should be fine, if we load a module linked with an older one into
+               the core linked with the newer one, but not the otherway round.
+               Otherwise, if the linker major version is not same, it is an error, as
+               per the current knowledge.
+               
+               This check is to be extended as new VS versions come out. */
+       if (14 == major && PHP_LINKER_MINOR < minor
+                       || PHP_LINKER_MAJOR != major) {
+               spprintf(err, 0, "Can't load module '%s' as it's linked with %u.%u, but the core is linked with %d.%d", name, major, minor, PHP_LINKER_MAJOR, PHP_LINKER_MINOR);
+               ImageUnload(img);
+               return FALSE;
+       }
+
+       ImageUnload(img);
+
+       return TRUE;
+}/*}}}*/
+
index 2899a712927eb8186381d0431f9c331a8d5f6937..594f2d8d6382a8101051d5ec48fd4de835bf62d3 100644 (file)
@@ -55,4 +55,6 @@ PHP_WINUTIL_API int php_win32_code_to_errno(unsigned long w32Err);
 
 PHP_WINUTIL_API char *php_win32_get_username(void);
 
+PHP_WINUTIL_API BOOL php_win32_image_compatible(const char *img, const char *path, char **err);
+
 #endif