codemod 2010-2016 to 2010-present
[hiphop-php.git] / hphp / runtime / vm / jit / func-effects.cpp
blob1e00f22bb70ad01677b879f14cc40cd504bbd8b1
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/func-effects.h"
19 #include "hphp/runtime/vm/func.h"
20 #include "hphp/runtime/vm/repo.h"
21 #include "hphp/runtime/vm/repo-global-data.h"
22 #include "hphp/runtime/vm/unit.h"
23 #include "hphp/runtime/vm/jit/normalized-instruction.h"
25 namespace HPHP { namespace jit {
27 namespace {
28 const StaticString
29 s_http_response_header("http_response_header"),
30 s_php_errormsg("php_errormsg");
32 using FuncSet = std::unordered_set<std::string, string_hashi, string_eqstri>;
34 * This is a conservative list of functions that we are certain won't inspect
35 * the caller frame (generally by either CallerFrame or vm_call_user_func).
37 FuncSet ignoresCallerFrame = {
38 "array_key_exists",
39 "key_exists",
40 "array_keys",
41 "array_pop",
42 "array_push",
43 "array_rand",
44 "array_search",
45 "array_shift",
46 "array_slice",
47 "array_splice",
48 "array_unique",
49 "array_unshift",
50 "array_values",
51 "compact",
52 "shuffle",
53 "count",
54 "sizeof",
55 "each",
56 "current",
57 "in_array",
58 "range",
59 "sort",
60 "rsort",
61 "asort",
62 "arsort",
63 "ksort",
64 "krsort",
65 "natsort",
66 "natcasesort",
67 "hphp_array_idx",
68 "ctype_alnum",
69 "ctype_alpha",
70 "ctype_cntrl",
71 "ctype_digit",
72 "ctype_graph",
73 "ctype_lower",
74 "ctype_print",
75 "ctype_punct",
76 "ctype_space",
77 "ctype_upper",
78 "ctype_xdigit",
79 "fb_serialize",
80 "fb_unserialize",
81 "fb_compact_serialize",
82 "fb_compact_unserialize",
83 "fb_utf8ize",
84 "fb_utf8_strlen",
85 "fb_utf8_strlen_deprecated",
86 "fb_utf8_substr",
87 "fb_get_code_coverage",
88 "fb_output_compression",
89 "fb_set_exit_callback",
90 "fb_get_last_flush_size",
91 "fb_lazy_lstat",
92 "fb_lazy_realpath",
93 "hash",
94 "hash_algos",
95 "hash_file",
96 "hash_final",
97 "hash_init",
98 "hash_update",
99 "hash_copy",
100 "hash_equals",
101 "furchash_hphp_ext",
102 "hphp_murmurhash",
103 "get_declared_classes",
104 "get_declared_interfaces",
105 "get_declared_traits",
106 "class_alias",
107 "class_exists",
108 "interface_exists",
109 "trait_exists",
110 "enum_exists",
111 "get_class_methods",
112 "get_class_constants",
113 "is_a",
114 "is_subclass_of",
115 "method_exists",
116 "property_exists",
117 "error_log",
118 "error_reporting",
119 "restore_error_handler",
120 "restore_exception_handler",
121 "set_error_handler",
122 "set_exception_handler",
123 "hphp_set_error_page",
124 "hphp_clear_unflushed",
125 "get_defined_functions",
126 "function_exists",
127 "min",
128 "max",
129 "abs",
130 "is_finite",
131 "is_infinite",
132 "is_nan",
133 "ceil",
134 "floor",
135 "round",
136 "deg2rad",
137 "rad2deg",
138 "decbin",
139 "dechex",
140 "decoct",
141 "bindec",
142 "hexdec",
143 "octdec",
144 "base_convert",
145 "pow",
146 "exp",
147 "expm1",
148 "log10",
149 "log1p",
150 "log",
151 "cos",
152 "cosh",
153 "sin",
154 "sinh",
155 "tan",
156 "tanh",
157 "acos",
158 "acosh",
159 "asin",
160 "asinh",
161 "atan",
162 "atanh",
163 "atan2",
164 "hypot",
165 "fmod",
166 "sqrt",
167 "getrandmax",
168 "srand",
169 "rand",
170 "mt_getrandmax",
171 "mt_srand",
172 "mt_rand",
173 "lcg_value",
174 "intdiv",
175 "flush",
176 "hphp_crash_log",
177 "hphp_stats",
178 "hphp_get_stats",
179 "hphp_get_status",
180 "hphp_get_iostatus",
181 "hphp_set_iostatus_address",
182 "hphp_get_timers",
183 "hphp_output_global_state",
184 "hphp_instruction_counter",
185 "hphp_get_hardware_counters",
186 "hphp_set_hardware_events",
187 "hphp_clear_hardware_events",
188 "wordwrap",
189 "sprintf",
190 "is_null",
191 "is_bool",
192 "is_int",
193 "is_float",
194 "is_numeric",
195 "is_string",
196 "is_scalar",
197 "is_array",
198 "HH\\is_vec",
199 "HH\\is_dict",
200 "HH\\is_keyset",
201 "is_object",
202 "is_resource",
203 "boolval",
204 "intval",
205 "floatval",
206 "strval",
207 "gettype",
208 "get_resource_type",
209 "settype",
210 "serialize",
211 "unserialize",
212 "addcslashes",
213 "stripcslashes",
214 "addslashes",
215 "stripslashes",
216 "bin2hex",
217 "hex2bin",
218 "nl2br",
219 "quotemeta",
220 "str_shuffle",
221 "strrev",
222 "strtolower",
223 "strtoupper",
224 "ucfirst",
225 "lcfirst",
226 "ucwords",
227 "strip_tags",
228 "trim",
229 "ltrim",
230 "rtrim",
231 "chop",
232 "explode",
233 "implode",
234 "join",
235 "str_split",
236 "chunk_split",
237 "strtok",
238 "str_replace",
239 "str_ireplace",
240 "substr_replace",
241 "substr",
242 "str_pad",
243 "str_repeat",
244 "html_entity_decode",
245 "htmlentities",
246 "htmlspecialchars_decode",
247 "htmlspecialchars",
248 "fb_htmlspecialchars",
249 "quoted_printable_encode",
250 "quoted_printable_decode",
251 "convert_uudecode",
252 "convert_uuencode",
253 "str_rot13",
254 "crc32",
255 "crypt",
256 "md5",
257 "sha1",
258 "strtr",
259 "convert_cyr_string",
260 "get_html_translation_table",
261 "hebrev",
262 "hebrevc",
263 "setlocale",
264 "localeconv",
265 "nl_langinfo",
266 "chr",
267 "ord",
268 "money_format",
269 "number_format",
270 "strcmp",
271 "strncmp",
272 "strnatcmp",
273 "strcasecmp",
274 "strncasecmp",
275 "strnatcasecmp",
276 "strcoll",
277 "substr_compare",
278 "strchr",
279 "strrchr",
280 "strstr",
281 "stristr",
282 "strpbrk",
283 "strpos",
284 "stripos",
285 "strrpos",
286 "strripos",
287 "substr_count",
288 "strspn",
289 "strcspn",
290 "strlen",
291 "str_getcsv",
292 "count_chars",
293 "str_word_count",
294 "levenshtein",
295 "similar_text",
296 "soundex",
297 "metaphone",
298 "base64_decode",
299 "base64_encode",
300 "get_headers",
301 "get_meta_tags",
302 "http_build_query",
303 "parse_url",
304 "rawurldecode",
305 "rawurlencode",
306 "urldecode",
307 "urlencode",
310 const StaticString s_assert("assert");
312 bool funcByNameNeedsCallerFrame(const StringData* fname) {
313 return ignoresCallerFrame.find(fname->data()) == ignoresCallerFrame.end();
316 bool disallowDynamicVarEnvFuncs() {
317 return (RuntimeOption::RepoAuthoritative &&
318 Repo::global().DisallowDynamicVarEnvFuncs) ||
319 RuntimeOption::DisallowDynamicVarEnvFuncs == HackStrictOption::ON;
324 bool funcDestroysLocals(const Func* callee) {
325 if (!callee->writesCallerFrame()) return false;
327 if (callee->fullName()->isame(s_assert.get())) {
329 * Assert is somewhat special. If RepoAuthoritative isn't set and the first
330 * parameter is a string, it will be evaled and can have arbitrary effects.
331 * If the assert fails, it may execute an arbitrary pre-registered callback
332 * which still might try to write to the assert caller's frame. This can't
333 * happen if calling such frame accessing functions dynamically is
334 * forbidden.
336 return !RuntimeOption::RepoAuthoritative || !disallowDynamicVarEnvFuncs();
338 return true;
341 bool callDestroysLocals(const NormalizedInstruction& inst,
342 const Func* caller) {
343 // We don't handle these two cases, because we don't compile functions
344 // containing them:
345 assertx(caller->lookupVarId(s_php_errormsg.get()) == -1);
346 assertx(caller->lookupVarId(s_http_response_header.get()) == -1);
348 auto* unit = caller->unit();
349 auto checkTaintId = [&](Id id) {
350 auto const str = unit->lookupLitstrId(id);
351 // Only builtins can destroy a caller's locals and if we can't lookup the
352 // function, we know its not a builtin.
353 auto const callee = Unit::lookupFunc(str);
354 return callee && funcDestroysLocals(callee);
357 if (inst.op() == OpFCallBuiltin) return checkTaintId(inst.imm[2].u_SA);
358 if (!isFCallStar(inst.op())) return false;
360 const FPIEnt *fpi = caller->findFPI(inst.source.offset());
361 assertx(fpi);
362 auto const fpushPc = unit->at(fpi->m_fpushOff);
363 auto const op = peek_op(fpushPc);
365 switch (op) {
366 case OpFPushFunc:
367 case OpFPushCufIter:
368 case OpFPushCuf:
369 case OpFPushCufF:
370 case OpFPushCufSafe: {
371 // Dynamic calls. If we've forbidden dynamic calls to functions which
372 // touch the caller's frame, we know this can't be one.
373 return !disallowDynamicVarEnvFuncs();
375 case OpFPushFuncD:
376 return checkTaintId(getImm(fpushPc, 1).u_SA);
377 case OpFPushFuncU:
378 return checkTaintId(getImm(fpushPc, 1).u_SA) ||
379 checkTaintId(getImm(fpushPc, 2).u_SA);
381 case OpFPushObjMethod:
382 case OpFPushObjMethodD:
383 case OpFPushClsMethod:
384 case OpFPushClsMethodF:
385 case OpFPushClsMethodD:
386 case OpFPushCtor:
387 case OpFPushCtorD:
388 case OpFPushCtorI:
389 // None of these touch the caller's frame because they all call methods,
390 // not top-level functions.
391 return false;
393 default:
394 always_assert("Unhandled FPush type in callDestroysLocals" && 0);
398 bool builtinFuncNeedsCallerFrame(const Func* callee) {
399 assertx(callee && callee->isCPPBuiltin());
400 return funcByNameNeedsCallerFrame(callee->name());
403 bool callNeedsCallerFrame(const NormalizedInstruction& inst,
404 const Func* caller) {
405 auto* unit = caller->unit();
406 auto checkTaintId = [&](Id id) {
407 auto const str = unit->lookupLitstrId(id);
409 if (!str) return true; // if the function was invoked dynamically we can't
410 // be sure
412 * Only C++ functions can inspect the caller frame, we know these are all
413 * loaded ahead of time and unique/persistent.
415 if (auto f = Unit::lookupFunc(str)) {
416 return f->isCPPBuiltin() && funcByNameNeedsCallerFrame(str);
418 return false;
420 if (inst.op() == OpFCallBuiltin) return checkTaintId(inst.imm[2].u_SA);
421 if (!isFCallStar(inst.op())) return false;
423 const FPIEnt *fpi = caller->findFPI(inst.source.offset());
424 assertx(fpi);
425 auto const fpushPc = unit->at(fpi->m_fpushOff);
426 auto const op = peek_op(fpushPc);
428 if (op == OpFPushFunc) return true;
429 if (op == OpFPushFuncD) return checkTaintId(getImm(fpushPc, 1).u_SA);
430 if (op == OpFPushFuncU) {
431 return checkTaintId(getImm(fpushPc, 1).u_SA) ||
432 checkTaintId(getImm(fpushPc, 2).u_SA);
435 return false;