diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | src/sp_config.h | 1 | ||||
| -rw-r--r-- | src/sp_config_keywords.c | 4 | ||||
| -rw-r--r-- | src/sp_config_utils.c | 20 | ||||
| -rw-r--r-- | src/sp_config_utils.h | 1 | ||||
| -rw-r--r-- | src/sp_disabled_functions.c | 48 | ||||
| -rw-r--r-- | src/sp_list.c | 18 | ||||
| -rw-r--r-- | src/sp_list.h | 1 | ||||
| -rw-r--r-- | src/tests/config/config_disabled_functions_chain.ini | 1 | ||||
| -rw-r--r-- | src/tests/disabled_functions_chain.phpt | 28 | ||||
| -rw-r--r-- | src/tests/disabled_functions_chain_not_matching.phpt | 28 |
11 files changed, 148 insertions, 3 deletions
| @@ -37,3 +37,4 @@ src/Makefile.objects | |||
| 37 | src/missing | 37 | src/missing |
| 38 | src/mkinstalldirs | 38 | src/mkinstalldirs |
| 39 | src/run-tests.php | 39 | src/run-tests.php |
| 40 | doc/build/ | ||
diff --git a/src/sp_config.h b/src/sp_config.h index 34096ce..d5cd1dc 100644 --- a/src/sp_config.h +++ b/src/sp_config.h | |||
| @@ -68,6 +68,7 @@ typedef struct { | |||
| 68 | 68 | ||
| 69 | char *function; | 69 | char *function; |
| 70 | pcre *r_function; | 70 | pcre *r_function; |
| 71 | sp_node_t *functions_list; | ||
| 71 | 72 | ||
| 72 | char *hash; | 73 | char *hash; |
| 73 | int simulation; | 74 | int simulation; |
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c index cf52ae3..d6107c3 100644 --- a/src/sp_config_keywords.c +++ b/src/sp_config_keywords.c | |||
| @@ -185,6 +185,10 @@ int parse_disabled_functions(char *line) { | |||
| 185 | return -1; | 185 | return -1; |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | if (df->function) { | ||
| 189 | df->functions_list = parse_functions_list(df->function); | ||
| 190 | } | ||
| 191 | |||
| 188 | if (df->param && strchr(df->param, '[')) { // assume that this is an array | 192 | if (df->param && strchr(df->param, '[')) { // assume that this is an array |
| 189 | df->param_array_keys = sp_new_list(); | 193 | df->param_array_keys = sp_new_list(); |
| 190 | if (0 != array_to_list(&df->param, &df->param_array_keys)) { | 194 | if (0 != array_to_list(&df->param, &df->param_array_keys)) { |
diff --git a/src/sp_config_utils.c b/src/sp_config_utils.c index 3ea82d0..b0c7d3c 100644 --- a/src/sp_config_utils.c +++ b/src/sp_config_utils.c | |||
| @@ -167,3 +167,23 @@ int array_to_list(char **name_ptr, sp_node_t **keys) { | |||
| 167 | *name_ptr = pestrdup(tmp, 1); | 167 | *name_ptr = pestrdup(tmp, 1); |
| 168 | return in_key; | 168 | return in_key; |
| 169 | } | 169 | } |
| 170 | |||
| 171 | |||
| 172 | zend_always_inline sp_node_t *parse_functions_list(char *value) { | ||
| 173 | const char *sep = ">"; | ||
| 174 | |||
| 175 | if (NULL == strchr(value, sep[0])) { | ||
| 176 | return NULL; | ||
| 177 | } | ||
| 178 | |||
| 179 | sp_node_t *list = sp_new_list(); | ||
| 180 | char* tmp = strdup(value); | ||
| 181 | char* function_name; | ||
| 182 | char *next_token = tmp; | ||
| 183 | while ((function_name = strtok_r(NULL, sep, &next_token))) { | ||
| 184 | sp_list_prepend(list, strdup(function_name)); | ||
| 185 | } | ||
| 186 | free(tmp); | ||
| 187 | |||
| 188 | return list; | ||
| 189 | } \ No newline at end of file | ||
diff --git a/src/sp_config_utils.h b/src/sp_config_utils.h index f2f8fce..e561d57 100644 --- a/src/sp_config_utils.h +++ b/src/sp_config_utils.h | |||
| @@ -4,5 +4,6 @@ | |||
| 4 | int parse_keywords(sp_config_functions *, char *); | 4 | int parse_keywords(sp_config_functions *, char *); |
| 5 | char *get_param(size_t *, char *restrict, sp_type, const char *restrict); | 5 | char *get_param(size_t *, char *restrict, sp_type, const char *restrict); |
| 6 | int array_to_list(char **, sp_node_t **); | 6 | int array_to_list(char **, sp_node_t **); |
| 7 | sp_node_t *parse_functions_list(char *value); | ||
| 7 | 8 | ||
| 8 | #endif /* SP_CONFIG_UTILS */ | 9 | #endif /* SP_CONFIG_UTILS */ |
diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c index b3e5cbc..b465a30 100644 --- a/src/sp_disabled_functions.c +++ b/src/sp_disabled_functions.c | |||
| @@ -31,6 +31,35 @@ static zend_always_inline char* get_complete_function_path( | |||
| 31 | return complete_path_function; | 31 | return complete_path_function; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | static bool is_functions_list_matching(zend_execute_data *execute_data, sp_node_t *functions_list) { | ||
| 35 | zend_execute_data *orig_execute_data, *current; | ||
| 36 | orig_execute_data = current = execute_data; | ||
| 37 | sp_node_t *it = functions_list; | ||
| 38 | |||
| 39 | while (current) { | ||
| 40 | if (it == NULL) { // every function in the list matched, we've got a match! | ||
| 41 | return true; | ||
| 42 | } | ||
| 43 | |||
| 44 | EG(current_execute_data) = current; | ||
| 45 | |||
| 46 | char *complete_path_function = get_complete_function_path(current); | ||
| 47 | int match = strcmp(((char*)it->data), complete_path_function); | ||
| 48 | efree(complete_path_function); | ||
| 49 | |||
| 50 | if (0 == match) { | ||
| 51 | it = it->next; | ||
| 52 | current = current->prev_execute_data; | ||
| 53 | } else { | ||
| 54 | EG(current_execute_data) = orig_execute_data; | ||
| 55 | return false; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | EG(current_execute_data) = orig_execute_data; | ||
| 60 | return false; | ||
| 61 | } | ||
| 62 | |||
| 34 | static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) { | 63 | static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) { |
| 35 | zend_execute_data *orig_execute_data = execute_data; | 64 | zend_execute_data *orig_execute_data = execute_data; |
| 36 | 65 | ||
| @@ -100,7 +129,14 @@ bool should_disable(zend_execute_data* execute_data) { | |||
| 100 | goto next; | 129 | goto next; |
| 101 | } | 130 | } |
| 102 | 131 | ||
| 103 | if (config_node->function) { /* Litteral match against the function name. */ | 132 | /* The order matters, since when we have `config_node->functions_list`, |
| 133 | we also do have `config_node->function` */ | ||
| 134 | if (config_node->functions_list) { | ||
| 135 | if (false == | ||
| 136 | is_functions_list_matching(execute_data, config_node->functions_list)) { | ||
| 137 | goto next; | ||
| 138 | } | ||
| 139 | } else if (config_node->function) { /* Litteral match against the function name. */ | ||
| 104 | if (0 != strcmp(config_node->function, complete_path_function)) { | 140 | if (0 != strcmp(config_node->function, complete_path_function)) { |
| 105 | goto next; | 141 | goto next; |
| 106 | } | 142 | } |
| @@ -110,6 +146,7 @@ bool should_disable(zend_execute_data* execute_data) { | |||
| 110 | goto next; | 146 | goto next; |
| 111 | } | 147 | } |
| 112 | } | 148 | } |
| 149 | |||
| 113 | if (config_node->var) { | 150 | if (config_node->var) { |
| 114 | if (false == is_local_var_matching(execute_data, config_node)) { | 151 | if (false == is_local_var_matching(execute_data, config_node)) { |
| 115 | goto next; | 152 | goto next; |
| @@ -207,8 +244,13 @@ bool should_disable(zend_execute_data* execute_data) { | |||
| 207 | goto allow; | 244 | goto allow; |
| 208 | } | 245 | } |
| 209 | 246 | ||
| 210 | sp_log_disable(complete_path_function, arg_name, arg_value_str, | 247 | if (config_node->functions_list) { |
| 211 | config_node); | 248 | sp_log_disable(config_node->function, arg_name, arg_value_str, |
| 249 | config_node); | ||
| 250 | } else { | ||
| 251 | sp_log_disable(complete_path_function, arg_name, arg_value_str, | ||
| 252 | config_node); | ||
| 253 | } | ||
| 212 | if (true == config_node->simulation) { | 254 | if (true == config_node->simulation) { |
| 213 | goto next; | 255 | goto next; |
| 214 | } else { // We've got a match, the function won't be executed | 256 | } else { // We've got a match, the function won't be executed |
diff --git a/src/sp_list.c b/src/sp_list.c index 04154b7..112d822 100644 --- a/src/sp_list.c +++ b/src/sp_list.c | |||
| @@ -35,3 +35,21 @@ void sp_list_insert(sp_node_t *list, void *data) { | |||
| 35 | list->next = new; | 35 | list->next = new; |
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | |||
| 39 | void sp_list_prepend(sp_node_t *list, void *data) { | ||
| 40 | if (list->head == NULL) { | ||
| 41 | list->data = data; | ||
| 42 | list->next = NULL; | ||
| 43 | list->head = list; | ||
| 44 | } else { | ||
| 45 | sp_node_t *new = pecalloc(sizeof(*new), 1, 1); | ||
| 46 | |||
| 47 | new->next = list->next; | ||
| 48 | list->next = new; | ||
| 49 | |||
| 50 | new->head = list; | ||
| 51 | |||
| 52 | new->data = list->data; | ||
| 53 | list->data = data; | ||
| 54 | } | ||
| 55 | } \ No newline at end of file | ||
diff --git a/src/sp_list.h b/src/sp_list.h index 48b11f6..476c64c 100644 --- a/src/sp_list.h +++ b/src/sp_list.h | |||
| @@ -11,5 +11,6 @@ typedef struct sp_node_s { | |||
| 11 | sp_node_t *sp_new_list(); | 11 | sp_node_t *sp_new_list(); |
| 12 | void sp_list_insert(sp_node_t *, void *); | 12 | void sp_list_insert(sp_node_t *, void *); |
| 13 | void sp_list_free(sp_node_t *); | 13 | void sp_list_free(sp_node_t *); |
| 14 | void sp_list_prepend(sp_node_t *, void *); | ||
| 14 | 15 | ||
| 15 | #endif | 16 | #endif |
diff --git a/src/tests/config/config_disabled_functions_chain.ini b/src/tests/config/config_disabled_functions_chain.ini new file mode 100644 index 0000000..f47af34 --- /dev/null +++ b/src/tests/config/config_disabled_functions_chain.ini | |||
| @@ -0,0 +1 @@ | |||
| sp.disable_functions.function("outer>inner").drop(); | |||
diff --git a/src/tests/disabled_functions_chain.phpt b/src/tests/disabled_functions_chain.phpt new file mode 100644 index 0000000..b25f800 --- /dev/null +++ b/src/tests/disabled_functions_chain.phpt | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | --TEST-- | ||
| 2 | Disable functions by matching the calltrace | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/config_disabled_functions_chain.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | |||
| 10 | function outer() { | ||
| 11 | function inner() { | ||
| 12 | echo "I'm in the inner function!\n"; | ||
| 13 | } | ||
| 14 | echo "I'm in the outer function, before the call!\n"; | ||
| 15 | inner(); | ||
| 16 | echo "I'm in the outer function, after the call!\n"; | ||
| 17 | } | ||
| 18 | |||
| 19 | echo "I'm before the call to outer\n"; | ||
| 20 | outer(); | ||
| 21 | echo "I'm after the call to outer\n"; | ||
| 22 | ?> | ||
| 23 | --EXPECTF-- | ||
| 24 | I'm before the call to outer | ||
| 25 | I'm in the outer function, before the call! | ||
| 26 | [snuffleupagus][0.0.0.0][disabled_function][drop] The call to the function 'outer>inner' in %a/disabled_functions_chain.php:%d has been disabled. | ||
| 27 | I'm in the outer function, after the call! | ||
| 28 | I'm after the call to outer | ||
diff --git a/src/tests/disabled_functions_chain_not_matching.phpt b/src/tests/disabled_functions_chain_not_matching.phpt new file mode 100644 index 0000000..3a0400a --- /dev/null +++ b/src/tests/disabled_functions_chain_not_matching.phpt | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | --TEST-- | ||
| 2 | Disable functions by matching the calltrace | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) die "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/config_disabled_functions_chain.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | |||
| 10 | function outer() { | ||
| 11 | function inner_() { | ||
| 12 | echo "I'm in the inner function!\n"; | ||
| 13 | } | ||
| 14 | echo "I'm in the outer function, before the call!\n"; | ||
| 15 | inner_(); | ||
| 16 | echo "I'm in the outer function, after the call!\n"; | ||
| 17 | } | ||
| 18 | |||
| 19 | echo "I'm before the call to outer\n"; | ||
| 20 | outer(); | ||
| 21 | echo "I'm after the call to outer\n"; | ||
| 22 | ?> | ||
| 23 | --EXPECTF-- | ||
| 24 | I'm before the call to outer | ||
| 25 | I'm in the outer function, before the call! | ||
| 26 | I'm in the inner function! | ||
| 27 | I'm in the outer function, after the call! | ||
| 28 | I'm after the call to outer | ||
