PageRenderTime 518ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/vm/jit/func-effects.cpp

https://gitlab.com/iranjith4/hhvm
C++ | 413 lines | 361 code | 20 blank | 32 comment | 28 complexity | 72df6b7a9c5d959b7d84a6371ca88167 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2016 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. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/vm/jit/func-effects.h"
  17. #include "hphp/runtime/vm/func.h"
  18. #include "hphp/runtime/vm/unit.h"
  19. #include "hphp/runtime/vm/jit/normalized-instruction.h"
  20. namespace HPHP { namespace jit {
  21. namespace {
  22. const StaticString
  23. s_http_response_header("http_response_header"),
  24. s_php_errormsg("php_errormsg"),
  25. s_extract("extract"),
  26. s_extractNative("__SystemLib\\extract"),
  27. s_parse_str("parse_str"),
  28. s_parse_strNative("__SystemLib\\parse_str"),
  29. s_assert("assert"),
  30. s_assertNative("__SystemLib\\assert"),
  31. s_set_frame_metadata("HH\\set_frame_metadata");
  32. bool funcByNameDestroysLocals(const StringData* fname) {
  33. if (!fname) return false;
  34. return fname->isame(s_extract.get()) ||
  35. fname->isame(s_extractNative.get()) ||
  36. fname->isame(s_parse_str.get()) ||
  37. fname->isame(s_parse_strNative.get()) ||
  38. fname->isame(s_assert.get()) ||
  39. fname->isame(s_assertNative.get()) ||
  40. fname->isame(s_set_frame_metadata.get());
  41. }
  42. using FuncSet = std::unordered_set<std::string, string_hashi, string_eqstri>;
  43. /*
  44. * This is a conservative list of functions that we are certain won't inspect
  45. * the caller frame (generally by either CallerFrame or vm_call_user_func).
  46. */
  47. FuncSet ignoresCallerFrame = {
  48. "array_key_exists",
  49. "key_exists",
  50. "array_keys",
  51. "array_pop",
  52. "array_push",
  53. "array_rand",
  54. "array_search",
  55. "array_shift",
  56. "array_slice",
  57. "array_splice",
  58. "array_unique",
  59. "array_unshift",
  60. "array_values",
  61. "compact",
  62. "shuffle",
  63. "count",
  64. "sizeof",
  65. "each",
  66. "current",
  67. "in_array",
  68. "range",
  69. "sort",
  70. "rsort",
  71. "asort",
  72. "arsort",
  73. "ksort",
  74. "krsort",
  75. "natsort",
  76. "natcasesort",
  77. "hphp_array_idx",
  78. "ctype_alnum",
  79. "ctype_alpha",
  80. "ctype_cntrl",
  81. "ctype_digit",
  82. "ctype_graph",
  83. "ctype_lower",
  84. "ctype_print",
  85. "ctype_punct",
  86. "ctype_space",
  87. "ctype_upper",
  88. "ctype_xdigit",
  89. "fb_serialize",
  90. "fb_unserialize",
  91. "fb_compact_serialize",
  92. "fb_compact_unserialize",
  93. "fb_utf8ize",
  94. "fb_utf8_strlen",
  95. "fb_utf8_strlen_deprecated",
  96. "fb_utf8_substr",
  97. "fb_get_code_coverage",
  98. "fb_output_compression",
  99. "fb_set_exit_callback",
  100. "fb_get_last_flush_size",
  101. "fb_lazy_lstat",
  102. "fb_lazy_realpath",
  103. "hash",
  104. "hash_algos",
  105. "hash_file",
  106. "hash_final",
  107. "hash_init",
  108. "hash_update",
  109. "hash_copy",
  110. "hash_equals",
  111. "furchash_hphp_ext",
  112. "hphp_murmurhash",
  113. "get_declared_classes",
  114. "get_declared_interfaces",
  115. "get_declared_traits",
  116. "class_alias",
  117. "class_exists",
  118. "interface_exists",
  119. "trait_exists",
  120. "enum_exists",
  121. "get_class_methods",
  122. "get_class_constants",
  123. "is_a",
  124. "is_subclass_of",
  125. "method_exists",
  126. "property_exists",
  127. "error_log",
  128. "error_reporting",
  129. "restore_error_handler",
  130. "restore_exception_handler",
  131. "set_error_handler",
  132. "set_exception_handler",
  133. "hphp_set_error_page",
  134. "hphp_clear_unflushed",
  135. "get_defined_functions",
  136. "function_exists",
  137. "min",
  138. "max",
  139. "abs",
  140. "is_finite",
  141. "is_infinite",
  142. "is_nan",
  143. "ceil",
  144. "floor",
  145. "round",
  146. "deg2rad",
  147. "rad2deg",
  148. "decbin",
  149. "dechex",
  150. "decoct",
  151. "bindec",
  152. "hexdec",
  153. "octdec",
  154. "base_convert",
  155. "pow",
  156. "exp",
  157. "expm1",
  158. "log10",
  159. "log1p",
  160. "log",
  161. "cos",
  162. "cosh",
  163. "sin",
  164. "sinh",
  165. "tan",
  166. "tanh",
  167. "acos",
  168. "acosh",
  169. "asin",
  170. "asinh",
  171. "atan",
  172. "atanh",
  173. "atan2",
  174. "hypot",
  175. "fmod",
  176. "sqrt",
  177. "getrandmax",
  178. "srand",
  179. "rand",
  180. "mt_getrandmax",
  181. "mt_srand",
  182. "mt_rand",
  183. "lcg_value",
  184. "intdiv",
  185. "flush",
  186. "hphp_crash_log",
  187. "hphp_stats",
  188. "hphp_get_stats",
  189. "hphp_get_status",
  190. "hphp_get_iostatus",
  191. "hphp_set_iostatus_address",
  192. "hphp_get_timers",
  193. "hphp_output_global_state",
  194. "hphp_instruction_counter",
  195. "hphp_get_hardware_counters",
  196. "hphp_set_hardware_events",
  197. "hphp_clear_hardware_events",
  198. "wordwrap",
  199. "sprintf",
  200. "is_null",
  201. "is_bool",
  202. "is_int",
  203. "is_float",
  204. "is_numeric",
  205. "is_string",
  206. "is_scalar",
  207. "is_array",
  208. "is_object",
  209. "is_resource",
  210. "boolval",
  211. "intval",
  212. "floatval",
  213. "strval",
  214. "gettype",
  215. "get_resource_type",
  216. "settype",
  217. "serialize",
  218. "unserialize",
  219. "addcslashes",
  220. "stripcslashes",
  221. "addslashes",
  222. "stripslashes",
  223. "bin2hex",
  224. "hex2bin",
  225. "nl2br",
  226. "quotemeta",
  227. "str_shuffle",
  228. "strrev",
  229. "strtolower",
  230. "strtoupper",
  231. "ucfirst",
  232. "lcfirst",
  233. "ucwords",
  234. "strip_tags",
  235. "trim",
  236. "ltrim",
  237. "rtrim",
  238. "chop",
  239. "explode",
  240. "implode",
  241. "join",
  242. "str_split",
  243. "chunk_split",
  244. "strtok",
  245. "str_replace",
  246. "str_ireplace",
  247. "substr_replace",
  248. "substr",
  249. "str_pad",
  250. "str_repeat",
  251. "html_entity_decode",
  252. "htmlentities",
  253. "htmlspecialchars_decode",
  254. "htmlspecialchars",
  255. "fb_htmlspecialchars",
  256. "quoted_printable_encode",
  257. "quoted_printable_decode",
  258. "convert_uudecode",
  259. "convert_uuencode",
  260. "str_rot13",
  261. "crc32",
  262. "crypt",
  263. "md5",
  264. "sha1",
  265. "strtr",
  266. "convert_cyr_string",
  267. "get_html_translation_table",
  268. "hebrev",
  269. "hebrevc",
  270. "setlocale",
  271. "localeconv",
  272. "nl_langinfo",
  273. "chr",
  274. "ord",
  275. "money_format",
  276. "number_format",
  277. "strcmp",
  278. "strncmp",
  279. "strnatcmp",
  280. "strcasecmp",
  281. "strncasecmp",
  282. "strnatcasecmp",
  283. "strcoll",
  284. "substr_compare",
  285. "strchr",
  286. "strrchr",
  287. "strstr",
  288. "stristr",
  289. "strpbrk",
  290. "strpos",
  291. "stripos",
  292. "strrpos",
  293. "strripos",
  294. "substr_count",
  295. "strspn",
  296. "strcspn",
  297. "strlen",
  298. "str_getcsv",
  299. "count_chars",
  300. "str_word_count",
  301. "levenshtein",
  302. "similar_text",
  303. "soundex",
  304. "metaphone",
  305. "base64_decode",
  306. "base64_encode",
  307. "get_headers",
  308. "get_meta_tags",
  309. "http_build_query",
  310. "parse_url",
  311. "rawurldecode",
  312. "rawurlencode",
  313. "urldecode",
  314. "urlencode",
  315. };
  316. bool funcByNameNeedsCallerFrame(const StringData* fname) {
  317. return ignoresCallerFrame.find(fname->data()) == ignoresCallerFrame.end();
  318. }
  319. }
  320. bool builtinFuncDestroysLocals(const Func* callee) {
  321. assertx(callee && callee->isCPPBuiltin());
  322. auto const fname = callee->name();
  323. return funcByNameDestroysLocals(fname);
  324. }
  325. bool callDestroysLocals(const NormalizedInstruction& inst,
  326. const Func* caller) {
  327. // We don't handle these two cases, because we don't compile functions
  328. // containing them:
  329. assertx(caller->lookupVarId(s_php_errormsg.get()) == -1);
  330. assertx(caller->lookupVarId(s_http_response_header.get()) == -1);
  331. auto* unit = caller->unit();
  332. auto checkTaintId = [&](Id id) {
  333. auto const str = unit->lookupLitstrId(id);
  334. return funcByNameDestroysLocals(str);
  335. };
  336. if (inst.op() == OpFCallBuiltin) return checkTaintId(inst.imm[2].u_SA);
  337. if (!isFCallStar(inst.op())) return false;
  338. const FPIEnt *fpi = caller->findFPI(inst.source.offset());
  339. assertx(fpi);
  340. auto const fpushPc = unit->at(fpi->m_fpushOff);
  341. auto const op = peek_op(fpushPc);
  342. if (op == OpFPushFunc) {
  343. // If the call has any arguments, the FPushFunc will be in a different
  344. // tracelet -- the tracelet will break on every FPass* because the reffiness
  345. // of the callee isn't knowable. So we have to say the call destroys locals,
  346. // to be conservative. If there aren't any arguments, then it can't destroy
  347. // locals -- even if the call is to extract(), there's no argument, so it
  348. // won't do anything.
  349. auto const numArgs = inst.imm[0].u_IVA;
  350. return (numArgs != 0);
  351. }
  352. if (op == OpFPushFuncD) return checkTaintId(getImm(fpushPc, 1).u_SA);
  353. if (op == OpFPushFuncU) {
  354. return checkTaintId(getImm(fpushPc, 1).u_SA) ||
  355. checkTaintId(getImm(fpushPc, 2).u_SA);
  356. }
  357. return false;
  358. }
  359. bool builtinFuncNeedsCallerFrame(const Func* callee) {
  360. assertx(callee && callee->isCPPBuiltin());
  361. return funcByNameNeedsCallerFrame(callee->name());
  362. }
  363. bool callNeedsCallerFrame(const NormalizedInstruction& inst,
  364. const Func* caller) {
  365. auto* unit = caller->unit();
  366. auto checkTaintId = [&](Id id) {
  367. auto const str = unit->lookupLitstrId(id);
  368. if (!str) return true; // if the function was invoked dynamically we can't
  369. // be sure
  370. /*
  371. * Only C++ functions can inspect the caller frame, we know these are all
  372. * loaded ahead of time and unique/persistent.
  373. */
  374. if (auto f = Unit::lookupFunc(str)) {
  375. return f->isCPPBuiltin() && funcByNameNeedsCallerFrame(str);
  376. }
  377. return false;
  378. };
  379. if (inst.op() == OpFCallBuiltin) return checkTaintId(inst.imm[2].u_SA);
  380. if (!isFCallStar(inst.op())) return false;
  381. const FPIEnt *fpi = caller->findFPI(inst.source.offset());
  382. assertx(fpi);
  383. auto const fpushPc = unit->at(fpi->m_fpushOff);
  384. auto const op = peek_op(fpushPc);
  385. if (op == OpFPushFunc) return true;
  386. if (op == OpFPushFuncD) return checkTaintId(getImm(fpushPc, 1).u_SA);
  387. if (op == OpFPushFuncU) {
  388. return checkTaintId(getImm(fpushPc, 1).u_SA) ||
  389. checkTaintId(getImm(fpushPc, 2).u_SA);
  390. }
  391. return false;
  392. }
  393. }}