[php-src] PHP-8.4: phpdbg: fix leaked lowercased lookup keys in phpdbg_resolve_opline_break

Jorg Sowa via Gina Peter Banyard <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Jorg Sowa (jorgsowa)
Committer: Gina Peter Banyard (Girgias)
Date: 2026-07-03T01:15:56+01:00

Commit: https://github.com/php/php-src/commit/b6938a25ee7dcce21f5386668e4a8ba58f2330a6
Raw diff: https://github.com/php/php-src/commit/b6938a25ee7dcce21f5386668e4a8ba58f2330a6.diff

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

Changed paths:
  A  sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt
  M  NEWS
  M  sapi/phpdbg/phpdbg_bp.c


Diff:

diff --git a/NEWS b/NEWS
index 6ac3e8db3939..1edc25b4899d 100644
--- a/NEWS
+++ b/NEWS
@@ -38,6 +38,8 @@ PHP                                                                        NEWS
 
 - PHPDBG:
   . Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal)
+  . Fixed fleaked lowercased lookup keys in phpdbg_resolve_opline_break.
+    (jorgsowa)
 
 - Reflection:
   . Fixed bug GH-22324 (Ignore leading namespace separator in
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
index 90f8ed7bb129..361e4635f1f9 100644
--- a/sapi/phpdbg/phpdbg_bp.c
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -604,13 +604,13 @@ PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break) /* {
 
 	if (new_break->class_name != NULL) {
 		zend_class_entry *ce;
-		if (!(ce = zend_hash_str_find_ptr(EG(class_table), zend_str_tolower_dup(new_break->class_name, new_break->class_len), new_break->class_len))) {
+		if (!(ce = zend_hash_str_find_ptr_lc(EG(class_table), new_break->class_name, new_break->class_len))) {
 			return FAILURE;
 		}
 		func_table = &ce->function_table;
 	}
 
-	if (!(func = zend_hash_str_find_ptr(func_table, zend_str_tolower_dup(new_break->func_name, new_break->func_len), new_break->func_len))) {
+	if (!(func = zend_hash_str_find_ptr_lc(func_table, new_break->func_name, new_break->func_len))) {
 		if (new_break->class_name != NULL && new_break->func_name != NULL) {
 			phpdbg_error("Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name);
 			return 2;
diff --git a/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt
new file mode 100644
index 000000000000..411697cee021
--- /dev/null
+++ b/sapi/phpdbg/tests/phpdbg_resolve_opline_break_leak.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Resolving method/function opline breakpoints must not leak the lookup keys
+--PHPDBG--
+b Foo::bar#0
+b baz#0
+b Foo::nope#0
+b Nope::bar#0
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at Foo::bar#0]
+prompt> [Breakpoint #1 added at baz#0]
+prompt> [Method nope doesn't exist in class Foo]
+prompt> [Pending breakpoint #3 at Nope::bar#0]
+prompt>
+--FILE--
+<?php
+class Foo {
+    public function bar($x) {
+        return $x + 1;
+    }
+}
+
+function baz($y) {
+    return $y * 2;
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.