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

#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
}
/* }}} */