Commit f5d89c83944 for php

commit f5d89c83944b949935fff888b0e3159fc65e4cf1
Author: Marc Bennewitz <marc-mabe@users.noreply.github.com>
Date:   Sun Sep 27 23:48:58 2026 +0200

    Fix mhash_keygen_s2k() $length truncation to int (#23951)

    A $length above INT_MAX was truncated to int before the range check,
    so e.g. 2**32 + 16 silently returned 16 bytes. Throw a ValueError instead.

diff --git a/NEWS b/NEWS
index 413c85c2aed..a6e87d987ca 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,10 @@ PHP                                                                        NEWS
   . Use recommended TYPE, HELP order for OpenMetrics metadata.
     (Marcel Hernandez)

+- Hash:
+  . Fixed mhash_keygen_s2k() truncating a $length above INT_MAX instead of
+    throwing a ValueError. (Marc Bennewitz)
+
 - Intl:
   . Fixed Collator attribute and strength methods not rejecting an
     unconstructed Collator. (Ilia Alshanetsky)
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index 8a4e857c9c1..a950d824700 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -1293,12 +1293,18 @@ PHP_FUNCTION(mhash_keygen_s2k)
 		RETURN_THROWS();
 	}

-	bytes = (int)l_bytes;
-	if (bytes <= 0){
+	if (l_bytes <= 0) {
 		zend_argument_value_error(4, "must be a greater than 0");
 		RETURN_THROWS();
 	}

+	if (ZEND_LONG_INT_OVFL(l_bytes)) {
+		zend_argument_value_error(4, "must be less than or equal to %d", INT_MAX);
+		RETURN_THROWS();
+	}
+
+	bytes = (int)l_bytes;
+
 	salt_len = MIN(salt_len, SALT_SIZE);

 	memcpy(padded_salt, salt, salt_len);
diff --git a/ext/hash/tests/gh23950.phpt b/ext/hash/tests/gh23950.phpt
new file mode 100644
index 00000000000..d9aa7dea408
--- /dev/null
+++ b/ext/hash/tests/gh23950.phpt
@@ -0,0 +1,29 @@
+--TEST--
+MHash: GH-23950 (mhash_keygen_s2k() $length must not be truncated to int)
+--SKIPIF--
+<?php
+if (!function_exists('mhash')) die('skip mhash compatibility layer not available');
+if (PHP_INT_SIZE == 4) die('skip only where a PHP integer is wider than an int');
+?>
+--FILE--
+<?php
+foreach ([2**31, 2**32, 2**32 + 16, PHP_INT_MAX] as $length) {
+    try {
+        var_dump(strlen(mhash_keygen_s2k(1, 'password', 'salt', $length)));
+    } catch (Throwable $e) {
+        echo $e::class, ': ', $e->getMessage(), "\n";
+    }
+}
+?>
+--EXPECTF--
+Deprecated: Function mhash_keygen_s2k() is deprecated since 8.1 in %s on line %d
+ValueError: mhash_keygen_s2k(): Argument #4 ($length) must be less than or equal to 2147483647
+
+Deprecated: Function mhash_keygen_s2k() is deprecated since 8.1 in %s on line %d
+ValueError: mhash_keygen_s2k(): Argument #4 ($length) must be less than or equal to 2147483647
+
+Deprecated: Function mhash_keygen_s2k() is deprecated since 8.1 in %s on line %d
+ValueError: mhash_keygen_s2k(): Argument #4 ($length) must be less than or equal to 2147483647
+
+Deprecated: Function mhash_keygen_s2k() is deprecated since 8.1 in %s on line %d
+ValueError: mhash_keygen_s2k(): Argument #4 ($length) must be less than or equal to 2147483647