Fix GH-21639: Protect frameless call args#21815
Fix GH-21639: Protect frameless call args#21815prateekbhujel wants to merge 1 commit intophp:PHP-8.4from
Conversation
|
Thanks for the PR.
I'm afraid this will need a more general solution. My hope was to avoid overhead in the VM, but it might be unavoidable for a proper fix, even if this is a largely artificial issue. |
35148db to
15ec47b
Compare
|
Thanks @iluuu1994. I reworked this so it no longer copies every frameless call. The updated patch adds a While checking the frameless list I also included |
15ec47b to
c818fa7
Compare
| { | ||
| zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline); | ||
| function(result, arg1); | ||
| if (UNEXPECTED(ZEND_FLF_USES_ARG_COPY(opline))) { |
There was a problem hiding this comment.
This has high overhead even if not needed - type analysis should be used, when the arg is non-object, copying/protecting the argument is useless.
There was a problem hiding this comment.
Good point on avoiding unnecessary copies. I don't think checking only the current arg type is enough for this bug though.
The original implode($separator, $pieces) case is the awkward part: $separator is a string and $pieces is an array, but an object inside $pieces can re-enter userland and overwrite both variables while the handler is still using the borrowed operands. class_exists($class) is another non-object top-level case, because autoload can re-enter userland.
So the decision is really about whether the handler can re-enter userland while it still has borrowed operands, not just whether a particular top-level operand is an object. I agree SSA/JIT type info could be used to keep more direct frameless calls when it can prove there is no object/array-of-object path. I avoided doing that in the generic VM handler because it does not have that type info cheaply, and a runtime recursive array scan would likely cost more than the refcount bump this is trying to avoid.
|
Yeah, fair enough. That overhead is worse than I expected. I'll stop pushing this direction then. If this comes back later, it probably needs a more targeted fix that doesn't touch the hot frameless path. |
Fixes GH-21639.
Frameless calls normally borrow operand zvals for the optimized handler call. That is unsafe for handlers that can re-enter userland while parsing or converting arguments, because the original variables can be overwritten before the handler is done with those operands.
This patch marks those frameless handlers with
ZEND_FLF_ARG_COPYin the generated frameless metadata. The VM copies operands only for marked handlers before entering the handler, so unaffected frameless calls keep the existing borrowed-argument fast path.The JIT direct frameless emission is skipped for marked calls, so those calls use the safe VM handler path instead of emitting a direct call with borrowed operands.
The regression tests cover
implode,in_array,strtr,str_replace,min/max, andpreg_replace.Tests run:
Also smoke-tested the affected calls with opcache JIT enabled.