summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Fuhrmannek2021-12-14 14:46:23 +0100
committerBen Fuhrmannek2021-12-14 14:46:23 +0100
commita5dcbb8cb802ba18e618ca38ea8e6acbf8b133ff (patch)
treedc28111885c5df01d94936a3cd95c20dab5435dd /src
parent4a45ba42b609d48c8297456d67cc8d955073b567 (diff)
fix: apply checks to internal function calls, too, so we can match internal class methods that are not hooked
Diffstat (limited to '')
-rw-r--r--src/sp_execute.c163
1 files changed, 84 insertions, 79 deletions
diff --git a/src/sp_execute.c b/src/sp_execute.c
index 0474fc8..21a68dd 100644
--- a/src/sp_execute.c
+++ b/src/sp_execute.c
@@ -12,25 +12,19 @@ static zend_result (*orig_zend_stream_open)(zend_file_handle *handle) = NULL;
12// FIXME handle symlink 12// FIXME handle symlink
13ZEND_COLD static inline void terminate_if_writable(const char *filename) { 13ZEND_COLD static inline void terminate_if_writable(const char *filename) {
14 const sp_config_readonly_exec *config_ro_exec = &(SPCFG(readonly_exec)); 14 const sp_config_readonly_exec *config_ro_exec = &(SPCFG(readonly_exec));
15
16 if (0 == access(filename, W_OK)) { 15 if (0 == access(filename, W_OK)) {
17 if (config_ro_exec->dump) { 16 if (config_ro_exec->dump) {
18 sp_log_request(config_ro_exec->dump, 17 sp_log_request(config_ro_exec->dump, config_ro_exec->textual_representation);
19 config_ro_exec->textual_representation);
20 } 18 }
21 if (true == config_ro_exec->simulation) { 19 if (true == config_ro_exec->simulation) {
22 sp_log_simulation("readonly_exec", 20 sp_log_simulation("readonly_exec", "Attempted execution of a writable file (%s).", filename);
23 "Attempted execution of a writable file (%s).",
24 filename);
25 } else { 21 } else {
26 sp_log_drop("readonly_exec", 22 sp_log_drop("readonly_exec", "Attempted execution of a writable file (%s).", filename);
27 "Attempted execution of a writable file (%s).", filename);
28 } 23 }
29 } else { 24 } else {
30 if (EACCES != errno) { 25 if (EACCES != errno) {
31 // LCOV_EXCL_START 26 // LCOV_EXCL_START
32 sp_log_err("Writable execution", "Error while accessing %s: %s", filename, 27 sp_log_err("Writable execution", "Error while accessing %s: %s", filename, strerror(errno));
33 strerror(errno));
34 // LCOV_EXCL_STOP 28 // LCOV_EXCL_STOP
35 } 29 }
36 } 30 }
@@ -113,97 +107,108 @@ static inline void sp_orig_execute(zend_execute_data *execute_data) {
113 SPG(execution_depth)--; 107 SPG(execution_depth)--;
114} 108}
115 109
116static void sp_execute_ex(zend_execute_data *execute_data) { 110static inline void sp_check_writable(zend_execute_data *execute_data) {
117 is_in_eval_and_whitelisted(execute_data); 111 if (execute_data && EX(func) && EX(func)->op_array.filename && SPCFG(readonly_exec).enable) {
118 const HashTable *config_disabled_functions = SPCFG(disabled_functions); 112 terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename));
113 }
114}
119 115
116static inline void sp_call_orig_execute(INTERNAL_FUNCTION_PARAMETERS, bool internal) {
117 if (internal) {
118 if (UNEXPECTED(NULL != orig_zend_execute_internal)) {
119 orig_zend_execute_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
120 } else {
121 EX(func)->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
122 }
123 } else {
124 sp_orig_execute(execute_data);
125 }
126}
127
128static inline void sp_execute_handler(INTERNAL_FUNCTION_PARAMETERS, bool internal) {
120 if (!execute_data) { 129 if (!execute_data) {
121 return; // LCOV_EXCL_LINE 130 return; // LCOV_EXCL_LINE
122 } 131 }
123 132
124 if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) { 133 is_in_eval_and_whitelisted(execute_data);
125 const sp_list_node *config = zend_hash_str_find_ptr(config_disabled_functions, ZEND_STRL("eval"));
126 134
127 zend_string *filename = get_eval_filename(zend_get_executed_filename()); 135 if (!internal) {
128 is_builtin_matching(filename, "eval", NULL, config, config_disabled_functions); 136 if (UNEXPECTED(EX(func)->op_array.type == ZEND_EVAL_CODE)) {
129 zend_string_release(filename); 137 const sp_list_node *config = zend_hash_str_find_ptr(SPCFG(disabled_functions), ZEND_STRL("eval"));
130 138
131 SPG(in_eval)++; 139 zend_string *filename = get_eval_filename(zend_get_executed_filename());
132 sp_orig_execute(execute_data); 140 is_builtin_matching(filename, "eval", NULL, config, SPCFG(disabled_functions));
133 SPG(in_eval)--; 141 zend_string_release(filename);
134 return;
135 }
136 142
137 if (NULL != EX(func)->op_array.filename) { 143 SPG(in_eval)++;
138 if (SPCFG(readonly_exec).enable) { 144 sp_orig_execute(execute_data);
139 terminate_if_writable(ZSTR_VAL(EX(func)->op_array.filename)); 145 SPG(in_eval)--;
146 return;
140 } 147 }
148
149 sp_check_writable(execute_data);
141 } 150 }
142 151
143 if (SPG(hook_execute)) { 152 if (!SPG(hook_execute)) {
144 char *function_name = get_complete_function_path(execute_data); 153 sp_call_orig_execute(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal);
145 zval ret_val; 154 return;
146 const sp_list_node *config_disabled_functions_reg = SPCFG(disabled_functions_reg).disabled_functions; 155 }
147 156
148 if (!function_name) { 157 char *function_name = get_complete_function_path(execute_data);
149 sp_orig_execute(execute_data);
150 return;
151 }
152 158
153 // If we're at an internal function 159 if (!function_name) {
154 if (!execute_data->prev_execute_data || 160 sp_call_orig_execute(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal);
155 !execute_data->prev_execute_data->func || 161 return;
156 !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) || 162 }
157 !execute_data->prev_execute_data->opline) { 163
158 should_disable_ht(execute_data, function_name, NULL, NULL, 164 const sp_list_node *config_disabled_functions_reg = SPCFG(disabled_functions_reg).disabled_functions;
159 config_disabled_functions_reg,
160 config_disabled_functions);
161 } else { // If we're at a userland function call
162 switch (execute_data->prev_execute_data->opline->opcode) {
163 case ZEND_DO_FCALL:
164 case ZEND_DO_FCALL_BY_NAME:
165 case ZEND_DO_ICALL:
166 case ZEND_DO_UCALL:
167 case ZEND_TICKS:
168 should_disable_ht(execute_data, function_name, NULL, NULL,
169 config_disabled_functions_reg,
170 config_disabled_functions);
171 default:
172 break;
173 }
174 }
175 165
176 // When a function's return value isn't used, php doesn't store it in the 166 // If we're at an internal function
177 // execute_data, so we need to use a local variable to be able to match on 167 if (!execute_data->prev_execute_data ||
178 // it later. 168 !execute_data->prev_execute_data->func ||
179 if (EX(return_value) == NULL) { 169 !ZEND_USER_CODE(execute_data->prev_execute_data->func->type) ||
180 memset(&ret_val, 0, sizeof(ret_val)); 170 !execute_data->prev_execute_data->opline) {
181 EX(return_value) = &ret_val; 171 should_disable_ht(execute_data, function_name, NULL, NULL, config_disabled_functions_reg, SPCFG(disabled_functions));
172 } else { // If we're at a userland function call
173 switch (execute_data->prev_execute_data->opline->opcode) {
174 case ZEND_DO_FCALL:
175 case ZEND_DO_FCALL_BY_NAME:
176 case ZEND_DO_ICALL:
177 case ZEND_DO_UCALL:
178 case ZEND_TICKS:
179 should_disable_ht(execute_data, function_name, NULL, NULL, config_disabled_functions_reg, SPCFG(disabled_functions));
180 default:
181 break;
182 } 182 }
183 }
183 184
184 sp_orig_execute(execute_data); 185 // When a function's return value isn't used, php doesn't store it in the
186 // execute_data, so we need to use a local variable to be able to match on
187 // it later.
188 zval ret_val;
189 if (EX(return_value) == NULL) {
190 memset(&ret_val, 0, sizeof(ret_val));
191 EX(return_value) = &ret_val;
192 }
185 193
186 should_drop_on_ret_ht(EX(return_value), function_name, SPCFG(disabled_functions_reg_ret).disabled_functions, SPCFG(disabled_functions_ret), execute_data); 194 sp_call_orig_execute(INTERNAL_FUNCTION_PARAM_PASSTHRU, internal);
187 efree(function_name);
188 195
189 if (EX(return_value) == &ret_val) { 196 should_drop_on_ret_ht(EX(return_value), function_name, SPCFG(disabled_functions_reg_ret).disabled_functions, SPCFG(disabled_functions_ret), execute_data);
190 EX(return_value) = NULL; 197 efree(function_name);
191 } 198
192 } else { 199 if (EX(return_value) == &ret_val) {
193 sp_orig_execute(execute_data); 200 EX(return_value) = NULL;
194 } 201 }
202
195} 203}
196 204
197static void sp_zend_execute_internal(INTERNAL_FUNCTION_PARAMETERS) {
198 is_in_eval_and_whitelisted(execute_data);
199 205
200 if (UNEXPECTED(NULL != orig_zend_execute_internal)) { 206static void sp_execute_ex(zend_execute_data *execute_data) {
201 // LCOV_EXCL_START 207 sp_execute_handler(execute_data, NULL, false);
202 orig_zend_execute_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); 208}
203 // LCOV_EXCL_STOP 209
204 } else { 210static void sp_zend_execute_internal(INTERNAL_FUNCTION_PARAMETERS) {
205 EX(func)->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 211 sp_execute_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
206 }
207} 212}
208 213
209static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_handle *handle) { 214static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_handle *handle) {