-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix GH-12215: Module entry being overwritten causes type errors in ext/dom #12219
Conversation
… ext/dom 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of reverting modifications, I would prefer to check if the module was already loaded before doing any modification.
The proposed approach should also work.
I would agree with Dmitry, checking whether a module is already loaded sounds like a more robust solution. |
I have an alternative solution (linked at the bottom of the PR description) that checks if the handle is null and then warns that the module is already loaded and bails out. Another approach is doing the same module name lookup in the registry that zend_register_module_ex does. However, it seems silly to allocate a name to do that lookup, the approach in this PR seems cheaper. And we cannot move the check to the caller as its part of the public API. WDYT? |
The alternative solution may be really unsafe. I'm not sure about multiple dlload()/dlunload(). It's possible to reset Lookup in the module registry and string lowercasing should be a performance problem. dlopen() should take significantly more time anyway. But I also wouldn't like additional complex checks. Anyway, modification of not owned data with the following roll-back looks wrong. I think, a better solution might be modifying prototype/behavior of |
…t/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.
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:
Important I have an alternative patch for this here: https://gist.github.com/nielsdos/f70055db1876c95aa1bd6ade3c0dd285
I chose the approach in this PR however because it seems like the safer option. E.g. depending on how a SAPI restarts, and the fact that dlclose doesn't necessarily unload a DSO, the zend_module_entry may survive, so the
handle == NULL
check may not be safe (i.e. may not be true even though the module is not in the registry). I'm mainly thinking about embed in this case.