Skip to content

Fix GH-16811: Crash in zend_test observer on runtime observe_function_names change#21635

Open
iliaal wants to merge 2 commits intophp:masterfrom
iliaal:fix/gh-16811-observer-ini-set-crash
Open

Fix GH-16811: Crash in zend_test observer on runtime observe_function_names change#21635
iliaal wants to merge 2 commits intophp:masterfrom
iliaal:fix/gh-16811-observer-ini-set-crash

Conversation

@iliaal
Copy link
Copy Markdown
Contributor

@iliaal iliaal commented Apr 4, 2026

zend_test_observer_OnUpdateCommaList crashes when ini_set changes observe_function_names at runtime. Two paths:

  1. Function exists in the function table but was never called. RUN_TIME_CACHE is NULL, so ZEND_OBSERVER_DATA null-derefs in zend_observer_remove_handler.

  2. observe_all=1 already installed the handler at first call. OnUpdateCommaList adds the same handler again, finds no free slot, hits ZEND_UNREACHABLE().

Fix: guard remove/add with runtime cache checks, and remove existing handlers before re-adding to prevent duplicates.

Fixes #16811

…ion_names change

OnUpdateCommaList called zend_observer_remove/add_begin_handler without
checking whether observer data was initialized. This null-dereferenced
when the function had never been called (no runtime cache), and hit
ZEND_UNREACHABLE() when observe_all had already installed the same
handler.

Guard both the remove and add blocks with runtime cache checks. Remove
existing handlers before re-adding to prevent slot overflow from
duplicates.

Closes phpGH-16811
Comment on lines +363 to +365
void *old_handler;
zend_observer_remove_begin_handler(func, observer_begin, (zend_observer_fcall_begin_handler *)&old_handler);
zend_observer_remove_end_handler(func, observer_end, (zend_observer_fcall_end_handler *)&old_handler);
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.

I don't understand why this is needed. Any previous handlers should have been removed above, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The first loop only removes handlers for functions in the previous comma list. If observe_all=1 installed the handler at first-call time (via observer_fcall_init), that function was never in the old comma list, so the first loop won't touch it. Without the remove here, add_begin_handler installs a duplicate and hits ZEND_UNREACHABLE().

The *ZEND_OBSERVER_DATA(func) check gates this: we only attempt removal when observer data already exists.

@iliaal iliaal requested a review from iluuu1994 April 4, 2026 21:45
@iluuu1994
Copy link
Copy Markdown
Member

@iliaal See the test failure, it looks related.

CircleCI ARM passes -d zend_test.observer.show_output=0 globally.
The test relied on the default (1) instead of setting it explicitly,
so the <!-- init --> lines were suppressed.
@iliaal
Copy link
Copy Markdown
Contributor Author

iliaal commented Apr 4, 2026

The test didn't set zend_test.observer.show_output=1 explicitly. CircleCI ARM passes -d zend_test.observer.show_output=0 globally, which suppressed the <!-- init --> lines. Just an output mismatch, not a real failure. Fixed in the latest push.

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.

Segmentation fault in zend observer

2 participants