Arquivos
hhvm/hphp/runtime/base/variable_unserializer.h
T
andrewparoski 6e7b02933c Fix bugs with unserializing collections
The 'r' encoding for unserialization was broken for collections because the
code was calling Variant::unserialize() on a temporary Variant, which is a
no-no. unserialize() must be called directly on the value where it resides
in the collection.

Second, there was an inconsistency between serialize and unserialize with
how id numbers worked for the 'r' and 'R' encodings. This diff fixes
serialize and unserialize to count collection keys when assigning id
numbers. I also took the opportunity to tighten up enforcement to prevent
collections keys and values from being taken by reference when during
unserialization.
2013-04-01 13:48:59 -07:00

138 linhas
4.4 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __HPHP_VARIABLE_UNSERIALIZER_H__
#define __HPHP_VARIABLE_UNSERIALIZER_H__
#include <runtime/base/types.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
class VariableUnserializer {
public:
/**
* Supported formats.
*/
enum Type {
Serialize,
APCSerialize,
};
public:
VariableUnserializer(const char *str, size_t len, Type type,
bool allowUnknownSerializableClass = false)
: m_type(type), m_buf(str), m_end(str + len),
m_unknownSerializable(allowUnknownSerializableClass) {}
VariableUnserializer(const char *str, const char *end, Type type,
bool allowUnknownSerializableClass = false)
: m_type(type), m_buf(str), m_end(end),
m_unknownSerializable(allowUnknownSerializableClass) {}
Type getType() const { return m_type;}
bool allowUnknownSerializableClass() const { return m_unknownSerializable;}
Variant unserialize();
Variant unserializeKey();
void add(Variant* v, Uns::Mode mode) {
if (mode == Uns::ValueMode) {
m_refs.emplace_back(RefInfo(v));
} else if (mode == Uns::KeyMode) {
// do nothing
} else if (mode == Uns::ColValueMode) {
m_refs.emplace_back(RefInfo::makeNonRefable(v));
} else {
assert(mode == Uns::ColKeyMode);
// We don't currently support using the 'r' encoding to refer
// to collection keys, but eventually we'll need to make this
// work to allow objects as keys. For now we encode collections
// keys in m_refs using a null pointer.
m_refs.emplace_back(RefInfo(nullptr));
}
}
// getByVal() is used to resolve the 'r' encoding
Variant* getByVal(int id) {
if (id <= 0 || id > (int)m_refs.size()) return nullptr;
Variant* ret = m_refs[id-1].var();
if (!ret) {
throw Exception("Referring to collection keys using the 'r' encoding "
"is not supported");
}
return ret;
}
// getByVal() is used to resolve the 'R' encoding
Variant* getByRef(int id) {
if (id <= 0 || id > (int)m_refs.size()) return nullptr;
if (!m_refs[id-1].canBeReferenced()) {
// If the low bit is set, that means the value cannot
// be taken by reference
throw Exception("Collection values cannot be taken by reference", id);
}
Variant* ret = m_refs[id-1].var();
if (!ret) {
throw Exception("Collection keys cannot be taken by reference", id);
}
return ret;
}
int64_t readInt();
double readDouble();
char readChar() {
check();
return *(m_buf++);
}
void read(char *buf, uint n);
char peek() {
check();
return *m_buf;
}
const char *head() { return m_buf; }
Variant &addVar();
private:
struct RefInfo {
explicit RefInfo(Variant* v) : m_data(reinterpret_cast<uintptr_t>(v)) {}
static RefInfo makeNonRefable(Variant* v) {
RefInfo r(v);
r.m_data |= 1;
return r;
}
Variant* var() const {
return reinterpret_cast<Variant*>(m_data & ~1);
}
bool canBeReferenced() const { return !(m_data & 1); }
private:
uintptr_t m_data;
};
Type m_type;
const char *m_buf;
const char *m_end;
std::vector<RefInfo> m_refs;
std::list<Variant> m_vars;
bool m_unknownSerializable;
void check() {
if (m_buf >= m_end) {
throw Exception("Unexpected end of buffer during unserialization");
}
}
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __HPHP_VARIABLE_UNSERIALIZER_H__