diff options
| author | xXx-caillou-xXx | 2018-07-13 10:36:50 +0200 |
|---|---|---|
| committer | jvoisin | 2018-07-13 08:36:50 +0000 |
| commit | 7963580d72a358975133f86f01de2d2eab08ba38 (patch) | |
| tree | 4bec345d70f687a2a6002b36e2f2fc79318959f6 /src/sp_execute.c | |
| parent | 12b740bc7bb01ffe397cecc5b6fa25b136304911 (diff) | |
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*`
Diffstat (limited to 'src/sp_execute.c')
| -rw-r--r-- | src/sp_execute.c | 142 |
1 files changed, 93 insertions, 49 deletions
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) { | |||
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | inline static void is_builtin_matching(const char *restrict const filename, | 39 | inline static void is_builtin_matching( |
| 40 | const char *restrict const function_name, | 40 | const zend_string *restrict const param_value, |
| 41 | const char *restrict const param_name, | 41 | const char *restrict const function_name, |
| 42 | const sp_list_node *config) { | 42 | const char *restrict const param_name, const sp_list_node *config, |
| 43 | const HashTable *ht) { | ||
| 43 | if (!config || !config->data) { | 44 | if (!config || !config->data) { |
| 44 | return; | 45 | return; |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | if (true == should_disable(EG(current_execute_data), function_name, filename, | 48 | if (true == |
| 48 | param_name)) { | 49 | should_disable_ht(EG(current_execute_data), function_name, param_value, |
| 50 | param_name, | ||
| 51 | SNUFFLEUPAGUS_G(config) | ||
| 52 | .config_disabled_functions_reg->disabled_functions, | ||
| 53 | ht)) { | ||
| 49 | sp_terminate(); | 54 | sp_terminate(); |
| 50 | } | 55 | } |
| 51 | } | 56 | } |
| @@ -70,7 +75,7 @@ is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { | |||
| 70 | return; | 75 | return; |
| 71 | } | 76 | } |
| 72 | 77 | ||
| 73 | char const *const current_function = ZSTR_VAL(EX(func)->common.function_name); | 78 | zend_string const *const current_function = EX(func)->common.function_name; |
| 74 | 79 | ||
| 75 | if (EXPECTED(NULL != current_function)) { | 80 | if (EXPECTED(NULL != current_function)) { |
| 76 | if (UNEXPECTED(false == check_is_in_eval_whitelist(current_function))) { | 81 | 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) { | |||
| 84 | sp_log_msg( | 89 | sp_log_msg( |
| 85 | "Eval_whitelist", SP_LOG_SIMULATION, | 90 | "Eval_whitelist", SP_LOG_SIMULATION, |
| 86 | "The function '%s' isn't in the eval whitelist, logging its call.", | 91 | "The function '%s' isn't in the eval whitelist, logging its call.", |
| 87 | current_function); | 92 | ZSTR_VAL(current_function)); |
| 88 | return; | 93 | return; |
| 89 | } else { | 94 | } else { |
| 90 | sp_log_msg( | 95 | sp_log_msg( |
| 91 | "Eval_whitelist", SP_LOG_DROP, | 96 | "Eval_whitelist", SP_LOG_DROP, |
| 92 | "The function '%s' isn't in the eval whitelist, dropping its call.", | 97 | "The function '%s' isn't in the eval whitelist, dropping its call.", |
| 93 | current_function); | 98 | ZSTR_VAL(current_function)); |
| 94 | sp_terminate(); | 99 | sp_terminate(); |
| 95 | } | 100 | } |
| 96 | } | 101 | } |
| @@ -100,15 +105,15 @@ is_in_eval_and_whitelisted(const zend_execute_data *execute_data) { | |||
| 100 | /* This function gets the filename in which `eval()` is called from, | 105 | /* This function gets the filename in which `eval()` is called from, |
| 101 | * since it looks like "foo.php(1) : eval()'d code", so we're starting | 106 | * since it looks like "foo.php(1) : eval()'d code", so we're starting |
| 102 | * from the end of the string until the second closing parenthesis. */ | 107 | * from the end of the string until the second closing parenthesis. */ |
| 103 | char *get_eval_filename(const char *const filename) { | 108 | zend_string *get_eval_filename(const char *const filename) { |
| 104 | size_t i = strlen(filename); | ||
| 105 | int count = 0; | 109 | int count = 0; |
| 106 | char *clean_filename = estrdup(filename); | 110 | zend_string *clean_filename = zend_string_init(filename, strlen(filename), 0); |
| 107 | 111 | ||
| 108 | while (i--) { | 112 | for (int i = ZSTR_LEN(clean_filename); i >= 0; i--) { |
| 109 | if (clean_filename[i] == '(') { | 113 | if (ZSTR_VAL(clean_filename)[i] == '(') { |
| 110 | if (count == 1) { | 114 | if (count == 1) { |
| 111 | clean_filename[i] = '\0'; | 115 | ZSTR_VAL(clean_filename)[i] = '\0'; |
| 116 | clean_filename = zend_string_truncate(clean_filename, i, 0); | ||
| 112 | break; | 117 | break; |
| 113 | } | 118 | } |
| 114 | count++; | 119 | count++; |
| @@ -125,11 +130,12 @@ static void sp_execute_ex(zend_execute_data *execute_data) { | |||
| 125 | } | 130 | } |
| 126 | 131 | ||
| 127 | if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { | 132 | if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { |
| 128 | const sp_list_node *config = | 133 | const sp_list_node *config = zend_hash_str_find_ptr( |
| 129 | SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval; | 134 | SNUFFLEUPAGUS_G(config).config_disabled_functions, "eval", 4); |
| 130 | char *filename = get_eval_filename((char *)zend_get_executed_filename()); | 135 | zend_string *filename = get_eval_filename(zend_get_executed_filename()); |
| 131 | is_builtin_matching(filename, "eval", NULL, config); | 136 | is_builtin_matching(filename, "eval", NULL, config, |
| 132 | efree(filename); | 137 | SNUFFLEUPAGUS_G(config).config_disabled_functions); |
| 138 | zend_string_release(filename); | ||
| 133 | 139 | ||
| 134 | SNUFFLEUPAGUS_G(in_eval)++; | 140 | SNUFFLEUPAGUS_G(in_eval)++; |
| 135 | orig_execute_ex(execute_data); | 141 | orig_execute_ex(execute_data); |
| @@ -137,34 +143,54 @@ static void sp_execute_ex(zend_execute_data *execute_data) { | |||
| 137 | return; | 143 | return; |
| 138 | } | 144 | } |
| 139 | 145 | ||
| 140 | if (!execute_data->prev_execute_data || | ||
| 141 | !execute_data->prev_execute_data->func || | ||
| 142 | !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) || | ||
| 143 | !execute_data->prev_execute_data->opline) { | ||
| 144 | if (UNEXPECTED(true == should_disable(execute_data, NULL, NULL, NULL))) { | ||
| 145 | sp_terminate(); | ||
| 146 | } | ||
| 147 | } else if ((execute_data->prev_execute_data->opline->opcode == | ||
| 148 | ZEND_DO_FCALL || | ||
| 149 | execute_data->prev_execute_data->opline->opcode == | ||
| 150 | ZEND_DO_UCALL || | ||
| 151 | execute_data->prev_execute_data->opline->opcode == | ||
| 152 | ZEND_DO_FCALL_BY_NAME)) { | ||
| 153 | if (UNEXPECTED(true == should_disable(execute_data, NULL, NULL, NULL))) { | ||
| 154 | sp_terminate(); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | if (NULL != EX(func)->op_array.filename) { | 146 | if (NULL != EX(func)->op_array.filename) { |
| 159 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { | 147 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { |
| 160 | terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename)); | 148 | terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename)); |
| 161 | } | 149 | } |
| 162 | } | 150 | } |
| 163 | 151 | ||
| 164 | orig_execute_ex(execute_data); | 152 | if (SNUFFLEUPAGUS_G(config).hook_execute) { |
| 153 | if (!execute_data->prev_execute_data || | ||
| 154 | !execute_data->prev_execute_data->func || | ||
| 155 | !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) || | ||
| 156 | !execute_data->prev_execute_data->opline) { | ||
| 157 | if (UNEXPECTED(true == | ||
| 158 | should_disable_ht( | ||
| 159 | execute_data, NULL, NULL, NULL, | ||
| 160 | SNUFFLEUPAGUS_G(config) | ||
| 161 | .config_disabled_functions_reg->disabled_functions, | ||
| 162 | SNUFFLEUPAGUS_G(config).config_disabled_functions))) { | ||
| 163 | sp_terminate(); | ||
| 164 | } | ||
| 165 | } else if ((execute_data->prev_execute_data->opline->opcode == | ||
| 166 | ZEND_DO_FCALL || | ||
| 167 | execute_data->prev_execute_data->opline->opcode == | ||
| 168 | ZEND_DO_UCALL || | ||
| 169 | execute_data->prev_execute_data->opline->opcode == | ||
| 170 | ZEND_DO_FCALL_BY_NAME)) { | ||
| 171 | if (UNEXPECTED(true == | ||
| 172 | should_disable_ht( | ||
| 173 | execute_data, NULL, NULL, NULL, | ||
| 174 | SNUFFLEUPAGUS_G(config) | ||
| 175 | .config_disabled_functions_reg->disabled_functions, | ||
| 176 | SNUFFLEUPAGUS_G(config).config_disabled_functions))) { | ||
| 177 | sp_terminate(); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | orig_execute_ex(execute_data); | ||
| 165 | 182 | ||
| 166 | if (UNEXPECTED(true == should_drop_on_ret(EX(return_value), execute_data))) { | 183 | if (UNEXPECTED( |
| 167 | sp_terminate(); | 184 | true == |
| 185 | should_drop_on_ret_ht( | ||
| 186 | EX(return_value), execute_data, | ||
| 187 | SNUFFLEUPAGUS_G(config) | ||
| 188 | .config_disabled_functions_reg_ret->disabled_functions, | ||
| 189 | SNUFFLEUPAGUS_G(config).config_disabled_functions_ret))) { | ||
| 190 | sp_terminate(); | ||
| 191 | } | ||
| 192 | } else { | ||
| 193 | orig_execute_ex(execute_data); | ||
| 168 | } | 194 | } |
| 169 | } | 195 | } |
| 170 | 196 | ||
| @@ -186,31 +212,49 @@ static int sp_stream_open(const char *filename, zend_file_handle *handle) { | |||
| 186 | goto end; | 212 | goto end; |
| 187 | } | 213 | } |
| 188 | 214 | ||
| 215 | zend_string *zend_filename = zend_string_init(filename, strlen(filename), 0); | ||
| 189 | switch (data->opline->opcode) { | 216 | switch (data->opline->opcode) { |
| 190 | case ZEND_INCLUDE_OR_EVAL: | 217 | case ZEND_INCLUDE_OR_EVAL: |
| 191 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { | 218 | if (true == SNUFFLEUPAGUS_G(config).config_readonly_exec->enable) { |
| 192 | terminate_if_writable(filename); | 219 | terminate_if_writable(filename); |
| 193 | } | 220 | } |
| 194 | const sp_list_node *config = | ||
| 195 | SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include; | ||
| 196 | switch (data->opline->extended_value) { | 221 | switch (data->opline->extended_value) { |
| 197 | case ZEND_INCLUDE: | 222 | case ZEND_INCLUDE: |
| 198 | is_builtin_matching(filename, "include", "inclusion path", config); | 223 | is_builtin_matching( |
| 224 | zend_filename, "include", "inclusion path", | ||
| 225 | zend_hash_str_find_ptr( | ||
| 226 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, | ||
| 227 | "include", 7), | ||
| 228 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); | ||
| 199 | break; | 229 | break; |
| 200 | case ZEND_REQUIRE: | 230 | case ZEND_REQUIRE: |
| 201 | is_builtin_matching(filename, "require", "inclusion path", config); | 231 | is_builtin_matching( |
| 232 | zend_filename, "require", "inclusion path", | ||
| 233 | zend_hash_str_find_ptr( | ||
| 234 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, | ||
| 235 | "require", 7), | ||
| 236 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); | ||
| 202 | break; | 237 | break; |
| 203 | case ZEND_REQUIRE_ONCE: | 238 | case ZEND_REQUIRE_ONCE: |
| 204 | is_builtin_matching(filename, "require_once", "inclusion path", | 239 | is_builtin_matching( |
| 205 | config); | 240 | zend_filename, "require_once", "inclusion path", |
| 241 | zend_hash_str_find_ptr( | ||
| 242 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, | ||
| 243 | "require_once", 12), | ||
| 244 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); | ||
| 206 | break; | 245 | break; |
| 207 | case ZEND_INCLUDE_ONCE: | 246 | case ZEND_INCLUDE_ONCE: |
| 208 | is_builtin_matching(filename, "include_once", "inclusion path", | 247 | is_builtin_matching( |
| 209 | config); | 248 | zend_filename, "include_once", "inclusion path", |
| 249 | zend_hash_str_find_ptr( | ||
| 250 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked, | ||
| 251 | "include_once", 12), | ||
| 252 | SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked); | ||
| 210 | break; | 253 | break; |
| 211 | EMPTY_SWITCH_DEFAULT_CASE(); | 254 | EMPTY_SWITCH_DEFAULT_CASE(); |
| 212 | } | 255 | } |
| 213 | } | 256 | } |
| 257 | efree(zend_filename); | ||
| 214 | 258 | ||
| 215 | end: | 259 | end: |
| 216 | return orig_zend_stream_open(filename, handle); | 260 | return orig_zend_stream_open(filename, handle); |
