55c20f79c1
I've found several bugs recently due to the TypedValue plausible check being too loose. Our DataType enum values are now sparse, and we miss garbage that happens to fall inside the [-10, 127] range, which is >50% of possible garbage values.
240 linhas
7.2 KiB
C++
240 linhas
7.2 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. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <runtime/base/complex_types.h>
|
|
#include <runtime/base/type_conversions.h>
|
|
|
|
#include <system/lib/systemlib.h>
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void tvCastToBooleanInPlace(TypedValue* tv) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
bool b;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull: b = false; break;
|
|
case KindOfBoolean: return;
|
|
case KindOfInt64: b = (tv->m_data.num != 0LL); break;
|
|
case KindOfDouble: b = (tv->m_data.dbl != 0); break;
|
|
case KindOfStaticString: b = tv->m_data.pstr->toBoolean(); break;
|
|
case KindOfString: b = tv->m_data.pstr->toBoolean(); tvDecRefStr(tv); break;
|
|
case KindOfArray: b = (!tv->m_data.parr->empty()); tvDecRefArr(tv); break;
|
|
case KindOfObject: b = (tv->m_data.pobj != nullptr); tvDecRefObj(tv); break;
|
|
default: assert(false); b = false; break;
|
|
}
|
|
tv->m_data.num = b;
|
|
tv->m_type = KindOfBoolean;
|
|
}
|
|
|
|
void tvCastToInt64InPlace(TypedValue* tv, int base /* = 10 */) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
int64_t i;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull:
|
|
tv->m_data.num = 0LL;
|
|
// Fall through
|
|
case KindOfBoolean:
|
|
assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL);
|
|
tv->m_type = KindOfInt64;
|
|
// Fall through
|
|
case KindOfInt64:
|
|
return;
|
|
case KindOfDouble: {
|
|
i = toInt64(tv->m_data.dbl);
|
|
break;
|
|
}
|
|
case KindOfStaticString: i = (tv->m_data.pstr->toInt64(base)); break;
|
|
case KindOfString: {
|
|
i = (tv->m_data.pstr->toInt64(base));
|
|
tvDecRefStr(tv);
|
|
break;
|
|
}
|
|
case KindOfArray: {
|
|
i = (tv->m_data.parr->empty() ? 0LL : 1LL);
|
|
tvDecRefArr(tv);
|
|
break;
|
|
}
|
|
case KindOfObject: {
|
|
i = (tv->m_data.pobj ? tv->m_data.pobj->o_toInt64() : 0LL);
|
|
tvDecRefObj(tv);
|
|
break;
|
|
}
|
|
default: assert(false); i = 0LL; break;
|
|
}
|
|
tv->m_data.num = i;
|
|
tv->m_type = KindOfInt64;
|
|
}
|
|
|
|
void tvCastToDoubleInPlace(TypedValue* tv) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
double d;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull: d = 0.0; break;
|
|
case KindOfBoolean: assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL);
|
|
case KindOfInt64: d = (double)(tv->m_data.num); break;
|
|
case KindOfDouble: return;
|
|
case KindOfStaticString: d = tv->m_data.pstr->toDouble(); break;
|
|
case KindOfString: d = tv->m_data.pstr->toDouble(); tvDecRefStr(tv); break;
|
|
case KindOfArray: {
|
|
d = (double)(tv->m_data.parr->empty() ? 0LL : 1LL);
|
|
tvDecRefArr(tv);
|
|
break;
|
|
}
|
|
case KindOfObject: {
|
|
d = (double)(tv->m_data.pobj ? tv->m_data.pobj->o_toDouble() : 0LL);
|
|
tvDecRefObj(tv);
|
|
break;
|
|
}
|
|
default: assert(false); d = 0.0; break;
|
|
}
|
|
tv->m_data.dbl = d;
|
|
tv->m_type = KindOfDouble;
|
|
}
|
|
|
|
void tvCastToStringInPlace(TypedValue* tv) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
StringData * s;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull: s = buildStringData(""); break;
|
|
case KindOfBoolean: s = buildStringData(tv->m_data.num ? "1" : ""); break;
|
|
case KindOfInt64: s = buildStringData(tv->m_data.num); break;
|
|
case KindOfDouble: s = buildStringData(tv->m_data.dbl); break;
|
|
case KindOfStaticString:
|
|
case KindOfString: return;
|
|
case KindOfArray: s = buildStringData("Array"); tvDecRefArr(tv); break;
|
|
case KindOfObject: {
|
|
// For objects, we fall back on the Variant machinery
|
|
tvAsVariant(tv) = tv->m_data.pobj->t___tostring();
|
|
return;
|
|
}
|
|
default: assert(false); s = buildStringData(""); break;
|
|
}
|
|
tv->m_data.pstr = s;
|
|
tv->m_type = KindOfString;
|
|
tv->m_data.pstr->incRefCount();
|
|
}
|
|
|
|
void tvCastToArrayInPlace(TypedValue* tv) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
ArrayData * a;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull: a = ArrayData::Create(); break;
|
|
case KindOfBoolean:
|
|
case KindOfInt64:
|
|
case KindOfDouble:
|
|
case KindOfStaticString: a = ArrayData::Create(tvAsVariant(tv)); break;
|
|
case KindOfString: {
|
|
a = ArrayData::Create(tvAsVariant(tv));
|
|
tvDecRefStr(tv);
|
|
break;
|
|
}
|
|
case KindOfArray: return;
|
|
case KindOfObject: {
|
|
// For objects, we fall back on the Variant machinery
|
|
tvAsVariant(tv) = tv->m_data.pobj->o_toArray();
|
|
return;
|
|
}
|
|
default: assert(false); a = ArrayData::Create(); break;
|
|
}
|
|
tv->m_data.parr = a;
|
|
tv->m_type = KindOfArray;
|
|
tv->m_data.parr->incRefCount();
|
|
}
|
|
|
|
void tvCastToObjectInPlace(TypedValue* tv) {
|
|
if (tv->m_type == KindOfRef) {
|
|
tvUnbox(tv);
|
|
}
|
|
ObjectData* o;
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull: o = SystemLib::AllocStdClassObject(); break;
|
|
case KindOfBoolean:
|
|
case KindOfInt64:
|
|
case KindOfDouble:
|
|
case KindOfStaticString: {
|
|
o = SystemLib::AllocStdClassObject();
|
|
o->o_set("scalar", tvAsVariant(tv));
|
|
break;
|
|
}
|
|
case KindOfString: {
|
|
o = SystemLib::AllocStdClassObject();
|
|
o->o_set("scalar", tvAsVariant(tv));
|
|
tvDecRefStr(tv);
|
|
break;
|
|
}
|
|
case KindOfArray: {
|
|
// For arrays, we fall back on the Variant machinery
|
|
tvAsVariant(tv) = tv->m_data.parr->toObject();
|
|
return;
|
|
}
|
|
case KindOfObject: return;
|
|
default: assert(false); o = SystemLib::AllocStdClassObject(); break;
|
|
}
|
|
tv->m_data.pobj = o;
|
|
tv->m_type = KindOfObject;
|
|
tv->m_data.pobj->incRefCount();
|
|
}
|
|
|
|
bool tvIsPlausible(const TypedValue* tv) {
|
|
if (!tv) return false;
|
|
auto okPtr = [](void* ptr) {
|
|
return ptr && (uintptr_t(ptr) % sizeof(ptr) == 0);
|
|
};
|
|
switch (tv->m_type) {
|
|
case KindOfUninit:
|
|
case KindOfNull:
|
|
return true;
|
|
case KindOfBoolean:
|
|
return tv->m_data.num == 0 || tv->m_data.num == 1;
|
|
case KindOfInt64:
|
|
case KindOfDouble:
|
|
return true;
|
|
case KindOfStaticString:
|
|
return okPtr(tv->m_data.pstr) && tv->m_data.pstr->isStatic();
|
|
case KindOfString:
|
|
case KindOfArray:
|
|
return okPtr(tv->m_data.parr) &&
|
|
is_refcount_realistic(tv->m_data.parr->getCount());
|
|
case KindOfObject:
|
|
return okPtr(tv->m_data.pobj) && !tv->m_data.pobj->isStatic();
|
|
case KindOfRef:
|
|
return okPtr(tv->m_data.pref) &&
|
|
tv->m_data.pref->tv()->m_type != KindOfRef;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|