source: trunk/xcache/xc_sandbox.c @ 991

Last change on this file since 991 was 991, checked in by moo, 2 years ago

refactor: split compatibility

  • Property svn:eol-style set to native
File size: 13.2 KB
Line 
1
2#include "xcache.h"
3#include "xc_sandbox.h"
4#include "xc_utils.h"
5#include "xcache_globals.h"
6
7/* utilities used by sandbox */
8static void (*old_zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) = NULL;
9static void call_old_zend_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, ...) /* {{{ */
10{
11    va_list args;
12    va_start(args, format);
13    old_zend_error_cb(type, error_filename, error_lineno, format, args);
14}
15/* }}} */
16#ifdef ZEND_ENGINE_2_1
17static zend_bool xc_auto_global_callback(ZEND_24(NOTHING, const) char *name, uint name_len TSRMLS_DC) /* {{{ */
18{
19    return 0;
20}
21/* }}} */
22static int xc_auto_global_arm(zend_auto_global *auto_global TSRMLS_DC) /* {{{ */
23{
24    if (auto_global->auto_global_callback) {
25        auto_global->armed = 1;
26        auto_global->auto_global_callback = xc_auto_global_callback;
27    }
28    else {
29        auto_global->armed = 0;
30    }
31    return ZEND_HASH_APPLY_KEEP;
32}
33/* }}} */
34#endif
35
36#ifdef HAVE_XCACHE_CONSTANT
37static void xc_free_zend_constant(zend_constant *c) /* {{{ */
38{
39    if (!(c->flags & CONST_PERSISTENT)) {
40        zval_dtor(&c->value);
41    }
42    free(ZSTR_V(c->name));
43}
44/* }}} */
45#endif
46
47typedef struct { /* sandbox {{{ */
48    ZEND_24(NOTHING, const) char *filename;
49
50    HashTable orig_included_files;
51    HashTable *tmp_included_files;
52
53#ifdef HAVE_XCACHE_CONSTANT
54    HashTable *orig_zend_constants;
55    HashTable tmp_zend_constants;
56#endif
57    HashTable *orig_function_table;
58    HashTable *orig_class_table;
59    HashTable *orig_auto_globals;
60    HashTable tmp_function_table;
61    HashTable tmp_class_table;
62    HashTable tmp_auto_globals;
63#ifdef HAVE_XCACHE_CONSTANT
64    Bucket    *tmp_internal_constant_tail;
65#endif
66    Bucket    *tmp_internal_function_tail;
67    Bucket    *tmp_internal_class_tail;
68
69#ifdef XCACHE_ERROR_CACHING
70    int orig_user_error_handler_error_reporting;
71    zend_uint compilererror_cnt;
72    zend_uint compilererror_size;
73    xc_compilererror_t *compilererrors;
74#endif
75
76#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
77    zend_uint orig_compiler_options;
78#endif
79} xc_sandbox_t;
80
81#undef TG
82#undef OG
83#define TG(x) (sandbox->tmp_##x)
84#define OG(x) (sandbox->orig_##x)
85/* }}} */
86#ifdef XCACHE_ERROR_CACHING
87static void xc_sandbox_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) /* {{{ */
88{
89    xc_compilererror_t *compilererror;
90    xc_sandbox_t *sandbox;
91    TSRMLS_FETCH();
92
93    sandbox = (xc_sandbox_t *) XG(sandbox);
94    if (!sandbox) {
95        old_zend_error_cb(type, error_filename, error_lineno, format, args);
96        return;
97    }
98
99    switch (type) {
100#ifdef E_STRICT
101    case E_STRICT:
102#endif
103#ifdef E_DEPRECATED
104    case E_DEPRECATED:
105#endif
106        if (sandbox->compilererror_cnt <= sandbox->compilererror_size) {
107            if (sandbox->compilererror_size) {
108                sandbox->compilererror_size += 16;
109                sandbox->compilererrors = erealloc(sandbox->compilererrors, sandbox->compilererror_size * sizeof(sandbox->compilererrors));
110            }
111            else {
112                sandbox->compilererror_size = 16;
113                sandbox->compilererrors = emalloc(sandbox->compilererror_size * sizeof(sandbox->compilererrors));
114            }
115        }
116        compilererror = &sandbox->compilererrors[sandbox->compilererror_cnt++];
117        compilererror->type = type;
118        compilererror->lineno = error_lineno;
119        compilererror->error_len = vspprintf(&compilererror->error, 0, format, args);
120        break;
121
122    default: {
123        /* give up, and user handler is not supported in this case */
124        zend_uint i;
125        zend_uint old_lineno = CG(zend_lineno);
126
127        for (i = 0; i < sandbox->compilererror_cnt; i ++) {
128            compilererror = &sandbox->compilererrors[i];
129            CG(zend_lineno) = compilererror->lineno;
130            call_old_zend_error_cb(compilererror->type, error_filename, error_lineno, "%s", compilererror->error);
131            efree(compilererror->error);
132        }
133        if (sandbox->compilererrors) {
134            efree(sandbox->compilererrors);
135            sandbox->compilererrors = NULL;
136        }
137        sandbox->compilererror_cnt  = 0;
138        sandbox->compilererror_size = 0;
139
140        CG(zend_lineno) = old_lineno;
141        old_zend_error_cb(type, error_filename, error_lineno, format, args);
142        break;
143    }
144    }
145}
146/* }}} */
147#endif
148
149static xc_sandbox_t *xc_sandbox_init(xc_sandbox_t *sandbox, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */
150{
151    HashTable *h;
152
153    assert(sandbox);
154    memset(sandbox, 0, sizeof(sandbox[0]));
155
156    memcpy(&OG(included_files), &EG(included_files), sizeof(EG(included_files)));
157
158#ifdef HAVE_XCACHE_CONSTANT
159    OG(zend_constants) = EG(zend_constants);
160    EG(zend_constants) = &TG(zend_constants);
161#endif
162
163    OG(function_table) = CG(function_table);
164    CG(function_table) = &TG(function_table);
165
166    OG(class_table) = CG(class_table);
167    CG(class_table) = &TG(class_table);
168    EG(class_table) = CG(class_table);
169
170#ifdef ZEND_ENGINE_2_1
171    OG(auto_globals) = CG(auto_globals);
172    CG(auto_globals) = &TG(auto_globals);
173#endif
174
175    TG(included_files) = &EG(included_files);
176
177    zend_hash_init_ex(TG(included_files), 5, NULL, NULL, 0, 1);
178#ifdef HAVE_XCACHE_CONSTANT
179    h = OG(zend_constants);
180    zend_hash_init_ex(&TG(zend_constants),  20, NULL, (dtor_func_t) xc_free_zend_constant, h->persistent, h->bApplyProtection);
181    xc_copy_internal_zend_constants(&TG(zend_constants), &XG(internal_constant_table));
182    TG(internal_constant_tail) = TG(zend_constants).pListTail;
183#endif
184    h = OG(function_table);
185    zend_hash_init_ex(&TG(function_table), 128, NULL, ZEND_FUNCTION_DTOR, h->persistent, h->bApplyProtection);
186    {
187        zend_function tmp_func;
188        zend_hash_copy(&TG(function_table), &XG(internal_function_table), NULL, (void *) &tmp_func, sizeof(tmp_func));
189    }
190    TG(internal_function_tail) = TG(function_table).pListTail;
191
192    h = OG(class_table);
193    zend_hash_init_ex(&TG(class_table),     16, NULL, ZEND_CLASS_DTOR, h->persistent, h->bApplyProtection);
194#if 0 && TODO
195    {
196        xc_cest_t tmp_cest;
197        zend_hash_copy(&TG(class_table), &XG(internal_class_table), NULL, (void *) &tmp_cest, sizeof(tmp_cest));
198    }
199#endif
200    TG(internal_class_tail) = TG(class_table).pListTail;
201
202#ifdef ZEND_ENGINE_2_1
203    /* shallow copy, don't destruct */
204    h = OG(auto_globals);
205    zend_hash_init_ex(&TG(auto_globals),     8, NULL,           NULL, h->persistent, h->bApplyProtection);
206    {
207        zend_auto_global tmp_autoglobal;
208
209        zend_hash_copy(&TG(auto_globals), OG(auto_globals), NULL, (void *) &tmp_autoglobal, sizeof(tmp_autoglobal));
210        zend_hash_apply(&TG(auto_globals), (apply_func_t) xc_auto_global_arm TSRMLS_CC);
211    }
212#endif
213
214    sandbox->filename = filename;
215
216#ifdef XCACHE_ERROR_CACHING
217    sandbox->orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
218    EG(user_error_handler_error_reporting) = 0;
219
220    sandbox->compilererror_cnt  = 0;
221    sandbox->compilererror_size = 0;
222#endif
223
224#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
225    sandbox->orig_compiler_options = CG(compiler_options);
226    /* Using ZEND_COMPILE_IGNORE_INTERNAL_CLASSES for ZEND_FETCH_CLASS_RT_NS_CHECK
227     */
228    CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES | ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_DELAYED_BINDING;
229#endif
230
231    XG(sandbox) = (void *) sandbox;
232    return sandbox;
233}
234/* }}} */
235
236#ifndef ZEND_COMPILE_DELAYED_BINDING
237static void xc_early_binding_cb(zend_op *opline, int oplineno, void *data TSRMLS_DC) /* {{{ */
238{
239    xc_sandbox_t *sandbox = (xc_sandbox_t *) data;
240    xc_do_early_binding(CG(active_op_array), OG(class_table), oplineno TSRMLS_CC);
241}
242/* }}} */
243#endif
244static void xc_sandbox_install(xc_sandbox_t *sandbox TSRMLS_DC) /* {{{ */
245{
246    zend_uint i;
247    Bucket *b;
248
249#ifdef HAVE_XCACHE_CONSTANT
250    for (b = TG(zend_constants).pListHead; b != NULL && b != TG(internal_constant_tail); b = b->pListNext) {
251        zend_constant *c = (zend_constant*) b->pData;
252        xc_free_zend_constant(c);
253    }
254
255    b = TG(internal_constant_tail) ? TG(internal_constant_tail)->pListNext : TG(zend_constants).pListHead;
256    /* install constants */
257    while (b != NULL) {
258        zend_constant *c = (zend_constant*) b->pData;
259        xc_install_constant(sandbox->filename, c,
260                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
261        b = b->pListNext;
262    }
263#endif
264
265    b = TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead;
266    /* install function */
267    while (b != NULL) {
268        zend_function *func = (zend_function*) b->pData;
269        xc_install_function(sandbox->filename, func,
270                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
271        b = b->pListNext;
272    }
273
274    b = TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
275    /* install class */
276    while (b != NULL) {
277        xc_install_class(sandbox->filename, (xc_cest_t*) b->pData, -1,
278                BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), b->nKeyLength, b->h TSRMLS_CC);
279        b = b->pListNext;
280    }
281
282#ifdef ZEND_ENGINE_2_1
283    /* trigger auto_globals jit */
284    for (b = TG(auto_globals).pListHead; b != NULL; b = b->pListNext) {
285        zend_auto_global *auto_global = (zend_auto_global *) b->pData;
286        /* check if actived */
287        if (auto_global->auto_global_callback && !auto_global->armed) {
288            zend_u_is_auto_global(BUCKET_KEY_TYPE(b), ZSTR(BUCKET_KEY_S(b)), auto_global->name_len TSRMLS_CC);
289        }
290    }
291#endif
292
293#ifdef ZEND_COMPILE_DELAYED_BINDING
294    zend_do_delayed_early_binding(CG(active_op_array) TSRMLS_CC);
295#else
296    xc_undo_pass_two(CG(active_op_array) TSRMLS_CC);
297    xc_foreach_early_binding_class(CG(active_op_array), xc_early_binding_cb, (void *) sandbox TSRMLS_CC);
298    xc_redo_pass_two(CG(active_op_array) TSRMLS_CC);
299#endif
300
301#ifdef XCACHE_ERROR_CACHING
302    /* restore trigger errors */
303    for (i = 0; i < sandbox->compilererror_cnt; i ++) {
304        xc_compilererror_t *error = &sandbox->compilererrors[i];
305        CG(zend_lineno) = error->lineno;
306        zend_error(error->type, "%s", error->error);
307    }
308    CG(zend_lineno) = 0;
309#endif
310
311    i = 1;
312    /* still needed because in zend_language_scanner.l, require()/include() check file_handle.handle.stream.handle */
313    zend_hash_add(&OG(included_files), sandbox->filename, strlen(sandbox->filename) + 1, (void *)&i, sizeof(int), NULL);
314}
315/* }}} */
316static void xc_sandbox_free(xc_sandbox_t *sandbox, zend_op_array *op_array TSRMLS_DC) /* {{{ */
317{
318    XG(sandbox) = NULL;
319#ifdef XCACHE_ERROR_CACHING
320    EG(user_error_handler_error_reporting) = sandbox->orig_user_error_handler_error_reporting;
321#endif
322
323    /* restore first first install function/class */
324#ifdef HAVE_XCACHE_CONSTANT
325    EG(zend_constants) = OG(zend_constants);
326#endif
327    CG(function_table) = OG(function_table);
328    CG(class_table)    = OG(class_table);
329    EG(class_table)    = CG(class_table);
330#ifdef ZEND_ENGINE_2_1
331    CG(auto_globals)   = OG(auto_globals);
332#endif
333
334    if (op_array) {
335        zend_op_array *old_active_op_array = CG(active_op_array);
336        CG(in_compilation)    = 1;
337        CG(compiled_filename) = ZEND_24(NOTHING, (char *)) sandbox->filename;
338        CG(zend_lineno)       = 0;
339
340        CG(active_op_array) = op_array;
341        xc_sandbox_install(sandbox TSRMLS_CC);
342        CG(active_op_array) = old_active_op_array;
343
344        CG(in_compilation)    = 0;
345        CG(compiled_filename) = NULL;
346
347        /* no free as it's installed */
348#ifdef HAVE_XCACHE_CONSTANT
349        TG(zend_constants).pDestructor = NULL;
350#endif
351        TG(function_table).pDestructor = NULL;
352        TG(class_table).pDestructor = NULL;
353    }
354
355    /* destroy all the tmp */
356#ifdef HAVE_XCACHE_CONSTANT
357    zend_hash_destroy(&TG(zend_constants));
358#endif
359    zend_hash_destroy(&TG(function_table));
360    zend_hash_destroy(&TG(class_table));
361#ifdef ZEND_ENGINE_2_1
362    zend_hash_destroy(&TG(auto_globals));
363#endif
364    zend_hash_destroy(TG(included_files));
365
366    /* restore orig here, as EG/CG holded tmp before */
367    memcpy(&EG(included_files), &OG(included_files), sizeof(EG(included_files)));
368
369#ifdef XCACHE_ERROR_CACHING
370    if (sandbox->compilererrors) {
371        zend_uint i;
372        for (i = 0; i < sandbox->compilererror_cnt; i ++) {
373            efree(sandbox->compilererrors[i].error);
374        }
375        efree(sandbox->compilererrors);
376    }
377#endif
378
379#ifdef ZEND_COMPILE_IGNORE_INTERNAL_CLASSES
380    CG(compiler_options) = sandbox->orig_compiler_options;
381#endif
382}
383/* }}} */
384zend_op_array *xc_sandbox(xc_sandboxed_func_t sandboxed_func, void *data, ZEND_24(NOTHING, const) char *filename TSRMLS_DC) /* {{{ */
385{
386    xc_sandbox_t sandbox;
387    zend_op_array *op_array = NULL;
388    zend_bool catched = 0;
389
390    memset(&sandbox, 0, sizeof(sandbox));
391    zend_try {
392        xc_sandbox_init(&sandbox, filename TSRMLS_CC);
393        op_array = sandboxed_func(data TSRMLS_CC);
394    } zend_catch {
395        catched = 1;
396    } zend_end_try();
397
398    xc_sandbox_free(&sandbox, op_array TSRMLS_CC);
399    if (catched) {
400        zend_bailout();
401    }
402    return op_array;
403}
404/* }}} */
405const Bucket *xc_sandbox_user_function_begin(TSRMLS_D) /* {{{ */
406{
407    xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
408    assert(sandbox);
409    return TG(internal_function_tail) ? TG(internal_function_tail)->pListNext : TG(function_table).pListHead;
410}
411/* }}} */
412const Bucket *xc_sandbox_user_class_begin(TSRMLS_D) /* {{{ */
413{
414    xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
415    assert(sandbox);
416    return TG(internal_class_tail) ? TG(internal_class_tail)->pListNext : TG(class_table).pListHead;
417}
418/* }}} */
419#ifdef XCACHE_ERROR_CACHING
420xc_compilererror_t *xc_sandbox_compilererrors(TSRMLS_D) /* {{{ */
421{
422    xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
423    assert(sandbox);
424    return sandbox->compilererrors;
425}
426/* }}} */
427zend_uint xc_sandbox_compilererror_cnt(TSRMLS_D) /* {{{ */
428{
429    xc_sandbox_t *sandbox = (xc_sandbox_t *) XG(sandbox);
430    assert(sandbox);
431    return sandbox->compilererror_cnt;
432}
433/* }}} */
434#endif
435
436/* init/destroy */
437int xc_util_init(int module_number TSRMLS_DC) /* {{{ */
438{
439#ifdef XCACHE_ERROR_CACHING
440    old_zend_error_cb = zend_error_cb;
441    zend_error_cb = xc_sandbox_error_cb;
442#endif
443
444    return SUCCESS;
445}
446/* }}} */
447void xc_util_destroy() /* {{{ */
448{
449#ifdef XCACHE_ERROR_CACHING
450    if (zend_error_cb == xc_sandbox_error_cb) {
451        zend_error_cb = old_zend_error_cb;
452    }
453#endif
454}
455/* }}} */
456
Note: See TracBrowser for help on using the repository browser.