Skip to content

Commit e09f2ac

Browse files
committed
ext/ldap: Use FCC for rebind_proc callback
1 parent e4ad271 commit e09f2ac

File tree

4 files changed

+98
-75
lines changed

4 files changed

+98
-75
lines changed

ext/ldap/ldap.c

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void ldap_memvfree(void **v)
7373
typedef struct {
7474
LDAP *link;
7575
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
76-
zval rebindproc;
76+
zend_fcall_info_cache rebind_proc_fcc;
7777
#endif
7878
zend_object std;
7979
} ldap_linkdata;
@@ -131,7 +131,9 @@ static void ldap_link_free(ldap_linkdata *ld)
131131
ld->link = NULL;
132132

133133
#if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC)
134-
zval_ptr_dtor(&ld->rebindproc);
134+
if (ZEND_FCC_INITIALIZED(ld->rebind_proc_fcc)) {
135+
zend_fcc_dtor(&ld->rebind_proc_fcc);
136+
}
135137
#endif
136138

137139
LDAPG(num_links)--;
@@ -3711,19 +3713,20 @@ int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req, ber_int_t msgi
37113713
}
37123714

37133715
/* link exists and callback set? */
3714-
if (Z_ISUNDEF(ld->rebindproc)) {
3716+
if (!ZEND_FCC_INITIALIZED(ld->rebind_proc_fcc)) {
37153717
php_error_docref(NULL, E_WARNING, "No callback set");
37163718
return LDAP_OTHER;
37173719
}
37183720

37193721
/* callback */
37203722
ZVAL_COPY_VALUE(&cb_args[0], cb_link);
37213723
ZVAL_STRING(&cb_args[1], url);
3722-
if (call_user_function(EG(function_table), NULL, &ld->rebindproc, &cb_retval, 2, cb_args) == SUCCESS && !Z_ISUNDEF(cb_retval)) {
3724+
zend_call_known_fcc(&ld->rebind_proc_fcc, &cb_retval, 2, cb_args, NULL);
3725+
if (EXPECTED(!Z_ISUNDEF(cb_retval))) {
3726+
// TODO Use zval_try_get_long()
37233727
retval = zval_get_long(&cb_retval);
37243728
zval_ptr_dtor(&cb_retval);
37253729
} else {
3726-
php_error_docref(NULL, E_WARNING, "rebind_proc PHP callback failed");
37273730
retval = LDAP_OTHER;
37283731
}
37293732
zval_ptr_dtor(&cb_args[1]);
@@ -3735,35 +3738,38 @@ int _ldap_rebind_proc(LDAP *ldap, const char *url, ber_tag_t req, ber_int_t msgi
37353738
PHP_FUNCTION(ldap_set_rebind_proc)
37363739
{
37373740
zval *link;
3738-
zend_fcall_info fci;
3739-
zend_fcall_info_cache fcc;
3741+
zend_fcall_info dummy_fci;
3742+
zend_fcall_info_cache fcc = empty_fcall_info_cache;
37403743
ldap_linkdata *ld;
37413744

3742-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of!", &link, ldap_link_ce, &fci, &fcc) == FAILURE) {
3745+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "OF!", &link, ldap_link_ce, &dummy_fci, &fcc) == FAILURE) {
37433746
RETURN_THROWS();
37443747
}
37453748

37463749
ld = Z_LDAP_LINK_P(link);
3747-
VERIFY_LDAP_LINK_CONNECTED(ld);
3748-
3749-
if (!ZEND_FCI_INITIALIZED(fci)) {
3750-
/* unregister rebind procedure */
3751-
if (!Z_ISUNDEF(ld->rebindproc)) {
3752-
zval_ptr_dtor(&ld->rebindproc);
3753-
ZVAL_UNDEF(&ld->rebindproc);
3754-
ldap_set_rebind_proc(ld->link, NULL, NULL);
3755-
}
3756-
RETURN_TRUE;
3750+
/* Inline VERIFY_LDAP_LINK_CONNECTED(ld); as we need to free trampoline */
3751+
if (!ld->link) {
3752+
zend_release_fcall_info_cache(&fcc);
3753+
zend_throw_error(NULL, "LDAP connection has already been closed");
3754+
RETURN_THROWS();
37573755
}
37583756

3759-
/* register rebind procedure */
3760-
if (Z_ISUNDEF(ld->rebindproc)) {
3757+
/* Free old FCC */
3758+
if (!ZEND_FCC_INITIALIZED(ld->rebind_proc_fcc)) {
3759+
zend_fcc_dtor(&ld->rebind_proc_fcc);
3760+
memcpy(&ld->rebind_proc_fcc, &empty_fcall_info_cache, sizeof(zend_fcall_info_cache));
3761+
}
3762+
if (ZEND_FCC_INITIALIZED(fcc)) {
3763+
/* register rebind procedure */
37613764
ldap_set_rebind_proc(ld->link, _ldap_rebind_proc, (void *) link);
3765+
zend_fcc_dup(&ld->rebind_proc_fcc, &fcc);
3766+
/* Clear potential trampoline */
3767+
zend_release_fcall_info_cache(&fcc);
37623768
} else {
3763-
zval_ptr_dtor(&ld->rebindproc);
3764-
}
3769+
/* unregister rebind procedure */
3770+
ldap_set_rebind_proc(ld->link, NULL, NULL);
3771+
}
37653772

3766-
ZVAL_COPY(&ld->rebindproc, &fci.function_name);
37673773
RETURN_TRUE;
37683774
}
37693775
/* }}} */

ext/ldap/tests/ldap_set_rebind_proc_basic.phpt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,27 @@ require_once('skipifbindfailure.inc');
1414
<?php
1515
require "connect.inc";
1616

17-
function rebind_proc ($ds, $ldap_url) {
18-
global $user;
19-
global $passwd;
20-
global $protocol_version;
17+
function rebind_proc ($ldap, $referral) {
18+
global $user;
19+
global $passwd;
20+
global $protocol_version;
2121

22-
// required by most modern LDAP servers, use LDAPv3
23-
ldap_set_option($a, LDAP_OPT_PROTOCOL_VERSION, $protocol_version);
22+
// required by most modern LDAP servers, use LDAPv3
23+
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, $protocol_version);
2424

25-
if (!ldap_bind($a, $user, $passwd)) {
25+
if (!ldap_bind($ldap, $user, $passwd)) {
26+
// Failure
2627
print "Cannot bind";
27-
}
28+
return 1;
29+
}
30+
// Success
31+
return 0;
2832
}
2933

3034
$link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version);
35+
ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, 3);
36+
ldap_set_option($link, LDAP_OPT_REFERRALS, true);
37+
3138
var_dump(ldap_set_rebind_proc($link, "rebind_proc"));
3239
var_dump(ldap_set_rebind_proc($link, null));
3340
?>

ext/ldap/tests/ldap_set_rebind_proc_error.phpt

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
ldap_set_rebind_proc() with a trampoline
3+
--EXTENSIONS--
4+
ldap
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists('ldap_set_rebind_proc')) die("skip ldap_set_rebind_proc() not available");
8+
require_once('skipifbindfailure.inc');
9+
?>
10+
--FILE--
11+
<?php
12+
require "connect.inc";
13+
14+
class TrampolineTest {
15+
public function __construct(private $user, private $password, private $protocol_version) {}
16+
public function __call(string $name, array $arguments) {
17+
echo 'Trampoline for ', $name, PHP_EOL;
18+
var_dump(count($arguments));
19+
if ($name === 'trampolineThrow') {
20+
throw new Exception('boo');
21+
}
22+
if ($name === 'trampolineWrongType') {
23+
return ['not an int'];
24+
}
25+
// required by most modern LDAP servers, use LDAPv3
26+
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, $this->$protocol_version);
27+
if (!ldap_bind($ldap, $this->$user, $this->$passwd)) {
28+
// Failure
29+
print "Cannot bind";
30+
return 1;
31+
}
32+
// Success
33+
return 0;
34+
}
35+
}
36+
$o = new TrampolineTest();
37+
$callback = [$o, 'trampoline'];
38+
$callbackThrow = [$o, 'trampolineThrow'];
39+
$callbackWrongType = [$o, 'trampolineWrongType'];
40+
41+
$link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version);
42+
var_dump(ldap_set_rebind_proc($link, $callback));
43+
var_dump(ldap_set_rebind_proc($link, null));
44+
var_dump(ldap_set_rebind_proc($link, $callbackThrow));
45+
var_dump(ldap_set_rebind_proc($link, null));
46+
var_dump(ldap_set_rebind_proc($link, $callbackWrongType));
47+
48+
var_dump(ldap_unbind($link));
49+
var_dump(ldap_set_rebind_proc($link, $callback));
50+
51+
?>
52+
--EXPECT--
53+
bool(true)
54+
bool(true)

0 commit comments

Comments
 (0)