Skip to content

Commit a25f6f8

Browse files
author
Yasuo Ohgaki
committed
Fixed Bug #66964 mb_convert_variables() cannot detect recursion
1 parent 8aad313 commit a25f6f8

File tree

2 files changed

+102
-12
lines changed

2 files changed

+102
-12
lines changed

ext/mbstring/mbstring.c

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3530,7 +3530,8 @@ PHP_FUNCTION(mb_convert_variables)
35303530
size_t elistsz;
35313531
const mbfl_encoding **elist;
35323532
char *to_enc;
3533-
void *ptmp;
3533+
void *ptmp;
3534+
int recursion_error = 0;
35343535

35353536
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ+", &to_enc, &to_enc_len, &zfrom_enc, &args, &argc) == FAILURE) {
35363537
return;
@@ -3593,6 +3594,11 @@ PHP_FUNCTION(mb_convert_variables)
35933594
target_hash = HASH_OF(*var);
35943595
if (target_hash != NULL) {
35953596
while (zend_hash_get_current_data(target_hash, (void **) &hash_entry) != FAILURE) {
3597+
if (++target_hash->nApplyCount > 1) {
3598+
--target_hash->nApplyCount;
3599+
recursion_error = 1;
3600+
goto detect_end;
3601+
}
35963602
zend_hash_move_forward(target_hash);
35973603
if (Z_TYPE_PP(hash_entry) == IS_ARRAY || Z_TYPE_PP(hash_entry) == IS_OBJECT) {
35983604
if (stack_level >= stack_max) {
@@ -3629,6 +3635,20 @@ PHP_FUNCTION(mb_convert_variables)
36293635
from_encoding = mbfl_encoding_detector_judge2(identd);
36303636
mbfl_encoding_detector_delete(identd);
36313637
}
3638+
if (recursion_error) {
3639+
while(stack_level-- && (var = stack[stack_level])) {
3640+
if (HASH_OF(*var)->nApplyCount > 1) {
3641+
HASH_OF(*var)->nApplyCount--;
3642+
}
3643+
}
3644+
efree(stack);
3645+
efree(args);
3646+
if (elist != NULL) {
3647+
efree((void *)elist);
3648+
}
3649+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot handle recursive references");
3650+
RETURN_FALSE;
3651+
}
36323652
efree(stack);
36333653

36343654
if (!from_encoding) {
@@ -3676,6 +3696,11 @@ PHP_FUNCTION(mb_convert_variables)
36763696
while (zend_hash_get_current_data(target_hash, (void **) &hash_entry) != FAILURE) {
36773697
zend_hash_move_forward(target_hash);
36783698
if (Z_TYPE_PP(hash_entry) == IS_ARRAY || Z_TYPE_PP(hash_entry) == IS_OBJECT) {
3699+
if (++(HASH_OF(*hash_entry)->nApplyCount) > 1) {
3700+
--(HASH_OF(*hash_entry)->nApplyCount);
3701+
recursion_error = 1;
3702+
goto conv_end;
3703+
}
36793704
if (stack_level >= stack_max) {
36803705
stack_max += PHP_MBSTR_STACK_BLOCK_SIZE;
36813706
ptmp = erealloc(stack, sizeof(zval **)*stack_max);
@@ -3684,7 +3709,6 @@ PHP_FUNCTION(mb_convert_variables)
36843709
stack[stack_level] = var;
36853710
stack_level++;
36863711
var = hash_entry;
3687-
SEPARATE_ZVAL(hash_entry);
36883712
target_hash = HASH_OF(*var);
36893713
if (target_hash != NULL) {
36903714
zend_hash_internal_pointer_reset(target_hash);
@@ -3701,25 +3725,38 @@ PHP_FUNCTION(mb_convert_variables)
37013725
} else {
37023726
zval_dtor(*hash_entry);
37033727
}
3704-
ZVAL_STRINGL(*hash_entry, (char *)ret->val, ret->len, 0);
3728+
ZVAL_STRINGL(*hash_entry, (char *)ret->val, ret->len, 0);
3729+
}
37053730
}
37063731
}
37073732
}
3708-
}
3709-
} else if (Z_TYPE_PP(var) == IS_STRING) {
3710-
string.val = (unsigned char *)Z_STRVAL_PP(var);
3711-
string.len = Z_STRLEN_PP(var);
3712-
ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3713-
if (ret != NULL) {
3714-
zval_dtor(*var);
3715-
ZVAL_STRINGL(*var, (char *)ret->val, ret->len, 0);
3733+
} else if (Z_TYPE_PP(var) == IS_STRING) {
3734+
string.val = (unsigned char *)Z_STRVAL_PP(var);
3735+
string.len = Z_STRLEN_PP(var);
3736+
ret = mbfl_buffer_converter_feed_result(convd, &string, &result);
3737+
if (ret != NULL) {
3738+
zval_dtor(*var);
3739+
ZVAL_STRINGL(*var, (char *)ret->val, ret->len, 0);
37163740
}
37173741
}
37183742
}
3719-
efree(stack);
37203743

3744+
conv_end:
37213745
MBSTRG(illegalchars) += mbfl_buffer_illegalchars(convd);
37223746
mbfl_buffer_converter_delete(convd);
3747+
3748+
if (recursion_error) {
3749+
while(stack_level-- && (var = stack[stack_level])) {
3750+
if (HASH_OF(*var)->nApplyCount > 1) {
3751+
HASH_OF(*var)->nApplyCount--;
3752+
}
3753+
}
3754+
efree(stack);
3755+
efree(args);
3756+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot handle recursive references");
3757+
RETURN_FALSE;
3758+
}
3759+
efree(stack);
37233760
}
37243761

37253762
efree(args);

ext/mbstring/tests/bug66964.phpt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
Bug #66964 (mb_convert_variables() cannot detect recursion)
3+
--SKIPIF--
4+
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
5+
--FILE--
6+
<?php
7+
$a[] = &$a;
8+
var_dump(mb_convert_variables('utf-8', 'auto', $a));
9+
var_dump(mb_convert_variables('utf-8', 'utf-8', $a));
10+
11+
unset($a);
12+
$a[] = '日本語テキスト';
13+
$a[] = '日本語テキスト';
14+
$a[] = '日本語テキスト';
15+
$a[] = '日本語テキスト';
16+
var_dump(mb_convert_variables('utf-8', 'utf-8', $a), $a);
17+
18+
$a[] = &$a;
19+
var_dump(mb_convert_variables('utf-8', 'utf-8', $a), $a);
20+
21+
?>
22+
--EXPECTF--
23+
Warning: mb_convert_variables(): %s on line %d
24+
bool(false)
25+
26+
Warning: mb_convert_variables(): %s on line %d
27+
bool(false)
28+
string(5) "UTF-8"
29+
array(4) {
30+
[0]=>
31+
string(21) "日本語テキスト"
32+
[1]=>
33+
string(21) "日本語テキスト"
34+
[2]=>
35+
string(21) "日本語テキスト"
36+
[3]=>
37+
string(21) "日本語テキスト"
38+
}
39+
40+
Warning: mb_convert_variables(): %s on line %d
41+
bool(false)
42+
array(5) {
43+
[0]=>
44+
string(21) "日本語テキスト"
45+
[1]=>
46+
string(21) "日本語テキスト"
47+
[2]=>
48+
string(21) "日本語テキスト"
49+
[3]=>
50+
string(21) "日本語テキスト"
51+
[4]=>
52+
*RECURSION*
53+
}

0 commit comments

Comments
 (0)