Skip to content

Commit 5ba9714

Browse files
committed
fixed possible crash in dealing with property_exists() calls (the zend engine does not inintialize the refcount for the parameter passed to property_exists(), which caused the PHP-CPP module to potentially crash when it read out the refcount)
1 parent 5bfb0bf commit 5ba9714

File tree

1 file changed

+58
-19
lines changed

1 file changed

+58
-19
lines changed

zend/classimpl.cpp

+58-19
Original file line numberDiff line numberDiff line change
@@ -1055,25 +1055,64 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z
10551055
ClassImpl *impl = self(entry);
10561056
ClassBase *meta = impl->_base;
10571057

1058-
// convert the name to a Value object
1059-
Value key(name);
1060-
1061-
// check if this is a callback property
1062-
if (impl->_properties.find(key) != impl->_properties.end()) return true;
1063-
1064-
// call the C++ object
1065-
if (!meta->callIsset(base, key)) return false;
1066-
1067-
// property exists, but what does the user want to know
1068-
if (has_set_exists == 2) return true;
1069-
1070-
// we have to retrieve the property
1071-
Value value = meta->callGet(base, key);
1072-
1073-
// should we check on NULL?
1074-
switch (has_set_exists) {
1075-
case 0: return value.type() != Type::Null;
1076-
default: return value.boolValue();
1058+
// Watch out: the Zend engine is doing weird stuff here, the "name" parameter
1059+
// that is passed to this method does not always have the "refcount" member
1060+
// initialized (especially not when the call comes from property_exists()),
1061+
// which makes it impossible to call Value{name} to convert name to a Php::Value
1062+
if (Z_TYPE_P(name) == IS_STRING)
1063+
{
1064+
// we need a std::string for the lookup in the std::map
1065+
std::string property(Z_STRVAL_P(name), Z_STRLEN_P(name));
1066+
1067+
// check if this is a callback property
1068+
if (impl->_properties.find(property) != impl->_properties.end()) return true;
1069+
1070+
// we need the to pass a Php::Value to pass to the __isset() and _get() methods
1071+
Value propvalue(property);
1072+
1073+
// call the C++ object
1074+
if (!meta->callIsset(base, propvalue)) return false;
1075+
1076+
// property exists, but what does the user want to know
1077+
if (has_set_exists == 2) return true;
1078+
1079+
// we have to retrieve the property
1080+
Value value = meta->callGet(base, propvalue);
1081+
1082+
// should we check on NULL?
1083+
switch (has_set_exists) {
1084+
case 0: return value.type() != Type::Null;
1085+
default: return value.boolValue();
1086+
}
1087+
}
1088+
else
1089+
{
1090+
// the passed parameter was not a string, which is odd, but there could be a
1091+
// scenario where the method is called with an object and the caller expect us
1092+
// to convert the object back to a string (does this happen!?). Anyway: the call
1093+
// does certainly not come from the property_exists() function because that function
1094+
// already forces the param to be a string, and since the call does not come from
1095+
// property_exists(), we also do not have to be extra careful in converting
1096+
// the name into a Value object and can do that right now:
1097+
Value property(name);
1098+
1099+
// check if this is a callback property
1100+
if (impl->_properties.find(property) != impl->_properties.end()) return true;
1101+
1102+
// call the C++ object
1103+
if (!meta->callIsset(base, property)) return false;
1104+
1105+
// property exists, but what does the user want to know
1106+
if (has_set_exists == 2) return true;
1107+
1108+
// we have to retrieve the property
1109+
Value value = meta->callGet(base, property);
1110+
1111+
// should we check on NULL?
1112+
switch (has_set_exists) {
1113+
case 0: return value.type() != Type::Null;
1114+
default: return value.boolValue();
1115+
}
10771116
}
10781117
}
10791118
catch (const NotImplemented &exception)

0 commit comments

Comments
 (0)