Skip to content

Commit 27be667

Browse files
committed
Correctly parse array_slice() argument in call_user_func_array() opt
We should be treating this argument using the normal zpp rules, rather than performing a simple integer cast.
1 parent 7db146e commit 27be667

File tree

4 files changed

+88
-4
lines changed

4 files changed

+88
-4
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Type check in call_user_func_array() + array_slice() optimization
3+
--FILE--
4+
<?php
5+
6+
$array = [1, 2, 3];
7+
8+
try {
9+
$len = [];
10+
call_user_func_array('var_dump', array_slice($array, 0, $len));
11+
} catch (TypeError $e) {
12+
echo $e->getMessage(), "\n";
13+
}
14+
15+
$len = 2.0;
16+
call_user_func_array('var_dump', array_slice($array, 0, $len));
17+
18+
$len = null;
19+
call_user_func_array('var_dump', array_slice($array, 1, $len));
20+
21+
?>
22+
--EXPECT--
23+
array_slice(): Argument #3 ($length) must be of type ?int, array given
24+
int(1)
25+
int(2)
26+
int(2)
27+
int(3)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Type check in call_user_func_array() + array_slice() optimization (strict types)
3+
--FILE--
4+
<?php
5+
declare(strict_types=1);
6+
7+
$array = [1, 2, 3];
8+
9+
try {
10+
$len = [];
11+
call_user_func_array('var_dump', array_slice($array, 0, $len));
12+
} catch (TypeError $e) {
13+
echo $e->getMessage(), "\n";
14+
}
15+
16+
try {
17+
$len = 2.0;
18+
call_user_func_array('var_dump', array_slice($array, 0, $len));
19+
} catch (TypeError $e) {
20+
echo $e->getMessage(), "\n";
21+
}
22+
23+
$len = null;
24+
call_user_func_array('var_dump', array_slice($array, 1, $len));
25+
26+
?>
27+
--EXPECT--
28+
array_slice(): Argument #3 ($length) must be of type ?int, array given
29+
array_slice(): Argument #3 ($length) must be of type ?int, float given
30+
int(2)
31+
int(3)

Zend/zend_vm_def.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5227,10 +5227,23 @@ ZEND_VM_C_LABEL(send_array):
52275227
if (OP2_TYPE != IS_UNUSED) {
52285228
/* We don't need to handle named params in this case,
52295229
* because array_slice() is called with $preserve_keys == false. */
5230-
zval *op2 = GET_OP2_ZVAL_PTR(BP_VAR_R);
5230+
zval *op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
52315231
uint32_t skip = opline->extended_value;
52325232
uint32_t count = zend_hash_num_elements(ht);
5233-
zend_long len = zval_get_long(op2);
5233+
zend_long len;
5234+
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
5235+
len = Z_LVAL_P(op2);
5236+
} else if (Z_TYPE_P(op2) == IS_NULL) {
5237+
len = count - skip;
5238+
} else if (EX_USES_STRICT_TYPES()
5239+
|| !zend_parse_arg_long_weak(op2, &len, /* arg_num */ 3)) {
5240+
zend_type_error(
5241+
"array_slice(): Argument #3 ($length) must be of type ?int, %s given",
5242+
zend_zval_type_name(op2));
5243+
FREE_OP2();
5244+
FREE_OP1();
5245+
HANDLE_EXCEPTION();
5246+
}
52345247

52355248
if (len < 0) {
52365249
len += (zend_long)(count - skip);

Zend/zend_vm_execute.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,10 +2334,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
23342334
if (opline->op2_type != IS_UNUSED) {
23352335
/* We don't need to handle named params in this case,
23362336
* because array_slice() is called with $preserve_keys == false. */
2337-
zval *op2 = get_zval_ptr(opline->op2_type, opline->op2, BP_VAR_R);
2337+
zval *op2 = get_zval_ptr_deref(opline->op2_type, opline->op2, BP_VAR_R);
23382338
uint32_t skip = opline->extended_value;
23392339
uint32_t count = zend_hash_num_elements(ht);
2340-
zend_long len = zval_get_long(op2);
2340+
zend_long len;
2341+
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
2342+
len = Z_LVAL_P(op2);
2343+
} else if (Z_TYPE_P(op2) == IS_NULL) {
2344+
len = count - skip;
2345+
} else if (EX_USES_STRICT_TYPES()
2346+
|| !zend_parse_arg_long_weak(op2, &len, /* arg_num */ 3)) {
2347+
zend_type_error(
2348+
"array_slice(): Argument #3 ($length) must be of type ?int, %s given",
2349+
zend_zval_type_name(op2));
2350+
FREE_OP(opline->op2_type, opline->op2.var);
2351+
FREE_OP(opline->op1_type, opline->op1.var);
2352+
HANDLE_EXCEPTION();
2353+
}
23412354

23422355
if (len < 0) {
23432356
len += (zend_long)(count - skip);

0 commit comments

Comments
 (0)