bool Condition::unserialize(PropStream& propStream) { uint8_t attr_type; while (propStream.read<uint8_t>(attr_type) && attr_type != CONDITIONATTR_END) { if (!unserializeProp(static_cast<ConditionAttr_t>(attr_type), propStream)) { return false; } } return true; }
bool Condition::unserialize(PropStream& propStream) { uint8_t attr_type; while(propStream.GET_UCHAR(attr_type) && attr_type != CONDITIONATTR_END) { if(!unserializeProp((ConditionAttr_t)attr_type, propStream)) return false; } return true; }
void Variant::unserialize(VariableUnserializer *uns, Uns::Mode mode /* = Uns::Mode::Value */) { // NOTE: If you make changes to how serialization and unserialization work, // make sure to update the reserialize() method in "runtime/ext/ext_apc.cpp" // and to update test_apc_reserialize() in "test/ext/test_ext_apc.cpp". char type, sep; type = uns->readChar(); sep = uns->readChar(); if (type != 'R') { uns->add(this, mode); } if (type == 'N') { if (sep != ';') throw Exception("Expected ';' but got '%c'", sep); setNull(); // NULL *IS* the value, without we get undefined warnings return; } if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } switch (type) { case 'r': { int64_t id = uns->readInt(); Variant *v = uns->getByVal(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } operator=(*v); } break; case 'R': { int64_t id = uns->readInt(); Variant *v = uns->getByRef(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } assignRef(*v); } break; case 'b': { int64_t v = uns->readInt(); operator=((bool)v); } break; case 'i': { int64_t v = uns->readInt(); operator=(v); } break; case 'd': { double v; char ch = uns->peek(); bool negative = false; char buf[4]; if (ch == '-') { negative = true; ch = uns->readChar(); ch = uns->peek(); } if (ch == 'I') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "INF")) { throw Exception("Expected 'INF' but got '%s'", buf); } v = atof("inf"); } else if (ch == 'N') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "NAN")) { throw Exception("Expected 'NAN' but got '%s'", buf); } v = atof("nan"); } else { v = uns->readDouble(); } operator=(negative ? -v : v); } break; case 's': { String v; v.unserialize(uns); operator=(v); } break; case 'S': if (uns->getType() == VariableUnserializer::Type::APCSerialize) { union { char buf[8]; StringData *sd; } u; uns->read(u.buf, 8); operator=(u.sd); } else { throw Exception("Unknown type '%c'", type); } break; case 'a': { Array v = Array::Create(); v.unserialize(uns); operator=(v); return; // array has '}' terminating } break; case 'L': { int64_t id = uns->readInt(); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } String rsrcName; rsrcName.unserialize(uns); sep = uns->readChar(); if (sep != '{') { throw Exception("Expected '{' but got '%c'", sep); } sep = uns->readChar(); if (sep != '}') { throw Exception("Expected '}' but got '%c'", sep); } DummyResource* rsrc = NEWOBJ(DummyResource); rsrc->o_setResourceId(id); rsrc->m_class_name = rsrcName; operator=(rsrc); return; // resource has '}' terminating } break; case 'O': case 'V': case 'K': { String clsName; clsName.unserialize(uns); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } int64_t size = uns->readInt(); char sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } sep = uns->readChar(); if (sep != '{') { throw Exception("Expected '{' but got '%c'", sep); } const bool allowObjectFormatForCollections = true; Class* cls; // If we are potentially dealing with a collection, we need to try to // load the collection class under an alternate name so that we can // deserialize data that was serialized before the migration of // collections to the HH namespace. if (type != 'O') { // Collections are CPP builtins; don't attempt to autoload cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } } else if (allowObjectFormatForCollections) { // In order to support the legacy {O|V}:{Set|Vector|Map} // serialization, we defer autoloading until we know that there's // no alternate (builtin) collection class. cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } if (!cls) { cls = Unit::loadClass(clsName.get()); // with autoloading } } else { cls = Unit::loadClass(clsName.get()); // with autoloading } Object obj; if (RuntimeOption::UnserializationWhitelistCheck && (type == 'O') && !uns->isWhitelistedClass(clsName)) { const char* err_msg = "The object being unserialized with class name '%s' " "is not in the given whitelist. " "See http://fburl.com/SafeSerializable for more detail"; if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) { raise_warning(err_msg, clsName.c_str()); } else { raise_error(err_msg, clsName.c_str()); } } if (cls) { // Only unserialize CPP extension types which can actually // support it. Otherwise, we risk creating a CPP object // without having it initialized completely. if (cls->instanceCtor() && !cls->isCppSerializable()) { obj = ObjectData::newInstance( SystemLib::s___PHP_Unserializable_ClassClass); obj->o_set(s_PHP_Unserializable_Class_Name, clsName); } else { obj = ObjectData::newInstance(cls); if (UNLIKELY(cls == c_Pair::classof() && size != 2)) { throw Exception("Pair objects must have exactly 2 elements"); } } } else { obj = ObjectData::newInstance( SystemLib::s___PHP_Incomplete_ClassClass); obj->o_set(s_PHP_Incomplete_Class_Name, clsName); } operator=(obj); if (size > 0) { if (type == 'O') { // Collections are not allowed if (obj->isCollection()) { if (size > 0) { throw Exception("%s does not support the 'O' serialization " "format", clsName.data()); } // Be lax and tolerate the 'O' serialization format for collection // classes if there are 0 properties. raise_warning("%s does not support the 'O' serialization " "format", clsName.data()); } /* Count backwards so that i is the number of properties remaining (to be used as an estimate for the total number of dynamic properties when we see the first dynamic prop). see getVariantPtr */ for (int64_t i = size; i--; ) { String key = uns->unserializeKey().toString(); int ksize = key.size(); const char *kdata = key.data(); int subLen = 0; if (kdata[0] == '\0') { if (UNLIKELY(!ksize)) { throw EmptyObjectPropertyException(); } // private or protected subLen = strlen(kdata + 1) + 2; if (UNLIKELY(subLen >= ksize)) { if (subLen == ksize) { throw EmptyObjectPropertyException(); } else { throw Exception("Mangled private object property"); } } String k(kdata + subLen, ksize - subLen, CopyString); Class* ctx = (Class*)-1; if (kdata[1] != '*') { ctx = Unit::lookupClass( String(kdata + 1, subLen - 2, CopyString).get()); } unserializeProp(uns, obj.get(), k, ctx, key, i + 1); } else { unserializeProp(uns, obj.get(), key, nullptr, key, i + 1); } } } else { assert(type == 'V' || type == 'K'); if (!obj->isCollection()) { throw Exception("%s is not a collection class", clsName.data()); } collectionUnserialize(obj.get(), uns, size, type); } } sep = uns->readChar(); if (sep != '}') { throw Exception("Expected '}' but got '%c'", sep); } obj->invokeWakeup(); return; // object has '}' terminating } break; case 'C': { String clsName; clsName.unserialize(uns); sep = uns->readChar(); if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } String serialized; serialized.unserialize(uns, '{', '}'); Object obj; try { obj = create_object_only(clsName); } catch (ClassNotFoundException &e) { if (!uns->allowUnknownSerializableClass()) { throw; } obj = create_object_only(s_PHP_Incomplete_Class); obj->o_set(s_PHP_Incomplete_Class_Name, clsName); obj->o_set("serialized", serialized); } if (!obj->instanceof(SystemLib::s_SerializableClass)) { raise_warning("Class %s has no unserializer", obj->o_getClassName().data()); } else { obj->o_invoke_few_args(s_unserialize, 1, serialized); obj.get()->clearNoDestruct(); } operator=(obj); return; // object has '}' terminating } break; default: throw Exception("Unknown type '%c'", type); } sep = uns->readChar(); if (sep != ';') { throw Exception("Expected ';' but got '%c'", sep); } }
void unserializeVariant(Variant& self, VariableUnserializer *uns, UnserializeMode mode /* = UnserializeMode::Value */) { // NOTE: If you make changes to how serialization and unserialization work, // make sure to update the reserialize() method in "runtime/ext/ext_apc.cpp" // and to update test_apc_reserialize() in "test/ext/test_ext_apc.cpp". char type = uns->readChar(); char sep = uns->readChar(); if (type != 'R') { uns->add(&self, mode); } if (type == 'N') { if (sep != ';') throw Exception("Expected ';' but got '%c'", sep); self.setNull(); // NULL *IS* the value, without we get undefined warnings return; } if (sep != ':') { throw Exception("Expected ':' but got '%c'", sep); } switch (type) { case 'r': { int64_t id = uns->readInt(); Variant *v = uns->getByVal(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } self = *v; } break; case 'R': { int64_t id = uns->readInt(); Variant *v = uns->getByRef(id); if (v == nullptr) { throw Exception("Id %" PRId64 " out of range", id); } self.assignRef(*v); } break; case 'b': { int64_t v = uns->readInt(); self = (bool)v; } break; case 'i': { int64_t v = uns->readInt(); self = v; } break; case 'd': { double v; char ch = uns->peek(); bool negative = false; char buf[4]; if (ch == '-') { negative = true; ch = uns->readChar(); ch = uns->peek(); } if (ch == 'I') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "INF")) { throw Exception("Expected 'INF' but got '%s'", buf); } v = atof("inf"); } else if (ch == 'N') { uns->read(buf, 3); buf[3] = '\0'; if (strcmp(buf, "NAN")) { throw Exception("Expected 'NAN' but got '%s'", buf); } v = atof("nan"); } else { v = uns->readDouble(); } self = negative ? -v : v; } break; case 's': { String v; v.unserialize(uns); self = std::move(v); if (!uns->endOfBuffer()) { // Semicolon *should* always be required, // but PHP's implementation allows omitting it // and still functioning. // Worse, it throws it away without any check. // So we'll do the same. Sigh. uns->readChar(); } } return; case 'S': if (uns->type() == VariableUnserializer::Type::APCSerialize) { union { char buf[8]; StringData *sd; } u; uns->read(u.buf, 8); self = u.sd; } else { throw Exception("Unknown type '%c'", type); } break; case 'a': { // Check stack depth to avoid overflow. check_recursion_throw(); auto v = Array::Create(); v.unserialize(uns); self = std::move(v); } return; // array has '}' terminating case 'L': { int64_t id = uns->readInt(); uns->expectChar(':'); String rsrcName; rsrcName.unserialize(uns); uns->expectChar('{'); uns->expectChar('}'); auto rsrc = makeSmartPtr<DummyResource>(); rsrc->o_setResourceId(id); rsrc->m_class_name = rsrcName; self = std::move(rsrc); } return; // resource has '}' terminating case 'O': case 'V': case 'K': { String clsName; clsName.unserialize(uns); uns->expectChar(':'); int64_t size = uns->readInt(); uns->expectChar(':'); uns->expectChar('{'); const bool allowObjectFormatForCollections = true; Class* cls; // If we are potentially dealing with a collection, we need to try to // load the collection class under an alternate name so that we can // deserialize data that was serialized before the migration of // collections to the HH namespace. if (type != 'O') { // Collections are CPP builtins; don't attempt to autoload cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } } else if (allowObjectFormatForCollections) { // In order to support the legacy {O|V}:{Set|Vector|Map} // serialization, we defer autoloading until we know that there's // no alternate (builtin) collection class. cls = Unit::getClass(clsName.get(), /* autoload */ false); if (!cls) { cls = tryAlternateCollectionClass(clsName.get()); } if (!cls) { cls = Unit::loadClass(clsName.get()); // with autoloading } } else { cls = Unit::loadClass(clsName.get()); // with autoloading } Object obj; if (RuntimeOption::UnserializationWhitelistCheck && (type == 'O') && !uns->isWhitelistedClass(clsName)) { const char* err_msg = "The object being unserialized with class name '%s' " "is not in the given whitelist. " "See http://fburl.com/SafeSerializable for more detail"; if (RuntimeOption::UnserializationWhitelistCheckWarningOnly) { raise_warning(err_msg, clsName.c_str()); } else { raise_error(err_msg, clsName.c_str()); } } if (cls) { // Only unserialize CPP extension types which can actually // support it. Otherwise, we risk creating a CPP object // without having it initialized completely. if (cls->instanceCtor() && !cls->isCppSerializable()) { assert(obj.isNull()); throw_null_pointer_exception(); } else { obj = Object{cls}; if (UNLIKELY(collections::isType(cls, CollectionType::Pair) && (size != 2))) { throw Exception("Pair objects must have exactly 2 elements"); } } } else { obj = Object{SystemLib::s___PHP_Incomplete_ClassClass}; obj->o_set(s_PHP_Incomplete_Class_Name, clsName); } assert(!obj.isNull()); self = obj; if (size > 0) { // Check stack depth to avoid overflow. check_recursion_throw(); if (type == 'O') { // Collections are not allowed if (obj->isCollection()) { throw Exception("%s does not support the 'O' serialization " "format", clsName.data()); } Variant serializedNativeData = init_null(); bool hasSerializedNativeData = false; /* Count backwards so that i is the number of properties remaining (to be used as an estimate for the total number of dynamic properties when we see the first dynamic prop). see getVariantPtr */ for (int64_t i = size; i--; ) { Variant v; unserializeVariant(v, uns, UnserializeMode::Key); String key = v.toString(); int ksize = key.size(); const char *kdata = key.data(); int subLen = 0; if (key == ObjectData::s_serializedNativeDataKey) { unserializeVariant(serializedNativeData, uns); hasSerializedNativeData = true; } else if (kdata[0] == '\0') { if (UNLIKELY(!ksize)) { raise_error("Cannot access empty property"); } // private or protected subLen = strlen(kdata + 1) + 2; if (UNLIKELY(subLen >= ksize)) { if (subLen == ksize) { raise_error("Cannot access empty property"); } else { throw Exception("Mangled private object property"); } } String k(kdata + subLen, ksize - subLen, CopyString); Class* ctx = (Class*)-1; if (kdata[1] != '*') { ctx = Unit::lookupClass( String(kdata + 1, subLen - 2, CopyString).get()); } unserializeProp(uns, obj.get(), k, ctx, key, i + 1); } else { unserializeProp(uns, obj.get(), key, nullptr, key, i + 1); } if (i > 0) { auto lastChar = uns->peekBack(); if ((lastChar != ';') && (lastChar != '}')) { throw Exception("Object property not terminated properly"); } } } // nativeDataWakeup is called last to ensure that all properties are // already unserialized. We also ensure that nativeDataWakeup is // invoked regardless of whether or not serialized native data exists // within the serialized content. if (obj->getAttribute(ObjectData::HasNativeData) && obj->getVMClass()->getNativeDataInfo()->isSerializable()) { Native::nativeDataWakeup(obj.get(), serializedNativeData); } else if (hasSerializedNativeData) { raise_warning("%s does not expect any serialized native data.", clsName.data()); } } else { assert(type == 'V' || type == 'K'); if (!obj->isCollection()) { throw Exception("%s is not a collection class", clsName.data()); } collections::unserialize(obj.get(), uns, size, type); } } uns->expectChar('}'); if (uns->type() != VariableUnserializer::Type::DebuggerSerialize || (cls && cls->instanceCtor() && cls->isCppSerializable())) { // Don't call wakeup when unserializing for the debugger, except for // natively implemented classes. obj->invokeWakeup(); } check_request_surprise_unlikely(); } return; // object has '}' terminating case 'C': { if (uns->type() == VariableUnserializer::Type::DebuggerSerialize) { raise_error("Debugger shouldn't call custom unserialize method"); } String clsName; clsName.unserialize(uns); uns->expectChar(':'); String serialized; serialized.unserialize(uns, '{', '}'); auto const obj = [&]() -> Object { if (auto const cls = Unit::loadClass(clsName.get())) { return Object::attach(g_context->createObject(cls, init_null_variant, false /* init */)); } if (!uns->allowUnknownSerializableClass()) { raise_error("unknown class %s", clsName.data()); } Object ret = create_object_only(s_PHP_Incomplete_Class); ret->o_set(s_PHP_Incomplete_Class_Name, clsName); ret->o_set("serialized", serialized); return ret; }(); if (!obj->instanceof(SystemLib::s_SerializableClass)) { raise_warning("Class %s has no unserializer", obj->getClassName().data()); } else { obj->o_invoke_few_args(s_unserialize, 1, serialized); obj.get()->clearNoDestruct(); } self = std::move(obj); } return; // object has '}' terminating default: throw Exception("Unknown type '%c'", type); } uns->expectChar(';'); }