summaryrefslogtreecommitdiff
path: root/src/sp_execute.c
diff options
context:
space:
mode:
authorxXx-caillou-xXx2018-07-13 10:36:50 +0200
committerjvoisin2018-07-13 08:36:50 +0000
commit7963580d72a358975133f86f01de2d2eab08ba38 (patch)
tree4bec345d70f687a2a6002b36e2f2fc79318959f6 /src/sp_execute.c
parent12b740bc7bb01ffe397cecc5b6fa25b136304911 (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.c142
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
39inline static void is_builtin_matching(const char *restrict const filename, 39inline 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. */
103char *get_eval_filename(const char *const filename) { 108zend_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
215end: 259end:
216 return orig_zend_stream_open(filename, handle); 260 return orig_zend_stream_open(filename, handle);