[PECL-CVS] [pecl-mail-mailparse] master: Fix arg zval leak in extract_callback_user_func

[email protected] (Ilia Alshanetsky via Remi Collet) Fri, 12 Jun 2026 13:40:02 +0000
Newsgroups php.pecl.cvs
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Committer: Remi Collet (remicollet)
Date: 2026-06-12T15:39:47+02:00

Commit: https://github.com/php/pecl-mail-mailparse/commit/696d40f8691b3b8d2a36f17eeedba65aa98d9f48
Raw diff: https://github.com/php/pecl-mail-mailparse/commit/696d40f8691b3b8d2a36f17eeedba65aa98d9f48.diff

Fix arg zval leak in extract_callback_user_func

The callback ZVAL_STRINGL's the chunk into `arg` but only released it on
the success path. Both early returns — when zend_fcall_info_init() fails
and when zend_call_function() fails — returned without dtoring `arg`,
leaking one zend_string per chunk. The init-failure path is reachable
from userland by passing a non-callable as the extract callback.

zend_fcall_info_argn() ZVAL_COPYs the argument into fci.params (its own
ref), so the original `arg` always needs exactly one zval_ptr_dtor.
Release it on every path; dtor `retval` only on success, where the
engine has actually written it.

Changed paths:
  M  mailparse.c


Diff:

diff --git a/mailparse.c b/mailparse.c
index 7d8d218..25553f3 100644
--- a/mailparse.c
+++ b/mailparse.c
@@ -1206,19 +1206,19 @@ static int extract_callback_user_func(php_mimepart *part, zval *userfunc, const
 
 	if (zend_fcall_info_init(userfunc, 0, &fci, &fcc, NULL, NULL) == FAILURE) {
 		zend_error(E_WARNING, "%s(): unable to call user function", get_active_function_name());
+		zval_ptr_dtor(&arg);
 		return 0;
 	}
 
 	zend_fcall_info_argn(&fci, 1, &arg);
 	fci.retval = &retval;
-	if (zend_call_function(&fci, &fcc)) {
-		zend_fcall_info_args_clear(&fci, 1);
+	if (zend_call_function(&fci, &fcc) == SUCCESS) {
+		zval_ptr_dtor(&retval);
+	} else {
 		zend_error(E_WARNING, "%s(): unable to call user function", get_active_function_name());
-		return 0;
 	}
 
 	zend_fcall_info_args_clear(&fci, 1);
-	zval_ptr_dtor(&retval);
 	zval_ptr_dtor(&arg);
 
 	return 0;