2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
19 #include "hphp/runtime/base/builtin-functions.h"
20 #include "hphp/runtime/base/coeffects-config.h"
21 #include "hphp/runtime/base/exceptions.h"
22 #include "hphp/runtime/base/object-data.h"
23 #include "hphp/runtime/base/runtime-error.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/request-info.h"
26 #include "hphp/runtime/ext/std/ext_std_closure.h"
27 #include "hphp/runtime/vm/act-rec.h"
28 #include "hphp/runtime/vm/bytecode.h"
29 #include "hphp/runtime/vm/class.h"
30 #include "hphp/runtime/vm/coeffects.h"
31 #include "hphp/runtime/vm/func.h"
32 #include "hphp/runtime/vm/hhbc.h"
33 #include "hphp/runtime/vm/reified-generics.h"
34 #include "hphp/runtime/vm/runtime.h"
35 #include "hphp/runtime/vm/type-constraint.h"
36 #include "hphp/util/text-util.h"
37 #include "hphp/util/trace.h"
39 #include <folly/Random.h>
43 template<class TGetCtx
>
44 void verifyParamType(const Func
* func
, int32_t id
, tv_lval val
,
46 assertx(id
< func
->numNonVariadicParams());
47 assertx(func
->numParams() == int(func
->params().size()));
48 const TypeConstraint
& tc
= func
->params()[id
].typeConstraint
;
49 if (tc
.isCheckable()) {
50 auto const ctx
= tc
.isThis() ? getCtx() : nullptr;
51 tc
.verifyParam(val
, ctx
, func
, id
);
53 if (func
->hasParamsWithMultiUBs()) {
54 auto& ubs
= const_cast<Func::ParamUBMap
&>(func
->paramUBs());
55 auto it
= ubs
.find(id
);
56 if (it
!= ubs
.end()) {
57 for (auto& ub
: it
->second
) {
58 applyFlagsToUB(ub
, tc
);
59 if (ub
.isCheckable()) {
60 auto const ctx
= ub
.isThis() ? getCtx() : nullptr;
61 ub
.verifyParam(val
, ctx
, func
, id
);
69 * RAII wrapper for popping/pushing generics from/to the VM stack.
71 struct GenericsSaver
{
72 explicit GenericsSaver(bool hasGenerics
) : m_generics(pop(hasGenerics
)) {}
73 ~GenericsSaver() { push(std::move(m_generics
)); }
75 static Array
pop(bool hasGenerics
) {
76 if (LIKELY(!hasGenerics
)) return Array();
77 assertx(tvIsVec(vmStack().topC()));
78 auto const generics
= vmStack().topC()->m_data
.parr
;
80 return Array::attach(generics
);
82 static void push(Array
&& generics
) {
83 if (LIKELY(generics
.isNull())) return;
84 vmStack().pushArrayLikeNoRc(generics
.detach());
92 * RAII wrapper for popping/pushing coeffects from/to the VM stack.
94 struct CoeffectsSaver
{
95 explicit CoeffectsSaver(bool hasCoeffects
) : m_coeffects(pop(hasCoeffects
)) {}
96 ~CoeffectsSaver() { push(m_coeffects
); }
98 static Optional
<RuntimeCoeffects
> pop(bool hasCoeffects
) {
99 if (LIKELY(!hasCoeffects
)) return std::nullopt
;
100 assertx(tvIsInt(vmStack().topC()));
101 auto const coeffects
= vmStack().topC()->m_data
.num
;
103 return RuntimeCoeffects::fromValue(coeffects
);
105 static void push(Optional
<RuntimeCoeffects
> coeffects
) {
106 if (LIKELY(!coeffects
)) return;
107 vmStack().pushInt(coeffects
->value());
111 Optional
<RuntimeCoeffects
> m_coeffects
;
114 inline void callerDynamicCallChecks(const Func
* func
,
115 bool allowDynCallNoPointer
= false) {
116 auto dynCallable
= func
->isDynamicallyCallable();
118 if (allowDynCallNoPointer
) return;
119 if (!RO::EvalForbidDynamicCallsWithAttr
) return;
121 auto level
= func
->isMethod()
123 ? RO::EvalForbidDynamicCallsToClsMeth
124 : RO::EvalForbidDynamicCallsToInstMeth
)
125 : RO::EvalForbidDynamicCallsToFunc
;
126 if (level
<= 0) return;
127 if (dynCallable
&& level
< 2) return;
129 if (auto const rate
= func
->dynCallSampleRate()) {
130 if (folly::Random::rand32(*rate
) != 0) return;
134 auto error_msg
= dynCallable
?
135 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE
:
136 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE
;
137 if (level
>= 3 || (level
>= 2 && !dynCallable
)) {
139 string_printf(msg
, error_msg
, func
->fullName()->data());
140 throw_invalid_operation_exception(makeStaticString(msg
));
142 raise_notice(error_msg
, func
->fullName()->data());
145 inline void callerDynamicConstructChecks(const Class
* cls
) {
146 auto level
= RO::EvalForbidDynamicConstructs
;
147 if (level
<= 0 || cls
->isDynamicallyConstructible()) return;
149 if (auto const rate
= cls
->dynConstructSampleRate()) {
150 if (folly::Random::rand32(*rate
) != 0) return;
158 Strings::CLASS_CONSTRUCTED_DYNAMICALLY
,
161 throw_invalid_operation_exception(makeStaticString(msg
));
164 Strings::CLASS_CONSTRUCTED_DYNAMICALLY
,
170 inline void calleeDynamicCallChecks(const Func
* func
, bool dynamicCall
,
171 bool allowDynCallNoPointer
= false) {
172 if (!dynamicCall
) return;
173 if (func
->isDynamicallyCallable() && allowDynCallNoPointer
) return;
175 auto error_msg
= func
->isDynamicallyCallable() ?
176 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE
:
177 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE
;
179 if (RuntimeOption::EvalNoticeOnBuiltinDynamicCalls
&& func
->isBuiltin()) {
182 func
->fullName()->data()
188 * Check if the `callee` satisfies the coeffect constraints on the prologue
189 * flags. Returns true if yes, otherwise raise a warning and return false or
190 * raise an exception.
192 inline bool calleeCoeffectChecks(const Func
* callee
,
193 RuntimeCoeffects providedCoeffects
,
194 uint32_t numArgsInclUnpack
,
196 if (!CoeffectsConfig::enabled()) {
197 if (callee
->hasCoeffectsLocal()) {
198 vmStack().pushInt(RuntimeCoeffects::none().value());
202 auto const requiredCoeffects
= [&] {
203 auto required
= callee
->requiredCoeffects();
204 if (!callee
->hasCoeffectRules()) return required
;
205 for (auto const& rule
: callee
->getCoeffectRules()) {
206 required
|= rule
.emit(callee
, numArgsInclUnpack
, prologueCtx
,
209 if (callee
->hasCoeffectsLocal()) vmStack().pushInt(required
.value());
212 if (LIKELY(providedCoeffects
.canCall(requiredCoeffects
))) return true;
213 raiseCoeffectsCallViolation(callee
, providedCoeffects
, requiredCoeffects
);
218 * Check for presence, count and wildcard match of generics.
220 inline void calleeGenericsChecks(const Func
* callee
, bool hasGenerics
) {
221 if (LIKELY(!callee
->hasReifiedGenerics())) {
222 if (UNLIKELY(hasGenerics
)) vmStack().popC();
227 if (!areAllGenericsSoft(callee
->getReifiedGenericsInfo())) {
228 throw_call_reified_func_without_generics(callee
);
231 raise_warning_for_soft_reified(0, true, callee
->fullName());
233 // Push an empty array, as the remainder of the call setup assumes generics
235 auto const ad
= ArrayData::CreateVec();
236 vmStack().pushArrayLikeNoRc(ad
);
240 auto const generics
= vmStack().topC();
241 assertx(tvIsVec(generics
));
242 checkFunReifiedGenericMismatch(callee
, val(generics
).parr
);
246 * Check for too few or too many arguments and trim extra args.
248 inline void calleeArgumentArityChecks(const Func
* callee
,
249 uint32_t& numArgsInclUnpack
) {
250 if (numArgsInclUnpack
< callee
->numRequiredParams()) {
251 throwMissingArgument(callee
, numArgsInclUnpack
);
254 if (numArgsInclUnpack
> callee
->numParams()) {
255 assertx(!callee
->hasVariadicCaptureParam());
256 assertx(numArgsInclUnpack
== callee
->numNonVariadicParams() + 1);
259 GenericsSaver gs
{callee
->hasReifiedGenerics()};
261 assertx(tvIsVec(vmStack().topC()));
262 auto const numUnpackArgs
= vmStack().topC()->m_data
.parr
->size();
265 if (numUnpackArgs
!= 0) {
266 raiseTooManyArguments(callee
, numArgsInclUnpack
+ numUnpackArgs
);
271 inline void calleeArgumentTypeChecks(const Func
* callee
,
272 uint32_t numArgsInclUnpack
,
274 // Builtins use a separate non-standard mechanism.
275 if (callee
->isCPPBuiltin()) return;
277 auto const getCtx
= [&] () -> const Class
* {
278 if (!callee
->cls()) return nullptr;
279 assertx(prologueCtx
);
280 auto const ctx
= callee
->isClosureBody()
281 ? reinterpret_cast<c_Closure
*>(prologueCtx
)->getThisOrClass()
283 return callee
->isStatic()
284 ? reinterpret_cast<Class
*>(ctx
)
285 : reinterpret_cast<ObjectData
*>(ctx
)->getVMClass();
289 std::min(numArgsInclUnpack
, callee
->numNonVariadicParams());
290 auto const firstArgIdx
=
291 numArgsInclUnpack
- 1 + (callee
->hasReifiedGenerics() ? 1 : 0);
292 for (auto i
= 0; i
< numArgs
; ++i
) {
293 verifyParamType(callee
, i
, vmStack().indC(firstArgIdx
- i
), getCtx
);
297 inline void initFuncInputs(const Func
* callee
, uint32_t numArgsInclUnpack
) {
298 assertx(numArgsInclUnpack
<= callee
->numParams());
300 // All arguments already initialized. Extra arguments already popped
301 // by calleeArgumentArityChecks().
302 if (LIKELY(numArgsInclUnpack
== callee
->numParams())) return;
304 CoeffectsSaver cs
{callee
->hasCoeffectsLocal()};
305 GenericsSaver gs
{callee
->hasReifiedGenerics()};
306 auto const numParams
= callee
->numNonVariadicParams();
307 while (numArgsInclUnpack
< numParams
) {
308 vmStack().pushUninit();
312 if (callee
->hasVariadicCaptureParam()) {
313 auto const ad
= ArrayData::CreateVec();
314 vmStack().pushArrayLikeNoRc(ad
);
318 assertx(numArgsInclUnpack
== callee
->numParams());
322 * This helper does a stack overflow check on *both* the native stack
325 * In some cases for re-entry, we're checking for space other than
326 * just the callee, and `extraCells' may need to be passed with a
327 * non-zero value. (We over-check in these situations, but it's fine.)
330 void checkStack(Stack
& stk
, const Func
* f
, int32_t extraCells
) {
332 * Check whether func's maximum stack usage would overflow the stack.
333 * Both native and VM stack overflows are independently possible.
335 * All stack checks are inflated by stackCheckPadding() to ensure
336 * there is space both for calling leaf functions /and/ for
337 * re-entry. (See kStackCheckReenterPadding and
338 * RuntimeOption::EvalStackCheckLeafPadding.)
340 auto limit
= f
->maxStackCells() + stackCheckPadding() + extraCells
;
341 if (LIKELY(stack_in_bounds() && !stk
.wouldOverflow(limit
))) return;
342 TRACE_MOD(Trace::gc
, 1, "Maximum stack depth exceeded.\n");
343 throw_stack_overflow();