Skip to content

Commit

Permalink
Revert "Remove runkit sandbox related code and tests"
Browse files Browse the repository at this point in the history
This reverts commit 3ad8180.
  • Loading branch information
TysonAndre committed Oct 1, 2017
1 parent a93b213 commit f19406d
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 1 deletion.
97 changes: 96 additions & 1 deletion php_runkit.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ static inline void* _debug_emalloc(void* data, int bytes, char* file, int line)
#endif

#define PHP_RUNKIT_VERSION "1.0.5b1"
#define PHP_RUNKIT_SANDBOX_CLASSNAME "Runkit_Sandbox"
#define PHP_RUNKIT_SANDBOX_PARENT_CLASSNAME "Runkit_Sandbox_Parent"

#define PHP_RUNKIT_IMPORT_FUNCTIONS 0x0001
#define PHP_RUNKIT_IMPORT_CLASS_METHODS 0x0002
Expand All @@ -81,6 +83,13 @@ static inline void* _debug_emalloc(void* data, int bytes, char* file, int line)
#endif
#endif

/* The TSRM interpreter patch required by runkit_sandbox was added in 5.1, but this package includes diffs for older versions
* Those diffs include an additional #define to indicate that they've been applied
*/
#if defined(ZTS) && defined(PHP_RUNKIT_FEATURE_SANDBOX)
#define PHP_RUNKIT_SANDBOX
#endif

#ifdef PHP_RUNKIT_FEATURE_MODIFY
#define PHP_RUNKIT_MANIPULATION
// TODO: Enable these macros once the corresponding functions/files compile and pass some of the tests.
Expand Down Expand Up @@ -161,15 +170,26 @@ PHP_FUNCTION(runkit_import);
#endif /* PHP_RUNKIT_MANIPULATION_IMPORT */
#endif /* PHP_RUNKIT_MANIPULATION */

#ifdef PHP_RUNKIT_SANDBOX
PHP_FUNCTION(runkit_sandbox_output_handler);
PHP_FUNCTION(runkit_lint);
PHP_FUNCTION(runkit_lint_file);

typedef struct _php_runkit_sandbox_object php_runkit_sandbox_object;
#endif /* PHP_RUNKIT_SANDBOX */

#ifdef PHP_RUNKIT_MANIPULATION
// typedef struct _php_runkit_default_class_members_list_element php_runkit_default_class_members_list_element;
#endif

#if defined(PHP_RUNKIT_SUPERGLOBALS) || defined(PHP_RUNKIT_MANIPULATION)
#if defined(PHP_RUNKIT_SUPERGLOBALS) || defined(PHP_RUNKIT_SANDBOX) || defined(PHP_RUNKIT_MANIPULATION)
ZEND_BEGIN_MODULE_GLOBALS(runkit)
#ifdef PHP_RUNKIT_SUPERGLOBALS
HashTable *superglobals;
#endif
#ifdef PHP_RUNKIT_SANDBOX
php_runkit_sandbox_object *current_sandbox;
#endif
#ifdef PHP_RUNKIT_MANIPULATION
HashTable *misplaced_internal_functions;
HashTable *replaced_internal_functions;
Expand Down Expand Up @@ -374,6 +394,69 @@ static inline void php_runkit_modify_function_doc_comment(zend_function *fe, zen

#endif /* PHP_RUNKIT_MANIPULATION */

#ifdef PHP_RUNKIT_SANDBOX
/* runkit_sandbox.c */
int php_runkit_init_sandbox(INIT_FUNC_ARGS);
int php_runkit_shutdown_sandbox(SHUTDOWN_FUNC_ARGS);

/* runkit_sandbox_parent.c */
int php_runkit_init_sandbox_parent(INIT_FUNC_ARGS);
int php_runkit_shutdown_sandbox_parent(SHUTDOWN_FUNC_ARGS);
int php_runkit_sandbox_array_deep_copy(RUNKIT_53_TSRMLS_ARG(zval **value), int num_args, va_list args, zend_hash_key *hash_key);

struct _php_runkit_sandbox_object {
zend_object obj;

void *context, *parent_context;

char *disable_functions;
char *disable_classes;
zval *output_handler; /* points to function which lives in the parent_context */

unsigned char bailed_out_in_eval; /* Patricide is an ugly thing. Especially when it leaves bailout address mis-set */

unsigned char active; /* A bailout will set this to 0 */
unsigned char parent_access; /* May Runkit_Sandbox_Parent be instantiated/used? */
unsigned char parent_read; /* May parent vars be read? */
unsigned char parent_write; /* May parent vars be written to? */
unsigned char parent_eval; /* May arbitrary code be run in the parent? */
unsigned char parent_include; /* May arbitrary code be included in the parent? (includes require(), and *_once()) */
unsigned char parent_echo; /* May content be echoed from the parent scope? */
unsigned char parent_call; /* May functions in the parent scope be called? */
unsigned char parent_die; /* Are $PARENT->die() / $PARENT->exit() enabled? */
unsigned long parent_scope; /* 0 == Global, 1 == Active, 2 == Active->prior, 3 == Active->prior->prior, etc... */

char *parent_scope_name; /* Combines with parent_scope to refer to a named array as a symbol table */
int parent_scope_namelen;
};


/* TODO: It'd be nice if objects and resources could make it across... */
#define PHP_SANDBOX_CROSS_SCOPE_ZVAL_COPY_CTOR(pzv) \
{ \
switch (Z_TYPE_P(pzv)) { \
case IS_RESOURCE: \
case IS_OBJECT: \
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to translate resource, or object variable to current context."); \
ZVAL_NULL(pzv); \
break; \
case IS_ARRAY: \
{ \
HashTable *original_hashtable = Z_ARRVAL_P(pzv); \
array_init(pzv); \
zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(original_hashtable), (apply_func_args_t)php_runkit_sandbox_array_deep_copy, 1, Z_ARRVAL_P(pzv) TSRMLS_CC); \
break; \
} \
default: \
zval_copy_ctor(pzv); \
} \
if (Z_REFCOUNTED_P(pzf)) \
Z_SET_REFCOUNT(pzv, 1); \
/*(pzv)->RUNKIT_IS_REF = 0; // I think I can get rid of that, since IS_REFERENCE is now part of Z_TYPE?*/ \
} \
}
#endif /* PHP_RUNKIT_SANDBOX */

#ifdef PHP_RUNKIT_MANIPULATION

// Split pnname into classname and pnname, if it contains the string "::"
Expand Down Expand Up @@ -623,6 +706,18 @@ void php_runkit_update_reflection_object_name(zend_object* object, int handle, c
} reflection_object;
#endif /* PHP_RUNKIT_MANIPULATION */

#ifdef PHP_RUNKIT_SANDBOX
// TODO: Figure out what the php7 equivalent of zend_object_store_bucket and zend_object_handle are.
/* {{{ php_runkit_zend_object_store_get_obj */
inline static zend_object *php_runkit_zend_object_store_get(const zval *zobject TSRMLS_DC)
{
// Note: Object handle may be removed from _zend_resource in the future.
int handle = Z_OBJ_HANDLE_P(zobject);
return EG(objects_store).object_buckets[handle];
}
/* }}} */
#endif

#endif /* PHP_RUNKIT_H */

/*
Expand Down
188 changes: 188 additions & 0 deletions php_runkit_sandbox.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| (c) 2008-2015 Dmitry Zenovich |
+----------------------------------------------------------------------+
| This source file is subject to the new BSD license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.opensource.org/licenses/BSD-3-Clause |
| If you did not receive a copy of the license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Dmitry Zenovich <[email protected]> |
+----------------------------------------------------------------------+
*/

#ifndef PHP_RUNKIT_SANDBOX_H
#define PHP_RUNKIT_SANDBOX_H

/* {{{ php_runkit_sandbox_has_property_int */
inline static int php_runkit_sandbox_has_property_int(int has_set_exists, zval *member TSRMLS_DC) {
zval **tmpzval;
int result = 0;

#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 1
/* Map PHP 5.0 has_property flag to PHP 5.1+ flag */
has_set_exists = (has_set_exists == 0) ? 2 : 1;
#endif

if (zend_hash_find(&EG(symbol_table), Z_STRVAL_P(member), Z_STRLEN_P(member) + 1, (void*)&tmpzval) == SUCCESS) {
switch (has_set_exists) {
case 0:
result = (Z_TYPE_PP(tmpzval) != IS_NULL);
break;
case 1:
switch (Z_TYPE_PP(tmpzval)) {
case IS_BOOL: case IS_LONG: case IS_RESOURCE:
result = (Z_LVAL_PP(tmpzval) != 0);
break;
case IS_DOUBLE:
result = (Z_DVAL_PP(tmpzval) != 0);
break;
case IS_STRING:
result = (Z_STRLEN_PP(tmpzval) > 1 || (Z_STRLEN_PP(tmpzval) == 1 && Z_STRVAL_PP(tmpzval)[0] != '0'));
break;
case IS_ARRAY:
result = zend_hash_num_elements(Z_ARRVAL_PP(tmpzval)) > 0;
break;
case IS_OBJECT:
/* TODO: Use ZE2 logic for this rather than ZE1 logic */
result = zend_hash_num_elements(Z_OBJPROP_PP(tmpzval)) > 0;
break;
case IS_NULL:
default:
result = 0;
}
break;
case 2:
result = 1;
break;
}
} else {
result = 0;
}
return result;
}
/* }}} */

/* {{{ php_runkit_sandbox_include_or_eval_int */
inline static zend_op_array *php_runkit_sandbox_include_or_eval_int(zval *return_value, zval *zcode, int type, int once, int *already_included TSRMLS_DC) {
zend_op_array *op_array = NULL;

if (type == ZEND_EVAL) {
/* eval() */
char *eval_desc = zend_make_compiled_string_description("Runkit_Sandbox Eval Code" TSRMLS_CC);
op_array = compile_string(zcode, eval_desc TSRMLS_CC);
efree(eval_desc);
} else if (!once) {
/* include() & requre() */
op_array = compile_filename(type, zcode TSRMLS_CC);
} else {
/* include_once() & require_once() */
int dummy = 1;
zend_file_handle file_handle;

if (SUCCESS == zend_stream_open(Z_STRVAL_P(zcode), &file_handle TSRMLS_CC)) {
if (!file_handle.opened_path) {
file_handle.opened_path = estrndup(Z_STRVAL_P(zcode), Z_STRLEN_P(zcode));
}
if (zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void*)&dummy, sizeof(int), NULL)==SUCCESS) {
op_array = zend_compile_file(&file_handle, type TSRMLS_CC);
zend_destroy_file_handle(&file_handle TSRMLS_CC);
} else {
RUNKIT_FILE_HANDLE_DTOR(&file_handle);
RETVAL_TRUE;
*already_included = 1;
}
}
}
return op_array;
}
/* }}} */

/* {{{ php_runkit_sandbox_call_int */
inline static void php_runkit_sandbox_call_int(zval *func_name, char **pname, zval **pretval, zval *args, zval *return_value, void *prior_context TSRMLS_DC) {
HashPosition pos;
int i;
zval **tmpzval;
int argc = zend_hash_num_elements(Z_ARRVAL_P(args));
zval ***sandbox_args = safe_emalloc(sizeof(zval**), argc, 0);

for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(args), &pos), i = 0;
(zend_hash_get_current_data_ex(Z_ARRVAL_P(args), (void*)&tmpzval, &pos) == SUCCESS) && (i < argc);
zend_hash_move_forward_ex(Z_ARRVAL_P(args), &pos), i++) {
sandbox_args[i] = emalloc(sizeof(zval*));
MAKE_STD_ZVAL(*sandbox_args[i]);
**sandbox_args[i] = **tmpzval;

if (Z_TYPE_P(*sandbox_args[i]) == IS_OBJECT && zend_get_class_entry(*sandbox_args[i], prior_context) == zend_ce_closure) {
zend_closure *closure;
zend_object *bucket;
bucket = php_runkit_zend_object_store_get_obj(*sandbox_args[i], prior_context);
closure = (zend_closure *) bucket->bucket.obj.object;
(*sandbox_args[i])->value.obj.handle = zend_objects_store_put(closure, NULL, NULL, bucket->bucket.obj.clone TSRMLS_CC);
} else
PHP_SANDBOX_CROSS_SCOPE_ZVAL_COPY_CTOR(*sandbox_args[i]);
}

/* Shouldn't be necessary */
argc = i;

/* Note: If this function is disabled by disable_functions or disable_classes,
* The user will get a confusing error message about (null)() being disabled for security reasons on line 0
* This will be fixable with a properly set EG(function_state_ptr)....just not yet
*/
if (call_user_function_ex(EG(function_table), NULL, func_name, pretval, argc, sandbox_args, 0, NULL TSRMLS_CC) == SUCCESS) {
if (*pretval) {
*return_value = **pretval;
} else {
RETVAL_TRUE;
}
} else {
php_error_docref1(NULL TSRMLS_CC, *pname, E_WARNING, "Unable to call function");
RETVAL_FALSE;
}
if (*pname) {
efree(*pname);
*pname = NULL;
}

for(i = 0; i < argc; i++) {
if (Z_TYPE_P(*sandbox_args[i]) == IS_OBJECT && zend_get_class_entry(*sandbox_args[i] TSRMLS_CC) == zend_ce_closure) {
zend_object_store_bucket *bucket = php_runkit_zend_object_store_get_obj(*sandbox_args[i] TSRMLS_CC);
zend_objects_store_del_ref(*sandbox_args[i] TSRMLS_CC);
zval_ptr_dtor(sandbox_args[i]);
bucket->bucket.obj.object = NULL;
}
zval_ptr_dtor(sandbox_args[i]);
efree(sandbox_args[i]);
}
efree(sandbox_args);
}
/* }}} */

/* {{{ php_runkit_sandbox_return_property_value */
inline static zval *php_runkit_sandbox_return_property_value(int prop_found, zval *retval TSRMLS_DC) {
if (prop_found) {
zval *return_value;

ALLOC_ZVAL(return_value);
*return_value = *retval;

/* ZE expects refcount == 0 for unowned values */
INIT_PZVAL(return_value);
PHP_SANDBOX_CROSS_SCOPE_ZVAL_COPY_CTOR(return_value);
return_value->RUNKIT_REFCOUNT--;

return return_value;
} else {
return EG(uninitialized_zval_ptr);
}
}
/* }}} */

#endif

29 changes: 29 additions & 0 deletions tests/runkit_sandbox_output_handler.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
runkit_sandbox_output_handler() function
--SKIPIF--
<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_SANDBOX) print "skip";
/* May not be available due to lack of TSRM interpreter support */
if(!function_exists("runkit_sandbox_output_handler")) print "skip"; ?>
--FILE--
<?php
$php = new Runkit_Sandbox();
runkit_sandbox_output_handler($php, 'test_handler');
$php->echo("foo\n");
$php->echo("Barish\n");
$php->echo("BAZimbly\n");

function test_handler($str) {
if (strlen($str) == 0) return NULL; /* flush() */
/* Echoing and returning have the same effect here, both go to parent's output chain */
echo 'Received string from sandbox: ' . strlen($str) . " bytes long.\n";

return strtoupper($str);
}
--EXPECTF--
Notice: runkit_sandbox_output_handler(): Use of runkit_sandbox_output_handler() is deprecated. Use $sandbox['output_handler'] instead. in %s on line %d
Received string from sandbox: 4 bytes long.
FOO
Received string from sandbox: 7 bytes long.
BARISH
Received string from sandbox: 9 bytes long.
BAZIMBLY
15 changes: 15 additions & 0 deletions tests/runkit_sandbox_with_register_globals.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Runkit_Sandbox with register_globals
--SKIPIF--
<?php
if(!extension_loaded("runkit") || !RUNKIT_FEATURE_SANDBOX) echo "skip";
if(version_compare(PHP_VERSION, '5.3.999', '>')) echo "skip";
?>
--INI--
register_globals=On
--FILE--
<?php
$php = new Runkit_Sandbox();
--EXPECTREGEX--
(((PHP )?Warning|Deprecated):\s+Directive 'register_globals' is deprecated in PHP 5\.3 and greater in Unknown on line 0)?(Fatal error: Directive 'register_globals' is no longer available in PHP in Unknown on line 0)?
--DONE--

0 comments on commit f19406d

Please sign in to comment.