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.
456 lines
13 KiB
456 lines
13 KiB
|
|
#include "xcache.h" |
|
#include "xc_sandbox.h" |
|
#include "xc_utils.h" |
|
#include "xcache_globals.h" |
|
|
|
/* utilities used by sandbox */ |
|
static void (*old_zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) = NULL; |
|
static void call_old_zend_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, ...) /* {{{ */ |
|
{ |
|
va_list args; |
|
va_start(args, format); |
|
old_zend_error_cb(type, error_filename, error_lineno, format, args); |
|
} |
|
/* }}} */ |
|
#ifdef ZEND_ENGINE_2_1 |
|
static zend_bool xc_auto_global_callback(ZEND_24(NOTHING, const) char *name, uint name_len TSRMLS_DC) /* {{{ */ |
|
{ |
|
return 0; |
|
} |
|
/* }}} */ |
|
static int xc_auto_global_arm(zend_auto_global *auto_global TSRMLS_DC) /* {{{ */ |
|
{ |
|
if (auto_global->auto_global_callback) { |
|
auto_global->armed = 1; |
|
auto_global->auto_global_callback = xc_auto_global_callback; |
|
} |
|
else { |
|
auto_global->armed = 0; |
|
} |
|
return ZEND_HASH_APPLY_KEEP; |
|
} |
|
/* }}} */ |
|
#endif |
|
|
|
#ifdef HAVE_XCACHE_CONSTANT |
|
static void xc_free_zend_constant(zend_constant *c) /* {{{ */ |
|
{ |
|
if (!(c->flags & CONST_PERSISTENT)) { |
|
zval_dtor(&c->value); |
|
} |
|
free(ZSTR_V(c->name)); |
|
} |
|
/* }}} */ |
|
#endif |
|
|
|
typedef struct { /* sandbox {{{ */ |
|
ZEND_24(NOTHING, const) char *filename; |
|
|
|
HashTable orig_included_files; |
|
HashTable *tmp_included_files; |
|
|
|
#ifdef HAVE_XCACHE_CONSTANT |
|
HashTable *orig_zend_constants; |
|
HashTable tmp_zend_constants; |
|
#endif |
|
HashTable *orig_function_table; |
|
HashTable *orig_class_table; |
|
HashTable *orig_auto_globals; |
|
HashTable tmp_function_table; |
|
HashTable tmp_class_table; |
|
HashTable tmp_auto_globals; |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
Bucket *tmp_internal_constant_tail; |
|
#endif |
|
Bucket *tmp_internal_function_tail; |
|
Bucket *tmp_internal_class_tail; |
|
|
|
#ifdef XCACHE_ERROR_CACHING |
|
int orig_user_error_handler_error_reporting; |
|
zend_uint compilererror_cnt; |
|
zend_uint compilererror_size; |
|
xc_compilererror_t *compilererrors; |
|
#endif |
|
|
|
#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES |
|
zend_uint orig_compiler_options; |
|
#endif |
|
} xc_sandbox_t; |
|
|
|
#undef TG |
|
#undef OG |
|
#define TG(x) (sandbox->tmp_##x) |
|
#define OG(x) (sandbox->orig_##x) |
|
/* }}} */ |
|
#ifdef XCACHE_ERROR_CACHING |
|
static void xc_sandbox_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) /* {{{ */ |
|
{ |
|
xc_compilererror_t *compilererror; |
|
xc_sandbox_t *sandbox; |
|
TSRMLS_FETCH(); |
|
|
|
sandbox = (xc_sandbox_t *) XG(sandbox); |
|
if (!sandbox) { |
|
old_zend_error_cb(type, error_filename, error_lineno, format, args); |
|
return; |
|
} |
|
|
|
switch (type) { |
|
#ifdef E_STRICT |
|
case E_STRICT: |
|
#endif |
|
#ifdef E_DEPRECATED |
|
case E_DEPRECATED: |
|
#endif |
|
if (sandbox->compilererror_cnt <= sandbox->compilererror_size) { |
|
if (sandbox->compilererror_size) { |
|
sandbox->compilererror_size += 16; |
|
sandbox->compilererrors = erealloc(sandbox->compilererrors, sandbox->compilererror_size * sizeof(sandbox->compilererrors)); |
|
} |
|
else { |
|
sandbox->compilererror_size = 16; |
|
sandbox->compilererrors = emalloc(sandbox->compilererror_size * sizeof(sandbox->compilererrors)); |
|
} |
|
} |
|
compilererror = &sandbox->compilererrors[sandbox->compilererror_cnt++]; |
|
compilererror->type = type; |
|
compilererror->lineno = error_lineno; |
|
compilererror->error_len = vspprintf(&compilererror->error, 0, format, args); |
|
break; |
|
|
|
default: { |
|
/* give up, and user handler is not supported in this case */ |
|
zend_uint i; |
|
zend_uint old_lineno = CG(zend_lineno); |
|
|
|
for (i = 0; i < sandbox->compilererror_cnt; i ++) { |
|
compilererror = &sandbox->compilererrors[i]; |
|
CG(zend_lineno) = compilererror->lineno; |
|
call_old_zend_error_cb(compilererror->type, error_filename, error_lineno, "%s", compilererror->error); |
|
efree(compilererror->error); |
|
} |
|
if (sandbox->compilererrors) { |
|
efree(sandbox->compilererrors); |
|
sandbox->compilererrors = NULL; |
|
} |
|
sandbox->compilererror_cnt = 0; |
|
sandbox->compilererror_size = 0; |
|
|
|
CG(zend_lineno) = old_lineno; |
|
old_zend_error_cb(type, error_filename, error_lineno, format, args); |
|
break; |
|
} |
|
} |
|
} |
|
/* }}} */ |
|
#endif |
|
|
|
static xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */ |
|
{ |
|
HashTable *h; |
|
|
|
assert(sandbox); |
|
memset(sandbox, 0, sizeof(sandbox[0])); |
|
|
|
memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files))); |
|
|
|
#ifdef HAVE_XCACHE_CONSTANT |
|
OG(zend_constants) = EG(zend_constants); |
|
EG(zend_constants) = &TG(zend_constants); |
|
#endif |
|
|
|
OG(function_table) = CG(function_table); |
|
CG(function_table) = &TG(function_table); |
|
|
|
OG(class_table) = CG(class_table); |
|
CG(class_table) = &TG(class_table); |
|
EG(class_table) = CG(class_table); |
|
|
|
#ifdef ZEND_ENGINE_2_1 |
|
OG(auto_globals) = CG(auto_globals); |
|
CG(auto_globals) = &TG(auto_globals); |
|
#endif |
|
|
|
TG(included_files) = &EG(included_files); |
|
|
|
zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1); |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
h = OG(zend_constants); |
|
zend_hash_init_ex(&TG(zend_constants), 20, NULL, (dtor_func_t) xc_free_zend_constant, h->persistent, h->bApplyProtection); |
|
xc_copy_internal_zend_constants(&TG(zend_constants), &XG(internal_constant_table)); |
|
TG(internal_constant_tail) = TG(zend_constants).pListTail; |
|
#endif |
|
h = OG(function_table); |
|
zend_hash_init_ex(&TG(function_table), 128, NULL, ZEND_FUNCTION_DTOR, h->persistent, h->bApplyProtection); |
|
{ |
|
zend_function tmp_func; |
|
zend_hash_copy(&TG(function_table), &XG(internal_function_table), NULL, (void *) &tmp_func, sizeof(tmp_func)); |
|
} |
|
TG(internal_function_tail) = TG(function_table).pListTail; |
|
|
|
h = OG(class_table); |
|
zend_hash_init_ex(&TG(class_table), 16, NULL, ZEND_CLASS_DTOR, h->persistent, h->bApplyProtection); |
|
#if 0 && TODO |
|
{ |
|
xc_cest_t tmp_cest; |
|
zend_hash_copy(&TG(class_table), &XG(internal_class_table), NULL, (void *) &tmp_cest, sizeof(tmp_cest)); |
|
} |
|
#endif |
|
TG(internal_class_tail) = TG(class_table).pListTail; |
|
|
|
#ifdef ZEND_ENGINE_2_1 |
|
/* shallow copy, don't destruct */ |
|
h = OG(auto_globals); |
|
zend_hash_init_ex(&TG(auto_globals), 8, NULL, NULL, h->persistent, h->bApplyProtection); |
|
{ |
|
zend_auto_global tmp_autoglobal; |
|
|
|
zend_hash_copy(&TG(auto_globals), OG(auto_globals), NULL, (void *) &tmp_autoglobal, sizeof(tmp_autoglobal)); |
|
zend_hash_apply(&TG(auto_globals), (apply_func_t) xc_auto_global_arm TSRMLS_CC); |
|
} |
|
#endif |
|
|
|
sandbox->filename = filename; |
|
|
|
#ifdef XCACHE_ERROR_CACHING |
|
sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting); |
|
EG(user_error_handler_error_reporting) = 0; |
|
|
|
sandbox->compilererror_cnt = 0; |
|
sandbox->compilererror_size = 0; |
|
#endif |
|
|
|
#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES |
|
sandbox->orig_compiler_options = CG(compiler_options); |
|
/* Using ZEND_COMPILE_IGNORE_INTERNAL_CLASSES for ZEND_FETCH_CLASS_RT_NS_CHECK |
|
*/ |
|
CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES | ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_DELAYED_BINDING; |
|
#endif |
|
|
|
XG(sandbox) = (void *) sandbox; |
|
return sandbox; |
|
} |
|
/* }}} */ |
|
|
|
#ifndef ZEND_COMPILE_DELAYED_BINDING |
|
static void xc_early_binding_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */ |
|
{ |
|
xc_sandbox_t *sandbox = (xc_sandbox_t *) data; |
|
xc_do_early_binding(CG(active_op_array), OG(class_table), oplineno TSRMLS_CC); |
|
} |
|
/* }}} */ |
|
#endif |
|
static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */ |
|
{ |
|
zend_uint i; |
|
Bucket *b; |
|
|
|
#ifdef HAVE_XCACHE_CONSTANT |
|
for (b = TG(zend_constants).pListHead; b != NULL && b != TG(internal_constant_tail); b = b->pListNext) { |
|
zend_constant *c = (zend_constant*) b->pData; |
|
xc_free_zend_constant(c); |
|
} |
|
|
|
b = TG(internal_constant_tail) ? TG(internal_constant_tail)->pListNext : TG(zend_constants).pListHead; |
|
/* install constants */ |
|
while (b != NULL) { |
|
zend_constant *c = (zend_constant*) b->pData; |
|
xc_install_constant(sandbox->filename, c, |
|
BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC); |
|
b = b->pListNext; |
|
} |
|
#endif |
|
|
|
b = TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead; |
|
/* install function */ |
|
while (b != NULL) { |
|
zend_function *func = (zend_function*) b->pData; |
|
xc_install_function(sandbox->filename, func, |
|
BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC); |
|
b = b->pListNext; |
|
} |
|
|
|
b = TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead; |
|
/* install class */ |
|
while (b != NULL) { |
|
xc_install_class(sandbox->filename, (xc_cest_t*) b->pData, -1, |
|
BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC); |
|
b = b->pListNext; |
|
} |
|
|
|
#ifdef ZEND_ENGINE_2_1 |
|
/* trigger auto_globals jit */ |
|
for (b = TG(auto_globals).pListHead; b != NULL; b = b->pListNext) { |
|
zend_auto_global *auto_global = (zend_auto_global *) b->pData; |
|
/* check if actived */ |
|
if (auto_global->auto_global_callback && !auto_global->armed) { |
|
zend_u_is_auto_global(BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), auto_global->name_len TSRMLS_CC); |
|
} |
|
} |
|
#endif |
|
|
|
#ifdef ZEND_COMPILE_DELAYED_BINDING |
|
zend_do_delayed_early_binding(CG(active_op_array) TSRMLS_CC); |
|
#else |
|
xc_undo_pass_two(CG(active_op_array) TSRMLS_CC); |
|
xc_foreach_early_binding_class(CG(active_op_array), xc_early_binding_cb, (void *) sandbox TSRMLS_CC); |
|
xc_redo_pass_two(CG(active_op_array) TSRMLS_CC); |
|
#endif |
|
|
|
#ifdef XCACHE_ERROR_CACHING |
|
/* restore trigger errors */ |
|
for (i = 0; i < sandbox->compilererror_cnt; i ++) { |
|
xc_compilererror_t *error = &sandbox->compilererrors[i]; |
|
CG(zend_lineno) = error->lineno; |
|
zend_error(error->type, "%s", error->error); |
|
} |
|
CG(zend_lineno) = 0; |
|
#endif |
|
|
|
i = 1; |
|
/* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */ |
|
zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL); |
|
} |
|
/* }}} */ |
|
static void xc_sandbox_free(xc_sandbox_t *sandbox, zend_op_array *op_array TSRMLS_DC) /* {{{ */ |
|
{ |
|
XG(sandbox) = NULL; |
|
#ifdef XCACHE_ERROR_CACHING |
|
EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting; |
|
#endif |
|
|
|
/* restore first first install function/class */ |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
EG(zend_constants) = OG(zend_constants); |
|
#endif |
|
CG(function_table) = OG(function_table); |
|
CG(class_table) = OG(class_table); |
|
EG(class_table) = CG(class_table); |
|
#ifdef ZEND_ENGINE_2_1 |
|
CG(auto_globals) = OG(auto_globals); |
|
#endif |
|
|
|
if (op_array) { |
|
zend_op_array *old_active_op_array = CG(active_op_array); |
|
CG(in_compilation) = 1; |
|
CG(compiled_filename) = ZEND_24(NOTHING, (char *)) sandbox->filename; |
|
CG(zend_lineno) = 0; |
|
|
|
CG(active_op_array) = op_array; |
|
xc_sandbox_install(sandbox TSRMLS_CC); |
|
CG(active_op_array) = old_active_op_array; |
|
|
|
CG(in_compilation) = 0; |
|
CG(compiled_filename) = NULL; |
|
|
|
/* no free as it's installed */ |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
TG(zend_constants).pDestructor = NULL; |
|
#endif |
|
TG(function_table).pDestructor = NULL; |
|
TG(class_table).pDestructor = NULL; |
|
} |
|
|
|
/* destroy all the tmp */ |
|
#ifdef HAVE_XCACHE_CONSTANT |
|
zend_hash_destroy(&TG(zend_constants)); |
|
#endif |
|
zend_hash_destroy(&TG(function_table)); |
|
zend_hash_destroy(&TG(class_table)); |
|
#ifdef ZEND_ENGINE_2_1 |
|
zend_hash_destroy(&TG(auto_globals)); |
|
#endif |
|
zend_hash_destroy(TG(included_files)); |
|
|
|
/* restore orig here, as EG/CG holded tmp before */ |
|
memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files))); |
|
|
|
#ifdef XCACHE_ERROR_CACHING |
|
if (sandbox->compilererrors) { |
|
zend_uint i; |
|
for (i = 0; i < sandbox->compilererror_cnt; i ++) { |
|
efree(sandbox->compilererrors[i].error); |
|
} |
|
efree(sandbox->compilererrors); |
|
} |
|
#endif |
|
|
|
#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES |
|
CG(compiler_options) = sandbox->orig_compiler_options; |
|
#endif |
|
} |
|
/* }}} */ |
|
zend_op_array *xc_sandbox(xc_sandboxed_func_t sandboxed_func, void *data, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */ |
|
{ |
|
xc_sandbox_t sandbox; |
|
zend_op_array *op_array = NULL; |
|
zend_bool catched = 0; |
|
|
|
memset(&sandbox, 0, sizeof(sandbox)); |
|
zend_try { |
|
xc_sandbox_init(&sandbox, filename TSRMLS_CC); |
|
op_array = sandboxed_func(data TSRMLS_CC); |
|
} zend_catch { |
|
catched = 1; |
|
} zend_end_try(); |
|
|
|
xc_sandbox_free(&sandbox, op_array TSRMLS_CC); |
|
if (catched) { |
|
zend_bailout(); |
|
} |
|
return op_array; |
|
} |
|
/* }}} */ |
|
const Bucket *xc_sandbox_user_function_begin(TSRMLS_D) /* {{{ */ |
|
{ |
|
xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox); |
|
assert(sandbox); |
|
return TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead; |
|
} |
|
/* }}} */ |
|
const Bucket *xc_sandbox_user_class_begin(TSRMLS_D) /* {{{ */ |
|
{ |
|
xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox); |
|
assert(sandbox); |
|
return TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead; |
|
} |
|
/* }}} */ |
|
#ifdef XCACHE_ERROR_CACHING |
|
xc_compilererror_t *xc_sandbox_compilererrors(TSRMLS_D) /* {{{ */ |
|
{ |
|
xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox); |
|
assert(sandbox); |
|
return sandbox->compilererrors; |
|
} |
|
/* }}} */ |
|
zend_uint xc_sandbox_compilererror_cnt(TSRMLS_D) /* {{{ */ |
|
{ |
|
xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox); |
|
assert(sandbox); |
|
return sandbox->compilererror_cnt; |
|
} |
|
/* }}} */ |
|
#endif |
|
|
|
/* init/destroy */ |
|
int xc_util_init(int module_number TSRMLS_DC) /* {{{ */ |
|
{ |
|
#ifdef XCACHE_ERROR_CACHING |
|
old_zend_error_cb = zend_error_cb; |
|
zend_error_cb = xc_sandbox_error_cb; |
|
#endif |
|
|
|
return SUCCESS; |
|
} |
|
/* }}} */ |
|
void xc_util_destroy() /* {{{ */ |
|
{ |
|
#ifdef XCACHE_ERROR_CACHING |
|
if (zend_error_cb == xc_sandbox_error_cb) { |
|
zend_error_cb = old_zend_error_cb; |
|
} |
|
#endif |
|
} |
|
/* }}} */ |
|
|
|
|