From 2c7e16c1beeb1c31bff3f718418e12c90b7e2c90 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 10 Mar 2026 21:34:50 +0000 Subject: [PATCH 1/3] ext/mysqli: Fix memory leak and missing error handling in mysqli_poll. In mysqlnd_zval_array_from_mysqlnd_array, dest_array was not released on the error path. Also the callers were not checking the return value. Found via static analysis, the error path is difficult to trigger in practice. --- ext/mysqli/mysqli_nonapi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 6f2aa50117a5a..31243336d4fb4 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -687,6 +687,7 @@ static zend_result mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zva MYSQLI_RESOURCE *my_res; mysqli_object *intern = Z_MYSQLI_P(elem); if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) { + zval_ptr_dtor(&dest_array); zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name)); return FAILURE; } @@ -782,12 +783,18 @@ PHP_FUNCTION(mysqli_poll) mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array); if (r_array != NULL) { - mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array); + if (UNEXPECTED(mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array) == FAILURE)) { + ret = FAIL; + goto cleanup; + } } if (e_array != NULL) { - mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array); + if (UNEXPECTED(mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array) == FAILURE)) { + ret = FAIL; + } } +cleanup: if (new_dont_poll_array) { efree(new_dont_poll_array); } From d980908634e21c52d0f34d5744ca91900b7d6b1d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 11 Mar 2026 05:21:52 +0000 Subject: [PATCH 2/3] review, using assertion instead --- ext/mysqli/mysqli_nonapi.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 31243336d4fb4..10b1362bd6edd 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -686,11 +686,8 @@ static zend_result mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zva MY_MYSQL *mysql; MYSQLI_RESOURCE *my_res; mysqli_object *intern = Z_MYSQLI_P(elem); - if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) { - zval_ptr_dtor(&dest_array); - zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name)); - return FAILURE; - } + my_res = (MYSQLI_RESOURCE *)intern->ptr; + ZEND_ASSERT(my_res); mysql = (MY_MYSQL *) my_res->ptr; if (mysql->mysql == *p) { dest_elem = zend_hash_next_index_insert(Z_ARRVAL(dest_array), elem); @@ -783,18 +780,12 @@ PHP_FUNCTION(mysqli_poll) mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array); if (r_array != NULL) { - if (UNEXPECTED(mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array) == FAILURE)) { - ret = FAIL; - goto cleanup; - } + mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array); } if (e_array != NULL) { - if (UNEXPECTED(mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array) == FAILURE)) { - ret = FAIL; - } + mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array); } -cleanup: if (new_dont_poll_array) { efree(new_dont_poll_array); } From e19c24e8cc060ca77eb6b22980abb56f4c209c94 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 11 Mar 2026 19:48:26 +0000 Subject: [PATCH 3/3] no more .. --- ext/mysqli/mysqli_nonapi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 10b1362bd6edd..0b0b14f70e303 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -669,7 +669,7 @@ static zend_result mysqlnd_zval_array_to_mysqlnd_array(zval *in_array, MYSQLND * /* }}} */ /* {{{ mysqlnd_zval_array_from_mysqlnd_array */ -static zend_result mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *out_array) +static void mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *out_array) { MYSQLND **p = in_array; zval dest_array; @@ -702,8 +702,6 @@ static zend_result mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zva /* destroy old array and add new one */ zval_ptr_dtor(out_array); ZVAL_COPY_VALUE(out_array, &dest_array); - - return SUCCESS; } /* }}} */