[php-src] PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-06-29T10:37:47-04:00

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

Merge branch 'PHP-8.4' into PHP-8.5

* PHP-8.4:
  Fix GH-18173: ext/hash relies on implementation-defined malloc alignment

Changed paths:
  M  NEWS
  M  ext/hash/hash.c
  M  ext/hash/hash_adler32.c
  M  ext/hash/hash_crc32.c
  M  ext/hash/hash_fnv.c
  M  ext/hash/hash_gost.c
  M  ext/hash/hash_haval.c
  M  ext/hash/hash_joaat.c
  M  ext/hash/hash_md.c
  M  ext/hash/hash_murmur.c
  M  ext/hash/hash_ripemd.c
  M  ext/hash/hash_sha.c
  M  ext/hash/hash_sha3.c
  M  ext/hash/hash_snefru.c
  M  ext/hash/hash_tiger.c
  M  ext/hash/hash_whirlpool.c
  M  ext/hash/hash_xxhash.c
  M  ext/hash/php_hash.h


Diff:

diff --git a/NEWS b/NEWS
index 89f41d2bb2d3..dc37bcf555da 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ PHP                                                                        NEWS
   . Fixed bug GH-22206 (missing return in global register detection).
     (P3p111n0)
 
+- Hash:
+  . Fixed bug GH-18173 (ext/hash relies on implementation-defined malloc
+    alignment). (iliaal)
+
 - Opcache:
   . Fixed bug GH-22158 (Tracing JIT dispatches the observer begin handler
     through the wrong run_time_cache slot on megamorphic calls). (ptondereau,
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index ec5391a62304..6e8bc49d34d1 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -388,7 +388,7 @@ static void php_hash_do_hash(
 		}
 		php_stream_close(stream);
 		if (n < 0) {
-			efree(context);
+			php_hash_free_context(ops, context);
 			RETURN_FALSE;
 		}
 	} else {
@@ -397,7 +397,7 @@ static void php_hash_do_hash(
 
 	digest = zend_string_alloc(ops->digest_size, 0);
 	ops->hash_final((unsigned char *) ZSTR_VAL(digest), context);
-	efree(context);
+	php_hash_free_context(ops, context);
 
 	if (raw_output) {
 		ZSTR_VAL(digest)[ops->digest_size] = 0;
@@ -536,7 +536,7 @@ static void php_hash_do_hash_hmac(
 		}
 		php_stream_close(stream);
 		if (n < 0) {
-			efree(context);
+			php_hash_free_context(ops, context);
 			efree(K);
 			zend_string_efree(digest);
 			RETURN_FALSE;
@@ -554,7 +554,7 @@ static void php_hash_do_hash_hmac(
 	/* Zero the key */
 	ZEND_SECURE_ZERO(K, ops->block_size);
 	efree(K);
-	efree(context);
+	php_hash_free_context(ops, context);
 
 	if (raw_output) {
 		ZSTR_VAL(digest)[ops->digest_size] = 0;
@@ -815,7 +815,7 @@ PHP_FUNCTION(hash_final)
 	ZSTR_VAL(digest)[digest_len] = 0;
 
 	/* Invalidate the object from further use */
-	efree(hash->context);
+	php_hash_free_context(hash->ops, hash->context);
 	hash->context = NULL;
 
 	if (raw_output) {
@@ -973,7 +973,7 @@ PHP_FUNCTION(hash_hkdf)
 	ZEND_SECURE_ZERO(digest, ops->digest_size);
 	ZEND_SECURE_ZERO(prk, ops->digest_size);
 	efree(K);
-	efree(context);
+	php_hash_free_context(ops, context);
 	efree(prk);
 	efree(digest);
 	ZSTR_VAL(returnval)[length] = 0;
@@ -1089,7 +1089,7 @@ PHP_FUNCTION(hash_pbkdf2)
 	efree(K1);
 	efree(K2);
 	efree(computed_salt);
-	efree(context);
+	php_hash_free_context(ops, context);
 	efree(digest);
 	efree(temp);
 
@@ -1356,7 +1356,7 @@ PHP_FUNCTION(mhash_keygen_s2k)
 				RETVAL_STRINGL(key, bytes);
 				ZEND_SECURE_ZERO(key, bytes);
 				efree(digest);
-				efree(context);
+				php_hash_free_context(ops, context);
 				efree(key);
 			}
 		}
@@ -1386,7 +1386,7 @@ static void php_hashcontext_dtor(zend_object *obj) {
 	php_hashcontext_object *hash = php_hashcontext_from_object(obj);
 
 	if (hash->context) {
-		efree(hash->context);
+		php_hash_free_context(hash->ops, hash->context);
 		hash->context = NULL;
 	}
 
@@ -1422,7 +1422,7 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) {
 	newobj->ops->hash_init(newobj->context, NULL);
 
 	if (SUCCESS != newobj->ops->hash_copy(newobj->ops, oldobj->context, newobj->context)) {
-		efree(newobj->context);
+		php_hash_free_context(newobj->ops, newobj->context);
 		newobj->context = NULL;
 		return znew;
 	}
diff --git a/ext/hash/hash_adler32.c b/ext/hash/hash_adler32.c
index 3898ea60e877..e1fdd765b372 100644
--- a/ext/hash/hash_adler32.c
+++ b/ext/hash/hash_adler32.c
@@ -70,5 +70,6 @@ const php_hash_ops php_hash_adler32_ops = {
 	4, /* what to say here? */
 	4,
 	sizeof(PHP_ADLER32_CTX),
+	0,
 	0
 };
diff --git a/ext/hash/hash_crc32.c b/ext/hash/hash_crc32.c
index a770d0b55416..2e8de00b518d 100644
--- a/ext/hash/hash_crc32.c
+++ b/ext/hash/hash_crc32.c
@@ -102,6 +102,7 @@ const php_hash_ops php_hash_crc32_ops = {
 	4, /* what to say here? */
 	4,
 	sizeof(PHP_CRC32_CTX),
+	0,
 	0
 };
 
@@ -117,6 +118,7 @@ const php_hash_ops php_hash_crc32b_ops = {
 	4, /* what to say here? */
 	4,
 	sizeof(PHP_CRC32_CTX),
+	0,
 	0
 };
 
@@ -132,5 +134,6 @@ const php_hash_ops php_hash_crc32c_ops = {
 	4, /* what to say here? */
 	4,
 	sizeof(PHP_CRC32_CTX),
+	0,
 	0
 };
diff --git a/ext/hash/hash_fnv.c b/ext/hash/hash_fnv.c
index 92d4922bd810..c126de61cfd1 100644
--- a/ext/hash/hash_fnv.c
+++ b/ext/hash/hash_fnv.c
@@ -32,6 +32,7 @@ const php_hash_ops php_hash_fnv132_ops = {
 	4,
 	4,
 	sizeof(PHP_FNV132_CTX),
+	0,
 	0
 };
 
@@ -47,6 +48,7 @@ const php_hash_ops php_hash_fnv1a32_ops = {
 	4,
 	4,
 	sizeof(PHP_FNV132_CTX),
+	0,
 	0
 };
 
@@ -62,6 +64,7 @@ const php_hash_ops php_hash_fnv164_ops = {
 	8,
 	4,
 	sizeof(PHP_FNV164_CTX),
+	0,
 	0
 };
 
@@ -77,6 +80,7 @@ const php_hash_ops php_hash_fnv1a64_ops = {
 	8,
 	4,
 	sizeof(PHP_FNV164_CTX),
+	0,
 	0
 };
 
diff --git a/ext/hash/hash_gost.c b/ext/hash/hash_gost.c
index bba585a11f9f..c2dfa144dcd9 100644
--- a/ext/hash/hash_gost.c
+++ b/ext/hash/hash_gost.c
@@ -329,7 +329,8 @@ const php_hash_ops php_hash_gost_ops = {
 	32,
 	32,
 	sizeof(PHP_GOST_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_gost_crypto_ops = {
@@ -344,5 +345,6 @@ const php_hash_ops php_hash_gost_crypto_ops = {
 	32,
 	32,
 	sizeof(PHP_GOST_CTX),
-	1
+	1,
+	0
 };
diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c
index 67bc2b2e4780..484b9a582915 100644
--- a/ext/hash/hash_haval.c
+++ b/ext/hash/hash_haval.c
@@ -252,7 +252,7 @@ const php_hash_ops php_hash_##p##haval##b##_ops = { \
 	php_hash_serialize, \
 	php_hash_unserialize, \
 	PHP_HAVAL_SPEC, \
-	((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1 }; \
+	((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1, 0 }; \
 PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args) \
 {	int i; context->count[0] = 	context->count[1] = 	0; \
 	for(i = 0; i < 8; i++) context->state[i] = D0[i]; \
diff --git a/ext/hash/hash_joaat.c b/ext/hash/hash_joaat.c
index 328f9292c4ca..6a16ceeeda14 100644
--- a/ext/hash/hash_joaat.c
+++ b/ext/hash/hash_joaat.c
@@ -33,6 +33,7 @@ const php_hash_ops php_hash_joaat_ops = {
 	4,
 	4,
 	sizeof(PHP_JOAAT_CTX),
+	0,
 	0
 };
 
diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c
index dd299e695891..4af74871fb03 100644
--- a/ext/hash/hash_md.c
+++ b/ext/hash/hash_md.c
@@ -29,7 +29,8 @@ const php_hash_ops php_hash_md5_ops = {
 	16,
 	64,
 	sizeof(PHP_MD5_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_md4_ops = {
@@ -44,7 +45,8 @@ const php_hash_ops php_hash_md4_ops = {
 	16,
 	64,
 	sizeof(PHP_MD4_CTX),
-	1
+	1,
+	0
 };
 
 static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv);
@@ -61,7 +63,8 @@ const php_hash_ops php_hash_md2_ops = {
 	16,
 	16,
 	sizeof(PHP_MD2_CTX),
-	1
+	1,
+	0
 };
 
 /* MD common stuff */
diff --git a/ext/hash/hash_murmur.c b/ext/hash/hash_murmur.c
index 0117b2e57d36..d69c5c3bb688 100644
--- a/ext/hash/hash_murmur.c
+++ b/ext/hash/hash_murmur.c
@@ -33,6 +33,7 @@ const php_hash_ops php_hash_murmur3a_ops = {
 	4,
 	4,
 	sizeof(PHP_MURMUR3A_CTX),
+	0,
 	0
 };
 
@@ -95,6 +96,7 @@ const php_hash_ops php_hash_murmur3c_ops = {
 	16,
 	4,
 	sizeof(PHP_MURMUR3C_CTX),
+	0,
 	0
 };
 
@@ -174,6 +176,7 @@ const php_hash_ops php_hash_murmur3f_ops = {
 	16,
 	8,
 	sizeof(PHP_MURMUR3F_CTX),
+	0,
 	0
 };
 
diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c
index 4802fdf9a1fc..188d0095cbc3 100644
--- a/ext/hash/hash_ripemd.c
+++ b/ext/hash/hash_ripemd.c
@@ -33,7 +33,8 @@ const php_hash_ops php_hash_ripemd128_ops = {
 	16,
 	64,
 	sizeof(PHP_RIPEMD128_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_ripemd160_ops = {
@@ -48,7 +49,8 @@ const php_hash_ops php_hash_ripemd160_ops = {
 	20,
 	64,
 	sizeof(PHP_RIPEMD160_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_ripemd256_ops = {
@@ -63,7 +65,8 @@ const php_hash_ops php_hash_ripemd256_ops = {
 	32,
 	64,
 	sizeof(PHP_RIPEMD256_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_ripemd320_ops = {
@@ -78,7 +81,8 @@ const php_hash_ops php_hash_ripemd320_ops = {
 	40,
 	64,
 	sizeof(PHP_RIPEMD320_CTX),
-	1
+	1,
+	0
 };
 
 /* {{{ PHP_RIPEMD128Init
diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c
index 3129446fcde4..c45947b8584f 100644
--- a/ext/hash/hash_sha.c
+++ b/ext/hash/hash_sha.c
@@ -75,7 +75,8 @@ const php_hash_ops php_hash_sha1_ops = {
 	20,
 	64,
 	sizeof(PHP_SHA1_CTX),
-	1
+	1,
+	0
 };
 
 /* sha224/sha256 */
@@ -92,7 +93,8 @@ const php_hash_ops php_hash_sha256_ops = {
 	32,
 	64,
 	sizeof(PHP_SHA256_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_sha224_ops = {
@@ -107,7 +109,8 @@ const php_hash_ops php_hash_sha224_ops = {
 	28,
 	64,
 	sizeof(PHP_SHA224_CTX),
-	1
+	1,
+	0
 };
 
 #define ROTR32(b,x)		((x >> b) | (x << (32 - b)))
@@ -624,7 +627,8 @@ const php_hash_ops php_hash_sha384_ops = {
 	48,
 	128,
 	sizeof(PHP_SHA384_CTX),
-	1
+	1,
+	0
 };
 
 /* {{{ PHP_SHA512InitArgs
@@ -803,7 +807,8 @@ const php_hash_ops php_hash_sha512_ops = {
 	64,
 	128,
 	sizeof(PHP_SHA512_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_sha512_256_ops = {
@@ -818,7 +823,8 @@ const php_hash_ops php_hash_sha512_256_ops = {
 	32,
 	128,
 	sizeof(PHP_SHA512_CTX),
-	1
+	1,
+	0
 };
 
 const php_hash_ops php_hash_sha512_224_ops = {
@@ -833,5 +839,6 @@ const php_hash_ops php_hash_sha512_224_ops = {
 	28,
 	128,
 	sizeof(PHP_SHA512_CTX),
-	1
+	1,
+	0
 };
diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c
index 8fa0a4b4e522..fea8998e55aa 100644
--- a/ext/hash/hash_sha3.c
+++ b/ext/hash/hash_sha3.c
@@ -251,7 +251,8 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \
 	bits >> 3, \
 	(1600 - (2 * bits)) >> 3, \
 	sizeof(PHP_SHA3_##bits##_CTX), \
-	1 \
+	1, \
+	0 \
 }
 
 #else
@@ -339,7 +340,8 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \
 	bits >> 3, \
 	(1600 - (2 * bits)) >> 3, \
 	sizeof(PHP_SHA3_CTX), \
-	1 \
+	1, \
+	0 \
 }
 
 #endif
diff --git a/ext/hash/hash_snefru.c b/ext/hash/hash_snefru.c
index b9b70f364208..7f167c38a8a5 100644
--- a/ext/hash/hash_snefru.c
+++ b/ext/hash/hash_snefru.c
@@ -214,5 +214,6 @@ const php_hash_ops php_hash_snefru_ops = {
 	32,
 	32,
 	sizeof(PHP_SNEFRU_CTX),
-	1
+	1,
+	0
 };
diff --git a/ext/hash/hash_tiger.c b/ext/hash/hash_tiger.c
index 62d1b7347143..8dbcdde0afb3 100644
--- a/ext/hash/hash_tiger.c
+++ b/ext/hash/hash_tiger.c
@@ -265,7 +265,8 @@ static hash_spec_result php_tiger_unserialize(php_hashcontext_object *hash, zend
 		b/8, \
 		64, \
 		sizeof(PHP_TIGER_CTX), \
-		1 \
+		1, \
+		0 \
 	}
 
 PHP_HASH_TIGER_OPS(3, 128);
diff --git a/ext/hash/hash_whirlpool.c b/ext/hash/hash_whirlpool.c
index fa3c216a64c4..b1ec068342c6 100644
--- a/ext/hash/hash_whirlpool.c
+++ b/ext/hash/hash_whirlpool.c
@@ -457,5 +457,6 @@ const php_hash_ops php_hash_whirlpool_ops = {
 	64,
 	64,
 	sizeof(PHP_WHIRLPOOL_CTX),
-	1
+	1,
+	0
 };
diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c
index add922e8e84f..690b5489c7ef 100644
--- a/ext/hash/hash_xxhash.c
+++ b/ext/hash/hash_xxhash.c
@@ -34,6 +34,7 @@ const php_hash_ops php_hash_xxh32_ops = {
 	4,
 	4,
 	sizeof(PHP_XXH32_CTX),
+	0,
 	0
 };
 
@@ -101,6 +102,7 @@ const php_hash_ops php_hash_xxh64_ops = {
 	8,
 	8,
 	sizeof(PHP_XXH64_CTX),
+	0,
 	0
 };
 
@@ -152,7 +154,8 @@ const php_hash_ops php_hash_xxh3_64_ops = {
 	8,
 	8,
 	sizeof(PHP_XXH3_64_CTX),
-	0
+	0,
+	64
 };
 
 typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t);
@@ -257,7 +260,8 @@ const php_hash_ops php_hash_xxh3_128_ops = {
 	16,
 	8,
 	sizeof(PHP_XXH3_128_CTX),
-	0
+	0,
+	64
 };
 
 PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h
index f56605a33be6..34d44137028d 100644
--- a/ext/hash/php_hash.h
+++ b/ext/hash/php_hash.h
@@ -60,6 +60,7 @@ typedef struct _php_hash_ops {
 	size_t block_size;
 	size_t context_size;
 	unsigned is_crypto: 1;
+	size_t context_align;
 } php_hash_ops;
 
 struct _php_hashcontext_object {
@@ -163,9 +164,26 @@ PHP_HASH_API hash_spec_result php_hash_unserialize_spec(php_hashcontext_object *
 
 static inline void *php_hash_alloc_context(const php_hash_ops *ops) {
 	/* Zero out context memory so serialization doesn't expose internals */
+	if (ops->context_align > 0) {
+		size_t align = ops->context_align;
+		char *base = ecalloc(1, ops->context_size + align);
+		size_t offset = align - ((uintptr_t)base & (align - 1));
+		char *ptr = base + offset;
+		ptr[-1] = (char)offset;
+		return ptr;
+	}
 	return ecalloc(1, ops->context_size);
 }
 
+static inline void php_hash_free_context(const php_hash_ops *ops, void *ctx) {
+	if (ops->context_align > 0) {
+		unsigned char offset = ((unsigned char *)ctx)[-1];
+		efree((char *)ctx - offset);
+		return;
+	}
+	efree(ctx);
+}
+
 static inline void php_hash_bin2hex(char *out, const unsigned char *in, size_t in_len)
 {
 	static const char hexits[17] = "0123456789abcdef";
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.