XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1133 lines
30 KiB

  1. #include "xcache.h"
  2. #include "xcache_globals.h"
  3. #include "xc_utils.h"
  4. #ifdef ZEND_ENGINE_2_1
  5. #include "zend_vm.h"
  6. #endif
  7. #include "xc_opcode_spec.h"
  8. #undef NDEBUG
  9. #include "assert.h"
  10. #ifndef max
  11. #define max(a, b) ((a) < (b) ? (b) : (a))
  12. #endif
  13. #ifndef ZEND_VM_SET_OPCODE_HANDLER
  14. # define ZEND_VM_SET_OPCODE_HANDLER(opline) do { } while (0)
  15. #endif
  16. #ifdef ZEND_ENGINE_2_4
  17. # define OP_ZVAL_DTOR(op) do { } while(0)
  18. #else
  19. # define OP_ZVAL_DTOR(op) do { \
  20. Z_UNSET_ISREF(Z_OP_CONSTANT(op)); \
  21. zval_dtor(&Z_OP_CONSTANT(op)); \
  22. } while(0)
  23. #endif
  24. static void (*old_zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) = NULL;
  25. static void call_old_zend_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, ...) /* {{{ */
  26. {
  27. va_list args;
  28. va_start(args, format);
  29. old_zend_error_cb(type, error_filename, error_lineno, format, args);
  30. }
  31. /* }}} */
  32. xc_compile_result_t *xc_compile_result_init(xc_compile_result_t *cr, /* {{{ */
  33. zend_op_array *op_array,
  34. HashTable *function_table,
  35. HashTable *class_table)
  36. {
  37. assert(cr);
  38. cr->op_array = op_array;
  39. cr->function_table = function_table;
  40. cr->class_table = class_table;
  41. return cr;
  42. }
  43. /* }}} */
  44. xc_compile_result_t *xc_compile_result_init_cur(xc_compile_result_t *cr, zend_op_array *op_array TSRMLS_DC) /* {{{ */
  45. {
  46. assert(cr);
  47. return xc_compile_result_init(cr, op_array, CG(function_table), CG(class_table));
  48. }
  49. /* }}} */
  50. void xc_compile_result_free(xc_compile_result_t *cr) /* {{{ */
  51. {
  52. }
  53. /* }}} */
  54. int xc_apply_function(zend_function *zf, apply_func_t applyer TSRMLS_DC) /* {{{ */
  55. {
  56. switch (zf->type) {
  57. case ZEND_USER_FUNCTION:
  58. case ZEND_EVAL_CODE:
  59. return applyer(&zf->op_array TSRMLS_CC);
  60. break;
  61. case ZEND_INTERNAL_FUNCTION:
  62. case ZEND_OVERLOADED_FUNCTION:
  63. break;
  64. EMPTY_SWITCH_DEFAULT_CASE();
  65. }
  66. return 0;
  67. }
  68. /* }}} */
  69. typedef struct {
  70. apply_func_t applyer;
  71. zend_class_entry *ce;
  72. } xc_apply_method_info;
  73. int xc_apply_method(zend_function *zf, xc_apply_method_info *mi TSRMLS_DC) /* {{{ */
  74. {
  75. /* avoid duplicate apply for shadowed method */
  76. #ifdef ZEND_ENGINE_2
  77. if (mi->ce != zf->common.scope) {
  78. /* fprintf(stderr, "avoided duplicate %s\n", zf->common.function_name); */
  79. return 0;
  80. }
  81. #else
  82. char *name = zf->common.function_name;
  83. int name_s = strlen(name) + 1;
  84. zend_class_entry *ce;
  85. zend_function *ptr;
  86. for (ce = mi->ce->parent; ce; ce = ce->parent) {
  87. if (zend_hash_find(&ce->function_table, name, name_s, (void **) &ptr) == SUCCESS) {
  88. if (ptr->op_array.refcount == zf->op_array.refcount) {
  89. return 0;
  90. }
  91. }
  92. }
  93. #endif
  94. return xc_apply_function(zf, mi->applyer TSRMLS_CC);
  95. }
  96. /* }}} */
  97. #if 0
  98. int xc_apply_class(zend_class_entry *ce, apply_func_t applyer TSRMLS_DC) /* {{{ */
  99. {
  100. xc_apply_method_info mi;
  101. mi.applyer = applyer;
  102. mi.ce = ce;
  103. zend_hash_apply_with_argument(&(ce->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
  104. return 0;
  105. }
  106. /* }}} */
  107. #endif
  108. static int xc_apply_cest(xc_cest_t *cest, apply_func_t applyer TSRMLS_DC) /* {{{ */
  109. {
  110. xc_apply_method_info mi;
  111. mi.applyer = applyer;
  112. mi.ce = CestToCePtr(*cest);
  113. zend_hash_apply_with_argument(&(CestToCePtr(*cest)->function_table), (apply_func_arg_t) xc_apply_method, &mi TSRMLS_CC);
  114. return 0;
  115. }
  116. /* }}} */
  117. int xc_apply_op_array(xc_compile_result_t *cr, apply_func_t applyer TSRMLS_DC) /* {{{ */
  118. {
  119. zend_hash_apply_with_argument(cr->function_table, (apply_func_arg_t) xc_apply_function, (void *) applyer TSRMLS_CC);
  120. zend_hash_apply_with_argument(cr->class_table, (apply_func_arg_t) xc_apply_cest, (void *) applyer TSRMLS_CC);
  121. return applyer(cr->op_array TSRMLS_CC);
  122. }
  123. /* }}} */
  124. int xc_undo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  125. {
  126. zend_op *opline, *end;
  127. #ifdef ZEND_ENGINE_2_4
  128. if (!(op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
  129. return 0;
  130. }
  131. #else
  132. if (!op_array->done_pass_two) {
  133. return 0;
  134. }
  135. #endif
  136. opline = op_array->opcodes;
  137. end = opline + op_array->last;
  138. while (opline < end) {
  139. #ifdef ZEND_ENGINE_2_4
  140. if (opline->op1_type == IS_CONST) {
  141. opline->op1.constant = opline->op1.literal - op_array->literals;
  142. }
  143. if (opline->op2_type == IS_CONST) {
  144. opline->op2.constant = opline->op2.literal - op_array->literals;
  145. }
  146. #endif
  147. #ifdef ZEND_ENGINE_2_1
  148. switch (opline->opcode) {
  149. #ifdef ZEND_GOTO
  150. case ZEND_GOTO:
  151. #endif
  152. case ZEND_JMP:
  153. assert(Z_OP(opline->op1).jmp_addr >= op_array->opcodes && (zend_uint) (Z_OP(opline->op1).jmp_addr - op_array->opcodes) < op_array->last);
  154. Z_OP(opline->op1).opline_num = Z_OP(opline->op1).jmp_addr - op_array->opcodes;
  155. break;
  156. case ZEND_JMPZ:
  157. case ZEND_JMPNZ:
  158. case ZEND_JMPZ_EX:
  159. case ZEND_JMPNZ_EX:
  160. #ifdef ZEND_JMP_SET
  161. case ZEND_JMP_SET:
  162. #endif
  163. #ifdef ZEND_JMP_SET_VAR
  164. case ZEND_JMP_SET_VAR:
  165. #endif
  166. assert(Z_OP(opline->op2).jmp_addr >= op_array->opcodes && (zend_uint) (Z_OP(opline->op2).jmp_addr - op_array->opcodes) < op_array->last);
  167. Z_OP(opline->op2).opline_num = Z_OP(opline->op2).jmp_addr - op_array->opcodes;
  168. break;
  169. }
  170. #endif
  171. opline++;
  172. }
  173. #ifdef ZEND_ENGINE_2_4
  174. op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
  175. #else
  176. op_array->done_pass_two = 0;
  177. #endif
  178. return 0;
  179. }
  180. /* }}} */
  181. int xc_redo_pass_two(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  182. {
  183. zend_op *opline, *end;
  184. #ifdef ZEND_ENGINE_2_4
  185. zend_literal *literal = op_array->literals;
  186. #endif
  187. #ifdef ZEND_ENGINE_2_4
  188. if ((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
  189. return 0;
  190. }
  191. #else
  192. if (op_array->done_pass_two) {
  193. return 0;
  194. }
  195. #endif
  196. /*
  197. op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
  198. op_array->size = op_array->last;
  199. */
  200. #ifdef ZEND_ENGINE_2_4
  201. if (literal) {
  202. zend_literal *end = literal + op_array->last_literal;
  203. while (literal < end) {
  204. Z_SET_ISREF(literal->constant);
  205. Z_SET_REFCOUNT(literal->constant, 2); /* Make sure is_ref won't be reset */
  206. literal++;
  207. }
  208. }
  209. #endif
  210. opline = op_array->opcodes;
  211. end = opline + op_array->last;
  212. while (opline < end) {
  213. #ifdef ZEND_ENGINE_2_4
  214. if (opline->op1_type == IS_CONST) {
  215. opline->op1.literal = op_array->literals + opline->op1.constant;
  216. }
  217. if (opline->op2_type == IS_CONST) {
  218. opline->op2.literal = op_array->literals + opline->op2.constant;
  219. }
  220. #else
  221. if (Z_OP_TYPE(opline->op1) == IS_CONST) {
  222. Z_SET_ISREF(Z_OP_CONSTANT(opline->op1));
  223. Z_SET_REFCOUNT(Z_OP_CONSTANT(opline->op1), 2); /* Make sure is_ref won't be reset */
  224. }
  225. if (Z_OP_TYPE(opline->op2) == IS_CONST) {
  226. Z_SET_ISREF(Z_OP_CONSTANT(opline->op2));
  227. Z_SET_REFCOUNT(Z_OP_CONSTANT(opline->op2), 2);
  228. }
  229. #endif
  230. #ifdef ZEND_ENGINE_2_1
  231. switch (opline->opcode) {
  232. #ifdef ZEND_GOTO
  233. case ZEND_GOTO:
  234. #endif
  235. case ZEND_JMP:
  236. assert(Z_OP(opline->op1).opline_num < op_array->last);
  237. Z_OP(opline->op1).jmp_addr = op_array->opcodes + Z_OP(opline->op1).opline_num;
  238. break;
  239. case ZEND_JMPZ:
  240. case ZEND_JMPNZ:
  241. case ZEND_JMPZ_EX:
  242. case ZEND_JMPNZ_EX:
  243. #ifdef ZEND_JMP_SET
  244. case ZEND_JMP_SET:
  245. #endif
  246. assert(Z_OP(opline->op2).opline_num < op_array->last);
  247. Z_OP(opline->op2).jmp_addr = op_array->opcodes + Z_OP(opline->op2).opline_num;
  248. break;
  249. }
  250. ZEND_VM_SET_OPCODE_HANDLER(opline);
  251. #endif
  252. opline++;
  253. }
  254. #ifdef ZEND_ENGINE_2_4
  255. op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
  256. #else
  257. op_array->done_pass_two = 1;
  258. #endif
  259. return 0;
  260. }
  261. /* }}} */
  262. static void xc_fix_opcode_ex_znode(int tofix, xc_op_spec_t spec, Z_OP_TYPEOF_TYPE *op_type, znode_op *op, int type TSRMLS_DC) /* {{{ */
  263. {
  264. #ifdef ZEND_ENGINE_2
  265. if ((*op_type != IS_UNUSED && (spec == OPSPEC_UCLASS || spec == OPSPEC_CLASS)) ||
  266. spec == OPSPEC_FETCH) {
  267. if (tofix) {
  268. switch (*op_type) {
  269. case IS_VAR:
  270. case IS_TMP_VAR:
  271. break;
  272. default:
  273. /* TODO: data lost, find a way to keep it */
  274. /* assert(*op_type == IS_CONST); */
  275. *op_type = IS_TMP_VAR;
  276. }
  277. }
  278. }
  279. switch (*op_type) {
  280. case IS_TMP_VAR:
  281. case IS_VAR:
  282. if (tofix) {
  283. Z_OP(*op).var /= sizeof(temp_variable);
  284. }
  285. else {
  286. Z_OP(*op).var *= sizeof(temp_variable);
  287. }
  288. }
  289. #endif
  290. }
  291. /* }}} */
  292. static void xc_fix_opcode_ex(zend_op_array *op_array, int tofix TSRMLS_DC) /* {{{ */
  293. {
  294. zend_op *opline;
  295. zend_uint i;
  296. opline = op_array->opcodes;
  297. for (i = 0; i < op_array->last; i ++, opline ++) {
  298. /* 3rd optimizer may have ... */
  299. if (opline->opcode < xc_get_opcode_spec_count()) {
  300. const xc_opcode_spec_t *spec;
  301. spec = xc_get_opcode_spec(opline->opcode);
  302. xc_fix_opcode_ex_znode(tofix, spec->op1, &Z_OP_TYPE(opline->op1), &opline->op1, 0 TSRMLS_CC);
  303. xc_fix_opcode_ex_znode(tofix, spec->op2, &Z_OP_TYPE(opline->op2), &opline->op2, 1 TSRMLS_CC);
  304. xc_fix_opcode_ex_znode(tofix, spec->res, &Z_OP_TYPE(opline->result), &opline->result, 2 TSRMLS_CC);
  305. }
  306. }
  307. }
  308. /* }}} */
  309. int xc_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  310. {
  311. xc_fix_opcode_ex(op_array, 1 TSRMLS_CC);
  312. return 0;
  313. }
  314. /* }}} */
  315. int xc_undo_fix_opcode(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  316. {
  317. xc_fix_opcode_ex(op_array, 0 TSRMLS_CC);
  318. return 0;
  319. }
  320. /* }}} */
  321. int xc_foreach_early_binding_class(zend_op_array *op_array, void (*callback)(zend_op *opline, int oplineno, void *data TSRMLS_DC), void *data TSRMLS_DC) /* {{{ */
  322. {
  323. zend_op *opline, *begin, *end, *next = NULL;
  324. opline = begin = op_array->opcodes;
  325. end = opline + op_array->last;
  326. while (opline < end) {
  327. switch (opline->opcode) {
  328. #ifdef ZEND_GOTO
  329. case ZEND_GOTO:
  330. #endif
  331. case ZEND_JMP:
  332. next = begin + Z_OP(opline->op1).opline_num;
  333. break;
  334. case ZEND_JMPZNZ:
  335. next = begin + max(Z_OP(opline->op2).opline_num, opline->extended_value);
  336. break;
  337. case ZEND_JMPZ:
  338. case ZEND_JMPNZ:
  339. case ZEND_JMPZ_EX:
  340. case ZEND_JMPNZ_EX:
  341. #ifdef ZEND_JMP_SET
  342. case ZEND_JMP_SET:
  343. #endif
  344. next = begin + Z_OP(opline->op2).opline_num;
  345. break;
  346. case ZEND_RETURN:
  347. opline = end;
  348. break;
  349. #ifdef ZEND_ENGINE_2
  350. case ZEND_DECLARE_INHERITED_CLASS:
  351. callback(opline, opline - begin, data TSRMLS_CC);
  352. break;
  353. #else
  354. case ZEND_DECLARE_FUNCTION_OR_CLASS:
  355. if (opline->extended_value == ZEND_DECLARE_INHERITED_CLASS) {
  356. callback(opline, opline - begin, data TSRMLS_CC);
  357. }
  358. break;
  359. #endif
  360. }
  361. if (opline < next) {
  362. opline = next;
  363. }
  364. else {
  365. opline ++;
  366. }
  367. }
  368. return SUCCESS;
  369. }
  370. /* }}} */
  371. #ifndef ZEND_COMPILE_DELAYED_BINDING
  372. static int xc_do_early_binding(zend_op_array *op_array, HashTable *class_table, int oplineno TSRMLS_DC) /* {{{ */
  373. {
  374. zend_op *opline;
  375. TRACE("binding %d", oplineno);
  376. assert(oplineno >= 0);
  377. /* do early binding */
  378. opline = &(op_array->opcodes[oplineno]);
  379. switch (opline->opcode) {
  380. #ifdef ZEND_ENGINE_2
  381. case ZEND_DECLARE_INHERITED_CLASS:
  382. {
  383. zval *parent_name;
  384. zend_class_entry **pce;
  385. /* don't early-bind classes that implement interfaces */
  386. if ((opline + 1)->opcode == ZEND_FETCH_CLASS && (opline + 2)->opcode == ZEND_ADD_INTERFACE) {
  387. return FAILURE;
  388. }
  389. parent_name = &(Z_OP_CONSTANT((opline - 1)->op2));
  390. TRACE("binding with parent %s", Z_STRVAL_P(parent_name));
  391. if (zend_lookup_class(Z_STRVAL_P(parent_name), Z_STRLEN_P(parent_name), &pce TSRMLS_CC) == FAILURE) {
  392. return FAILURE;
  393. }
  394. if (do_bind_inherited_class(opline, class_table, *pce, 1 TSRMLS_CC) == NULL) {
  395. return FAILURE;
  396. }
  397. }
  398. /* clear unnecessary ZEND_FETCH_CLASS opcode */
  399. if (opline > op_array->opcodes
  400. && (opline - 1)->opcode == ZEND_FETCH_CLASS) {
  401. zend_op *fetch_class_opline = opline - 1;
  402. TRACE("%s %p", Z_STRVAL(Z_OP_CONSTANT(fetch_class_opline->op2)), Z_STRVAL(Z_OP_CONSTANT(fetch_class_opline->op2)));
  403. OP_ZVAL_DTOR(fetch_class_opline->op2);
  404. fetch_class_opline->opcode = ZEND_NOP;
  405. ZEND_VM_SET_OPCODE_HANDLER(fetch_class_opline);
  406. memset(&fetch_class_opline->op1, 0, sizeof(znode));
  407. memset(&fetch_class_opline->op2, 0, sizeof(znode));
  408. SET_UNUSED(fetch_class_opline->op1);
  409. SET_UNUSED(fetch_class_opline->op2);
  410. SET_UNUSED(fetch_class_opline->result);
  411. }
  412. /* clear unnecessary ZEND_VERIFY_ABSTRACT_CLASS opcode */
  413. if ((opline + 1)->opcode == ZEND_VERIFY_ABSTRACT_CLASS) {
  414. zend_op *abstract_op = opline + 1;
  415. memset(abstract_op, 0, sizeof(abstract_op[0]));
  416. abstract_op->lineno = 0;
  417. SET_UNUSED(abstract_op->op1);
  418. SET_UNUSED(abstract_op->op2);
  419. SET_UNUSED(abstract_op->result);
  420. abstract_op->opcode = ZEND_NOP;
  421. ZEND_VM_SET_OPCODE_HANDLER(abstract_op);
  422. }
  423. #else
  424. case ZEND_DECLARE_FUNCTION_OR_CLASS:
  425. if (do_bind_function_or_class(opline, NULL, class_table, 1) == FAILURE) {
  426. return FAILURE;
  427. }
  428. #endif
  429. break;
  430. default:
  431. return FAILURE;
  432. }
  433. zend_hash_del(class_table, Z_OP_CONSTANT(opline->op1).value.str.val, Z_OP_CONSTANT(opline->op1).value.str.len);
  434. OP_ZVAL_DTOR(opline->op1);
  435. OP_ZVAL_DTOR(opline->op2);
  436. opline->opcode = ZEND_NOP;
  437. ZEND_VM_SET_OPCODE_HANDLER(opline);
  438. memset(&opline->op1, 0, sizeof(znode));
  439. memset(&opline->op2, 0, sizeof(znode));
  440. SET_UNUSED(opline->op1);
  441. SET_UNUSED(opline->op2);
  442. return SUCCESS;
  443. }
  444. /* }}} */
  445. #endif
  446. #ifdef HAVE_XCACHE_CONSTANT
  447. void xc_install_constant(ZEND_24(NOTHING, const) char *filename, zend_constant *constant, zend_uchar type, const24_zstr key, uint len, ulong h TSRMLS_DC) /* {{{ */
  448. {
  449. if (zend_u_hash_add(EG(zend_constants), type, key, len,
  450. constant, sizeof(zend_constant),
  451. NULL
  452. ) == FAILURE) {
  453. CG(zend_lineno) = 0;
  454. #ifdef IS_UNICODE
  455. zend_error(E_NOTICE, "Constant %R already defined", type, key);
  456. #else
  457. zend_error(E_NOTICE, "Constant %s already defined", key);
  458. #endif
  459. free(ZSTR_V(constant->name));
  460. if (!(constant->flags & CONST_PERSISTENT)) {
  461. zval_dtor(&constant->value);
  462. }
  463. }
  464. }
  465. /* }}} */
  466. #endif
  467. void xc_install_function(ZEND_24(NOTHING, const) char *filename, zend_function *func, zend_uchar type, const24_zstr key, uint len, ulong h TSRMLS_DC) /* {{{ */
  468. {
  469. zend_bool istmpkey;
  470. if (func->type == ZEND_USER_FUNCTION) {
  471. #ifdef IS_UNICODE
  472. istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
  473. #else
  474. istmpkey = ZSTR_S(key)[0] == 0;
  475. #endif
  476. if (istmpkey) {
  477. zend_u_hash_update(CG(function_table), type, key, len,
  478. func, sizeof(zend_op_array),
  479. NULL
  480. );
  481. }
  482. else if (zend_u_hash_add(CG(function_table), type, key, len,
  483. func, sizeof(zend_op_array),
  484. NULL
  485. ) == FAILURE) {
  486. CG(zend_lineno) = ZESW(func->op_array.opcodes[0].lineno, func->op_array.line_start);
  487. #ifdef IS_UNICODE
  488. zend_error(E_ERROR, "Cannot redeclare %R()", type, key);
  489. #else
  490. zend_error(E_ERROR, "Cannot redeclare %s()", key);
  491. #endif
  492. }
  493. }
  494. }
  495. /* }}} */
  496. ZESW(xc_cest_t *, void) xc_install_class(ZEND_24(NOTHING, const) char *filename, xc_cest_t *cest, int oplineno, zend_uchar type, const24_zstr key, uint len, ulong h TSRMLS_DC) /* {{{ */
  497. {
  498. zend_bool istmpkey;
  499. zend_class_entry *cep = CestToCePtr(*cest);
  500. ZESW(void *stored_ce_ptr, NOTHING);
  501. #ifdef IS_UNICODE
  502. istmpkey = (type == IS_STRING && ZSTR_S(key)[0] == 0) || ZSTR_U(key)[0] == 0;
  503. #else
  504. istmpkey = ZSTR_S(key)[0] == 0;
  505. #endif
  506. if (istmpkey) {
  507. zend_u_hash_quick_update(CG(class_table), type, key, len, h,
  508. cest, sizeof(xc_cest_t),
  509. ZESW(&stored_ce_ptr, NULL)
  510. );
  511. #ifndef ZEND_COMPILE_DELAYED_BINDING
  512. if (oplineno != -1) {
  513. xc_do_early_binding(CG(active_op_array), CG(class_table), oplineno TSRMLS_CC);
  514. }
  515. #endif
  516. }
  517. else if (zend_u_hash_quick_add(CG(class_table), type, key, len, h,
  518. cest, sizeof(xc_cest_t),
  519. ZESW(&stored_ce_ptr, NULL)
  520. ) == FAILURE) {
  521. CG(zend_lineno) = ZESW(0, Z_CLASS_INFO(*cep).line_start);
  522. #ifdef IS_UNICODE
  523. zend_error(E_ERROR, "Cannot redeclare class %R", type, cep->name);
  524. #else
  525. zend_error(E_ERROR, "Cannot redeclare class %s", cep->name);
  526. #endif
  527. assert(oplineno == -1);
  528. }
  529. ZESW(return (xc_cest_t *) stored_ce_ptr, NOTHING);
  530. }
  531. /* }}} */
  532. typedef struct { /* sandbox {{{ */
  533. ZEND_24(NOTHING, const) char *filename;
  534. HashTable orig_included_files;
  535. HashTable *tmp_included_files;
  536. #ifdef HAVE_XCACHE_CONSTANT
  537. HashTable *orig_zend_constants;
  538. HashTable tmp_zend_constants;
  539. #endif
  540. HashTable *orig_function_table;
  541. HashTable *orig_class_table;
  542. HashTable *orig_auto_globals;
  543. HashTable tmp_function_table;
  544. HashTable tmp_class_table;
  545. HashTable tmp_auto_globals;
  546. #ifdef HAVE_XCACHE_CONSTANT
  547. Bucket *tmp_internal_constant_tail;
  548. #endif
  549. Bucket *tmp_internal_function_tail;
  550. Bucket *tmp_internal_class_tail;
  551. #ifdef XCACHE_ERROR_CACHING
  552. int orig_user_error_handler_error_reporting;
  553. zend_uint compilererror_cnt;
  554. zend_uint compilererror_size;
  555. xc_compilererror_t *compilererrors;
  556. #endif
  557. #ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
  558. zend_uint orig_compiler_options;
  559. #endif
  560. } xc_sandbox_t;
  561. #undef TG
  562. #undef OG
  563. #define TG(x) (sandbox->tmp_##x)
  564. #define OG(x) (sandbox->orig_##x)
  565. /* }}} */
  566. #ifdef XCACHE_ERROR_CACHING
  567. static void xc_sandbox_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) /* {{{ */
  568. {
  569. xc_compilererror_t *compilererror;
  570. xc_sandbox_t *sandbox;
  571. TSRMLS_FETCH();
  572. sandbox = (xc_sandbox_t *) XG(sandbox);
  573. if (!sandbox) {
  574. old_zend_error_cb(type, error_filename, error_lineno, format, args);
  575. return;
  576. }
  577. switch (type) {
  578. #ifdef E_STRICT
  579. case E_STRICT:
  580. #endif
  581. #ifdef E_DEPRECATED
  582. case E_DEPRECATED:
  583. #endif
  584. if (sandbox->compilererror_cnt <= sandbox->compilererror_size) {
  585. if (sandbox->compilererror_size) {
  586. sandbox->compilererror_size += 16;
  587. sandbox->compilererrors = erealloc(sandbox->compilererrors, sandbox->compilererror_size * sizeof(sandbox->compilererrors));
  588. }
  589. else {
  590. sandbox->compilererror_size = 16;
  591. sandbox->compilererrors = emalloc(sandbox->compilererror_size * sizeof(sandbox->compilererrors));
  592. }
  593. }
  594. compilererror = &sandbox->compilererrors[sandbox->compilererror_cnt++];
  595. compilererror->type = type;
  596. compilererror->lineno = error_lineno;
  597. compilererror->error_len = vspprintf(&compilererror->error, 0, format, args);
  598. break;
  599. default: {
  600. /* give up, and user handler is not supported in this case */
  601. zend_uint i;
  602. zend_uint old_lineno = CG(zend_lineno);
  603. for (i = 0; i < sandbox->compilererror_cnt; i ++) {
  604. compilererror = &sandbox->compilererrors[i];
  605. CG(zend_lineno) = compilererror->lineno;
  606. call_old_zend_error_cb(compilererror->type, error_filename, error_lineno, "%s", compilererror->error);
  607. efree(compilererror->error);
  608. }
  609. if (sandbox->compilererrors) {
  610. efree(sandbox->compilererrors);
  611. sandbox->compilererrors = NULL;
  612. }
  613. sandbox->compilererror_cnt = 0;
  614. sandbox->compilererror_size = 0;
  615. CG(zend_lineno) = old_lineno;
  616. old_zend_error_cb(type, error_filename, error_lineno, format, args);
  617. break;
  618. }
  619. }
  620. }
  621. /* }}} */
  622. #endif
  623. #ifdef ZEND_ENGINE_2_1
  624. static zend_bool xc_auto_global_callback(ZEND_24(NOTHING, const) char *name, uint name_len TSRMLS_DC) /* {{{ */
  625. {
  626. return 0;
  627. }
  628. /* }}} */
  629. static int xc_auto_global_arm(zend_auto_global *auto_global TSRMLS_DC) /* {{{ */
  630. {
  631. if (auto_global->auto_global_callback) {
  632. auto_global->armed = 1;
  633. auto_global->auto_global_callback = xc_auto_global_callback;
  634. }
  635. else {
  636. auto_global->armed = 0;
  637. }
  638. return ZEND_HASH_APPLY_KEEP;
  639. }
  640. /* }}} */
  641. #endif
  642. void xc_hash_copy_if(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, xc_if_func_t checker) /* {{{ */
  643. {
  644. Bucket *p;
  645. void *new_entry;
  646. zend_bool setTargetPointer;
  647. setTargetPointer = !target->pInternalPointer;
  648. p = source->pListHead;
  649. while (p) {
  650. if (checker(p->pData)) {
  651. if (setTargetPointer && source->pInternalPointer == p) {
  652. target->pInternalPointer = NULL;
  653. }
  654. if (p->nKeyLength) {
  655. zend_u_hash_quick_update(target, p->key.type, ZSTR(BUCKET_KEY_S(p)), p->nKeyLength, p->h, p->pData, size, &new_entry);
  656. } else {
  657. zend_hash_index_update(target, p->h, p->pData, size, &new_entry);
  658. }
  659. if (pCopyConstructor) {
  660. pCopyConstructor(new_entry);
  661. }
  662. }
  663. p = p->pListNext;
  664. }
  665. if (!target->pInternalPointer) {
  666. target->pInternalPointer = target->pListHead;
  667. }
  668. }
  669. /* }}} */
  670. #ifdef HAVE_XCACHE_CONSTANT
  671. static zend_bool xc_is_internal_zend_constant(zend_constant *c) /* {{{ */
  672. {
  673. return (c->flags & CONST_PERSISTENT) ? 1 : 0;
  674. }
  675. /* }}} */
  676. void xc_zend_constant_ctor(zend_constant *c) /* {{{ */
  677. {
  678. assert((c->flags & CONST_PERSISTENT));
  679. ZSTR_U(c->name) = UNISW(zend_strndup, zend_ustrndup)(ZSTR_U(c->name), c->name_len - 1);
  680. }
  681. /* }}} */
  682. void xc_zend_constant_dtor(zend_constant *c) /* {{{ */
  683. {
  684. free(ZSTR_V(c->name));
  685. }
  686. /* }}} */
  687. static void xc_free_zend_constant(zend_constant *c) /* {{{ */
  688. {
  689. if (!(c->flags & CONST_PERSISTENT)) {
  690. zval_dtor(&c->value);
  691. }
  692. free(ZSTR_V(c->name));
  693. }
  694. /* }}} */
  695. void xc_copy_internal_zend_constants(HashTable *target, HashTable *source) /* {{{ */
  696. {
  697. zend_constant tmp_const;
  698. xc_hash_copy_if(target, source, (copy_ctor_func_t) xc_zend_constant_ctor, (void *) &tmp_const, sizeof(zend_constant), (xc_if_func_t) xc_is_internal_zend_constant);
  699. }
  700. /* }}} */
  701. #endif
  702. static xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */
  703. {
  704. HashTable *h;
  705. assert(sandbox);
  706. memset(sandbox, 0, sizeof(sandbox[0]));
  707. memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files)));
  708. #ifdef HAVE_XCACHE_CONSTANT
  709. OG(zend_constants) = EG(zend_constants);
  710. EG(zend_constants) = &TG(zend_constants);
  711. #endif
  712. OG(function_table) = CG(function_table);
  713. CG(function_table) = &TG(function_table);
  714. OG(class_table) = CG(class_table);
  715. CG(class_table) = &TG(class_table);
  716. EG(class_table) = CG(class_table);
  717. #ifdef ZEND_ENGINE_2_1
  718. OG(auto_globals) = CG(auto_globals);
  719. CG(auto_globals) = &TG(auto_globals);
  720. #endif
  721. TG(included_files) = &EG(included_files);
  722. zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1);
  723. #ifdef HAVE_XCACHE_CONSTANT
  724. h = OG(zend_constants);
  725. zend_hash_init_ex(&TG(zend_constants), 20, NULL, (dtor_func_t) xc_free_zend_constant, h->persistent, h->bApplyProtection);
  726. xc_copy_internal_zend_constants(&TG(zend_constants), &XG(internal_constant_table));
  727. TG(internal_constant_tail) = TG(zend_constants).pListTail;
  728. #endif
  729. h = OG(function_table);
  730. zend_hash_init_ex(&TG(function_table), 128, NULL, ZEND_FUNCTION_DTOR, h->persistent, h->bApplyProtection);
  731. {
  732. zend_function tmp_func;
  733. zend_hash_copy(&TG(function_table), &XG(internal_function_table), NULL, (void *) &tmp_func, sizeof(tmp_func));
  734. }
  735. TG(internal_function_tail) = TG(function_table).pListTail;
  736. h = OG(class_table);
  737. zend_hash_init_ex(&TG(class_table), 16, NULL, ZEND_CLASS_DTOR, h->persistent, h->bApplyProtection);
  738. #if 0 && TODO
  739. {
  740. xc_cest_t tmp_cest;
  741. zend_hash_copy(&TG(class_table), &XG(internal_class_table), NULL, (void *) &tmp_cest, sizeof(tmp_cest));
  742. }
  743. #endif
  744. TG(internal_class_tail) = TG(class_table).pListTail;
  745. #ifdef ZEND_ENGINE_2_1
  746. /* shallow copy, don't destruct */
  747. h = OG(auto_globals);
  748. zend_hash_init_ex(&TG(auto_globals), 8, NULL, NULL, h->persistent, h->bApplyProtection);
  749. {
  750. zend_auto_global tmp_autoglobal;
  751. zend_hash_copy(&TG(auto_globals), OG(auto_globals), NULL, (void *) &tmp_autoglobal, sizeof(tmp_autoglobal));
  752. zend_hash_apply(&TG(auto_globals), (apply_func_t) xc_auto_global_arm TSRMLS_CC);
  753. }
  754. #endif
  755. sandbox->filename = filename;
  756. #ifdef XCACHE_ERROR_CACHING
  757. sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
  758. EG(user_error_handler_error_reporting) = 0;
  759. sandbox->compilererror_cnt = 0;
  760. sandbox->compilererror_size = 0;
  761. #endif
  762. #ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
  763. sandbox->orig_compiler_options = CG(compiler_options);
  764. /* Using ZEND_COMPILE_IGNORE_INTERNAL_CLASSES for ZEND_FETCH_CLASS_RT_NS_CHECK
  765. */
  766. CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES | ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_DELAYED_BINDING;
  767. #endif
  768. XG(sandbox) = (void *) sandbox;
  769. return sandbox;
  770. }
  771. /* }}} */
  772. #ifndef ZEND_COMPILE_DELAYED_BINDING
  773. static void xc_early_binding_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
  774. {
  775. xc_sandbox_t *sandbox = (xc_sandbox_t *) data;
  776. xc_do_early_binding(CG(active_op_array), OG(class_table), oplineno TSRMLS_CC);
  777. }
  778. /* }}} */
  779. #endif
  780. static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */
  781. {
  782. zend_uint i;
  783. Bucket *b;
  784. #ifdef HAVE_XCACHE_CONSTANT
  785. for (b = TG(zend_constants).pListHead; b != NULL && b != TG(internal_constant_tail); b = b->pListNext) {
  786. zend_constant *c = (zend_constant*) b->pData;
  787. xc_free_zend_constant(c);
  788. }
  789. b = TG(internal_constant_tail) ? TG(internal_constant_tail)->pListNext : TG(zend_constants).pListHead;
  790. /* install constants */
  791. while (b != NULL) {
  792. zend_constant *c = (zend_constant*) b->pData;
  793. xc_install_constant(sandbox->filename, c,
  794. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
  795. b = b->pListNext;
  796. }
  797. #endif
  798. b = TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead;
  799. /* install function */
  800. while (b != NULL) {
  801. zend_function *func = (zend_function*) b->pData;
  802. xc_install_function(sandbox->filename, func,
  803. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
  804. b = b->pListNext;
  805. }
  806. b = TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
  807. /* install class */
  808. while (b != NULL) {
  809. xc_install_class(sandbox->filename, (xc_cest_t*) b->pData, -1,
  810. BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
  811. b = b->pListNext;
  812. }
  813. #ifdef ZEND_ENGINE_2_1
  814. /* trigger auto_globals jit */
  815. for (b = TG(auto_globals).pListHead; b != NULL; b = b->pListNext) {
  816. zend_auto_global *auto_global = (zend_auto_global *) b->pData;
  817. /* check if actived */
  818. if (auto_global->auto_global_callback && !auto_global->armed) {
  819. zend_u_is_auto_global(BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), auto_global->name_len TSRMLS_CC);
  820. }
  821. }
  822. #endif
  823. #ifdef ZEND_COMPILE_DELAYED_BINDING
  824. zend_do_delayed_early_binding(CG(active_op_array) TSRMLS_CC);
  825. #else
  826. xc_undo_pass_two(CG(active_op_array) TSRMLS_CC);
  827. xc_foreach_early_binding_class(CG(active_op_array), xc_early_binding_cb, (void *) sandbox TSRMLS_CC);
  828. xc_redo_pass_two(CG(active_op_array) TSRMLS_CC);
  829. #endif
  830. #ifdef XCACHE_ERROR_CACHING
  831. /* restore trigger errors */
  832. for (i = 0; i < sandbox->compilererror_cnt; i ++) {
  833. xc_compilererror_t *error = &sandbox->compilererrors[i];
  834. CG(zend_lineno) = error->lineno;
  835. zend_error(error->type, "%s", error->error);
  836. }
  837. CG(zend_lineno) = 0;
  838. #endif
  839. i = 1;
  840. /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
  841. zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL);
  842. }
  843. /* }}} */
  844. static void xc_sandbox_free(xc_sandbox_t *sandbox, zend_op_array *op_array TSRMLS_DC) /* {{{ */
  845. {
  846. XG(sandbox) = NULL;
  847. #ifdef XCACHE_ERROR_CACHING
  848. EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting;
  849. #endif
  850. /* restore first first install function/class */
  851. #ifdef HAVE_XCACHE_CONSTANT
  852. EG(zend_constants) = OG(zend_constants);
  853. #endif
  854. CG(function_table) = OG(function_table);
  855. CG(class_table) = OG(class_table);
  856. EG(class_table) = CG(class_table);
  857. #ifdef ZEND_ENGINE_2_1
  858. CG(auto_globals) = OG(auto_globals);
  859. #endif
  860. if (op_array) {
  861. zend_op_array *old_active_op_array = CG(active_op_array);
  862. CG(in_compilation) = 1;
  863. CG(compiled_filename) = ZEND_24(NOTHING, (char *)) sandbox->filename;
  864. CG(zend_lineno) = 0;
  865. CG(active_op_array) = op_array;
  866. xc_sandbox_install(sandbox TSRMLS_CC);
  867. CG(active_op_array) = old_active_op_array;
  868. CG(in_compilation) = 0;
  869. CG(compiled_filename) = NULL;
  870. /* no free as it's installed */
  871. #ifdef HAVE_XCACHE_CONSTANT
  872. TG(zend_constants).pDestructor = NULL;
  873. #endif
  874. TG(function_table).pDestructor = NULL;
  875. TG(class_table).pDestructor = NULL;
  876. }
  877. /* destroy all the tmp */
  878. #ifdef HAVE_XCACHE_CONSTANT
  879. zend_hash_destroy(&TG(zend_constants));
  880. #endif
  881. zend_hash_destroy(&TG(function_table));
  882. zend_hash_destroy(&TG(class_table));
  883. #ifdef ZEND_ENGINE_2_1
  884. zend_hash_destroy(&TG(auto_globals));
  885. #endif
  886. zend_hash_destroy(TG(included_files));
  887. /* restore orig here, as EG/CG holded tmp before */
  888. memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files)));
  889. #ifdef XCACHE_ERROR_CACHING
  890. if (sandbox->compilererrors) {
  891. zend_uint i;
  892. for (i = 0; i < sandbox->compilererror_cnt; i ++) {
  893. efree(sandbox->compilererrors[i].error);
  894. }
  895. efree(sandbox->compilererrors);
  896. }
  897. #endif
  898. #ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
  899. CG(compiler_options) = sandbox->orig_compiler_options;
  900. #endif
  901. }
  902. /* }}} */
  903. zend_op_array *xc_sandbox(xc_sandboxed_func_t sandboxed_func, void *data, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */
  904. {
  905. xc_sandbox_t sandbox;
  906. zend_op_array *op_array = NULL;
  907. zend_bool catched = 0;
  908. memset(&sandbox, 0, sizeof(sandbox));
  909. zend_try {
  910. xc_sandbox_init(&sandbox, filename TSRMLS_CC);
  911. op_array = sandboxed_func(data TSRMLS_CC);
  912. } zend_catch {
  913. catched = 1;
  914. } zend_end_try();
  915. xc_sandbox_free(&sandbox, op_array TSRMLS_CC);
  916. if (catched) {
  917. zend_bailout();
  918. }
  919. return op_array;
  920. }
  921. /* {{{ */
  922. const Bucket *xc_sandbox_user_function_begin(TSRMLS_D) /* {{{ */
  923. {
  924. xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
  925. assert(sandbox);
  926. return TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead;
  927. }
  928. /* {{{ */
  929. const Bucket *xc_sandbox_user_class_begin(TSRMLS_D) /* {{{ */
  930. {
  931. xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
  932. assert(sandbox);
  933. return TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
  934. }
  935. /* {{{ */
  936. #ifdef XCACHE_ERROR_CACHING
  937. xc_compilererror_t *xc_sandbox_compilererrors(TSRMLS_D) /* {{{ */
  938. {
  939. xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
  940. assert(sandbox);
  941. return sandbox->compilererrors;
  942. }
  943. /* }}} */
  944. zend_uint xc_sandbox_compilererror_cnt(TSRMLS_D) /* {{{ */
  945. {
  946. xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
  947. assert(sandbox);
  948. return sandbox->compilererror_cnt;
  949. }
  950. /* }}} */
  951. #endif
  952. int xc_vtrace(const char *fmt, va_list args) /* {{{ */
  953. {
  954. return vfprintf(stderr, fmt, args);
  955. }
  956. /* }}} */
  957. int xc_trace(const char *fmt, ...) /* {{{ */
  958. {
  959. va_list args;
  960. int ret;
  961. va_start(args, fmt);
  962. ret = xc_vtrace(fmt, args);
  963. va_end(args);
  964. return ret;
  965. }
  966. /* }}} */
  967. #ifndef ZEND_ENGINE_2_3
  968. #include "ext/standard/php_string.h"
  969. size_t xc_dirname(char *path, size_t len) /* {{{ */
  970. {
  971. #ifdef ZEND_ENGINE_2
  972. return php_dirname(path, len);
  973. #else
  974. php_dirname(path, len);
  975. return strlen(path);
  976. #endif
  977. }
  978. /* }}} */
  979. long xc_atol(const char *str, int str_len) /* {{{ */
  980. {
  981. long retval;
  982. if (!str_len) {
  983. str_len = strlen(str);
  984. }
  985. retval = strtol(str, NULL, 0);
  986. if (str_len > 0) {
  987. switch (str[str_len - 1]) {
  988. case 'g':
  989. case 'G':
  990. retval *= 1024;
  991. /* break intentionally missing */
  992. case 'm':
  993. case 'M':
  994. retval *= 1024;
  995. /* break intentionally missing */
  996. case 'k':
  997. case 'K':
  998. retval *= 1024;
  999. break;
  1000. }
  1001. }
  1002. return retval;
  1003. }
  1004. /* }}} */
  1005. #endif
  1006. /* init/destroy */
  1007. int xc_util_init(int module_number TSRMLS_DC) /* {{{ */
  1008. {
  1009. #ifdef XCACHE_ERROR_CACHING
  1010. old_zend_error_cb = zend_error_cb;
  1011. zend_error_cb = xc_sandbox_error_cb;
  1012. #endif
  1013. return SUCCESS;
  1014. }
  1015. /* }}} */
  1016. void xc_util_destroy() /* {{{ */
  1017. {
  1018. #ifdef XCACHE_ERROR_CACHING
  1019. if (zend_error_cb == xc_sandbox_error_cb) {
  1020. zend_error_cb = old_zend_error_cb;
  1021. }
  1022. #endif
  1023. }
  1024. /* }}} */