Commit a15654ece0c for php.net

commit a15654ece0cd29330b8b2632a1dcc587abb9151c
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Tue Jun 16 15:01:55 2026 -0400

    Fix freeing uninitialized memory in LDAP sort control parsing

    _php_ldap_control_from_array() allocated the sort_keys array with
    safe_emalloc() and only wrote its NULL terminator after the per-key loop
    finished. A sort key missing the "attr" entry makes the loop bail out
    early, leaving the array partially uninitialized; the failure cleanup
    then walks it as a NULL-terminated list and calls efree() on the
    uninitialized slots. Zero the array after allocation so the unwritten
    slots are NULL. Reachable from userland via the $controls argument of
    ldap_search() and the other control-taking LDAP functions.

    Closes GH-22342

diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index e6ea4d908af..2b3cb0e8a70 100644
--- a/ext/ldap/ldap.c
+++ b/ext/ldap/ldap.c
@@ -564,6 +564,7 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra

 			num_keys = zend_hash_num_elements(Z_ARRVAL_P(val));
 			sort_keys = safe_emalloc((num_keys+1), sizeof(LDAPSortKey*), 0);
+			memset(sort_keys, 0, (num_keys+1) * sizeof(LDAPSortKey*));
 			tmpstrings1 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
 			tmpstrings2 = safe_emalloc(num_keys, sizeof(zend_string*), 0);
 			num_tmpstrings1 = 0;
diff --git a/ext/ldap/tests/ldap_sort_control_missing_attr.phpt b/ext/ldap/tests/ldap_sort_control_missing_attr.phpt
new file mode 100644
index 00000000000..0d6c3921f00
--- /dev/null
+++ b/ext/ldap/tests/ldap_sort_control_missing_attr.phpt
@@ -0,0 +1,30 @@
+--TEST--
+ldap_search(): malformed sort control (sort key missing "attr") must not free uninitialized memory
+--EXTENSIONS--
+ldap
+--FILE--
+<?php
+// No server needed: the control array is validated before the search is sent.
+// A sort key missing "attr" makes php_ldap_control_from_array() bail mid-loop;
+// the failure cleanup must not walk/free the partially built sort_keys array.
+$ld = ldap_connect("ldap://127.0.0.1:389");
+
+try {
+    ldap_search($ld, "dc=example,dc=com", "(objectClass=*)", [], 0, -1, -1, LDAP_DEREF_NEVER, [
+        [
+            'oid' => LDAP_CONTROL_SORTREQUEST,
+            'value' => [
+                ['attr' => 'cn'],
+                ['reverse' => true],
+            ],
+        ],
+    ]);
+} catch (\ValueError $e) {
+    echo $e->getMessage(), "\n";
+}
+
+echo "ok\n";
+?>
+--EXPECT--
+ldap_search(): Sort key list must have an "attr" key
+ok