[security][CVE-2022-27809] Builtins should always take int64_t, not int
[hiphop-php.git] / hphp / runtime / vm / preclass-emitter.cpp
blob64178f44765a6a2db40e25461d3b2955b9e6eb5b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/preclass-emitter.h"
18 #include <limits>
20 #include <folly/Memory.h>
22 #include "hphp/runtime/base/array-iterator.h"
23 #include "hphp/runtime/base/coeffects-config.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/vm/repo-autoload-map-builder.h"
26 #include "hphp/runtime/vm/native.h"
27 #include "hphp/runtime/vm/native-data.h"
29 #include "hphp/util/blob-encoder.h"
31 namespace HPHP {
33 //=============================================================================
34 // PreClassEmitter::Prop.
36 PreClassEmitter::Prop::Prop(const PreClassEmitter* pce,
37 const StringData* n,
38 Attr attrs,
39 const StringData* userType,
40 const TypeConstraint& typeConstraint,
41 const UpperBoundVec& ubs,
42 const StringData* docComment,
43 const TypedValue* val,
44 RepoAuthType repoAuthType,
45 UserAttributeMap userAttributes)
46 : m_name(n)
47 , m_attrs(attrs)
48 , m_userType(userType)
49 , m_docComment(docComment)
50 , m_repoAuthType(repoAuthType)
51 , m_typeConstraint(typeConstraint)
52 , m_ubs(ubs)
53 , m_userAttributes(userAttributes)
55 memcpy(&m_val, val, sizeof(TypedValue));
58 PreClassEmitter::Prop::~Prop() {
61 //=============================================================================
62 // PreClassEmitter.
64 PreClassEmitter::PreClassEmitter(UnitEmitter& ue,
65 Id id,
66 const std::string& n)
67 : m_ue(ue)
68 , m_name(makeStaticString(n))
69 , m_id(id) {}
71 void PreClassEmitter::init(int line1, int line2, Attr attrs,
72 const StringData* parent,
73 const StringData* docComment) {
74 m_line1 = line1;
75 m_line2 = line2;
76 m_attrs = attrs;
77 m_parent = parent;
78 m_docComment = docComment;
79 if (!SystemLib::s_inited) {
80 m_attrs = m_attrs | AttrBuiltin;
84 PreClassEmitter::~PreClassEmitter() {
85 for (MethodVec::const_iterator it = m_methods.begin();
86 it != m_methods.end(); ++it) {
87 delete *it;
91 void PreClassEmitter::addInterface(const StringData* n) {
92 m_interfaces.push_back(n);
95 void PreClassEmitter::addEnumInclude(const StringData* n) {
96 m_enumIncludes.push_back(n);
99 bool PreClassEmitter::addMethod(FuncEmitter* method) {
100 MethodMap::const_iterator it = m_methodMap.find(method->name);
101 if (it != m_methodMap.end()) {
102 return false;
104 m_methods.push_back(method);
105 m_methodMap[method->name] = method;
106 return true;
109 void PreClassEmitter::renameMethod(const StringData* oldName,
110 const StringData* newName) {
111 assertx(m_methodMap.count(oldName));
112 auto it = m_methodMap.find(oldName);
113 auto fe = it->second;
114 m_methodMap.erase(it);
115 fe->name = newName;
116 m_methodMap[newName] = fe;
119 bool PreClassEmitter::addProperty(const StringData* n, Attr attrs,
120 const StringData* userType,
121 const TypeConstraint& typeConstraint,
122 const UpperBoundVec& ubs,
123 const StringData* docComment,
124 const TypedValue* val,
125 RepoAuthType repoAuthType,
126 UserAttributeMap userAttributes) {
127 assertx(typeConstraint.validForProp());
128 PropMap::Builder::const_iterator it = m_propMap.find(n);
129 if (it != m_propMap.end()) {
130 return false;
132 PreClassEmitter::Prop prop{
133 this,
135 attrs,
136 userType,
137 typeConstraint,
138 ubs,
139 docComment,
140 val,
141 repoAuthType,
142 userAttributes
144 m_propMap.add(prop.name(), prop);
145 return true;
148 bool PreClassEmitter::addAbstractConstant(const StringData* n,
149 ConstModifiers::Kind kind,
150 bool fromTrait) {
151 assertx(kind == ConstModifiers::Kind::Value ||
152 kind == ConstModifiers::Kind::Type);
154 auto it = m_constMap.find(n);
155 if (it != m_constMap.end()) {
156 return false;
158 PreClassEmitter::Const cns(n, nullptr, nullptr,
159 {}, Array{}, kind,
160 Const::Invariance::None,
161 true, fromTrait);
162 m_constMap.add(cns.name(), cns);
163 return true;
166 bool PreClassEmitter::addContextConstant(
167 const StringData* n,
168 PreClassEmitter::Const::CoeffectsVec coeffects,
169 bool isAbstract,
170 bool fromTrait) {
171 auto it = m_constMap.find(n);
172 if (it != m_constMap.end()) {
173 return false;
175 PreClassEmitter::Const cns(n, nullptr, nullptr,
176 std::move(coeffects),
177 Array{},
178 ConstModifiers::Kind::Context,
179 Const::Invariance::None,
180 isAbstract, fromTrait);
181 m_constMap.add(cns.name(), cns);
182 return true;
185 bool PreClassEmitter::addConstant(const StringData* n,
186 const StringData* cls,
187 const TypedValue* val,
188 Array resolvedTypeStructure,
189 ConstModifiers::Kind kind,
190 Const::Invariance invariance,
191 bool fromTrait,
192 bool isAbstract) {
193 assertx(kind == ConstModifiers::Kind::Value ||
194 kind == ConstModifiers::Kind::Type);
195 assertx(IMPLIES(kind == ConstModifiers::Kind::Value, !cls));
196 assertx(IMPLIES(kind == ConstModifiers::Kind::Value,
197 resolvedTypeStructure.isNull()));
198 assertx(IMPLIES(!resolvedTypeStructure.isNull(),
199 resolvedTypeStructure.isDict() &&
200 !resolvedTypeStructure.empty() &&
201 resolvedTypeStructure->isStatic()));
202 assertx(IMPLIES(invariance != Const::Invariance::None,
203 !resolvedTypeStructure.isNull()));
204 assertx(val);
206 ConstMap::Builder::const_iterator it = m_constMap.find(n);
207 if (it != m_constMap.end()) {
208 return false;
210 PreClassEmitter::Const cns(n, cls, val, {}, std::move(resolvedTypeStructure),
211 kind, invariance, isAbstract, fromTrait);
212 m_constMap.add(cns.name(), cns);
213 return true;
216 void PreClassEmitter::addUsedTrait(const StringData* traitName) {
217 m_usedTraits.push_back(traitName);
220 void PreClassEmitter::addTraitPrecRule(
221 const PreClass::TraitPrecRule &rule) {
222 m_traitPrecRules.push_back(rule);
225 void PreClassEmitter::addTraitAliasRule(
226 const PreClass::TraitAliasRule &rule) {
227 m_traitAliasRules.push_back(rule);
230 const StaticString
231 s_nativedata("__nativedata"),
232 s_DynamicallyConstructible("__DynamicallyConstructible"),
233 s_invoke("__invoke"),
234 s_coeffectsProp("86coeffects");
236 PreClass* PreClassEmitter::create(Unit& unit) const {
237 Attr attrs = m_attrs;
238 if (attrs & AttrPersistent &&
239 !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) {
240 attrs = Attr(attrs & ~AttrPersistent);
243 auto const dynConstructSampleRate = [&] () -> int64_t {
244 if (!(attrs & AttrDynamicallyConstructible)) return -1;
246 auto const it = m_userAttributes.find(s_DynamicallyConstructible.get());
247 if (it == m_userAttributes.end()) return -1;
249 assertx(isArrayLikeType(type(it->second)));
250 auto const rate = val(it->second).parr->get(int64_t(0));
251 if (!isIntType(type(rate)) || val(rate).num < 0) return -1;
253 attrs = Attr(attrs & ~AttrDynamicallyConstructible);
254 return val(rate).num;
255 }();
257 auto const invoke = lookupMethod(s_invoke.get());
258 if (invoke && invoke->isClosureBody) {
259 attrs |= AttrIsClosureClass;
260 if (!invoke->coeffectRules.empty()) {
261 assertx(invoke->coeffectRules.size() == 1);
262 if (invoke->coeffectRules[0].isClosureParentScope()) {
263 attrs |= AttrHasClosureCoeffectsProp;
264 } else {
265 assertx(invoke->coeffectRules[0].isCaller());
270 assertx(attrs & AttrPersistent || SystemLib::s_inited);
272 auto pc = std::make_unique<PreClass>(
273 &unit, m_line1, m_line2, m_name,
274 attrs, m_parent, m_docComment, m_id);
275 pc->m_interfaces = m_interfaces;
276 pc->m_includedEnums = m_enumIncludes;
277 pc->m_usedTraits = m_usedTraits;
278 pc->m_requirements = m_requirements;
279 pc->m_traitPrecRules = m_traitPrecRules;
280 pc->m_traitAliasRules = m_traitAliasRules;
281 pc->m_enumBaseTy = m_enumBaseTy;
282 pc->m_numDeclMethods = -1;
283 pc->m_ifaceVtableSlot = m_ifaceVtableSlot;
284 pc->m_dynConstructSampleRate = dynConstructSampleRate;
286 // Set user attributes.
287 [&] {
288 pc->m_userAttributes = m_userAttributes;
289 pc->m_nativeDataInfo = nullptr;
290 if (!m_userAttributes.size()) return;
292 // Check for <<__NativeData("Type")>>.
293 auto it = m_userAttributes.find(s_nativedata.get());
294 if (it == m_userAttributes.end()) return;
296 TypedValue ndiInfo = it->second;
297 if (!isArrayLikeType(ndiInfo.m_type)) return;
299 // Use the first string label which references a registered type. In
300 // practice, there should generally only be one item and it should be a
301 // string, but maybe that'll be extended...
302 for (ArrayIter it(ndiInfo.m_data.parr); it; ++it) {
303 Variant val = it.second();
304 if (!val.isString()) continue;
306 pc->m_nativeDataInfo = Native::getNativeDataInfo(val.toString().get());
307 if (pc->m_nativeDataInfo) break;
309 }();
311 PreClass::MethodMap::Builder methodBuild;
312 for (MethodVec::const_iterator it = m_methods.begin();
313 it != m_methods.end(); ++it) {
314 Func* f = (*it)->create(unit, pc.get());
315 if (f->attrs() & AttrTrait) {
316 if (pc->m_numDeclMethods == -1) {
317 pc->m_numDeclMethods = it - m_methods.begin();
319 } else if (!f->isGenerated()) {
320 assertx(pc->m_numDeclMethods == -1);
322 methodBuild.add(f->name(), f);
324 pc->m_methods.create(methodBuild);
326 PreClass::PropMap::Builder propBuild;
327 if (pc->attrs() & AttrHasClosureCoeffectsProp) {
328 TypedValue tvInit;
329 tvWriteUninit(tvInit);
331 propBuild.add(s_coeffectsProp.get(), PreClass::Prop(pc.get(),
332 s_coeffectsProp.get(),
333 AttrPrivate|AttrSystemInitialValue,
334 staticEmptyString(),
335 TypeConstraint(),
336 CompactVector<TypeConstraint>{},
337 staticEmptyString(),
338 tvInit,
339 RepoAuthType{},
340 UserAttributeMap{}
343 for (unsigned i = 0; i < m_propMap.size(); ++i) {
344 const Prop& prop = m_propMap[i];
345 propBuild.add(prop.name(), PreClass::Prop(pc.get(),
346 prop.name(),
347 prop.attrs(),
348 prop.userType(),
349 prop.typeConstraint(),
350 prop.upperBounds(),
351 prop.docComment(),
352 prop.val(),
353 prop.repoAuthType(),
354 prop.userAttributes()));
356 pc->m_properties.create(propBuild);
358 PreClass::ConstMap::Builder constBuild;
359 for (unsigned i = 0; i < m_constMap.size(); ++i) {
360 const Const& const_ = m_constMap[i];
361 TypedValueAux tvaux;
362 tvaux.constModifiers() = {};
363 tvaux.constModifiers().setIsAbstract(const_.isAbstract());
364 if (const_.kind() == ConstModifiers::Kind::Context) {
365 auto const coeffects =
366 getCoeffectsInfoFromList(const_.coeffects(), false).first;
367 tvaux.constModifiers().setCoeffects(coeffects);
368 if (!const_.coeffects().empty()) {
369 tvCopy(make_tv<KindOfInt64>(0), tvaux); // dummy value for m_data
370 } else {
371 tvWriteConstValMissing(tvaux);
373 } else {
374 if (const_.valOption()) {
375 tvCopy(const_.val(), tvaux);
376 } else {
377 tvWriteConstValMissing(tvaux);
381 tvaux.constModifiers().setKind(const_.kind());
383 assertx(
384 IMPLIES(const_.kind() != ConstModifiers::Kind::Type, !const_.cls())
386 assertx(
387 IMPLIES(const_.kind() != ConstModifiers::Kind::Type,
388 const_.resolvedTypeStructure().isNull())
390 assertx(
391 IMPLIES(!const_.resolvedTypeStructure().isNull(),
392 const_.resolvedTypeStructure().isDict() &&
393 !const_.resolvedTypeStructure().empty())
395 assertx(
396 IMPLIES(const_.invariance() != Const::Invariance::None,
397 !const_.resolvedTypeStructure().isNull())
400 constBuild.add(
401 const_.name(),
402 PreClass::Const(
403 const_.name(),
404 const_.cls(),
405 tvaux,
406 const_.resolvedTypeStructure(),
407 const_.invariance(),
408 const_.isFromTrait()
412 if (auto nativeConsts = Native::getClassConstants(m_name)) {
413 for (auto cnsMap : *nativeConsts) {
414 TypedValueAux tvaux;
415 tvCopy(cnsMap.second, tvaux);
416 tvaux.constModifiers() = {};
417 constBuild.add(cnsMap.first, PreClass::Const(cnsMap.first,
418 nullptr,
419 tvaux,
420 Array{},
421 Const::Invariance::None,
422 false));
426 pc->m_constants.create(constBuild);
427 return pc.release();
430 template<class SerDe> void PreClassEmitter::serdeMetaData(SerDe& sd) {
431 // NOTE: name and a few other fields currently
432 // serialized outside of this.
433 sd(m_line1)
434 (m_line2)
435 (m_attrs)
436 (m_parent)
437 (m_docComment)
438 (m_ifaceVtableSlot)
440 (m_interfaces)
441 (m_enumIncludes)
442 (m_usedTraits)
443 (m_requirements)
444 (m_traitPrecRules)
445 (m_traitAliasRules)
446 (m_userAttributes)
447 (m_propMap, [](Prop p) { return p.name(); })
448 (m_constMap, [](Const c) { return c.name(); })
449 (m_enumBaseTy)
453 template void PreClassEmitter::serdeMetaData<>(BlobDecoder&);
454 template void PreClassEmitter::serdeMetaData<>(BlobEncoder&);
456 } // HPHP