Fix GH-12215: Module entry being overwritten causes type errors in ext/dom (<= PHP 8.3)

When we try to load an extension multiple times, we still overwrite the
type, module number, and handle. If the module number is used to
indicate module boundaries (e.g. in reflection and in dom, see e.g.
dom_objects_set_class_ex), then all sorts of error can happen.

In the case of ext/dom, OP's error happens because the following
happens:
- The property handler is set up incorrectly in
  dom_objects_set_class_ex() because the wrong module number is
  specified. The class highest in the hierarchy is DOMNode, so the
  property handler is incorrectly set to that of DOMNode instead of
  DOMDocument.
- The documentElement property doesn't exist on DOMNode, it only exists
  on DOMDocument, so it tries to read using zend_std_read_property().
  As there is no user property called documentElement, that read
  operation returns an undef value.
  However, the type is still checked, resulting in the strange exception.

Closes GH-12219.
This commit is contained in:
Niels Dossche 2023-09-15 17:15:11 +02:00
parent 1a4e401bf0
commit da6097ffc8
2 changed files with 18 additions and 1 deletions

2
NEWS
View File

@ -5,6 +5,8 @@ PHP NEWS
- Core:
. Fixed bug GH-12207 (memory leak when class using trait with doc block).
(rioderelfte)
. Fixed bug GH-12215 (Module entry being overwritten causes type errors in
ext/dom). (nielsdos)
- Filter:
. Fix explicit FILTER_REQUIRE_SCALAR with FILTER_CALLBACK (ilutov)

View File

@ -225,15 +225,30 @@ PHPAPI int php_load_extension(const char *filename, int type, int start_now)
DL_UNLOAD(handle);
return FAILURE;
}
int old_type = module_entry->type;
int old_module_number = module_entry->module_number;
void *old_handle = module_entry->handle;
module_entry->type = type;
module_entry->module_number = zend_next_free_module();
module_entry->handle = handle;
if ((module_entry = zend_register_module_ex(module_entry)) == NULL) {
zend_module_entry *added_module_entry;
if ((added_module_entry = zend_register_module_ex(module_entry)) == NULL) {
/* Module loading failed, potentially because the module was already loaded.
* It is especially important in that case to restore the old type, module_number, and handle.
* Overwriting the values for an already-loaded module causes problem when these fields are used
* to uniquely identify module boundaries (e.g. in dom and reflection). */
module_entry->type = old_type;
module_entry->module_number = old_module_number;
module_entry->handle = old_handle;
DL_UNLOAD(handle);
return FAILURE;
}
module_entry = added_module_entry;
if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry) == FAILURE) {
DL_UNLOAD(handle);
return FAILURE;