示例#1
0
APCHandle* ConcurrentTableSharedStore::unserialize(const String& key,
                                                   StoreValue* sval) {
  auto const sAddr = sval->data.right();
  assert(sAddr != nullptr);

  try {
    auto const sType =
      apcExtension::EnableApcSerialize
        ? VariableUnserializer::Type::APCSerialize
        : VariableUnserializer::Type::Serialize;

    VariableUnserializer vu(sAddr, sval->getSerializedSize(), sType);
    Variant v;
    unserializeVariant(v, &vu);
    auto const pair = APCHandle::Create(v, sval->isSerializedObj());
    sval->data = pair.handle;
    sval->dataSize = pair.size;
    APCStats::getAPCStats().addAPCValue(pair.handle, pair.size, true);
    return pair.handle;
  } catch (ResourceExceededException&) {
    throw;
  } catch (Exception& e) {
    raise_notice("APC Primed fetch failed: key %s (%s).",
                 key.c_str(), e.getMessage().c_str());
    return nullptr;
  }
}
示例#2
0
static void unserializeProp(VariableUnserializer* uns,
                            ObjectData* obj,
                            const String& key,
                            Class* ctx,
                            const String& realKey,
                            int nProp) {
  // Do a two-step look up
  auto const lookup = obj->getProp(ctx, key.get());
  Variant* t;

  if (!lookup.prop || !lookup.accessible) {
    // Dynamic property. If this is the first, and we're using MixedArray,
    // we need to pre-allocate space in the array to ensure the elements
    // dont move during unserialization.
    //
    // TODO(#2881866): this assumption means we can't do reallocations
    // when promoting kPackedKind -> kMixedKind.
    t = &obj->reserveProperties(nProp).lvalAt(realKey, AccessFlags::Key);
  } else {
    t = &tvAsVariant(lookup.prop);
  }

  if (UNLIKELY(IS_REFCOUNTED_TYPE(t->getRawType()))) {
    uns->putInOverwrittenList(*t);
  }

  unserializeVariant(*t, uns);
  if (!RuntimeOption::RepoAuthoritative) return;
  if (!Repo::get().global().HardPrivatePropInference) return;

  /*
   * We assume for performance reasons in repo authoriative mode that
   * we can see all the sets to private properties in a class.
   *
   * It's a hole in this if we don't check unserialization doesn't
   * violate what we've seen, which we handle by throwing if the repo
   * was built with this option.
   */
  auto const cls  = obj->getVMClass();
  auto const slot = cls->lookupDeclProp(key.get());
  if (UNLIKELY(slot == kInvalidSlot)) return;
  auto const repoTy = obj->getVMClass()->declPropRepoAuthType(slot);
  if (LIKELY(tvMatchesRepoAuthType(*t->asTypedValue(), repoTy))) {
    return;
  }

  auto msg = folly::format(
    "Property {} for class {} was deserialized with type ({}) that "
    "didn't match what we inferred in static analysis",
    key.data(),
    obj->getVMClass()->name()->data(),
    tname(t->asTypedValue()->m_type)
  ).str();
  throw Exception(msg);
}
示例#3
0
Variant VariableUnserializer::unserialize() {
  Variant v;
  unserializeVariant(v, this);
  return v;
}
示例#4
0
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(';');
}