Skip to content

Commit 726c96f

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Backport fix GH-17280: ldap_search() fails when $attributes array has holes
2 parents 9d04d41 + 26f3bec commit 726c96f

File tree

5 files changed

+82
-43
lines changed

5 files changed

+82
-43
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ PHP NEWS
6565
- Iconv:
6666
. Fixed bug GH-17047 (UAF on iconv filter failure). (nielsdos)
6767

68+
- LDAP:
69+
. Fixed bug GH-17280 (ldap_search() fails when $attributes array has holes).
70+
(nielsdos)
71+
6872
- LibXML:
6973
. Fixed bug GH-17223 (Memory leak in libxml encoding handling). (nielsdos)
7074

ext/ldap/ldap.c

+56-39
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,22 @@ static void ldap_result_entry_free_obj(zend_object *obj)
234234
} \
235235
}
236236

237+
static bool php_ldap_is_numerically_indexed_array(zend_array *arr)
238+
{
239+
if (zend_hash_num_elements(arr) == 0 || HT_IS_PACKED(arr)) {
240+
return true;
241+
}
242+
243+
zend_string *str_key;
244+
ZEND_HASH_MAP_FOREACH_STR_KEY(arr, str_key) {
245+
if (str_key) {
246+
return false;
247+
}
248+
} ZEND_HASH_FOREACH_END();
249+
250+
return false;
251+
}
252+
237253
/* {{{ Parse controls from and to arrays */
238254
static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array, int request)
239255
{
@@ -1473,20 +1489,22 @@ static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
14731489
num_attribs = zend_hash_num_elements(Z_ARRVAL_P(attrs));
14741490
ldap_attrs = safe_emalloc((num_attribs+1), sizeof(char *), 0);
14751491

1476-
for (i = 0; i<num_attribs; i++) {
1477-
if ((attr = zend_hash_index_find(Z_ARRVAL_P(attrs), i)) == NULL) {
1478-
php_error_docref(NULL, E_WARNING, "Array initialization wrong");
1479-
ret = 0;
1480-
goto cleanup;
1481-
}
1492+
if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(attrs))) {
1493+
php_error_docref(NULL, E_WARNING, "Argument #4 ($attributes) must be an array with numeric keys");
1494+
ret = 0;
1495+
goto cleanup;
1496+
}
14821497

1498+
i = 0;
1499+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(attrs), attr) {
14831500
convert_to_string(attr);
14841501
if (EG(exception)) {
14851502
ret = 0;
14861503
goto cleanup;
14871504
}
1488-
ldap_attrs[i] = Z_STRVAL_P(attr);
1489-
}
1505+
ldap_attrs[i++] = Z_STRVAL_P(attr);
1506+
} ZEND_HASH_FOREACH_END();
1507+
14901508
ldap_attrs[num_attribs] = NULL;
14911509
ZEND_FALLTHROUGH;
14921510
default:
@@ -2259,14 +2277,16 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
22592277
ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_P(value);
22602278
ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_P(value);
22612279
} else {
2262-
for (j = 0; j < num_values; j++) {
2263-
if ((ivalue = zend_hash_index_find(Z_ARRVAL_P(value), j)) == NULL) {
2264-
zend_argument_value_error(3, "must contain arrays with consecutive integer indices starting from 0");
2265-
num_berval[i] = j;
2266-
num_attribs = i + 1;
2267-
RETVAL_FALSE;
2268-
goto cleanup;
2269-
}
2280+
if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(value))) {
2281+
zend_argument_value_error(3, "must be an array with numeric keys");
2282+
RETVAL_FALSE;
2283+
num_berval[i] = 0;
2284+
num_attribs = i + 1;
2285+
goto cleanup;
2286+
}
2287+
2288+
j = 0;
2289+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(value), ivalue) {
22702290
convert_to_string(ivalue);
22712291
if (EG(exception)) {
22722292
num_berval[i] = j;
@@ -2277,7 +2297,8 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
22772297
ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
22782298
ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_P(ivalue);
22792299
ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(ivalue);
2280-
}
2300+
j++;
2301+
} ZEND_HASH_FOREACH_END();
22812302
}
22822303
ldap_mods[i]->mod_bvalues[num_values] = NULL;
22832304
zend_hash_move_forward(Z_ARRVAL_P(entry));
@@ -2545,7 +2566,7 @@ PHP_FUNCTION(ldap_modify_batch)
25452566
zval *fetched;
25462567
char *dn;
25472568
size_t dn_len;
2548-
int i, j, k;
2569+
int i, j;
25492570
int num_mods, num_modprops, num_modvals;
25502571
LDAPMod **ldap_mods;
25512572
LDAPControl **lserverctrls = NULL;
@@ -2605,12 +2626,14 @@ PHP_FUNCTION(ldap_modify_batch)
26052626

26062627
num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
26072628

2608-
for (i = 0; i < num_mods; i++) {
2609-
/* is the numbering consecutive? */
2610-
if ((fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i)) == NULL) {
2611-
zend_argument_value_error(3, "must have consecutive integer indices starting from 0");
2612-
RETURN_THROWS();
2613-
}
2629+
if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(mods))) {
2630+
zend_argument_value_error(3, "must be an array with numeric keys");
2631+
RETURN_THROWS();
2632+
}
2633+
2634+
i = 0;
2635+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(mods), fetched) {
2636+
ZVAL_DEREF(fetched);
26142637
mod = fetched;
26152638

26162639
/* is it an array? */
@@ -2708,19 +2731,10 @@ PHP_FUNCTION(ldap_modify_batch)
27082731
RETURN_THROWS();
27092732
}
27102733

2711-
/* are its keys integers? */
2712-
if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
2713-
zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be integer-indexed", get_active_function_name());
2734+
if (!php_ldap_is_numerically_indexed_array(Z_ARRVAL_P(modinfo))) {
2735+
zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must be an array with numeric keys", get_active_function_name());
27142736
RETURN_THROWS();
27152737
}
2716-
2717-
/* are the keys consecutive? */
2718-
for (k = 0; k < num_modvals; k++) {
2719-
if ((fetched = zend_hash_index_find(Z_ARRVAL_P(modinfo), k)) == NULL) {
2720-
zend_value_error("%s(): Option \"" LDAP_MODIFY_BATCH_VALUES "\" must have consecutive integer indices starting from 0", get_active_function_name());
2721-
RETURN_THROWS();
2722-
}
2723-
}
27242738
}
27252739

27262740
zend_hash_move_forward(Z_ARRVAL_P(mod));
@@ -2734,7 +2748,9 @@ PHP_FUNCTION(ldap_modify_batch)
27342748
zend_value_error("%s(): Required option \"" LDAP_MODIFY_BATCH_MODTYPE "\" is missing", get_active_function_name());
27352749
RETURN_THROWS();
27362750
}
2737-
}
2751+
2752+
i++;
2753+
} ZEND_HASH_FOREACH_END();
27382754
}
27392755
/* validation was successful */
27402756

@@ -2788,9 +2804,9 @@ PHP_FUNCTION(ldap_modify_batch)
27882804
ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0);
27892805

27902806
/* for each value */
2791-
for (j = 0; j < num_modvals; j++) {
2807+
j = 0;
2808+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(vals), fetched) {
27922809
/* fetch it */
2793-
fetched = zend_hash_index_find(Z_ARRVAL_P(vals), j);
27942810
modval = zval_get_string(fetched);
27952811
if (EG(exception)) {
27962812
RETVAL_FALSE;
@@ -2806,7 +2822,8 @@ PHP_FUNCTION(ldap_modify_batch)
28062822
ldap_mods[i]->mod_bvalues[j]->bv_len = ZSTR_LEN(modval);
28072823
ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(ZSTR_VAL(modval), ZSTR_LEN(modval));
28082824
zend_string_release(modval);
2809-
}
2825+
j++;
2826+
} ZEND_HASH_FOREACH_END();
28102827

28112828
/* NULL-terminate values */
28122829
ldap_mods[i]->mod_bvalues[num_modvals] = NULL;

ext/ldap/tests/gh17280.phpt

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
GH-17280 (ldap_search() fails when $attributes array has holes)
3+
--EXTENSIONS--
4+
ldap
5+
--FILE--
6+
<?php
7+
8+
/* We are assuming 3333 is not connectable */
9+
$ldap = ldap_connect('ldap://127.0.0.1:3333');
10+
11+
// Creating an array with a hole in it
12+
$attr = array_unique(['cn', 'uid', 'uid', 'mail']);
13+
var_dump(ldap_search($ldap, 'ou=people,dc=example,dc=com', 'uid=admin', $attr));
14+
15+
?>
16+
--EXPECTF--
17+
Warning: ldap_search(): Search: Can't contact LDAP server in %s on line %d
18+
bool(false)

ext/ldap/tests/ldap_add_error.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ try {
4343
ldap_add($link, "dc=my-domain2,dc=com", array(
4444
"objectClass" => array(
4545
0 => "top",
46-
2 => "dcObject",
46+
"x" => "dcObject",
4747
5 => "organization"),
4848
"dc" => "my-domain",
4949
"o" => "my-domain",
@@ -104,7 +104,7 @@ Warning: ldap_add(): Add: Already exists in %s on line %d
104104
bool(false)
105105
string(14) "Already exists"
106106
int(68)
107-
ldap_add(): Argument #3 ($entry) must contain arrays with consecutive integer indices starting from 0
107+
ldap_add(): Argument #3 ($entry) must be an array with numeric keys
108108

109109
Warning: ldap_add(): Add: Undefined attribute type in %s on line %d
110110
bool(false)

ext/ldap/tests/ldap_search_error.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ $filter = "(dc=*)";
1919
$result = ldap_search($link, $dn, $filter);
2020
var_dump($result);
2121

22-
$result = ldap_search($link, $dn, $filter, array(1 => 'top'));
22+
$result = ldap_search($link, $dn, $filter, array('foo' => 'top'));
2323
var_dump($result);
2424

2525
try {
@@ -57,7 +57,7 @@ try {
5757
Warning: ldap_search(): Search: No such object in %s on line %d
5858
bool(false)
5959

60-
Warning: ldap_search(): Array initialization wrong in %s on line %d
60+
Warning: ldap_search(): Argument #4 ($attributes) must be an array with numeric keys in %s on line %d
6161
bool(false)
6262
ldap_search(): Argument #1 ($ldap) must not be empty
6363
ldap_search(): Argument #2 ($base) must have the same number of elements as the links array

0 commit comments

Comments
 (0)