Skip to content

Conversation

nielsdos
Copy link
Member

Due to closures, the fbc address isn't unique if the memory address is reused. So for user closures we need to distinguish using a unique key, while internal functions can't disappear and therefore can use the fbc address as unique key.

As for performance:
This adds a load+compare for the function type, although we already load and compare the function type down below, so that impact on performance should be small. It also adds a load on the opcodes array, but we access fields from the same cacheline too so that should also have only a small impact.
It can make a performance improvement for shared opcodes arrays in case of traits as the cache slot can be reused.

…losures can cause a crash

Due to closures, the `fbc` address isn't unique if the memory address is reused.
So for user closures we need to distinguish using a unique key,
while internal functions can't disappear and therefore can use the `fbc` address
as unique key.

As for performance:
This adds a load+compare for the function type, although we already load
and compare the function type down below, so that impact on performance
should be small. It also adds a load on the opcodes array, but we access
fields from the same cacheline too so that should also have only a small
impact.
@nielsdos nielsdos marked this pull request as ready for review August 31, 2025 20:10
@nielsdos nielsdos requested a review from dstogov as a code owner August 31, 2025 20:10
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, I didn't understand the problem from the explanation at the first place, but when I started writing a comment, I found that the explanation says almost the same that I liked to say :) - "fbc" can't be stored in cache, because PHP may free the closure and allocate another one (with the same address).

Comment on lines +5061 to +5064
/* Due to closures, the `fbc` address isn't unique if the memory address is reused.
* So for user closures we need to distinguish using a unique key, while internal functions can't disappear
* and therefore can use the `fbc` address as unique key. */
void *unique_id = EXPECTED(fbc->type == ZEND_USER_FUNCTION) ? (void *) fbc->op_array.opcodes : (void *) fbc;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fbc->op_array.opcodes is not unique unless ZEND_ACC_IMMUTABLE is set. E.g.

function usage1(Closure $c) {
    $c(a: 1);
}

usage1(eval('return function($a) { var_dump($a); };'));
usage1(eval('return function($b) { var_dump($b); };'));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Closure named argument unpacking between temporary closures can cause a crash
3 participants