Skip to content

Commit

Permalink
Fix inference for INIT_ARRAY with illegal offset type
Browse files Browse the repository at this point in the history
Extract assign_dim_array_result_type() helper that can be reused
for INIT_ARRAY and implements all this logic correctly.

Fixes oss-fuzz 5156868775870464.
  • Loading branch information
nikic committed Sep 29, 2021
1 parent 9346da8 commit 944d653
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 55 deletions.
93 changes: 38 additions & 55 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -2085,6 +2085,41 @@ ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int w
return tmp;
}

static uint32_t assign_dim_array_result_type(
uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
uint32_t tmp = 0;
/* Only add key type if we have a value type. We want to maintain the invariant that a
* key type exists iff a value type exists even in dead code that may use empty types. */
if (value_type & (MAY_BE_ANY|MAY_BE_UNDEF)) {
if (value_type & MAY_BE_UNDEF) {
value_type |= MAY_BE_NULL;
}
if (dim_op_type == IS_UNUSED) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
} else {
if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
if (dim_type & MAY_BE_STRING) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
if (dim_op_type != IS_CONST) {
// FIXME: numeric string
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
}
if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
}
}
}
/* Only add value type if we have a key type. It might be that the key type is illegal
* for arrays. */
if (tmp & MAY_BE_ARRAY_KEY_ANY) {
tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
}
return tmp;
}

static uint32_t assign_dim_result_type(
uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) {
uint32_t tmp = arr_type & ~(MAY_BE_RC1|MAY_BE_RCN);
Expand All @@ -2100,35 +2135,7 @@ static uint32_t assign_dim_result_type(
tmp |= MAY_BE_RC1 | MAY_BE_RCN;
}
if (tmp & MAY_BE_ARRAY) {
/* Only add key type if we have a value type. We want to maintain the invariant that a
* key type exists iff a value type exists even in dead code that may use empty types. */
if (value_type & (MAY_BE_ANY|MAY_BE_UNDEF)) {
if (value_type & MAY_BE_UNDEF) {
value_type |= MAY_BE_NULL;
}
if (dim_op_type == IS_UNUSED) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
} else {
if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
if (dim_type & MAY_BE_STRING) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
if (dim_op_type != IS_CONST) {
// FIXME: numeric string
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
}
if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
}
}
}
/* Only add value type if we have a key type. It might be that the key type is illegal
* for arrays. */
if (tmp & MAY_BE_ARRAY_KEY_ANY) {
tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
}
tmp |= assign_dim_array_result_type(arr_type, dim_type, value_type, dim_op_type);
}
return tmp;
}
Expand Down Expand Up @@ -3181,41 +3188,17 @@ static zend_always_inline int _zend_update_type_info(
}
if (ssa_op->result_def >= 0) {
uint32_t arr_type;

if (opline->opcode == ZEND_INIT_ARRAY) {
arr_type = 0;
} else {
arr_type = RES_USE_INFO();
}
tmp = MAY_BE_RC1|MAY_BE_ARRAY;
if (ssa_op->result_use >= 0) {
tmp |= ssa_var_info[ssa_op->result_use].type;
}
tmp = MAY_BE_RC1|MAY_BE_ARRAY|arr_type;
if (opline->op1_type != IS_UNUSED) {
tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
if (t1 & MAY_BE_UNDEF) {
tmp |= MAY_BE_ARRAY_OF_NULL;
}
tmp |= assign_dim_array_result_type(arr_type, t2, t1, opline->op2_type);
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF;
}
if (opline->op2_type == IS_UNUSED) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
} else {
if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) {
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
if (t2 & (MAY_BE_STRING)) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
if (opline->op2_type != IS_CONST) {
// FIXME: numeric string
tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG;
}
}
if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) {
tmp |= MAY_BE_ARRAY_KEY_STRING;
}
}
}
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
}
Expand Down
15 changes: 15 additions & 0 deletions Zend/tests/init_array_illegal_offset_type.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
INIT_ARRAY with illegal offset type
--FILE--
<?php
function test() {
return [new stdClass => null];
}
try {
test();
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Illegal offset type

0 comments on commit 944d653

Please sign in to comment.