summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/sp_config.h1
-rw-r--r--src/sp_config_keywords.c4
-rw-r--r--src/sp_config_utils.c20
-rw-r--r--src/sp_config_utils.h1
-rw-r--r--src/sp_disabled_functions.c48
-rw-r--r--src/sp_list.c18
-rw-r--r--src/sp_list.h1
-rw-r--r--src/tests/config/config_disabled_functions_chain.ini1
-rw-r--r--src/tests/disabled_functions_chain.phpt28
-rw-r--r--src/tests/disabled_functions_chain_not_matching.phpt28
11 files changed, 148 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 68dfa7a..c3941e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,4 @@ src/Makefile.objects
37src/missing 37src/missing
38src/mkinstalldirs 38src/mkinstalldirs
39src/run-tests.php 39src/run-tests.php
40doc/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
172zend_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 @@
4int parse_keywords(sp_config_functions *, char *); 4int parse_keywords(sp_config_functions *, char *);
5char *get_param(size_t *, char *restrict, sp_type, const char *restrict); 5char *get_param(size_t *, char *restrict, sp_type, const char *restrict);
6int array_to_list(char **, sp_node_t **); 6int array_to_list(char **, sp_node_t **);
7sp_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
34static 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
34static bool is_local_var_matching(zend_execute_data *execute_data, const sp_disabled_function *const config_node) { 63static 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
39void 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 {
11sp_node_t *sp_new_list(); 11sp_node_t *sp_new_list();
12void sp_list_insert(sp_node_t *, void *); 12void sp_list_insert(sp_node_t *, void *);
13void sp_list_free(sp_node_t *); 13void sp_list_free(sp_node_t *);
14void 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--
2Disable functions by matching the calltrace
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_chain.ini
7--FILE--
8<?php
9
10function 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
19echo "I'm before the call to outer\n";
20outer();
21echo "I'm after the call to outer\n";
22?>
23--EXPECTF--
24I'm before the call to outer
25I'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.
27I'm in the outer function, after the call!
28I'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--
2Disable functions by matching the calltrace
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_disabled_functions_chain.ini
7--FILE--
8<?php
9
10function 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
19echo "I'm before the call to outer\n";
20outer();
21echo "I'm after the call to outer\n";
22?>
23--EXPECTF--
24I'm before the call to outer
25I'm in the outer function, before the call!
26I'm in the inner function!
27I'm in the outer function, after the call!
28I'm after the call to outer