Skip to content

Commit

Permalink
[Fix] serialization ref problem with array
Browse files Browse the repository at this point in the history
Summary:
When we serialize array with ref, we still check whether the ArrayData
already exist in pointer map. If the same array happens to be serialized
by value earlier, it will be treated as a ref to that array. This
behavior is incorrect and can also manifest as an off-by-1 problem in
unserialization when counting the sequence number.

Test Plan:
make fast_tests

The newly added test case become:
a:3:{i:0;a:1:{i:0;i:123;}i:1;a:1:{i:0;i:123;}i:2;R:4;}
Without this diff:
a:3:{i:0;a:1:{i:0;i:123;}i:1;R:2;i:2;R:4;}

also tried dumping a huge stack in hphpd and verify the previous failed
case worked

Reviewers: mwilliams, myang

Reviewed By: myang

CC: gregschechte, andrewparoski, ps, mwilliams, qigao, myang

Differential Revision: 338346
  • Loading branch information
qigao authored and macvicar committed Oct 18, 2011
1 parent dea256f commit b1e3c15
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/runtime/base/array/array_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,13 +417,13 @@ void ArrayData::serializeImpl(VariableSerializer *serializer) const {
}

void ArrayData::serialize(VariableSerializer *serializer,
bool isObject /* = false */) const {
bool skipNestCheck /* = false */) const {
if (size() == 0) {
serializer->writeArrayHeader(this, 0);
serializer->writeArrayFooter(this);
return;
}
if (!isObject) {
if (!skipNestCheck) {
if (serializer->incNestedLevel((void*)this)) {
serializer->writeOverflow((void*)this);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/base/array/array_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class ArrayData : public Countable {
* specialized implementation, which is normally more optimized.
*/
void serialize(VariableSerializer *serializer,
bool isObject = false) const;
bool skipNestCheck = false) const;

virtual void dump();
virtual void dump(std::string &out);
Expand Down
9 changes: 6 additions & 3 deletions src/runtime/base/type_variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3603,7 +3603,8 @@ void Variant::setEvalScalar() const {
// output functions

void Variant::serialize(VariableSerializer *serializer,
bool isArrayKey /* = false */) const {
bool isArrayKey /* = false */,
bool skipNestCheck /* = false */) const {
if (m_type == KindOfVariant) {
// Ugly, but behavior is different for serialize
if (serializer->getType() == VariableSerializer::Serialize ||
Expand All @@ -3612,7 +3613,8 @@ void Variant::serialize(VariableSerializer *serializer,
if (serializer->incNestedLevel(m_data.pvar)) {
serializer->writeOverflow(m_data.pvar);
} else {
m_data.pvar->serialize(serializer, isArrayKey);
// Tell the inner variant to skip the nesting check for data inside
m_data.pvar->serialize(serializer, isArrayKey, true);
}
serializer->decNestedLevel(m_data.pvar);
} else {
Expand Down Expand Up @@ -3641,7 +3643,8 @@ void Variant::serialize(VariableSerializer *serializer,
break;
case KindOfArray:
ASSERT(!isArrayKey);
m_data.parr->serialize(serializer); break;
m_data.parr->serialize(serializer, skipNestCheck);
break;
case KindOfObject:
ASSERT(!isArrayKey);
m_data.pobj->serialize(serializer); break;
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/base/type_variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,8 @@ class Variant {
* Output functions
*/
void serialize(VariableSerializer *serializer,
bool isArrayKey = false) const;
bool isArrayKey = false,
bool skipNestCheck = false) const;
void unserialize(VariableUnserializer *unserializer);

/**
Expand Down
16 changes: 16 additions & 0 deletions src/test/test_code_run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ bool TestCodeRun::RunTests(const std::string &which) {
RUN_TEST(TestCopyProp);
RUN_TEST(TestParser);
RUN_TEST(TestTypeAssertions);
RUN_TEST(TestSerialize);

// PHP 5.3 features
RUN_TEST(TestVariableClassName);
Expand Down Expand Up @@ -18658,6 +18659,21 @@ bool TestCodeRun::TestCopyProp() {
return true;
}

bool TestCodeRun::TestSerialize() {
MVCR("<?php\n"
"function f() {\n"
" $a = array(123);\n"
" $b = $a;\n"
" $c = &$b;\n"
" $d = new stdClass();\n"
" $v = array(&$a, &$b, &$c, $d, $d);\n"
" $s = serialize($v);\n"
" echo $s;\n"
"}\n"
"f();\n");
return true;
}

bool TestCodeRun::TestVariableClassName() {
MVCRO(
"<?php\n"
Expand Down
1 change: 1 addition & 0 deletions src/test/test_code_run.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class TestCodeRun : public TestBase {
bool TestMaxInt();
bool TestParser();
bool TestTypeAssertions();
bool TestSerialize();

// PHP 5.3
bool TestVariableClassName();
Expand Down

0 comments on commit b1e3c15

Please sign in to comment.