456 lines
13 KiB
C
456 lines
13 KiB
C
|
|
#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
|
|
|
|
/* MINIT/MSHUTDOWN */
|
|
int xc_sandbox_module_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_sandbox_module_shutdown() /* {{{ */
|
|
{
|
|
#ifdef XCACHE_ERROR_CACHING
|
|
if (zend_error_cb == xc_sandbox_error_cb) {
|
|
zend_error_cb = old_zend_error_cb;
|
|
}
|
|
#endif
|
|
}
|
|
/* }}} */
|