diff options
| -rw-r--r-- | src/sp_execute.c | 163 |
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 |
| 13 | ZEND_COLD static inline void terminate_if_writable(const char *filename) { | 13 | ZEND_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 | ||
| 116 | static void sp_execute_ex(zend_execute_data *execute_data) { | 110 | static 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 | ||
| 116 | static 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 | |||
| 128 | static 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 | ||
| 197 | static 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)) { | 206 | static 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 { | 210 | static 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 | ||
| 209 | static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_handle *handle) { | 214 | static inline void sp_stream_open_checks(zend_string *zend_filename, zend_file_handle *handle) { |
