203 Commits
Author SHA1 Message Date
ndossche 96de5492c8 Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
  phpdbg: Fix wrong command names
2026-10-11 01:38:22 +02:00
ndossche 0adf274615 Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
  phpdbg: Fix wrong command names
2026-10-11 01:38:04 +02:00
ndosscheandWeilin Du f735f33d49 phpdbg: Fix wrong command names
Closes GH-24054.

Co-authored-by: Weilin Du <weilindu@php.net>
2026-10-11 01:37:26 +02:00
Osama Aldemeery e721b3fdc3 Deprecate returning from a finally block. (#22509)
RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_returning_from_a_finally_block
2026-08-18 20:09:21 +01:00
Ilia Alshanetsky 250f138329 Fix phpdbg over-read when watching a packed array element (#22756)
phpdbg watched every array element as a Bucket, but packed arrays store
bare zvals and de-indirected stack variables aren't buckets, so reading
Bucket.h/.key over-read the neighbour and a sibling write tripped a
phantom break. Watch such elements as WATCH_ON_ZVAL and compare the type
info first, reading the value union only for initialised slots so an
uninitialised CV is never compared by value.

A separate pre-existing bug crashed 32-bit: phpdbg_btree_insert_or_update
linked each new node into the tree before initialising its child
pointers, so a write to a node on a watched page faulted into the
watchpoint handler, which walked the half-built node. Build the node
fully, then publish it with a single store.

Closes GH-22756
2026-07-24 11:14:15 -04:00
Gina Peter Banyard 34c686a5b4 stream: remove path parameter in stream error functions (#22792)
Instead rely on the mechanism gated by the error_include_args INI to display them.

This actually aligns the behaviour of what was accepted in https://wiki.php.net/rfc/display_error_function_args.
2026-07-19 16:15:01 +01:00
Ilia Alshanetsky 207dd1d23f Fix GH-22480: phpdbg use-after-free re-watching a watched variable (#22493)
phpdbg_add_watch_element() frees the passed element and returns the
existing one when the variable is already watched, but several callers
kept using the freed pointer, so re-watching a variable was a
use-after-free in more places than the two reported. Report the duplicate
back through the parse callback and register nothing, and reject a
recursive watch's hashtable element that collides with an existing watch,
at creation and recreation, rather than freeing a still-referenced one. A
recursive watch over an already-array-watched variable now watches only
the root and skips the shared hashtable.

Running the tests under ASAN exposed a second, latent use-after-free at
shutdown: the watch tables are module-scoped but hold request-pool memory,
so under the tracked allocator freeing a watched variable during executor
teardown re-queues an element after the last drain, tracked_free_all frees
the pool, and phpdbg_destroy_watchpoints reads it at module shutdown. Tear
the watch state down in a post_deactivate handler, which runs after the
executor frees values but before the memory manager is torn down.

Dropping watch_006's xfail surfaced a separate pre-existing bug on
large-page platforms: the fault handler rechecks every watchpoint on the
faulting page, so on 16 KB pages a value-only write to an element can
fault the page holding a watched hashtable's own struct and run the
recursive rescan, which re-reported existing elements as freshly added.
Only run that rescan when the element count actually changed.

Fixes GH-22480
2026-07-14 14:23:34 -04:00
Gina Peter Banyard 092de40341 Merge branch 'PHP-8.5'
* PHP-8.5:
  phpdbg: fix leaked lowercased lookup keys in phpdbg_resolve_opline_break
2026-07-03 01:17:51 +01:00
Gina Peter Banyard 00aa0e39c3 Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
  phpdbg: fix leaked lowercased lookup keys in phpdbg_resolve_opline_break
2026-07-03 01:17:06 +01:00
Jorg Sowa b6938a25ee phpdbg: fix leaked lowercased lookup keys in phpdbg_resolve_opline_break
phpdbg_resolve_opline_break() passed the result of zend_str_tolower_dup()
directly as the lookup key to zend_hash_str_find_ptr() for both the class
and the function/method lookups. That emalloc'd buffer was never freed,
leaking on every method/function opline breakpoint resolution.

Use zend_hash_str_find_ptr_lc(), which lowercases into a (stack-allocated
for short names) temporary and frees it internally, instead of duplicating
the key by hand and leaking it.

Add a regression test that exercises all three paths through the two
lookups (class + method found, method not found, class not found).

Closes GH-22563
2026-07-03 01:15:56 +01:00
Ilia Alshanetsky 10f1d04831 Merge branch 'PHP-8.5'
* PHP-8.5:
  Fix GH-17387: crash on unterminated quote in phpdbg lexer
2026-06-29 07:04:07 -04:00
Ilia Alshanetsky d92b5f9d1c Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
  Fix GH-17387: crash on unterminated quote in phpdbg lexer
2026-06-29 07:03:55 -04:00
Ilia Alshanetsky 2e564a747c Fix GH-17387: crash on unterminated quote in phpdbg lexer
In the NORMAL condition a lone unterminated quote matched no rule, so
re2c backtracked to a zero-length GENERIC_ID accept. The rule body then
computed yyleng - unescape_string(yytext) as 0 - N, underflowing size_t
into a multi-gigabyte estrndup. Give a bare quote an explicit
one-character accept so the cursor advances and unescape_string is never
reached with a malformed token.

Fixes GH-17387
Closes GH-22506
2026-06-29 06:39:54 -04:00
Ilija Tovilo 6173a9a109 VAR|TMP overhaul (GH-20628)
The aim of this PR is twofold:

- Reduce the number of highly similar TMP|VAR handlers
- Avoid ZVAL_DEREF in most of these cases

This is achieved by guaranteeing that all zend_compile_expr() calls, as well as
all other compile calls with BP_VAR_{R,IS}, will result in a TMP variable. This
implies that the result will not contain an IS_INDIRECT or IS_REFERENCE value,
which was mostly already the case, with two exceptions:

- Calls to return-by-reference functions. Because return-by-reference functions
  are quite rare, this is solved by delegating the DEREF to the RETURN_BY_REF
  handler, which will examine the stack to check whether the caller expects a
  VAR or TMP to understand whether the DEREF is needed. Internal functions will
  also need to adjust by calling the zend_return_unwrap_ref() function.

- By-reference assignments, including both $a = &$b, as well as $a = [&$b]. When
  the result of these expressions is used in a BP_VAR_R context, the reference
  is unwrapped via a ZEND_QM_ASSIGN opcode beforehand. This is exceptionally
  rare.

Closes GH-20628
2026-01-31 19:44:56 +01:00
Tim Düsterhus 3d9d68e1ca zend_compile: Deprecate backticks as an alias for shell_exec() (#19443)
RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_backticks_as_an_alias_for_shell_exec
2025-08-12 12:02:13 +02:00
Arnaud Le Blanc 76d7c616bb Pass opline as argument to opcode handlers in CALL VM
This changes the signature of opcode handlers in the CALL VM so that the opline
is passed directly via arguments. This reduces the number of memory operations
on EX(opline), and makes the CALL VM considerably faster.

Additionally, this unifies the CALL and HYBRID VMs a bit, as EX(opline) is now
handled in the same way in both VMs.

This is a part of GH-17849.

Currently we have two VMs:

 * HYBRID: Used when compiling with GCC. execute_data and opline are global
   register variables
 * CALL: Used when compiling with something else. execute_data is passed as
   opcode handler arg, but opline is passed via execute_data->opline
   (EX(opline)).

The Call VM looks like this:

    while (1) {
        ret = execute_data->opline->handler(execute_data);
        if (UNEXPECTED(ret != 0)) {
            if (ret > 0) { // returned by ZEND_VM_ENTER() / ZEND_VM_LEAVE()
                execute_data = EG(current_execute_data);
            } else {       // returned by ZEND_VM_RETURN()
                return;
            }
        }
    }

    // example op handler
    int ZEND_INIT_FCALL_SPEC_CONST_HANDLER(zend_execute_data *execute_data) {
        // load opline
        const zend_op *opline = execute_data->opline;

        // instruction execution

        // dispatch
        // ZEND_VM_NEXT_OPCODE():
        execute_data->opline++;
        return 0; // ZEND_VM_CONTINUE()
    }

Opcode handlers return a positive value to signal that the loop must load a
new execute_data from EG(current_execute_data), typically when entering
or leaving a function.

Here I make the following changes:

 * Pass opline as opcode handler argument
 * Return next opline from opcode handlers
 * ZEND_VM_ENTER / ZEND_VM_LEAVE return opline|(1<<0) to signal that
   execute_data must be reloaded from EG(current_execute_data)

This gives us:

    while (1) {
        opline = opline->handler(execute_data, opline);
        if (UNEXPECTED((uintptr_t) opline & ZEND_VM_ENTER_BIT) {
            opline = opline & ~ZEND_VM_ENTER_BIT;
            if (opline != 0) { // ZEND_VM_ENTER() / ZEND_VM_LEAVE()
                execute_data = EG(current_execute_data);
            } else {           // ZEND_VM_RETURN()
                return;
            }
        }
    }

    // example op handler
    const zend_op * ZEND_INIT_FCALL_SPEC_CONST_HANDLER(zend_execute_data *execute_data, const zend_op *opline) {
        // opline already loaded

        // instruction execution

        // dispatch
        // ZEND_VM_NEXT_OPCODE():
        return ++opline;
    }

bench.php is 23% faster on Linux / x86_64, 18% faster on MacOS / M1.

Symfony Demo is 2.8% faster.

When using the HYBRID VM, JIT'ed code stores execute_data/opline in two fixed
callee-saved registers and rarely touches EX(opline), just like the VM.

Since the registers are callee-saved, the JIT'ed code doesn't have to
save them before calling other functions, and can assume they always
contain execute_data/opline. The code also avoids saving/restoring them in
prologue/epilogue, as execute_ex takes care of that (JIT'ed code is called
exclusively from there).

The CALL VM can now use a fixed register for execute_data/opline as well, but
we can't rely on execute_ex to save the registers for us as it may use these
registers itself. So we have to save/restore the two registers in JIT'ed code
prologue/epilogue.

Closes GH-17952
2025-04-15 18:51:54 +02:00
Niels Dossche 29bafa6323 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix memory leak in phpdbg calling registered function
  Partially fix GH-17387
2025-01-30 19:31:38 +01:00
ndossche 62bbfdebaa Fix memory leak in phpdbg calling registered function
Closes GH-17635.
2025-01-30 19:28:26 +01:00
Niels Dossche 2860c3d641 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix crashes in function registration + test
2025-01-06 21:31:03 +01:00
Niels Dossche 1235c74828 Fix crashes in function registration + test
Internal function won't need their refcount increased as they outlive
the debugger session, and userland functions won't be unloaded either.
So no refcount management is necessary for registered functions.
2025-01-06 21:30:34 +01:00
Niels Dossche 5ff67f8720 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-15208: Segfault with breakpoint map and phpdbg_clear()
2024-11-26 22:27:35 +01:00
Niels Dossche 3c3ec0e61a Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-15208: Segfault with breakpoint map and phpdbg_clear()
2024-11-26 22:27:06 +01:00
Niels Dossche 97b03186c4 Fix GH-15208: Segfault with breakpoint map and phpdbg_clear()
It crashes because it's gonna try accessing the breakpoint which was cleared
by user code in `phpdbg_clear();`. Not all breakpoint data was properly
cleaned.

Closes GH-16953.
2024-11-26 22:26:37 +01:00
Christoph M. Becker 980d41cb3a Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-16174: Empty string is an invalid expression for phpdbg-ev
2024-10-10 01:47:23 +02:00
Christoph M. Becker 5c6fc09e45 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-16174: Empty string is an invalid expression for phpdbg-ev
2024-10-10 01:46:30 +02:00
Christoph M. Becker 6bcba24eb0 Fix GH-16174: Empty string is an invalid expression for phpdbg-ev
Strings may be empty, so we must not assume they are not.

Closes GH-16177.
2024-10-10 01:45:37 +02:00
Christoph M. Becker 3c1af3febb Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-16181: phpdbg: exit in exception handler reports fatal error
2024-10-03 11:39:03 +02:00
Christoph M. Becker 95c97c81b7 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-16181: phpdbg: exit in exception handler reports fatal error
2024-10-03 11:37:15 +02:00
Christoph M. Becker f14e5cfaaa Fix GH-16181: phpdbg: exit in exception handler reports fatal error
When running PHP code, we must not handle `UnwindExit` exceptions, but
rather have to ignore them.

Closes GH-16182.
2024-10-03 11:36:13 +02:00
Christoph M. Becker 7a8767fe62 Merge branch 'PHP-8.3'
* PHP-8.3:
  Fix GH-15901: phpdbg: Assertion failure on `i funcs`
2024-09-18 23:51:45 +02:00
Christoph M. Becker c76913fde0 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-15901: phpdbg: Assertion failure on `i funcs`
2024-09-18 23:50:20 +02:00
Christoph M. Becker 422aa17b9b Fix GH-15901: phpdbg: Assertion failure on i funcs
New hash tables are not automatically packed, so we must not treat them
as such.  Therefore we guard the foreach appropriately.

Closes GH-15929.
2024-09-18 23:48:31 +02:00
Niels Dossche c426157823 Merge branch 'PHP-8.3'
* PHP-8.3:
  Add necessary SKIPIFs to new phpdbg tests
2024-08-10 01:11:14 +02:00
Niels Dossche 04adeeaef8 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Add necessary SKIPIFs to new phpdbg tests
2024-08-10 01:11:09 +02:00
Niels Dossche 4d71580e00 Add necessary SKIPIFs to new phpdbg tests
Similarly to other watchpoint tests, we add SKIPIFs.
These TRACKED_ALLOC issues should be investigated though [1] [2].

[1] https://github.com/php/php-src/commit/de5c760c698e98b82a1a168b693e4e07bc3a4134#comments
[2] https://github.com/php/php-src/pull/15229#pullrequestreview-2230563480
2024-08-10 01:10:01 +02:00
David Carlier 667a5651ca Merge branch 'PHP-8.3' 2024-08-09 21:15:07 +01:00
David Carlier de5c760c69 Merge branch 'PHP-8.2' into PHP-8.3 2024-08-09 21:12:34 +01:00
David Carlier 9aeb6761b5 Fix GH-15210: phpdbg_print_changed_zvals working on a real copy instead.
Close GH-15229
2024-08-09 21:12:11 +01:00
Niels Dossche a5c834219b Merge branch 'PHP-8.3'
* PHP-8.3:
  Fix GH-15268: heap buffer overflow in phpdbg (zend_hash_num_elements() Zend/zend_hash.h)
2024-08-08 20:35:57 +02:00
Niels Dossche 47e4991d38 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-15268: heap buffer overflow in phpdbg (zend_hash_num_elements() Zend/zend_hash.h)
2024-08-08 20:35:50 +02:00
Niels Dossche a6c547d1dd Fix GH-15268: heap buffer overflow in phpdbg (zend_hash_num_elements() Zend/zend_hash.h)
The class is not yet linked, so we cannot access `parent`, but only
`parent_name`.

Closes GH-15277.
2024-08-08 20:35:20 +02:00
Niels Dossche c69dedabb2 Merge branch 'PHP-8.3'
* PHP-8.3:
  Fix GH-14553: Bug in phpdbg8.3 (also 8.1 and 8.2) echo output - trimmed at NULL byte (?)
2024-07-04 18:44:29 +02:00
Niels Dossche 751c267850 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-14553: Bug in phpdbg8.3 (also 8.1 and 8.2) echo output - trimmed at NULL byte (?)
2024-07-04 18:44:17 +02:00
Niels Dossche 95889979f2 Fix GH-14553: Bug in phpdbg8.3 (also 8.1 and 8.2) echo output - trimmed at NULL byte (?)
This broke in 6318040df2 when phpdbg
stopped using its custom printing routines. By relying on standard
printing routines, the embedded NUL bytes are causing the strings to be
cut off, even when using %.*s. Solve this by going straight to the
output routine, which is what the printf routine would've done anyway.

Closes GH-14822.
2024-07-04 18:43:42 +02:00
David Carlier ac947925c0 Merge branch 'PHP-8.3' 2024-06-17 17:46:54 +01:00
David Carlier 8690d522a3 Merge branch 'PHP-8.2' into PHP-8.3 2024-06-17 17:46:24 +01:00
David Carlier 03f0776d08 Fix GH-13681: segfault when adding watchpoint fails.
thus when removing its entry, no watch point is set and crash on
pointer access.

close GH-14513
2024-06-17 17:45:53 +01:00
Niels Dossche 584a7b8e78 Merge branch 'PHP-8.3'
* PHP-8.3:
  Fix GH-13931: Applying zero offset to null pointer in Zend/zend_opcode.c
2024-04-15 19:40:08 +02:00
Niels Dossche 550e0ceb79 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-13931: Applying zero offset to null pointer in Zend/zend_opcode.c
2024-04-15 19:39:59 +02:00
Niels Dossche c3acfb1b57 Fix GH-13931: Applying zero offset to null pointer in Zend/zend_opcode.c
In the test cases, the compiler bails out due to a fatal error.
The data structures used by the compiler will contain stale values.
In particular, for the test case CG(loop_var_stack) will contain data.
The next compilation will incorrectly use elements from the previous
stack.
To solve this, we reset part of the compiler data structures.
We don't do a full re-initialization via init_compiler() because that will
also reset streams and resources.

Closes GH-13938.
2024-04-15 19:39:05 +02:00