void throw_invalid_object_type(const Variant& p) { auto tv = p.asCell(); switch (tv->m_type) { case KindOfNull: case KindOfUninit: throw_null_pointer_exception(); case KindOfObject: throw_invalid_object_type(tv->m_data.pobj->getClassName().c_str()); case KindOfResource: throw_invalid_object_type(tv->m_data.pres->o_getClassName().c_str()); default: throw_invalid_object_type(tname(tv->m_type).c_str()); } }
MutableArrayIter Object::begin(Variant *key, Variant &val, CStrRef context /* = null_string */) const { if (!m_px) throw_null_pointer_exception(); return m_px->begin(key, val, context); }
ArrayIter Object::begin(CStrRef context /* = null_string */) const { if (!m_px) throw_null_pointer_exception(); return m_px->begin(context); }
Variant Object::o_getPublic(CStrRef propName, bool error /* = true */) const { if (!m_px) throw_null_pointer_exception(); return m_px->o_getPublic(propName, error); }
Variant Object::o_get(CStrRef propName, bool error /* = true */, CStrRef context /* = null_string */) const { if (!m_px) throw_null_pointer_exception(); return m_px->o_get(propName, error, context); }
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(';'); }
void throw_invalid_object_type(ObjectData* p) { if (!p) throw_null_pointer_exception(); throw_invalid_object_type(p->getClassName().c_str()); }