[PECL-CVS] [pecl-mail-mailparse] master: Avoid double allocation when joining To/Cc headers

[email protected] (Ilia Alshanetsky via Remi Collet) Sat, 13 Jun 2026 04:59:38 +0000
Newsgroups php.pecl.cvs
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Committer: Remi Collet (remicollet)
Date: 2026-06-13T06:59:18+02:00

Commit: https://github.com/php/pecl-mail-mailparse/commit/36ef73988881f499b172b89a04b5fda578bbb624
Raw diff: https://github.com/php/pecl-mail-mailparse/commit/36ef73988881f499b172b89a04b5fda578bbb624.diff

Avoid double allocation when joining To/Cc headers

Folding repeated To:/Cc: headers built a throwaway buffer with
emalloc + strcpy + two strcat calls, then handed it to
add_assoc_string, which copied it again into a zend_string before the
temporary was freed. That is two allocations and two copies per fold.

Build the joined value directly into a single zend_string with memcpy
and store it with add_assoc_str, which takes ownership. One allocation,
one copy. Output ("a, b, c") is unchanged.

Changed paths:
  M  php_mailparse_mime.c


Diff:

diff --git a/php_mailparse_mime.c b/php_mailparse_mime.c
index e7229ec..b634eec 100644
--- a/php_mailparse_mime.c
+++ b/php_mailparse_mime.c
@@ -435,17 +435,16 @@ static int php_mimepart_process_header(php_mimepart *part)
 		 * join multiple To: or Cc: lines together */
 		header_zstring = zend_string_init(header_key, strlen(header_key), 0);
 		if ((strcmp(header_key, "to") == 0 || strcmp(header_key, "cc") == 0) && (zheaderval = zend_hash_find(Z_ARRVAL_P(&part->headerhash), header_zstring)) != NULL) {
-			int newlen;
-			char *newstr;
-
-			newlen = strlen(header_val) + Z_STRLEN_P(zheaderval) + 3;
-			newstr = emalloc(newlen);
-
-			strcpy(newstr, Z_STRVAL_P(zheaderval));
-			strcat(newstr, ", ");
-			strcat(newstr, header_val);
-			add_assoc_string(&part->headerhash, header_key, newstr);
-			efree(newstr);
+			zend_string *existing = Z_STR_P(zheaderval);
+			size_t existing_len = ZSTR_LEN(existing);
+			size_t add_len = strlen(header_val);
+			zend_string *joined = zend_string_alloc(existing_len + 2 + add_len, 0);
+
+			memcpy(ZSTR_VAL(joined), ZSTR_VAL(existing), existing_len);
+			memcpy(ZSTR_VAL(joined) + existing_len, ", ", 2);
+			memcpy(ZSTR_VAL(joined) + existing_len + 2, header_val, add_len);
+			ZSTR_VAL(joined)[existing_len + 2 + add_len] = '\0';
+			add_assoc_str(&part->headerhash, header_key, joined);
 		} else {
 			if((zheaderval = zend_hash_find(Z_ARRVAL_P(&part->headerhash), header_zstring)) != NULL) {
 			      if(Z_TYPE_P(zheaderval) == IS_ARRAY) {