Skip to content

Commit ddce7ad

Browse files
committed
Implement stricter extension compatibility check
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.
1 parent 2733420 commit ddce7ad

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

ext/standard/dl.c

+10
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ PHPAPI int php_load_extension(char *filename, int type, int start_now)
167167
efree(err1);
168168
}
169169

170+
#ifdef PHP_WIN32
171+
if (!php_win32_image_compatible(libpath, NULL, &err1)) {
172+
php_error_docref(NULL, error_type, err1);
173+
efree(err1);
174+
efree(libpath);
175+
DL_UNLOAD(handle);
176+
return FAILURE;
177+
}
178+
#endif
179+
170180
efree(libpath);
171181

172182
get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");

main/php_ini.c

+17
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,13 @@ static void php_load_zend_extension_cb(void *arg)
339339
#endif
340340

341341
if (IS_ABSOLUTE_PATH(filename, length)) {
342+
#ifdef PHP_WIN32
343+
char *err;
344+
if (!php_win32_image_compatible(filename, NULL, &err)) {
345+
php_error(E_CORE_WARNING, err);
346+
return FAILURE;
347+
}
348+
#endif
342349
zend_load_extension(filename);
343350
} else {
344351
DL_HANDLE handle;
@@ -384,6 +391,16 @@ static void php_load_zend_extension_cb(void *arg)
384391
efree(err1);
385392
}
386393

394+
#ifdef PHP_WIN32
395+
if (!php_win32_image_compatible(libpath, NULL, &err1)) {
396+
php_error(E_CORE_WARNING, err1);
397+
efree(err1);
398+
efree(libpath);
399+
DL_UNLOAD(handle);
400+
return FAILURE;
401+
}
402+
#endif
403+
387404
zend_load_extension_handle(handle, libpath);
388405
efree(libpath);
389406
}

win32/winutil.c

+37
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "codepage.h"
2323
#include <bcrypt.h>
2424
#include <lmcons.h>
25+
#include <imagehlp.h>
26+
2527

2628
PHP_WINUTIL_API char *php_win32_error_to_msg(HRESULT error)
2729
{/*{{{*/
@@ -435,3 +437,38 @@ PHP_WINUTIL_API char *php_win32_get_username(void)
435437

436438
return uname;
437439
}/*}}}*/
440+
441+
PHP_WINUTIL_API BOOL php_win32_image_compatible(const char *name, const char *path, char **err)
442+
{/*{{{*/
443+
PLOADED_IMAGE img = ImageLoad(name, NULL);
444+
445+
if (!img) {
446+
DWORD _err = GetLastError();
447+
char *err_txt = php_win32_error_to_msg(_err);
448+
spprintf(err, 0, "Failed to load %s, %s", name, err_txt);
449+
free(err_txt);
450+
return FALSE;
451+
}
452+
453+
DWORD major = img->FileHeader->OptionalHeader.MajorLinkerVersion;
454+
DWORD minor = img->FileHeader->OptionalHeader.MinorLinkerVersion;
455+
456+
/* VS 2015, 2017 and 2019 are binary compatible, but only forward compatible.
457+
It should be fine, if we load a module linked with an older one into
458+
the core linked with the newer one, but not the otherway round.
459+
Otherwise, if the linker major version is not same, it is an error, as
460+
per the current knowledge.
461+
462+
This check is to be extended as new VS versions come out. */
463+
if (14 == major && PHP_LINKER_MINOR < minor
464+
|| PHP_LINKER_MAJOR != major) {
465+
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);
466+
ImageUnload(img);
467+
return FALSE;
468+
}
469+
470+
ImageUnload(img);
471+
472+
return TRUE;
473+
}/*}}}*/
474+

win32/winutil.h

+2
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,6 @@ PHP_WINUTIL_API int php_win32_code_to_errno(unsigned long w32Err);
5555

5656
PHP_WINUTIL_API char *php_win32_get_username(void);
5757

58+
PHP_WINUTIL_API BOOL php_win32_image_compatible(const char *img, const char *path, char **err);
59+
5860
#endif

0 commit comments

Comments
 (0)