Skip to content

Add ZEND_ACC2_FORBID_DYN_CALLS#21818

Draft
arnaud-lb wants to merge 2 commits intophp:masterfrom
arnaud-lb:forbid-dyn-call
Draft

Add ZEND_ACC2_FORBID_DYN_CALLS#21818
arnaud-lb wants to merge 2 commits intophp:masterfrom
arnaud-lb:forbid-dyn-call

Conversation

@arnaud-lb
Copy link
Copy Markdown
Member

@arnaud-lb arnaud-lb commented Apr 21, 2026

Flag functions that forbid dynamic calls with ZEND_ACC2_FORBID_DYN_CALLS, so that we can recognize them without making a hard-coded list.

This will be used in the PFA implementation.

See individual commits for details.

UPGRADING.INTERNALS:

Functions using zend_forbid_dynamic_call() must be flagged with ZEND_ACC2_FORBID_DYN_CALLS (@forbid-dynamic-calls in stubs)

Change zend_function_entry.flags to a uint64_t to that both ZEND_ACC_ and
ZEND_ACC2_ flags can be represented.

Introduce ZEND_FENTRY_FLAGS(flags, flags2) to pass ZEND_ACC2_ flags to
ZEND_RAW_FENTRY(), ZEND_FENTRY().

Source-level backwards compatibility is maintained, as passing raw ZEND_ACC_
flags to ZEND_RAW_FENTRY(), ZEND_FENTRY() still works.
Functions that use zend_forbid_dynamic_call() must be flagged with
ZEND_ACC2_FORBID_DYN_CALLS. In stubs, this is done by using
@forbid-dynamic-calls.

To ensure consistency, we assert that the flag exists in
zend_forbid_dynamic_call(), and we assert that flagged functions thrown after a
dynamic call.
Copy link
Copy Markdown
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

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

Should Closure::getCurrent() be added to that list or does it work?

Comment thread Zend/zend_vm_def.h
Comment on lines +4163 to +4165
if (ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) {
ZEND_ASSERT(!(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS));
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An if() just for a ZEND_ASSERT feels wrong.

Suggested change
if (ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) {
ZEND_ASSERT(!(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS));
}
ZEND_ASSERT(!(ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC) || !(call->func->common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS));

Comment thread Zend/zend_closures.c
Comment on lines +752 to +754
#if ZEND_DEBUG
ZEND_ASSERT(!(closure->func.common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS) || EG(exception));
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this assert wrapped in a ZEND_DEBUG check?

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.

2 participants