diff --git a/ext/standard/tests/serialize/serialize_as_undef.phpt b/ext/standard/tests/serialize/serialize_as_undef.phpt new file mode 100644 index 000000000000..77189d315669 --- /dev/null +++ b/ext/standard/tests/serialize/serialize_as_undef.phpt @@ -0,0 +1,24 @@ +--TEST-- +serialize() serializing array values as undefined +--FILE-- + true]) +echo ($s = serialize($x)), "\n"; +$sCopy = unserialize($s); +var_export($sCopy); +echo "\n"; +try { + $sCopy->x = 'invalid'; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +O:7:"NoSleep":1:{s:1:"x";U;} +NoSleep::__set_state(array( +)) +Cannot assign string to property NoSleep::$x of type int diff --git a/ext/standard/var.c b/ext/standard/var.c index e1bc4c5597f4..9c92e9192924 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -879,7 +879,22 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable zval *data; zend_ulong index; - ZEND_HASH_FOREACH_KEY_VAL_IND(ht, index, key, data) { + ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, data) { + if (Z_TYPE_P(data) == IS_INDIRECT) { + data = Z_INDIRECT_P(data); + } + if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) { + if (Z_TYPE_P(struc) != IS_OBJECT) { + continue; + } + if (!key) { + php_var_serialize_long(buf, index); + } else { + php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key)); + } + smart_str_appendl(buf, "U;", 2); + continue; + } if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) { continue; } @@ -1013,7 +1028,20 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ php_var_serialize_class_name(buf, &obj); smart_str_append_unsigned(buf, zend_array_count(Z_ARRVAL(retval))); smart_str_appendl(buf, ":{", 2); - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(retval), index, key, data) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(retval), index, key, data) { + if (Z_TYPE_P(_z) == IS_INDIRECT) { + _z = Z_INDIRECT_P(_z); + } + if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) { + // TODO: Only do this if different from the default? + if (!key) { + php_var_serialize_long(buf, index); + } else { + php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key)); + } + smart_str_appendl(buf, "U;", 2); + continue; + } if (!key) { php_var_serialize_long(buf, index); } else { @@ -1087,10 +1115,11 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_ myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE); /* count after serializing name, since php_var_serialize_class_name * changes the count if the variable is incomplete class */ - count = zend_array_count(myht); + count = zend_hash_num_elements(myht); if (count > 0 && incomplete_class) { --count; } + // Need to call a different helper from zend_array_count, I guess. php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash); zend_release_properties(myht); return; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index c395f3cb5ca2..0dcc9602586c 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -593,21 +593,31 @@ string_key: goto failure; } } + // TODO: Assert `d` is not IS_UNDEF + // Delete the created property if needed. if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) { zval_ptr_dtor(&key); goto failure; } - - if (UNEXPECTED(info)) { - if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { - zval_ptr_dtor(data); - ZVAL_UNDEF(data); - zval_dtor(&key); + if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) { + if (UNEXPECTED(!obj)) { + zend_error(E_WARNING, "Cannot unserialize undefined value in an array"); goto failure; } - if (Z_ISREF_P(data)) { - ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info); + // Don't perform type checks if this is undefined + // FIXME delete the newly set property if it isn't a declared property + } else { + if (UNEXPECTED(info)) { + if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { + zval_ptr_dtor(data); + ZVAL_UNDEF(data); + zval_dtor(&key); + goto failure; + } + if (Z_ISREF_P(data)) { + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info); + } } } @@ -852,6 +862,12 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key) return 1; } +"U;" { + *p = YYCURSOR; + ZVAL_UNDEF(rval); + return 1; +} + "b:0;" { *p = YYCURSOR; ZVAL_FALSE(rval);