From 7963580d72a358975133f86f01de2d2eab08ba38 Mon Sep 17 00:00:00 2001 From: xXx-caillou-xXx Date: Fri, 13 Jul 2018 10:36:50 +0200 Subject: Massively optimize how rules are handled This commit does a lot of things: - Use hashtables instead of lists to store the rules - Rules that can be applied at launch time won't be tried at runtime - Improve feedback when writing nonsensical rules - Make intensive use of `zend_string` instead of `char*`--- src/sp_execute.c | 142 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 49 deletions(-) (limited to 'src/sp_execute.c') diff --git a/src/sp_execute.c b/src/sp_execute.c index fb956ca..6e38c75 100644 --- a/src/sp_execute.c +++ b/src/sp_execute.c @@ -36,16 +36,21 @@ ZEND_COLD static inline void terminate_if_writable(const char *filename) { } } -inline static void is_builtin_matching(const char *restrict const filename, - const char *restrict const function_name, - const char *restrict const param_name, - const sp_list_node *config) { +inline static void is_builtin_matching( + const zend_string *restrict const param_value, + const char *restrict const function_name, + const char *restrict const param_name, const sp_list_node *config, + const HashTable *ht) { if (!config || !config->data) { return; } - if (true == should_disable(EG(current_execute_data), function_name, filename, - param_name)) { + if (true == + should_disable_ht(EG(current_execute_data), function_name, param_value, + param_name, + SNUFFLEUPAGUS_G(config) + .config_disabled_functions_reg->disabled_functions, + ht)) { sp_terminate(); } } @@ -70,7 +75,7 @@ is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { return; } - char const *const current_function = ZSTR_VAL(EX(func)->common.function_name); + zend_string const *const current_function = EX(func)->common.function_name; if (EXPECTED(NULL != current_function)) { if (UNEXPECTED(false == check_is_in_eval_whitelist(current_function))) { @@ -84,13 +89,13 @@ is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { sp_log_msg( "Eval_whitelist", SP_LOG_SIMULATION, "The function '%s' isn't in the eval whitelist, logging its call.", - current_function); + ZSTR_VAL(current_function)); return; } else { sp_log_msg( "Eval_whitelist", SP_LOG_DROP, "The function '%s' isn't in the eval whitelist, dropping its call.", - current_function); + ZSTR_VAL(current_function)); sp_terminate(); } } @@ -100,15 +105,15 @@ is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { /* This function gets the filename in which `eval()` is called from, * since it looks like "foo.php(1) : eval()'d code", so we're starting * from the end of the string until the second closing parenthesis. */ -char *get_eval_filename(const char *const filename) { - size_t i = strlen(filename); +zend_string *get_eval_filename(const char *const filename) { int count = 0; - char *clean_filename = estrdup(filename); + zend_string *clean_filename = zend_string_init(filename, strlen(filename), 0); - while (i--) { - if (clean_filename[i] == '(') { + for (int i = ZSTR_LEN(clean_filename); i >= 0; i--) { + if (ZSTR_VAL(clean_filename)[i] == '(') { if (count == 1) { - clean_filename[i] = '\0'; + ZSTR_VAL(clean_filename)[i] = '\0'; + clean_filename = zend_string_truncate(clean_filename, i, 0); break; } count++; @@ -125,11 +130,12 @@ static void sp_execute_ex(zend_execute_data *execute_data) { } if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { - const sp_list_node *config = - SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval; - char *filename = get_eval_filename((char *)zend_get_executed_filename()); - is_builtin_matching(filename, "eval", NULL, config); - efree(filename); + const sp_list_node *config = zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(config).config_disabled_functions, "eval", 4); + zend_string *filename = get_eval_filename(zend_get_executed_filename()); + is_builtin_matching(filename, "eval", NULL, config, + SNUFFLEUPAGUS_G(config).config_disabled_functions); + zend_string_release(filename); SNUFFLEUPAGUS_G(in_eval)++; orig_execute_ex(execute_data); @@ -137,34 +143,54 @@ static void sp_execute_ex(zend_execute_data *execute_data) { return; } - if (!execute_data->prev_execute_data || - !execute_data->prev_execute_data->func || - !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) || - !execute_data->prev_execute_data->opline) { - if (UNEXPECTED(true == should_disable(execute_data, NULL, NULL, NULL))) { - sp_terminate(); - } - } else if ((execute_data->prev_execute_data->opline->opcode == - ZEND_DO_FCALL || - execute_data->prev_execute_data->opline->opcode == - ZEND_DO_UCALL || - execute_data->prev_execute_data->opline->opcode == - ZEND_DO_FCALL_BY_NAME)) { - if (UNEXPECTED(true == should_disable(execute_data, NULL, NULL, NULL))) { - sp_terminate(); - } - } - if (NULL != EX(func)->op_array.filename) { if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename)); } } - orig_execute_ex(execute_data); + if (SNUFFLEUPAGUS_G(config).hook_execute) { + if (!execute_data->prev_execute_data || + !execute_data->prev_execute_data->func || + !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) || + !execute_data->prev_execute_data->opline) { + if (UNEXPECTED(true == + should_disable_ht( + execute_data, NULL, NULL, NULL, + SNUFFLEUPAGUS_G(config) + .config_disabled_functions_reg->disabled_functions, + SNUFFLEUPAGUS_G(config).config_disabled_functions))) { + sp_terminate(); + } + } else if ((execute_data->prev_execute_data->opline->opcode == + ZEND_DO_FCALL || + execute_data->prev_execute_data->opline->opcode == + ZEND_DO_UCALL || + execute_data->prev_execute_data->opline->opcode == + ZEND_DO_FCALL_BY_NAME)) { + if (UNEXPECTED(true == + should_disable_ht( + execute_data, NULL, NULL, NULL, + SNUFFLEUPAGUS_G(config) + .config_disabled_functions_reg->disabled_functions, + SNUFFLEUPAGUS_G(config).config_disabled_functions))) { + sp_terminate(); + } + } + + orig_execute_ex(execute_data); - if (UNEXPECTED(true == should_drop_on_ret(EX(return_value), execute_data))) { - sp_terminate(); + if (UNEXPECTED( + true == + should_drop_on_ret_ht( + EX(return_value), execute_data, + SNUFFLEUPAGUS_G(config) + .config_disabled_functions_reg_ret->disabled_functions, + SNUFFLEUPAGUS_G(config).config_disabled_functions_ret))) { + sp_terminate(); + } + } else { + orig_execute_ex(execute_data); } } @@ -186,31 +212,49 @@ static int sp_stream_open(const char *filename, zend_file_handle *handle) { goto end; } + zend_string *zend_filename = zend_string_init(filename, strlen(filename), 0); switch (data->opline->opcode) { case ZEND_INCLUDE_OR_EVAL: if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { terminate_if_writable(filename); } - const sp_list_node *config = - SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include; switch (data->opline->extended_value) { case ZEND_INCLUDE: - is_builtin_matching(filename, "include", "inclusion path", config); + is_builtin_matching( + zend_filename, "include", "inclusion path", + zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, + "include", 7), + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); break; case ZEND_REQUIRE: - is_builtin_matching(filename, "require", "inclusion path", config); + is_builtin_matching( + zend_filename, "require", "inclusion path", + zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, + "require", 7), + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); break; case ZEND_REQUIRE_ONCE: - is_builtin_matching(filename, "require_once", "inclusion path", - config); + is_builtin_matching( + zend_filename, "require_once", "inclusion path", + zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, + "require_once", 12), + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); break; case ZEND_INCLUDE_ONCE: - is_builtin_matching(filename, "include_once", "inclusion path", - config); + is_builtin_matching( + zend_filename, "include_once", "inclusion path", + zend_hash_str_find_ptr( + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, + "include_once", 12), + SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); break; EMPTY_SWITCH_DEFAULT_CASE(); } } + efree(zend_filename); end: return orig_zend_stream_open(filename, handle); -- cgit v1.3