Fix GH-21639: Protect frameless args during __toString reentry#21815
Fix GH-21639: Protect frameless args during __toString reentry#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
|
@iluuu1994 I pushed a different version that drops the VM/metadata/JIT approach. The current patch keeps frameless dispatch as-is and guards only the local reentry boundaries in the affected handlers. In particular, I reran the focused tests and checked the hot cases from the earlier benchmark locally. This should address the main issue with the previous version without turning the bug fix into a broader engine-level change. |
c818fa7 to
08cc08f
Compare
08cc08f to
38e2314
Compare
|
The other alternative would be checking inside the tostring handler whether the parent frame is currently at a frameless opcode and then safely copy its CV args to a buffer, set EG(vm_interrupt) and free them on the next EG(vm_interrupt), completely moving the overhead off the main paths and be a truly generic solution. Obviously comes at a small tostring handler cost, but I'd really rather see overhead there...? |
|
@bwoebi Yeah, that sounds like a better direction. I went with the local handler guards here because it kept the patch contained, but I agree that paying the cost at the actual __toString() reentry point would be cleaner if we can make the lifetime handling safe. I'll look into that approach instead of growing the per-handler guards further. |
38e2314 to
163b9ef
Compare
|
@bwoebi I pushed a version of that direction. It copies the active frameless call's array/string CV operands before entering I kept the tests focused on the lifetime issue from GH-21639 and the related cases @iluuu1994 listed. I did not try to fold in the separate parse-time sibling-argument mutation case here; that would need the VM to pass copied operands into the handler, not only keep borrowed payloads alive. |
163b9ef to
9ae220f
Compare
Fixes GH-21639.
Frameless calls borrow CV operands from the caller frame. If an argument conversion reaches
__toString(), userland can overwrite the variables behind those operands while the C handler is still using borrowed strings or arrays.This moves the protection to the
__toString()reentry point: before entering userland, the engine checks whether the active opcode is a frameless call and keeps copies of any array/string CV operands. Those copies are released after the frame has moved past that opcode, or during executor shutdown if the request unwinds first.That keeps the individual frameless handlers and normal dispatch path unchanged; the cost is paid only when
__toString()is actually entered during a frameless call.Tests run: