summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/snuffleupagus.c53
-rw-r--r--src/sp_config.c36
-rw-r--r--src/sp_config.h58
-rw-r--r--src/sp_config_keywords.c199
-rw-r--r--src/sp_config_utils.c15
-rw-r--r--src/sp_config_utils.h2
-rw-r--r--src/sp_cookie_encryption.c16
-rw-r--r--src/sp_crypt.c29
-rw-r--r--src/sp_crypt.h4
-rw-r--r--src/sp_disabled_functions.c304
-rw-r--r--src/sp_disabled_functions.h9
-rw-r--r--src/sp_execute.c142
-rw-r--r--src/sp_execute.h2
-rw-r--r--src/sp_pcre_compat.h2
-rw-r--r--src/sp_session.c4
-rw-r--r--src/sp_sloppy.c9
-rw-r--r--src/sp_unserialize.c12
-rw-r--r--src/sp_upload_validation.c26
-rw-r--r--src/sp_utils.c147
-rw-r--r--src/sp_utils.h16
-rw-r--r--src/sp_var_value.c7
21 files changed, 632 insertions, 460 deletions
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index 08b2083..edca185 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -39,6 +39,14 @@ PHP_INI_ENTRY("sp.configuration_file", "", PHP_INI_SYSTEM,
39 OnUpdateConfiguration) 39 OnUpdateConfiguration)
40PHP_INI_END() 40PHP_INI_END()
41 41
42void free_disabled_functions_hashtable(HashTable *ht) {
43 void* ptr = NULL;
44 ZEND_HASH_FOREACH_PTR(ht, ptr) {
45 sp_list_free(ptr);
46 }
47 ZEND_HASH_FOREACH_END();
48}
49
42ZEND_DLEXPORT zend_extension zend_extension_entry = { 50ZEND_DLEXPORT zend_extension zend_extension_entry = {
43 PHP_SNUFFLEUPAGUS_EXTNAME, 51 PHP_SNUFFLEUPAGUS_EXTNAME,
44 PHP_SNUFFLEUPAGUS_VERSION, 52 PHP_SNUFFLEUPAGUS_VERSION,
@@ -69,6 +77,10 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
69 SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook); 77 SP_INIT_HT(snuffleupagus_globals->disabled_functions_hook);
70 SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook); 78 SP_INIT_HT(snuffleupagus_globals->sp_internal_functions_hook);
71 SP_INIT_HT(snuffleupagus_globals->sp_eval_blacklist_functions_hook); 79 SP_INIT_HT(snuffleupagus_globals->sp_eval_blacklist_functions_hook);
80 SP_INIT_HT(snuffleupagus_globals->config.config_disabled_functions);
81 SP_INIT_HT(snuffleupagus_globals->config.config_disabled_functions_hooked);
82 SP_INIT_HT(snuffleupagus_globals->config.config_disabled_functions_ret);
83 SP_INIT_HT(snuffleupagus_globals->config.config_disabled_functions_ret_hooked);
72 84
73 SP_INIT(snuffleupagus_globals->config.config_unserialize); 85 SP_INIT(snuffleupagus_globals->config.config_unserialize);
74 SP_INIT(snuffleupagus_globals->config.config_random); 86 SP_INIT(snuffleupagus_globals->config.config_random);
@@ -79,20 +91,15 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
79 SP_INIT(snuffleupagus_globals->config.config_snuffleupagus); 91 SP_INIT(snuffleupagus_globals->config.config_snuffleupagus);
80 SP_INIT(snuffleupagus_globals->config.config_disable_xxe); 92 SP_INIT(snuffleupagus_globals->config.config_disable_xxe);
81 SP_INIT(snuffleupagus_globals->config.config_upload_validation); 93 SP_INIT(snuffleupagus_globals->config.config_upload_validation);
82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions); 94 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_reg);
83 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret); 95 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_reg_ret);
84 SP_INIT(snuffleupagus_globals->config.config_cookie); 96 SP_INIT(snuffleupagus_globals->config.config_cookie);
85 SP_INIT(snuffleupagus_globals->config.config_session); 97 SP_INIT(snuffleupagus_globals->config.config_session);
86 SP_INIT(snuffleupagus_globals->config.config_disabled_constructs);
87 SP_INIT(snuffleupagus_globals->config.config_eval); 98 SP_INIT(snuffleupagus_globals->config.config_eval);
88 99
89 snuffleupagus_globals->config.config_disabled_constructs->construct_include = 100 snuffleupagus_globals->config.config_disabled_functions_reg
90 NULL; 101 ->disabled_functions = NULL;
91 snuffleupagus_globals->config.config_disabled_constructs->construct_eval = 102 snuffleupagus_globals->config.config_disabled_functions_reg_ret
92 NULL;
93 snuffleupagus_globals->config.config_disabled_functions->disabled_functions =
94 NULL;
95 snuffleupagus_globals->config.config_disabled_functions_ret
96 ->disabled_functions = NULL; 103 ->disabled_functions = NULL;
97 snuffleupagus_globals->config.config_cookie->cookies = NULL; 104 snuffleupagus_globals->config.config_cookie->cookies = NULL;
98 snuffleupagus_globals->config.config_eval->blacklist = NULL; 105 snuffleupagus_globals->config.config_eval->blacklist = NULL;
@@ -109,12 +116,21 @@ PHP_MINIT_FUNCTION(snuffleupagus) {
109} 116}
110 117
111PHP_MSHUTDOWN_FUNCTION(snuffleupagus) { 118PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
119 free_disabled_functions_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions);
120 free_disabled_functions_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked);
121 free_disabled_functions_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions_ret);
122 free_disabled_functions_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions_ret_hooked);
123
112#define FREE_HT(F) \ 124#define FREE_HT(F) \
113 zend_hash_destroy(SNUFFLEUPAGUS_G(F)); \ 125 zend_hash_destroy(SNUFFLEUPAGUS_G(F)); \
114 pefree(SNUFFLEUPAGUS_G(F), 1); 126 pefree(SNUFFLEUPAGUS_G(F), 1);
115 127
116 FREE_HT(disabled_functions_hook); 128 FREE_HT(disabled_functions_hook);
117 FREE_HT(sp_eval_blacklist_functions_hook); 129 FREE_HT(sp_eval_blacklist_functions_hook);
130 FREE_HT(config.config_disabled_functions);
131 FREE_HT(config.config_disabled_functions_hooked);
132 FREE_HT(config.config_disabled_functions_ret);
133 FREE_HT(config.config_disabled_functions_ret_hooked);
118 134
119#undef FREE_HT 135#undef FREE_HT
120 136
@@ -135,19 +151,16 @@ PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
135 sp_list_free(_n); \ 151 sp_list_free(_n); \
136 } while (0) 152 } while (0)
137 153
138 FREE_LST_DISABLE(config.config_disabled_functions->disabled_functions); 154 FREE_LST_DISABLE(config.config_disabled_functions_reg->disabled_functions);
139 FREE_LST_DISABLE(config.config_disabled_functions_ret->disabled_functions); 155 FREE_LST_DISABLE(config.config_disabled_functions_reg_ret->disabled_functions);
140 FREE_LST_DISABLE(config.config_disabled_constructs->construct_include);
141 FREE_LST_DISABLE(config.config_disabled_constructs->construct_eval);
142 sp_list_free(SNUFFLEUPAGUS_G(config).config_cookie->cookies); 156 sp_list_free(SNUFFLEUPAGUS_G(config).config_cookie->cookies);
143 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->blacklist); 157 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->blacklist);
144 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->whitelist); 158 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->whitelist);
145 159
146#undef FREE_LST_DISABLE 160#undef FREE_LST_DISABLE
147 161
148 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions), 1); 162 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg), 1);
149 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_ret), 1); 163 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg_ret), 1);
150 pefree(SNUFFLEUPAGUS_G(config.config_disabled_constructs), 1);
151 pefree(SNUFFLEUPAGUS_G(config.config_cookie), 1); 164 pefree(SNUFFLEUPAGUS_G(config.config_cookie), 1);
152 165
153 UNREGISTER_INI_ENTRIES(); 166 UNREGISTER_INI_ENTRIES();
@@ -249,6 +262,12 @@ static PHP_INI_MH(OnUpdateConfiguration) {
249 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; 262 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
250 } 263 }
251 264
265 SNUFFLEUPAGUS_G(config).hook_execute =
266 SNUFFLEUPAGUS_G(config).config_disabled_functions_reg->disabled_functions ||
267 SNUFFLEUPAGUS_G(config).config_disabled_functions_reg_ret->disabled_functions ||
268 zend_hash_num_elements(SNUFFLEUPAGUS_G(config).config_disabled_functions) ||
269 zend_hash_num_elements(SNUFFLEUPAGUS_G(config).config_disabled_functions_ret);
270
252 return SUCCESS; 271 return SUCCESS;
253} 272}
254 273
diff --git a/src/sp_config.c b/src/sp_config.c
index eb5b324..c652984 100644
--- a/src/sp_config.c
+++ b/src/sp_config.c
@@ -63,29 +63,29 @@ int parse_empty(char *restrict line, char *restrict keyword, void *retval) {
63 63
64int parse_php_type(char *restrict line, char *restrict keyword, void *retval) { 64int parse_php_type(char *restrict line, char *restrict keyword, void *retval) {
65 size_t consumed = 0; 65 size_t consumed = 0;
66 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 66 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
67 if (value) { 67 if (value) {
68 if (0 == strcasecmp("undef", value)) { 68 if (zend_string_equals_literal_ci(value, "undef")) {
69 *(sp_php_type *)retval = SP_PHP_TYPE_UNDEF; 69 *(sp_php_type *)retval = SP_PHP_TYPE_UNDEF;
70 } else if (0 == strcasecmp("null", value)) { 70 } else if (zend_string_equals_literal_ci(value, "null")) {
71 *(sp_php_type *)retval = SP_PHP_TYPE_NULL; 71 *(sp_php_type *)retval = SP_PHP_TYPE_NULL;
72 } else if (0 == strcasecmp("true", value)) { 72 } else if (zend_string_equals_literal_ci(value, "true")) {
73 *(sp_php_type *)retval = SP_PHP_TYPE_TRUE; 73 *(sp_php_type *)retval = SP_PHP_TYPE_TRUE;
74 } else if (0 == strcasecmp("false", value)) { 74 } else if (zend_string_equals_literal_ci(value, "false")) {
75 *(sp_php_type *)retval = SP_PHP_TYPE_FALSE; 75 *(sp_php_type *)retval = SP_PHP_TYPE_FALSE;
76 } else if (0 == strcasecmp("long", value)) { 76 } else if (zend_string_equals_literal_ci(value, "long")) {
77 *(sp_php_type *)retval = SP_PHP_TYPE_LONG; 77 *(sp_php_type *)retval = SP_PHP_TYPE_LONG;
78 } else if (0 == strcasecmp("double", value)) { 78 } else if (zend_string_equals_literal_ci(value, "double")) {
79 *(sp_php_type *)retval = SP_PHP_TYPE_DOUBLE; 79 *(sp_php_type *)retval = SP_PHP_TYPE_DOUBLE;
80 } else if (0 == strcasecmp("string", value)) { 80 } else if (zend_string_equals_literal_ci(value, "string")) {
81 *(sp_php_type *)retval = SP_PHP_TYPE_STRING; 81 *(sp_php_type *)retval = SP_PHP_TYPE_STRING;
82 } else if (0 == strcasecmp("array", value)) { 82 } else if (zend_string_equals_literal_ci(value, "array")) {
83 *(sp_php_type *)retval = SP_PHP_TYPE_ARRAY; 83 *(sp_php_type *)retval = SP_PHP_TYPE_ARRAY;
84 } else if (0 == strcasecmp("object", value)) { 84 } else if (zend_string_equals_literal_ci(value, "object")) {
85 *(sp_php_type *)retval = SP_PHP_TYPE_OBJECT; 85 *(sp_php_type *)retval = SP_PHP_TYPE_OBJECT;
86 } else if (0 == strcasecmp("resource", value)) { 86 } else if (zend_string_equals_literal_ci(value, "resource")) {
87 *(sp_php_type *)retval = SP_PHP_TYPE_RESOURCE; 87 *(sp_php_type *)retval = SP_PHP_TYPE_RESOURCE;
88 } else if (0 == strcasecmp("reference", value)) { 88 } else if (zend_string_equals_literal_ci(value, "reference")) {
89 *(sp_php_type *)retval = SP_PHP_TYPE_REFERENCE; 89 *(sp_php_type *)retval = SP_PHP_TYPE_REFERENCE;
90 } else { 90 } else {
91 pefree(value, 1); 91 pefree(value, 1);
@@ -105,12 +105,12 @@ int parse_php_type(char *restrict line, char *restrict keyword, void *retval) {
105} 105}
106 106
107int parse_str(char *restrict line, char *restrict keyword, void *retval) { 107int parse_str(char *restrict line, char *restrict keyword, void *retval) {
108 char *value = NULL; 108 zend_string *value = NULL;
109 109
110 size_t consumed = 0; 110 size_t consumed = 0;
111 value = get_param(&consumed, line, SP_TYPE_STR, keyword); 111 value = get_param(&consumed, line, SP_TYPE_STR, keyword);
112 if (value) { 112 if (value) {
113 *(char **)retval = value; 113 *(zend_string **)retval = value;
114 return consumed; 114 return consumed;
115 } 115 }
116 return -1; 116 return -1;
@@ -118,11 +118,11 @@ int parse_str(char *restrict line, char *restrict keyword, void *retval) {
118 118
119int parse_cidr(char *restrict line, char *restrict keyword, void *retval) { 119int parse_cidr(char *restrict line, char *restrict keyword, void *retval) {
120 size_t consumed = 0; 120 size_t consumed = 0;
121 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 121 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
122 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1); 122 sp_cidr *cidr = pecalloc(sizeof(sp_cidr), 1, 1);
123 123
124 if (value) { 124 if (value) {
125 if (-1 == get_ip_and_cidr(value, cidr)) { 125 if (-1 == get_ip_and_cidr(ZSTR_VAL(value), cidr)) {
126 return -1; 126 return -1;
127 } 127 }
128 *(sp_cidr **)retval = cidr; 128 *(sp_cidr **)retval = cidr;
@@ -139,10 +139,10 @@ int parse_regexp(char *restrict line, char *restrict keyword, void *retval) {
139 * (http://www.pcre.org/original/doc/html/pcre_study.html) 139 * (http://www.pcre.org/original/doc/html/pcre_study.html)
140 * maybe not: http://sljit.sourceforge.net/pcre.html*/ 140 * maybe not: http://sljit.sourceforge.net/pcre.html*/
141 size_t consumed = 0; 141 size_t consumed = 0;
142 char *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 142 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
143 143
144 if (value) { 144 if (value) {
145 sp_pcre *compiled_re = sp_pcre_compile(value); 145 sp_pcre *compiled_re = sp_pcre_compile(ZSTR_VAL(value));
146 if (NULL != compiled_re) { 146 if (NULL != compiled_re) {
147 *(sp_pcre **)retval = compiled_re; 147 *(sp_pcre **)retval = compiled_re;
148 return consumed; 148 return consumed;
diff --git a/src/sp_config.h b/src/sp_config.h
index 979feda..cfc3c8f 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -38,15 +38,15 @@ typedef struct {
38} sp_cidr; 38} sp_cidr;
39 39
40typedef struct { 40typedef struct {
41 char *encryption_key; 41 zend_string *encryption_key;
42 char *cookies_env_var; 42 zend_string *cookies_env_var;
43} sp_config_global; 43} sp_config_global;
44 44
45typedef struct { 45typedef struct {
46 bool enable; 46 bool enable;
47 bool simulation; 47 bool simulation;
48 char *dump; 48 zend_string *dump;
49 char *textual_representation; 49 zend_string *textual_representation;
50} sp_config_readonly_exec; 50} sp_config_readonly_exec;
51 51
52typedef struct { bool enable; } sp_config_global_strict; 52typedef struct { bool enable; } sp_config_global_strict;
@@ -62,7 +62,7 @@ typedef struct { bool enable; } sp_config_disable_xxe;
62typedef struct { 62typedef struct {
63 enum samesite_type { strict = 1, lax = 2 } samesite; 63 enum samesite_type { strict = 1, lax = 2 } samesite;
64 bool encrypt; 64 bool encrypt;
65 char *name; 65 zend_string *name;
66 sp_pcre *name_r; 66 sp_pcre *name_r;
67 bool simulation; 67 bool simulation;
68} sp_cookie; 68} sp_cookie;
@@ -75,21 +75,21 @@ typedef struct {
75typedef struct { 75typedef struct {
76 bool enable; 76 bool enable;
77 bool simulation; 77 bool simulation;
78 char *dump; 78 zend_string *dump;
79 char *textual_representation; 79 zend_string *textual_representation;
80} sp_config_unserialize; 80} sp_config_unserialize;
81 81
82typedef struct { 82typedef struct {
83 char *textual_representation; 83 zend_string *textual_representation;
84 84
85 char *filename; 85 zend_string *filename;
86 sp_pcre *r_filename; 86 sp_pcre *r_filename;
87 87
88 char *function; 88 zend_string *function;
89 sp_pcre *r_function; 89 sp_pcre *r_function;
90 sp_list_node *functions_list; 90 sp_list_node *functions_list;
91 91
92 char *hash; 92 zend_string *hash;
93 int simulation; 93 int simulation;
94 94
95 sp_tree *param; 95 sp_tree *param;
@@ -98,18 +98,18 @@ typedef struct {
98 int pos; 98 int pos;
99 unsigned int line; 99 unsigned int line;
100 100
101 char *ret;
102 sp_pcre *r_ret; 101 sp_pcre *r_ret;
102 zend_string *ret;
103 sp_php_type ret_type; 103 sp_php_type ret_type;
104 104
105 sp_pcre *value_r; 105 sp_pcre *r_value;
106 char *value; 106 zend_string *value;
107 107
108 sp_pcre *r_key; 108 sp_pcre *r_key;
109 char *key; 109 zend_string *key;
110 110
111 char *dump; 111 zend_string *dump;
112 char *alias; 112 zend_string *alias;
113 bool param_is_array; 113 bool param_is_array;
114 bool var_is_array; 114 bool var_is_array;
115 sp_list_node *param_array_keys; 115 sp_list_node *param_array_keys;
@@ -126,8 +126,8 @@ typedef struct {
126 sp_list_node *blacklist; 126 sp_list_node *blacklist;
127 sp_list_node *whitelist; 127 sp_list_node *whitelist;
128 bool simulation; 128 bool simulation;
129 char *dump; 129 zend_string *dump;
130 char *textual_representation; 130 zend_string *textual_representation;
131} sp_config_eval; 131} sp_config_eval;
132 132
133typedef struct { 133typedef struct {
@@ -139,14 +139,7 @@ typedef struct {
139} sp_config_cookie; 139} sp_config_cookie;
140 140
141typedef struct { 141typedef struct {
142 sp_list_node 142 zend_string *script;
143 *construct_include; // list of rules for `(include|require)_(once)?`
144 sp_list_node *construct_eval;
145 sp_list_node *construct_echo;
146} sp_config_disabled_constructs;
147
148typedef struct {
149 char *script;
150 bool simulation; 143 bool simulation;
151 bool enable; 144 bool enable;
152} sp_config_upload_validation; 145} sp_config_upload_validation;
@@ -155,8 +148,6 @@ typedef struct {
155 sp_config_random *config_random; 148 sp_config_random *config_random;
156 sp_config_sloppy *config_sloppy; 149 sp_config_sloppy *config_sloppy;
157 sp_config_unserialize *config_unserialize; 150 sp_config_unserialize *config_unserialize;
158 sp_config_disabled_functions *config_disabled_functions;
159 sp_config_disabled_functions *config_disabled_functions_ret;
160 sp_config_readonly_exec *config_readonly_exec; 151 sp_config_readonly_exec *config_readonly_exec;
161 sp_config_upload_validation *config_upload_validation; 152 sp_config_upload_validation *config_upload_validation;
162 sp_config_cookie *config_cookie; 153 sp_config_cookie *config_cookie;
@@ -164,9 +155,16 @@ typedef struct {
164 sp_config_auto_cookie_secure *config_auto_cookie_secure; 155 sp_config_auto_cookie_secure *config_auto_cookie_secure;
165 sp_config_global_strict *config_global_strict; 156 sp_config_global_strict *config_global_strict;
166 sp_config_disable_xxe *config_disable_xxe; 157 sp_config_disable_xxe *config_disable_xxe;
167 sp_config_disabled_constructs *config_disabled_constructs;
168 sp_config_eval *config_eval; 158 sp_config_eval *config_eval;
169 sp_config_session *config_session; 159 sp_config_session *config_session;
160 bool hook_execute;
161
162 HashTable *config_disabled_functions;
163 HashTable *config_disabled_functions_hooked;
164 HashTable *config_disabled_functions_ret;
165 HashTable *config_disabled_functions_ret_hooked;
166 sp_config_disabled_functions *config_disabled_functions_reg;
167 sp_config_disabled_functions *config_disabled_functions_reg_ret;
170} sp_config; 168} sp_config;
171 169
172typedef struct { 170typedef struct {
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index 2a570cd..81f43f7 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -1,39 +1,8 @@
1#include "php_snuffleupagus.h" 1#include "php_snuffleupagus.h"
2#include "zend_types.h"
2 3
3ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 4ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
4 5
5static const struct {
6 unsigned int type;
7 char *keys[5]; // Update this value if necessary
8} CONSTRUCTS_TYPES[] = {
9 {.type = ZEND_INCLUDE_OR_EVAL,
10 .keys = {"include", "include_once", "require", "require_once", NULL}},
11 {.type = ZEND_ECHO, .keys = {"echo", NULL}},
12 {.type = ZEND_NEW, .keys = {"new", NULL}},
13 {.type = ZEND_EXIT, .keys = {"exit", NULL}},
14 {.type = ZEND_STRLEN, .keys = {"strlen", NULL}},
15 {.type = ZEND_EVAL_CODE, .keys = {"eval", NULL}},
16 {.type = 0, .keys = {NULL}}};
17
18static int get_construct_type(sp_disabled_function const *const df) {
19 for (size_t i = 0; 0 != CONSTRUCTS_TYPES[i].type; i++) {
20 for (size_t j = 0; NULL != CONSTRUCTS_TYPES[i].keys[j]; j++) {
21 assert(df->function || df->r_function);
22 if (df->function) {
23 if (0 == strcmp(df->function, CONSTRUCTS_TYPES[i].keys[j])) {
24 return CONSTRUCTS_TYPES[i].type;
25 }
26 } else {
27 if (true == sp_is_regexp_matching(df->r_function,
28 CONSTRUCTS_TYPES[i].keys[j])) {
29 return CONSTRUCTS_TYPES[i].type;
30 }
31 }
32 }
33 }
34 return -1;
35}
36
37static int parse_enable(char *line, bool *restrict retval, 6static int parse_enable(char *line, bool *restrict retval,
38 bool *restrict simulation) { 7 bool *restrict simulation) {
39 bool enable = false, disable = false; 8 bool enable = false, disable = false;
@@ -137,7 +106,7 @@ int parse_unserialize(char *line) {
137 {parse_str, SP_TOKEN_DUMP, &(unserialize->dump)}, 106 {parse_str, SP_TOKEN_DUMP, &(unserialize->dump)},
138 {0}}; 107 {0}};
139 108
140 unserialize->textual_representation = estrdup(line); 109 unserialize->textual_representation = zend_string_init(line, strlen(line), 1);
141 110
142 int ret = parse_keywords(sp_config_funcs, line); 111 int ret = parse_keywords(sp_config_funcs, line);
143 if (0 != ret) { 112 if (0 != ret) {
@@ -167,7 +136,8 @@ int parse_readonly_exec(char *line) {
167 {parse_str, SP_TOKEN_DUMP, &(readonly_exec->dump)}, 136 {parse_str, SP_TOKEN_DUMP, &(readonly_exec->dump)},
168 {0}}; 137 {0}};
169 138
170 readonly_exec->textual_representation = estrdup(line); 139 readonly_exec->textual_representation =
140 zend_string_init(line, strlen(line), 1);
171 int ret = parse_keywords(sp_config_funcs, line); 141 int ret = parse_keywords(sp_config_funcs, line);
172 142
173 if (0 != ret) { 143 if (0 != ret) {
@@ -196,8 +166,8 @@ int parse_global(char *line) {
196} 166}
197 167
198static int parse_eval_filter_conf(char *line, sp_list_node **list) { 168static int parse_eval_filter_conf(char *line, sp_list_node **list) {
199 char *token; 169 char *token, *tmp;
200 char *rest; 170 zend_string *rest = NULL;
201 sp_config_eval *eval = SNUFFLEUPAGUS_G(config).config_eval; 171 sp_config_eval *eval = SNUFFLEUPAGUS_G(config).config_eval;
202 172
203 sp_config_functions sp_config_funcs[] = { 173 sp_config_functions sp_config_funcs[] = {
@@ -207,15 +177,19 @@ static int parse_eval_filter_conf(char *line, sp_list_node **list) {
207 {parse_str, SP_TOKEN_DUMP, &(SNUFFLEUPAGUS_G(config).config_eval->dump)}, 177 {parse_str, SP_TOKEN_DUMP, &(SNUFFLEUPAGUS_G(config).config_eval->dump)},
208 {0}}; 178 {0}};
209 179
210 eval->textual_representation = estrdup(line); 180 eval->textual_representation = zend_string_init(line, strlen(line), 1);
211 181
212 int ret = parse_keywords(sp_config_funcs, line); 182 int ret = parse_keywords(sp_config_funcs, line);
213 if (0 != ret) { 183 if (0 != ret) {
214 return ret; 184 return ret;
215 } 185 }
216 186
217 while ((token = strtok_r(rest, ",", &rest))) { 187 tmp = ZSTR_VAL(rest);
218 *list = sp_list_insert(*list, token); 188 while ((token = strtok_r(tmp, ",", &tmp))) {
189 *list = sp_list_insert(*list, zend_string_init(token, strlen(token), 1));
190 }
191 if (rest != NULL) {
192 pefree(rest, 1);
219 } 193 }
220 return SUCCESS; 194 return SUCCESS;
221} 195}
@@ -232,7 +206,7 @@ int parse_eval_whitelist(char *line) {
232 206
233int parse_cookie(char *line) { 207int parse_cookie(char *line) {
234 int ret = 0; 208 int ret = 0;
235 char *samesite = NULL; 209 zend_string *samesite = NULL;
236 sp_cookie *cookie = pecalloc(sizeof(sp_cookie), 1, 1); 210 sp_cookie *cookie = pecalloc(sizeof(sp_cookie), 1, 1);
237 211
238 sp_config_functions sp_config_funcs_cookie_encryption[] = { 212 sp_config_functions sp_config_funcs_cookie_encryption[] = {
@@ -274,7 +248,7 @@ int parse_cookie(char *line) {
274 sp_line_no); 248 sp_line_no);
275 return -1; 249 return -1;
276 } 250 }
277 if ((!cookie->name || '\0' == cookie->name[0]) && !cookie->name_r) { 251 if ((!cookie->name || 0 == ZSTR_LEN(cookie->name)) && !cookie->name_r) {
278 sp_log_err("config", 252 sp_log_err("config",
279 "You must specify a cookie name/regexp on line " 253 "You must specify a cookie name/regexp on line "
280 "%zu.", 254 "%zu.",
@@ -289,16 +263,17 @@ int parse_cookie(char *line) {
289 return -1; 263 return -1;
290 } 264 }
291 if (samesite) { 265 if (samesite) {
292 if (0 == strcasecmp(samesite, SP_TOKEN_SAMESITE_LAX)) { 266 if (zend_string_equals_literal_ci(samesite, SP_TOKEN_SAMESITE_LAX)) {
293 cookie->samesite = lax; 267 cookie->samesite = lax;
294 } else if (0 == strcasecmp(samesite, SP_TOKEN_SAMESITE_STRICT)) { 268 } else if (zend_string_equals_literal_ci(samesite,
269 SP_TOKEN_SAMESITE_STRICT)) {
295 cookie->samesite = strict; 270 cookie->samesite = strict;
296 } else { 271 } else {
297 sp_log_err( 272 sp_log_err(
298 "config", 273 "config",
299 "%s is an invalid value to samesite (expected %s or %s) on line " 274 "%s is an invalid value to samesite (expected %s or %s) on line "
300 "%zu.", 275 "%zu.",
301 samesite, SP_TOKEN_SAMESITE_LAX, SP_TOKEN_SAMESITE_STRICT, 276 ZSTR_VAL(samesite), SP_TOKEN_SAMESITE_LAX, SP_TOKEN_SAMESITE_STRICT,
302 sp_line_no); 277 sp_line_no);
303 return -1; 278 return -1;
304 } 279 }
@@ -308,11 +283,22 @@ int parse_cookie(char *line) {
308 return SUCCESS; 283 return SUCCESS;
309} 284}
310 285
286int add_df_to_hashtable(HashTable *ht, sp_disabled_function *df) {
287 zval *list = zend_hash_find(ht, df->function);
288
289 if (NULL == list) {
290 zend_hash_add_ptr(ht, df->function, sp_list_insert(NULL, df));
291 } else {
292 Z_PTR_P(list) = sp_list_insert(Z_PTR_P(list), df);
293 }
294 return SUCCESS;
295}
296
311int parse_disabled_functions(char *line) { 297int parse_disabled_functions(char *line) {
312 int ret = 0; 298 int ret = 0;
313 bool enable = true, disable = false, allow = false, drop = false; 299 bool enable = true, disable = false, allow = false, drop = false;
314 char *pos = NULL, *var = NULL, *param = NULL; 300 zend_string *pos = NULL, *var = NULL, *param = NULL;
315 char *line_number = NULL; 301 zend_string *line_number = NULL;
316 sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1); 302 sp_disabled_function *df = pecalloc(sizeof(*df), 1, 1);
317 df->pos = -1; 303 df->pos = -1;
318 304
@@ -330,7 +316,7 @@ int parse_disabled_functions(char *line) {
330 {parse_empty, SP_TOKEN_DROP, &(drop)}, 316 {parse_empty, SP_TOKEN_DROP, &(drop)},
331 {parse_str, SP_TOKEN_HASH, &(df->hash)}, 317 {parse_str, SP_TOKEN_HASH, &(df->hash)},
332 {parse_str, SP_TOKEN_PARAM, &(param)}, 318 {parse_str, SP_TOKEN_PARAM, &(param)},
333 {parse_regexp, SP_TOKEN_VALUE_REGEXP, &(df->value_r)}, 319 {parse_regexp, SP_TOKEN_VALUE_REGEXP, &(df->r_value)},
334 {parse_str, SP_TOKEN_VALUE, &(df->value)}, 320 {parse_str, SP_TOKEN_VALUE, &(df->value)},
335 {parse_str, SP_TOKEN_KEY, &(df->key)}, 321 {parse_str, SP_TOKEN_KEY, &(df->key)},
336 {parse_regexp, SP_TOKEN_KEY_REGEXP, &(df->r_key)}, 322 {parse_regexp, SP_TOKEN_KEY_REGEXP, &(df->r_key)},
@@ -360,7 +346,7 @@ int parse_disabled_functions(char *line) {
360 return 1; \ 346 return 1; \
361 } 347 }
362 348
363 MUTUALLY_EXCLUSIVE(df->value, df->value_r, "value", "regexp"); 349 MUTUALLY_EXCLUSIVE(df->value, df->r_value, "value", "regexp");
364 MUTUALLY_EXCLUSIVE(df->r_function, df->function, "r_function", "function"); 350 MUTUALLY_EXCLUSIVE(df->r_function, df->function, "r_function", "function");
365 MUTUALLY_EXCLUSIVE(df->filename, df->r_filename, "r_filename", "filename"); 351 MUTUALLY_EXCLUSIVE(df->filename, df->r_filename, "r_filename", "filename");
366 MUTUALLY_EXCLUSIVE(df->ret, df->r_ret, "r_ret", "ret"); 352 MUTUALLY_EXCLUSIVE(df->ret, df->r_ret, "r_ret", "ret");
@@ -375,25 +361,38 @@ int parse_disabled_functions(char *line) {
375 "'.r_param', '.param' and '.pos' are mutually exclusive on line %zu.", 361 "'.r_param', '.param' and '.pos' are mutually exclusive on line %zu.",
376 line, sp_line_no); 362 line, sp_line_no);
377 return -1; 363 return -1;
378 } else if ((df->r_key || df->key) && (df->value_r || df->value)) { 364 } else if ((df->r_key || df->key) && (df->r_value || df->value)) {
379 sp_log_err("config", 365 sp_log_err("config",
380 "Invalid configuration line: 'sp.disabled_functions%s':" 366 "Invalid configuration line: 'sp.disabled_functions%s':"
381 "`key` and `value` are mutually exclusive on line %zu.", 367 "`key` and `value` are mutually exclusive on line %zu.",
382 line, sp_line_no); 368 line, sp_line_no);
383 return -1; 369 return -1;
384 } else if ((df->r_ret || df->ret) && (df->r_param || param)) { 370 } else if ((df->r_ret || df->ret || df->ret_type) && (df->r_param || param)) {
385 sp_log_err("config", 371 sp_log_err("config",
386 "Invalid configuration line: 'sp.disabled_functions%s':" 372 "Invalid configuration line: 'sp.disabled_functions%s':"
387 "`ret` and `param` are mutually exclusive on line %zu.", 373 "`ret` and `param` are mutually exclusive on line %zu.",
388 line, sp_line_no); 374 line, sp_line_no);
389 return -1; 375 return -1;
376 } else if ((df->r_ret || df->ret || df->ret_type) && (var)) {
377 sp_log_err("config",
378 "Invalid configuration line: 'sp.disabled_functions%s':"
379 "`ret` and `var` are mutually exclusive on line %zu.",
380 line, sp_line_no);
381 return -1;
382 } else if ((df->r_ret || df->ret || df->ret_type) &&
383 (df->value || df->r_value)) {
384 sp_log_err("config",
385 "Invalid configuration line: 'sp.disabled_functions%s':"
386 "`ret` and `value` are mutually exclusive on line %zu.",
387 line, sp_line_no);
388 return -1;
390 } else if (!(df->r_function || df->function)) { 389 } else if (!(df->r_function || df->function)) {
391 sp_log_err("config", 390 sp_log_err("config",
392 "Invalid configuration line: 'sp.disabled_functions%s':" 391 "Invalid configuration line: 'sp.disabled_functions%s':"
393 " must take a function name on line %zu.", 392 " must take a function name on line %zu.",
394 line, sp_line_no); 393 line, sp_line_no);
395 return -1; 394 return -1;
396 } else if (df->filename && *df->filename != '/') { 395 } else if (df->filename && *ZSTR_VAL(df->filename) != '/') {
397 sp_log_err("config", 396 sp_log_err("config",
398 "Invalid configuration line: 'sp.disabled_functions%s':" 397 "Invalid configuration line: 'sp.disabled_functions%s':"
399 "'.filename' must be an absolute path on line %zu.", 398 "'.filename' must be an absolute path on line %zu.",
@@ -410,10 +409,10 @@ int parse_disabled_functions(char *line) {
410 if (pos) { 409 if (pos) {
411 errno = 0; 410 errno = 0;
412 char *endptr; 411 char *endptr;
413 df->pos = (int)strtol(pos, &endptr, 10); 412 df->pos = (int)strtol(ZSTR_VAL(pos), &endptr, 10);
414 if (errno != 0 || endptr == pos) { 413 if (errno != 0 || endptr == ZSTR_VAL(pos)) {
415 sp_log_err("config", "Failed to parse arg '%s' of `pos` on line %zu.", 414 sp_log_err("config", "Failed to parse arg '%s' of `pos` on line %zu.",
416 pos, sp_line_no); 415 ZSTR_VAL(pos), sp_line_no);
417 return -1; 416 return -1;
418 } 417 }
419 } 418 }
@@ -421,46 +420,46 @@ int parse_disabled_functions(char *line) {
421 if (line_number) { 420 if (line_number) {
422 errno = 0; 421 errno = 0;
423 char *endptr; 422 char *endptr;
424 df->line = (unsigned int)strtoul(line_number, &endptr, 10); 423 df->line = (unsigned int)strtoul(ZSTR_VAL(line_number), &endptr, 10);
425 if (errno != 0 || endptr == line_number) { 424 if (errno != 0 || endptr == ZSTR_VAL(line_number)) {
426 sp_log_err("config", "Failed to parse arg '%s' of `line` on line %zu.", 425 sp_log_err("config", "Failed to parse arg '%s' of `line` on line %zu.",
427 line_number, sp_line_no); 426 ZSTR_VAL(line_number), sp_line_no);
428 return -1; 427 return -1;
429 } 428 }
430 } 429 }
431 df->allow = allow; 430 df->allow = allow;
432 df->textual_representation = estrdup(line); 431 df->textual_representation = zend_string_init(line, strlen(line), 1);
433 432
434 if (df->function) { 433 if (df->function) {
435 df->functions_list = parse_functions_list(df->function); 434 df->functions_list = parse_functions_list(ZSTR_VAL(df->function));
436 } 435 }
437 436
438 if (param) { 437 if (param) {
439 if (strlen(param) > 0 && param[0] != '$') { 438 if (ZSTR_LEN(param) > 0 && ZSTR_VAL(param)[0] != '$') {
440 /* This is an ugly hack. We're prefixing with a `$` because otherwise 439 /* This is an ugly hack. We're prefixing with a `$` because otherwise
441 * the parser treats this as a constant. 440 * the parser treats this as a constant.
442 * FIXME: Remove this, and improve our (weird) parser. */ 441 * FIXME: Remove this, and improve our (weird) parser. */
443 char *new = pecalloc(strlen(param) + 2, 1, 1); 442 char *new = pecalloc(ZSTR_LEN(param) + 2, 1, 1);
444 new[0] = '$'; 443 new[0] = '$';
445 memcpy(new + 1, param, strlen(param)); 444 memcpy(new + 1, ZSTR_VAL(param), ZSTR_LEN(param));
446 df->param = sp_parse_var(new); 445 df->param = sp_parse_var(new);
447 free(new); 446 free(new);
448 } else { 447 } else {
449 df->param = sp_parse_var(param); 448 df->param = sp_parse_var(ZSTR_VAL(param));
450 } 449 }
451 if (!df->param) { 450 if (!df->param) {
452 sp_log_err("config", "Invalid value '%s' for `param` on line %zu.", param, 451 sp_log_err("config", "Invalid value '%s' for `param` on line %zu.",
453 sp_line_no); 452 ZSTR_VAL(param), sp_line_no);
454 return -1; 453 return -1;
455 } 454 }
456 } 455 }
457 456
458 if (var) { 457 if (var) {
459 if (*var) { 458 if (ZSTR_LEN(var)) {
460 df->var = sp_parse_var(var); 459 df->var = sp_parse_var(ZSTR_VAL(var));
461 if (!df->var) { 460 if (!df->var) {
462 sp_log_err("config", "Invalid value '%s' for `var` on line %zu.", var, 461 sp_log_err("config", "Invalid value '%s' for `var` on line %zu.",
463 sp_line_no); 462 ZSTR_VAL(var), sp_line_no);
464 return -1; 463 return -1;
465 } 464 }
466 } else { 465 } else {
@@ -469,38 +468,33 @@ int parse_disabled_functions(char *line) {
469 } 468 }
470 } 469 }
471 470
472 switch (get_construct_type(df)) {
473 case ZEND_INCLUDE_OR_EVAL:
474 SNUFFLEUPAGUS_G(config)
475 .config_disabled_constructs->construct_include = sp_list_insert(
476 SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_include,
477 df);
478 return ret;
479 case ZEND_EVAL_CODE:
480 SNUFFLEUPAGUS_G(config)
481 .config_disabled_constructs->construct_eval = sp_list_insert(
482 SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval,
483 df);
484 return ret;
485 case ZEND_ECHO:
486 default:
487 break;
488 }
489
490 if (true == disable) { 471 if (true == disable) {
491 return ret; 472 return ret;
492 } 473 }
493 474
494 if (df->ret || df->r_ret || df->ret_type) { 475 if (df->function && !df->functions_list) {
495 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions = 476 if (df->ret || df->r_ret || df->ret_type) {
496 sp_list_insert(SNUFFLEUPAGUS_G(config) 477 add_df_to_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions_ret,
497 .config_disabled_functions_ret->disabled_functions, 478 df);
498 df); 479 } else {
480 add_df_to_hashtable(SNUFFLEUPAGUS_G(config).config_disabled_functions,
481 df);
482 }
499 } else { 483 } else {
500 SNUFFLEUPAGUS_G(config) 484 if (df->ret || df->r_ret || df->ret_type) {
501 .config_disabled_functions->disabled_functions = sp_list_insert( 485 SNUFFLEUPAGUS_G(config)
502 SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions, 486 .config_disabled_functions_reg_ret->disabled_functions =
503 df); 487 sp_list_insert(
488 SNUFFLEUPAGUS_G(config)
489 .config_disabled_functions_reg_ret->disabled_functions,
490 df);
491 } else {
492 SNUFFLEUPAGUS_G(config)
493 .config_disabled_functions_reg->disabled_functions =
494 sp_list_insert(SNUFFLEUPAGUS_G(config)
495 .config_disabled_functions_reg->disabled_functions,
496 df);
497 }
504 } 498 }
505 return ret; 499 return ret;
506} 500}
@@ -529,20 +523,21 @@ int parse_upload_validation(char *line) {
529 } 523 }
530 SNUFFLEUPAGUS_G(config).config_upload_validation->enable = enable; 524 SNUFFLEUPAGUS_G(config).config_upload_validation->enable = enable;
531 525
532 char const *script = SNUFFLEUPAGUS_G(config).config_upload_validation->script; 526 zend_string const *script =
527 SNUFFLEUPAGUS_G(config).config_upload_validation->script;
533 528
534 if (!script) { 529 if (!script) {
535 sp_log_err("config", 530 sp_log_err("config",
536 "The `script` directive is mandatory in '%s' on line %zu.", line, 531 "The `script` directive is mandatory in '%s' on line %zu.", line,
537 sp_line_no); 532 sp_line_no);
538 return -1; 533 return -1;
539 } else if (-1 == access(script, F_OK)) { 534 } else if (-1 == access(ZSTR_VAL(script), F_OK)) {
540 sp_log_err("config", "The `script` (%s) doesn't exist on line %zu.", script, 535 sp_log_err("config", "The `script` (%s) doesn't exist on line %zu.",
541 sp_line_no); 536 ZSTR_VAL(script), sp_line_no);
542 return -1; 537 return -1;
543 } else if (-1 == access(script, X_OK)) { 538 } else if (-1 == access(ZSTR_VAL(script), X_OK)) {
544 sp_log_err("config", "The `script` (%s) isn't executable on line %zu.", 539 sp_log_err("config", "The `script` (%s) isn't executable on line %zu.",
545 script, sp_line_no); 540 ZSTR_VAL(script), sp_line_no);
546 return -1; 541 return -1;
547 } 542 }
548 543
diff --git a/src/sp_config_utils.c b/src/sp_config_utils.c
index 130f07e..d72561b 100644
--- a/src/sp_config_utils.c
+++ b/src/sp_config_utils.c
@@ -32,18 +32,18 @@ int parse_keywords(sp_config_functions *funcs, char *line) {
32 return 0; 32 return 0;
33} 33}
34 34
35char *get_param(size_t *consumed, char *restrict line, sp_type type, 35zend_string *get_param(size_t *consumed, char *restrict line, sp_type type,
36 const char *restrict keyword) { 36 const char *restrict keyword) {
37 enum { IN_ESCAPE, NONE } state = NONE; 37 enum { IN_ESCAPE, NONE } state = NONE;
38 char *original_line = line; 38 char *original_line = line;
39 size_t j = 0; 39 size_t j = 0;
40 40
41 char *ret = NULL; 41 zend_string *ret = NULL;
42 if (NULL == line || '\0' == *line) { 42 if (NULL == line || '\0' == *line) {
43 goto err; 43 goto err;
44 } 44 }
45 45
46 ret = pecalloc(sizeof(char), strlen(original_line) + 1, 1); 46 ret = zend_string_alloc(strlen(line) + 1, 1);
47 47
48 /* The first char of a string is always '"', since they MUST be quoted. */ 48 /* The first char of a string is always '"', since they MUST be quoted. */
49 if ('"' == *line) { 49 if ('"' == *line) {
@@ -65,6 +65,11 @@ char *get_param(size_t *consumed, char *restrict line, sp_type type,
65 2. the SP_TOKEN_END_PARAM 65 2. the SP_TOKEN_END_PARAM
66 */ 66 */
67 *consumed = i + 2; 67 *consumed = i + 2;
68 // Make sure that the string we return is the right size,
69 // as it can be smaller than strlen(line)
70 ret = zend_string_truncate(ret, j, 1);
71 // truncate does not add a \0
72 ZSTR_VAL(ret)[ZSTR_LEN(ret)] = 0;
68 return ret; 73 return ret;
69 } else if (state == IN_ESCAPE) { 74 } else if (state == IN_ESCAPE) {
70 break; // we're on an escped double quote 75 break; // we're on an escped double quote
@@ -82,7 +87,7 @@ char *get_param(size_t *consumed, char *restrict line, sp_type type,
82 if (state == IN_ESCAPE) { 87 if (state == IN_ESCAPE) {
83 state = NONE; 88 state = NONE;
84 } 89 }
85 ret[j++] = line[i]; 90 ZSTR_VAL(ret)[j++] = line[i];
86 } 91 }
87err: 92err:
88 if (0 == j) { 93 if (0 == j) {
diff --git a/src/sp_config_utils.h b/src/sp_config_utils.h
index 9fef996..a63cadc 100644
--- a/src/sp_config_utils.h
+++ b/src/sp_config_utils.h
@@ -2,7 +2,7 @@
2#define SP_CONFIG_UTILS 2#define SP_CONFIG_UTILS
3 3
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); 5zend_string *get_param(size_t *, char *restrict, sp_type, const char *restrict);
6int array_to_list(char **, sp_list_node **); 6int array_to_list(char **, sp_list_node **);
7sp_list_node *parse_functions_list(char *value); 7sp_list_node *parse_functions_list(char *value);
8 8
diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c
index 6cb1ff7..3f58fb0 100644
--- a/src/sp_cookie_encryption.c
+++ b/src/sp_cookie_encryption.c
@@ -4,8 +4,8 @@
4 4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6 6
7static inline const sp_cookie *sp_lookup_cookie_config(const char *key) { 7static inline const sp_cookie *sp_lookup_cookie_config(const zend_string *key) {
8 sp_list_node *it = SNUFFLEUPAGUS_G(config).config_cookie->cookies; 8 const sp_list_node *it = SNUFFLEUPAGUS_G(config).config_cookie->cookies;
9 9
10 while (it) { 10 while (it) {
11 const sp_cookie *config = it->data; 11 const sp_cookie *config = it->data;
@@ -20,7 +20,7 @@ static inline const sp_cookie *sp_lookup_cookie_config(const char *key) {
20/* called at RINIT time with each cookie, eventually decrypt said cookie */ 20/* called at RINIT time with each cookie, eventually decrypt said cookie */
21int decrypt_cookie(zval *pDest, int num_args, va_list args, 21int decrypt_cookie(zval *pDest, int num_args, va_list args,
22 zend_hash_key *hash_key) { 22 zend_hash_key *hash_key) {
23 const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key)); 23 const sp_cookie *cookie = sp_lookup_cookie_config(hash_key->key);
24 24
25 /* If the cookie isn't in the conf, it shouldn't be encrypted. */ 25 /* If the cookie isn't in the conf, it shouldn't be encrypted. */
26 if (!cookie || !cookie->encrypt) { 26 if (!cookie || !cookie->encrypt) {
@@ -35,9 +35,9 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args,
35 return decrypt_zval(pDest, cookie->simulation, hash_key); 35 return decrypt_zval(pDest, cookie->simulation, hash_key);
36} 36}
37 37
38static zend_string *encrypt_data(char *data, unsigned long long data_len) { 38static zend_string *encrypt_data(zend_string *data) {
39 zend_string *z = encrypt_zval(data, data_len); 39 zend_string *z = encrypt_zval(data);
40 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val); 40 sp_log_debug("cookie_encryption", "Cookie value:%s:", ZSTR_VAL(z));
41 return z; 41 return z;
42} 42}
43 43
@@ -77,7 +77,7 @@ PHP_FUNCTION(sp_setcookie) {
77 } 77 }
78 78
79 /* lookup existing configuration for said cookie */ 79 /* lookup existing configuration for said cookie */
80 cookie_node = sp_lookup_cookie_config(ZSTR_VAL(name)); 80 cookie_node = sp_lookup_cookie_config(name);
81 81
82 /* If the cookie's value is encrypted, it won't be usable by 82 /* If the cookie's value is encrypted, it won't be usable by
83 * javascript anyway. 83 * javascript anyway.
@@ -88,7 +88,7 @@ PHP_FUNCTION(sp_setcookie) {
88 88
89 /* Shall we encrypt the cookie's value? */ 89 /* Shall we encrypt the cookie's value? */
90 if (cookie_node && cookie_node->encrypt && value) { 90 if (cookie_node && cookie_node->encrypt && value) {
91 zend_string *encrypted_data = encrypt_data(value->val, value->len); 91 zend_string *encrypted_data = encrypt_data(value);
92 ZVAL_STR_COPY(&params[1], encrypted_data); 92 ZVAL_STR_COPY(&params[1], encrypted_data);
93 zend_string_release(encrypted_data); 93 zend_string_release(encrypted_data);
94 } else if (value) { 94 } else if (value) {
diff --git a/src/sp_crypt.c b/src/sp_crypt.c
index 6a46d06..d3588b4 100644
--- a/src/sp_crypt.c
+++ b/src/sp_crypt.c
@@ -7,10 +7,13 @@ ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
7void generate_key(unsigned char *key) { 7void generate_key(unsigned char *key) {
8 PHP_SHA256_CTX ctx; 8 PHP_SHA256_CTX ctx;
9 const char *user_agent = getenv("HTTP_USER_AGENT"); 9 const char *user_agent = getenv("HTTP_USER_AGENT");
10 const char *env_var = 10 const zend_string *env_var_zend =
11 getenv(SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); 11 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var;
12 const char *encryption_key = 12 const zend_string *encryption_key_zend =
13 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key; 13 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key;
14 const char *env_var = (env_var_zend ? getenv(ZSTR_VAL(env_var_zend)) : NULL);
15 const char *encryption_key =
16 (encryption_key_zend ? ZSTR_VAL(encryption_key_zend) : NULL);
14 17
15 assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256. 18 assert(32 == crypto_secretbox_KEYBYTES); // 32 is the size of a SHA256.
16 assert(encryption_key); // Encryption key can't be NULL 19 assert(encryption_key); // Encryption key can't be NULL
@@ -24,10 +27,12 @@ void generate_key(unsigned char *key) {
24 if (env_var) { 27 if (env_var) {
25 PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var)); 28 PHP_SHA256Update(&ctx, (unsigned char *)env_var, strlen(env_var));
26 } else { 29 } else {
27 sp_log_err("cookie_encryption", 30 sp_log_err(
28 "The environment variable '%s'" 31 "cookie_encryption",
29 "is empty, cookies are weakly encrypted.", 32 "The environment variable '%s'"
30 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var); 33 "is empty, cookies are weakly encrypted.",
34 ZSTR_VAL(
35 SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var));
31 } 36 }
32 37
33 if (encryption_key) { 38 if (encryption_key) {
@@ -119,8 +124,9 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
119** form `base64(nonce | encrypted_data)` (with `|` being the concatenation 124** form `base64(nonce | encrypted_data)` (with `|` being the concatenation
120** operation). 125** operation).
121*/ 126*/
122zend_string *encrypt_zval(char *data, unsigned long long data_len) { 127zend_string *encrypt_zval(zend_string *data) {
123 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; 128 const size_t encrypted_msg_len =
129 crypto_secretbox_ZEROBYTES + ZSTR_LEN(data) + 1;
124 // FIXME : We know that this len is too long 130 // FIXME : We know that this len is too long
125 const size_t emsg_and_nonce_len = 131 const size_t emsg_and_nonce_len =
126 encrypted_msg_len + crypto_secretbox_NONCEBYTES; 132 encrypted_msg_len + crypto_secretbox_NONCEBYTES;
@@ -137,7 +143,8 @@ zend_string *encrypt_zval(char *data, unsigned long long data_len) {
137 143
138 /* tweetnacl's API requires the message to be padded with 144 /* tweetnacl's API requires the message to be padded with
139 crypto_secretbox_ZEROBYTES zeroes. */ 145 crypto_secretbox_ZEROBYTES zeroes. */
140 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len); 146 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, ZSTR_VAL(data),
147 ZSTR_LEN(data));
141 148
142 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES); 149 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES);
143 150
@@ -149,4 +156,4 @@ zend_string *encrypt_zval(char *data, unsigned long long data_len) {
149 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len); 156 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
150 157
151 return z; 158 return z;
152} \ No newline at end of file 159}
diff --git a/src/sp_crypt.h b/src/sp_crypt.h
index 3ede104..22a571e 100644
--- a/src/sp_crypt.h
+++ b/src/sp_crypt.h
@@ -12,6 +12,6 @@
12 12
13void generate_key(unsigned char *key); 13void generate_key(unsigned char *key);
14int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key); 14int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key);
15zend_string *encrypt_zval(char *data, unsigned long long data_len); 15zend_string *encrypt_zval(zend_string *);
16 16
17#endif /*__SP_CRYPT */ \ No newline at end of file 17#endif /*__SP_CRYPT */
diff --git a/src/sp_disabled_functions.c b/src/sp_disabled_functions.c
index 341c0a4..14783f6 100644
--- a/src/sp_disabled_functions.c
+++ b/src/sp_disabled_functions.c
@@ -65,7 +65,7 @@ static bool is_functions_list_matching(zend_execute_data* execute_data,
65static bool is_local_var_matching( 65static bool is_local_var_matching(
66 zend_execute_data* execute_data, 66 zend_execute_data* execute_data,
67 const sp_disabled_function* const config_node) { 67 const sp_disabled_function* const config_node) {
68 zval* var_value; 68 zval* var_value = {0};
69 69
70 var_value = sp_get_var_value(execute_data, config_node->var, false); 70 var_value = sp_get_var_value(execute_data, config_node->var, false);
71 if (var_value) { 71 if (var_value) {
@@ -76,14 +76,13 @@ static bool is_local_var_matching(
76 return true; 76 return true;
77 } 77 }
78 } else if (sp_match_array_value(var_value, config_node->value, 78 } else if (sp_match_array_value(var_value, config_node->value,
79 config_node->value_r)) { 79 config_node->r_value)) {
80 return true; 80 return true;
81 } 81 }
82 } else { 82 } else {
83 char* var_value_str = sp_convert_to_string(var_value); 83 const zend_string* var_value_str = sp_zval_to_zend_string(var_value);
84 bool match = sp_match_value(var_value_str, config_node->value, 84 bool match = sp_match_value(var_value_str, config_node->value,
85 config_node->value_r); 85 config_node->r_value);
86 efree(var_value_str);
87 86
88 if (true == match) { 87 if (true == match) {
89 return true; 88 return true;
@@ -93,28 +92,12 @@ static bool is_local_var_matching(
93 return false; 92 return false;
94} 93}
95 94
96static inline const sp_list_node* get_config_node(const char* builtin_name) {
97 if (EXPECTED(!builtin_name)) {
98 return SNUFFLEUPAGUS_G(config)
99 .config_disabled_functions->disabled_functions;
100 } else if (!strcmp(builtin_name, "eval")) {
101 return SNUFFLEUPAGUS_G(config).config_disabled_constructs->construct_eval;
102 } else if (!strcmp(builtin_name, "include") ||
103 !strcmp(builtin_name, "include_once") ||
104 !strcmp(builtin_name, "require") ||
105 !strcmp(builtin_name, "require_once")) {
106 return SNUFFLEUPAGUS_G(config)
107 .config_disabled_constructs->construct_include;
108 }
109 ZEND_ASSUME(0);
110}
111
112static bool is_param_matching(zend_execute_data* execute_data, 95static bool is_param_matching(zend_execute_data* execute_data,
113 sp_disabled_function const* const config_node, 96 sp_disabled_function const* const config_node,
114 const char* builtin_name, 97 const zend_string* builtin_param,
115 const char* builtin_param, const char** arg_name, 98 const char** arg_name,
116 const char* builtin_param_name, 99 const char* builtin_param_name,
117 const char** arg_value_str) { 100 const zend_string** arg_value_str) {
118 int nb_param = ZEND_CALL_NUM_ARGS(execute_data); 101 int nb_param = ZEND_CALL_NUM_ARGS(execute_data);
119 int i = 0; 102 int i = 0;
120 zval* arg_value; 103 zval* arg_value;
@@ -136,14 +119,14 @@ static bool is_param_matching(zend_execute_data* execute_data,
136 } 119 }
137 } 120 }
138 121
139 if (builtin_name) { 122 if (builtin_param) {
140 /* We're matching on a language construct (here named "builtin"), 123 /* We're matching on a language construct (here named "builtin"),
141 * and they can only take a single argument, but PHP considers them 124 * and they can only take a single argument, but PHP considers them
142 * differently than functions arguments. */ 125 * differently than functions arguments. */
143 *arg_name = builtin_param_name; 126 *arg_name = builtin_param_name;
144 *arg_value_str = builtin_param; 127 *arg_value_str = builtin_param;
145 return sp_match_value(builtin_param, config_node->value, 128 return sp_match_value(builtin_param, config_node->value,
146 config_node->value_r); 129 config_node->r_value);
147 } else if (config_node->r_param || config_node->pos != -1) { 130 } else if (config_node->r_param || config_node->pos != -1) {
148 // We're matching on a function (and not a language construct) 131 // We're matching on a function (and not a language construct)
149 for (; i < nb_param; i++) { 132 for (; i < nb_param; i++) {
@@ -165,20 +148,20 @@ static bool is_param_matching(zend_execute_data* execute_data,
165 return true; 148 return true;
166 } 149 }
167 } else if (Z_TYPE_P(arg_value) == IS_ARRAY) { 150 } else if (Z_TYPE_P(arg_value) == IS_ARRAY) {
168 *arg_value_str = sp_convert_to_string(arg_value); 151 *arg_value_str = sp_zval_to_zend_string(arg_value);
169 if (config_node->key || config_node->r_key) { 152 if (config_node->key || config_node->r_key) {
170 if (sp_match_array_key(arg_value, config_node->key, 153 if (sp_match_array_key(arg_value, config_node->key,
171 config_node->r_key)) { 154 config_node->r_key)) {
172 return true; 155 return true;
173 } 156 }
174 } else if (sp_match_array_value(arg_value, config_node->value, 157 } else if (sp_match_array_value(arg_value, config_node->value,
175 config_node->value_r)) { 158 config_node->r_value)) {
176 return true; 159 return true;
177 } 160 }
178 } else { 161 } else {
179 *arg_value_str = sp_convert_to_string(arg_value); 162 *arg_value_str = sp_zval_to_zend_string(arg_value);
180 if (sp_match_value(*arg_value_str, config_node->value, 163 if (sp_match_value(*arg_value_str, config_node->value,
181 config_node->value_r)) { 164 config_node->r_value)) {
182 return true; 165 return true;
183 } 166 }
184 } 167 }
@@ -189,7 +172,7 @@ static bool is_param_matching(zend_execute_data* execute_data,
189 arg_value = sp_get_var_value(execute_data, config_node->param, true); 172 arg_value = sp_get_var_value(execute_data, config_node->param, true);
190 173
191 if (arg_value) { 174 if (arg_value) {
192 *arg_value_str = sp_convert_to_string(arg_value); 175 *arg_value_str = sp_zval_to_zend_string(arg_value);
193 if (config_node->param_type) { // Are we matching on the `type`? 176 if (config_node->param_type) { // Are we matching on the `type`?
194 if (config_node->param_type == Z_TYPE_P(arg_value)) { 177 if (config_node->param_type == Z_TYPE_P(arg_value)) {
195 return true; 178 return true;
@@ -201,11 +184,11 @@ static bool is_param_matching(zend_execute_data* execute_data,
201 return true; 184 return true;
202 } 185 }
203 } else if (sp_match_array_value(arg_value, config_node->value, 186 } else if (sp_match_array_value(arg_value, config_node->value,
204 config_node->value_r)) { 187 config_node->r_value)) {
205 return true; 188 return true;
206 } 189 }
207 } else if (sp_match_value(*arg_value_str, config_node->value, 190 } else if (sp_match_value(*arg_value_str, config_node->value,
208 config_node->value_r)) { 191 config_node->r_value)) {
209 return true; 192 return true;
210 } 193 }
211 } 194 }
@@ -216,7 +199,7 @@ static bool is_param_matching(zend_execute_data* execute_data,
216static zend_execute_data* is_file_matching( 199static zend_execute_data* is_file_matching(
217 zend_execute_data* const execute_data, 200 zend_execute_data* const execute_data,
218 sp_disabled_function const* const config_node, 201 sp_disabled_function const* const config_node,
219 char const* const current_filename) { 202 zend_string const* const current_filename) {
220#define ITERATE(ex) \ 203#define ITERATE(ex) \
221 ex = ex->prev_execute_data; \ 204 ex = ex->prev_execute_data; \
222 while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) \ 205 while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) \
@@ -225,22 +208,21 @@ static zend_execute_data* is_file_matching(
225 208
226 zend_execute_data* ex = execute_data; 209 zend_execute_data* ex = execute_data;
227 if (config_node->filename) { 210 if (config_node->filename) {
228 if (0 == strcmp(current_filename, config_node->filename)) { 211 if (zend_string_equals_literal(current_filename, config_node->filename)) {
229 return ex; 212 return ex;
230 } 213 }
231 ITERATE(ex); 214 ITERATE(ex);
232 if (0 == 215 if (zend_string_equals_literal(ex->func->op_array.filename,
233 strcmp(ZSTR_VAL(ex->func->op_array.filename), config_node->filename)) { 216 config_node->filename)) {
234 return ex; 217 return ex;
235 } 218 }
236 } else if (config_node->r_filename) { 219 } else if (config_node->r_filename) {
237 if (true == 220 if (sp_is_regexp_matching_zend(config_node->r_filename, current_filename)) {
238 sp_is_regexp_matching(config_node->r_filename, current_filename)) {
239 return ex; 221 return ex;
240 } 222 }
241 ITERATE(ex); 223 ITERATE(ex);
242 if (true == sp_is_regexp_matching(config_node->r_filename, 224 if (sp_is_regexp_matching_zend(config_node->r_filename,
243 ZSTR_VAL(ex->func->op_array.filename))) { 225 ex->func->op_array.filename)) {
244 return ex; 226 return ex;
245 } 227 }
246 } 228 }
@@ -251,10 +233,10 @@ static zend_execute_data* is_file_matching(
251static bool check_is_builtin_name( 233static bool check_is_builtin_name(
252 sp_disabled_function const* const config_node) { 234 sp_disabled_function const* const config_node) {
253 if (config_node->function) { 235 if (config_node->function) {
254 return (!strcmp(config_node->function, "include") || 236 return (zend_string_equals_literal(config_node->function, "include") ||
255 !strcmp(config_node->function, "include_once") || 237 zend_string_equals_literal(config_node->function, "include_once") ||
256 !strcmp(config_node->function, "require") || 238 zend_string_equals_literal(config_node->function, "require") ||
257 !strcmp(config_node->function, "require_once")); 239 zend_string_equals_literal(config_node->function, "require_once"));
258 } 240 }
259 if (config_node->r_function) { 241 if (config_node->r_function) {
260 return (sp_is_regexp_matching(config_node->r_function, "include") || 242 return (sp_is_regexp_matching(config_node->r_function, "include") ||
@@ -265,43 +247,67 @@ static bool check_is_builtin_name(
265 return false; 247 return false;
266} 248}
267 249
268bool should_disable(zend_execute_data* execute_data, const char* builtin_name, 250bool should_disable_ht(zend_execute_data* execute_data,
269 const char* builtin_param, const char* builtin_param_name) { 251 const char* builtin_name,
270 char current_file_hash[SHA256_SIZE * 2 + 1] = {0}; 252 const zend_string* builtin_param,
271 const sp_list_node* config = get_config_node(builtin_name); 253 const char* builtin_param_name,
272 char* complete_path_function = NULL; 254 const sp_list_node* config, const HashTable* ht) {
273 const char* current_filename = NULL; 255 char* complete_function_path = NULL;
274 unsigned int line = 0; 256 const sp_list_node* ht_entry = NULL;
275 char* filename = NULL; 257 bool ret = false;
258 zend_string* current_filename;
276 259
277 if (!execute_data) { 260 if (!execute_data) {
278 return false; 261 return false;
279 } 262 }
280 263
281 if (!config || !config->data) { 264 if (builtin_name) {
282 return false; 265 complete_function_path = estrdup(builtin_name);
266 } else {
267 complete_function_path = get_complete_function_path(execute_data);
268 if (!complete_function_path) {
269 return false;
270 }
283 } 271 }
284 272
285 if (UNEXPECTED(builtin_name && !strcmp(builtin_name, "eval"))) { 273 if (UNEXPECTED(builtin_param && !strcmp(complete_function_path, "eval"))) {
286 current_filename = get_eval_filename(zend_get_executed_filename()); 274 current_filename = get_eval_filename(zend_get_executed_filename());
287 } else { 275 } else {
288 current_filename = zend_get_executed_filename(); 276 const char* tmp = zend_get_executed_filename();
277 current_filename = zend_string_init(tmp, strlen(tmp), 0);
289 } 278 }
290 279
291 complete_path_function = get_complete_function_path(execute_data); 280 ht_entry = zend_hash_str_find_ptr(ht, complete_function_path,
292 if (!complete_path_function) { 281 strlen(complete_function_path));
293 if (builtin_name) { 282
294 complete_path_function = estrdup(builtin_name); 283 if (ht_entry &&
295 } else { 284 should_disable(execute_data, complete_function_path, builtin_param,
296 return false; 285 builtin_param_name, ht_entry, current_filename)) {
297 } 286 ret = true;
287 } else if (config && config->data) {
288 ret = should_disable(execute_data, complete_function_path, builtin_param,
289 builtin_param_name, config, current_filename);
298 } 290 }
299 291
292 efree(complete_function_path);
293 efree(current_filename);
294 return ret;
295}
296
297bool should_disable(zend_execute_data* execute_data,
298 const char* complete_function_path,
299 const zend_string* builtin_param,
300 const char* builtin_param_name, const sp_list_node* config,
301 const zend_string* current_filename) {
302 char current_file_hash[SHA256_SIZE * 2 + 1] = {0};
303 unsigned int line = 0;
304 char* filename = NULL;
305
300 while (config) { 306 while (config) {
301 sp_disabled_function const* const config_node = 307 sp_disabled_function const* const config_node =
302 (sp_disabled_function*)(config->data); 308 (sp_disabled_function*)(config->data);
303 const char* arg_name = NULL; 309 const char* arg_name = NULL;
304 const char* arg_value_str = NULL; 310 const zend_string* arg_value_str = NULL;
305 311
306 /* The order matters, since when we have `config_node->functions_list`, 312 /* The order matters, since when we have `config_node->functions_list`,
307 we also do have `config_node->function` */ 313 we also do have `config_node->function` */
@@ -311,12 +317,13 @@ bool should_disable(zend_execute_data* execute_data, const char* builtin_name,
311 goto next; 317 goto next;
312 } 318 }
313 } else if (config_node->function) { 319 } else if (config_node->function) {
314 if (0 != strcmp(config_node->function, complete_path_function)) { 320 if (0 !=
321 strcmp(ZSTR_VAL(config_node->function), complete_function_path)) {
315 goto next; 322 goto next;
316 } 323 }
317 } else if (config_node->r_function) { 324 } else if (config_node->r_function) {
318 if (false == sp_is_regexp_matching(config_node->r_function, 325 if (false == sp_is_regexp_matching(config_node->r_function,
319 complete_path_function)) { 326 complete_function_path)) {
320 goto next; 327 goto next;
321 } 328 }
322 } 329 }
@@ -350,9 +357,10 @@ bool should_disable(zend_execute_data* execute_data, const char* builtin_name,
350 357
351 if (config_node->hash) { 358 if (config_node->hash) {
352 if ('\0' == current_file_hash[0]) { 359 if ('\0' == current_file_hash[0]) {
353 compute_hash(current_filename, current_file_hash); 360 compute_hash(ZSTR_VAL(current_filename), current_file_hash);
354 } 361 }
355 if (0 != strncmp(current_file_hash, config_node->hash, SHA256_SIZE)) { 362 if (0 != strncmp(current_file_hash, ZSTR_VAL(config_node->hash),
363 SHA256_SIZE)) {
356 goto next; 364 goto next;
357 } 365 }
358 } 366 }
@@ -360,25 +368,25 @@ bool should_disable(zend_execute_data* execute_data, const char* builtin_name,
360 /* Check if we filter on parameter value*/ 368 /* Check if we filter on parameter value*/
361 if (config_node->param || config_node->r_param || 369 if (config_node->param || config_node->r_param ||
362 (config_node->pos != -1)) { 370 (config_node->pos != -1)) {
363 if (!builtin_name && execute_data->func->op_array.arg_info->is_variadic) { 371 if (!builtin_param &&
372 execute_data->func->op_array.arg_info->is_variadic) {
364 sp_log_err( 373 sp_log_err(
365 "disable_function", 374 "disable_function",
366 "Snuffleupagus doesn't support variadic functions yet, sorry. " 375 "Snuffleupagus doesn't support variadic functions yet, sorry. "
367 "Check https://github.com/nbs-system/snuffleupagus/issues/164 for " 376 "Check https://github.com/nbs-system/snuffleupagus/issues/164 for "
368 "details."); 377 "details.");
369 } else if (false == is_param_matching(execute_data, config_node, 378 } else if (false == is_param_matching(
370 builtin_name, builtin_param, 379 execute_data, config_node, builtin_param,
371 &arg_name, builtin_param_name, 380 &arg_name, builtin_param_name, &arg_value_str)) {
372 &arg_value_str)) {
373 goto next; 381 goto next;
374 } 382 }
375 } 383 }
376 384
377 if (config_node->value_r || config_node->value) { 385 if (config_node->r_value || config_node->value) {
378 if (check_is_builtin_name(config_node)) { 386 if (check_is_builtin_name(config_node)) {
379 if (false == is_param_matching(execute_data, config_node, builtin_name, 387 if (false == is_param_matching(execute_data, config_node, builtin_param,
380 builtin_param, &arg_name, 388 &arg_name, builtin_param_name,
381 builtin_param_name, &arg_value_str)) { 389 &arg_value_str)) {
382 goto next; 390 goto next;
383 } 391 }
384 } 392 }
@@ -390,63 +398,76 @@ bool should_disable(zend_execute_data* execute_data, const char* builtin_name,
390 } 398 }
391 399
392 if (config_node->functions_list) { 400 if (config_node->functions_list) {
393 sp_log_disable(config_node->function, arg_name, arg_value_str, 401 sp_log_disable(ZSTR_VAL(config_node->function), arg_name, arg_value_str,
394 config_node, line, filename); 402 config_node, line, filename);
395 } else { 403 } else {
396 sp_log_disable(complete_path_function, arg_name, arg_value_str, 404 sp_log_disable(complete_function_path, arg_name, arg_value_str,
397 config_node, line, filename); 405 config_node, line, filename);
398 } 406 }
399 if (true == config_node->simulation) { 407 if (true == config_node->simulation) {
400 goto next; 408 goto next;
401 } else { // We've got a match, the function won't be executed 409 } else { // We've got a match, the function won't be executed
402 efree(complete_path_function);
403 return true; 410 return true;
404 } 411 }
405 next: 412 next:
406 config = config->next; 413 config = config->next;
407 } 414 }
408allow: 415allow:
409 efree(complete_path_function);
410 return false; 416 return false;
411} 417}
412 418
413bool should_drop_on_ret(zval* return_value, 419bool should_drop_on_ret_ht(zval* return_value,
414 const zend_execute_data* const execute_data) { 420 const zend_execute_data* const execute_data,
415 const sp_list_node* config = 421 const sp_list_node* config, const HashTable* ht) {
416 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret->disabled_functions; 422 char* complete_function_path = get_complete_function_path(execute_data);
417 char* complete_path_function = get_complete_function_path(execute_data); 423 const sp_list_node* ht_entry = NULL;
418 const char* current_filename = zend_get_executed_filename(TSRMLS_C); 424 bool ret = false;
419 char current_file_hash[SHA256_SIZE * 2 + 1] = {0};
420 bool match_type = false, match_value = false;
421 425
422 if (!complete_path_function) { 426 if (!complete_function_path) {
423 return false; 427 return ret;
424 } 428 }
425 429
426 if (!config || !config->data) { 430 ht_entry = zend_hash_str_find_ptr(ht, complete_function_path,
427 return false; 431 strlen(complete_function_path));
432
433 if (ht_entry &&
434 should_drop_on_ret(return_value, ht_entry, complete_function_path)) {
435 ret = true;
436 } else if (config && config->data) {
437 ret = should_drop_on_ret(return_value, config, complete_function_path);
428 } 438 }
429 439
440 efree(complete_function_path);
441 return ret;
442}
443
444bool should_drop_on_ret(zval* return_value, const sp_list_node* config,
445 const char* complete_function_path) {
446 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
447 char current_file_hash[SHA256_SIZE * 2 + 1] = {0};
448 bool match_type = false, match_value = false;
449
430 while (config) { 450 while (config) {
431 char* ret_value_str = NULL; 451 const zend_string* ret_value_str = NULL;
432 sp_disabled_function const* const config_node = 452 sp_disabled_function const* const config_node =
433 (sp_disabled_function*)(config->data); 453 (sp_disabled_function*)(config->data);
434 454
435 assert(config_node->function || config_node->r_function); 455 assert(config_node->function || config_node->r_function);
436 456
437 if (config_node->function) { 457 if (config_node->function) {
438 if (0 != strcmp(config_node->function, complete_path_function)) { 458 if (0 !=
459 strcmp(ZSTR_VAL(config_node->function), complete_function_path)) {
439 goto next; 460 goto next;
440 } 461 }
441 } else if (config_node->r_function) { 462 } else if (config_node->r_function) {
442 if (false == sp_is_regexp_matching(config_node->r_function, 463 if (false == sp_is_regexp_matching(config_node->r_function,
443 complete_path_function)) { 464 complete_function_path)) {
444 goto next; 465 goto next;
445 } 466 }
446 } 467 }
447 468
448 if (config_node->filename) { /* Check the current file name. */ 469 if (config_node->filename) { /* Check the current file name. */
449 if (0 != strcmp(current_filename, config_node->filename)) { 470 if (0 != strcmp(current_filename, ZSTR_VAL(config_node->filename))) {
450 goto next; 471 goto next;
451 } 472 }
452 } else if (config_node->r_filename) { 473 } else if (config_node->r_filename) {
@@ -460,12 +481,13 @@ bool should_drop_on_ret(zval* return_value,
460 if ('\0' == current_file_hash[0]) { 481 if ('\0' == current_file_hash[0]) {
461 compute_hash(current_filename, current_file_hash); 482 compute_hash(current_filename, current_file_hash);
462 } 483 }
463 if (0 != strncmp(current_file_hash, config_node->hash, SHA256_SIZE)) { 484 if (0 != strncmp(current_file_hash, ZSTR_VAL(config_node->hash),
485 SHA256_SIZE)) {
464 goto next; 486 goto next;
465 } 487 }
466 } 488 }
467 489
468 ret_value_str = sp_convert_to_string(return_value); 490 ret_value_str = sp_zval_to_zend_string(return_value);
469 491
470 match_type = (config_node->ret_type) && 492 match_type = (config_node->ret_type) &&
471 (config_node->ret_type == Z_TYPE_P(return_value)); 493 (config_node->ret_type == Z_TYPE_P(return_value));
@@ -475,22 +497,16 @@ bool should_drop_on_ret(zval* return_value,
475 497
476 if (true == match_type || true == match_value) { 498 if (true == match_type || true == match_value) {
477 if (true == config_node->allow) { 499 if (true == config_node->allow) {
478 efree(complete_path_function);
479 efree(ret_value_str);
480 return false; 500 return false;
481 } 501 }
482 sp_log_disable_ret(complete_path_function, ret_value_str, config_node); 502 sp_log_disable_ret(complete_function_path, ret_value_str, config_node);
483 if (false == config_node->simulation) { 503 if (false == config_node->simulation) {
484 efree(complete_path_function);
485 efree(ret_value_str);
486 return true; 504 return true;
487 } 505 }
488 } 506 }
489 next: 507 next:
490 efree(ret_value_str);
491 config = config->next; 508 config = config->next;
492 } 509 }
493 efree(complete_path_function);
494 return false; 510 return false;
495} 511}
496 512
@@ -498,7 +514,11 @@ ZEND_FUNCTION(check_disabled_function) {
498 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); 514 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
499 const char* current_function_name = get_active_function_name(TSRMLS_C); 515 const char* current_function_name = get_active_function_name(TSRMLS_C);
500 516
501 if (true == should_disable(execute_data, NULL, NULL, NULL)) { 517 if (true == should_disable_ht(
518 execute_data, NULL, NULL, NULL,
519 SNUFFLEUPAGUS_G(config)
520 .config_disabled_functions_reg->disabled_functions,
521 SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked)) {
502 sp_terminate(); 522 sp_terminate();
503 } 523 }
504 524
@@ -506,23 +526,29 @@ ZEND_FUNCTION(check_disabled_function) {
506 SNUFFLEUPAGUS_G(disabled_functions_hook), current_function_name, 526 SNUFFLEUPAGUS_G(disabled_functions_hook), current_function_name,
507 strlen(current_function_name)); 527 strlen(current_function_name));
508 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 528 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
509 if (true == should_drop_on_ret(return_value, execute_data)) { 529 if (true ==
530 should_drop_on_ret_ht(
531 return_value, execute_data,
532 SNUFFLEUPAGUS_G(config)
533 .config_disabled_functions_reg_ret->disabled_functions,
534 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret_hooked)) {
510 sp_terminate(); 535 sp_terminate();
511 } 536 }
512} 537}
513 538
514static int hook_functions(const sp_list_node* config) { 539static int hook_functions_regexp(const sp_list_node* config) {
515 while (config && config->data) { 540 while (config && config->data) {
516 const char* function_name = ((sp_disabled_function*)config->data)->function; 541 const zend_string* function_name =
542 ((sp_disabled_function*)config->data)->function;
517 const sp_pcre* function_name_regexp = 543 const sp_pcre* function_name_regexp =
518 ((sp_disabled_function*)config->data)->r_function; 544 ((sp_disabled_function*)config->data)->r_function;
519 545
520 assert(function_name || function_name_regexp); 546 assert(function_name || function_name_regexp);
521 547
522 if (NULL != function_name) { // hook function by name 548 if (function_name) {
523 HOOK_FUNCTION(function_name, disabled_functions_hook, 549 HOOK_FUNCTION(ZSTR_VAL(function_name), disabled_functions_hook,
524 PHP_FN(check_disabled_function)); 550 PHP_FN(check_disabled_function));
525 } else if (NULL != function_name_regexp) { // hook function by regexp 551 } else {
526 HOOK_FUNCTION_BY_REGEXP(function_name_regexp, disabled_functions_hook, 552 HOOK_FUNCTION_BY_REGEXP(function_name_regexp, disabled_functions_hook,
527 PHP_FN(check_disabled_function)); 553 PHP_FN(check_disabled_function));
528 } 554 }
@@ -532,16 +558,36 @@ static int hook_functions(const sp_list_node* config) {
532 return SUCCESS; 558 return SUCCESS;
533} 559}
534 560
561static int hook_functions(HashTable* to_hook_ht, HashTable* hooked_ht) {
562 zend_string* key;
563 zval* value;
564
565 ZEND_HASH_FOREACH_STR_KEY_VAL(to_hook_ht, key, value) {
566 if (!HOOK_FUNCTION(ZSTR_VAL(key), disabled_functions_hook,
567 PHP_FN(check_disabled_function)) ||
568 check_is_builtin_name(((sp_list_node*)Z_PTR_P(value))->data)) {
569 zend_symtable_add_new(hooked_ht, key, value);
570 zend_hash_del(to_hook_ht, key);
571 }
572 }
573 ZEND_HASH_FOREACH_END();
574 return SUCCESS;
575}
576
535ZEND_FUNCTION(eval_blacklist_callback) { 577ZEND_FUNCTION(eval_blacklist_callback) {
536 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS); 578 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
537 const char* current_function_name = get_active_function_name(TSRMLS_C); 579 const char* current_function_name = get_active_function_name(TSRMLS_C);
580 zend_string* tmp =
581 zend_string_init(current_function_name, strlen(current_function_name), 0);
538 582
539 if (true == check_is_in_eval_whitelist(current_function_name)) { 583 if (true == check_is_in_eval_whitelist(tmp)) {
584 zend_string_release(tmp);
540 goto whitelisted; 585 goto whitelisted;
541 } 586 }
587 zend_string_release(tmp);
542 588
543 if (SNUFFLEUPAGUS_G(in_eval) > 0) { 589 if (SNUFFLEUPAGUS_G(in_eval) > 0) {
544 char* filename = get_eval_filename(zend_get_executed_filename()); 590 zend_string* filename = get_eval_filename(zend_get_executed_filename());
545 const int line_number = zend_get_executed_lineno(TSRMLS_C); 591 const int line_number = zend_get_executed_lineno(TSRMLS_C);
546 if (SNUFFLEUPAGUS_G(config).config_eval->dump) { 592 if (SNUFFLEUPAGUS_G(config).config_eval->dump) {
547 sp_log_request( 593 sp_log_request(
@@ -549,14 +595,14 @@ ZEND_FUNCTION(eval_blacklist_callback) {
549 SNUFFLEUPAGUS_G(config).config_eval->textual_representation, 595 SNUFFLEUPAGUS_G(config).config_eval->textual_representation,
550 SP_TOKEN_EVAL_BLACKLIST); 596 SP_TOKEN_EVAL_BLACKLIST);
551 } 597 }
552 if (1 == SNUFFLEUPAGUS_G(config).config_eval->simulation) { 598 if (SNUFFLEUPAGUS_G(config).config_eval->simulation) {
553 sp_log_msg("eval", SP_LOG_SIMULATION, 599 sp_log_msg("eval", SP_LOG_SIMULATION,
554 "A call to %s was tried in eval, in %s:%d, logging it.", 600 "A call to %s was tried in eval, in %s:%d, logging it.",
555 current_function_name, filename, line_number); 601 current_function_name, ZSTR_VAL(filename), line_number);
556 } else { 602 } else {
557 sp_log_msg("eval", SP_LOG_DROP, 603 sp_log_msg("eval", SP_LOG_DROP,
558 "A call to %s was tried in eval, in %s:%d, dropping it.", 604 "A call to %s was tried in eval, in %s:%d, dropping it.",
559 current_function_name, filename, line_number); 605 current_function_name, ZSTR_VAL(filename), line_number);
560 sp_terminate(); 606 sp_terminate();
561 } 607 }
562 efree(filename); 608 efree(filename);
@@ -574,21 +620,31 @@ int hook_disabled_functions(void) {
574 620
575 int ret = SUCCESS; 621 int ret = SUCCESS;
576 622
623 ret |=
624 hook_functions(SNUFFLEUPAGUS_G(config).config_disabled_functions,
625 SNUFFLEUPAGUS_G(config).config_disabled_functions_hooked);
626
577 ret |= hook_functions( 627 ret |= hook_functions(
578 SNUFFLEUPAGUS_G(config).config_disabled_functions->disabled_functions); 628 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret,
579 ret |= hook_functions(SNUFFLEUPAGUS_G(config) 629 SNUFFLEUPAGUS_G(config).config_disabled_functions_ret_hooked);
580 .config_disabled_functions_ret->disabled_functions); 630
631 ret |= hook_functions_regexp(
632 SNUFFLEUPAGUS_G(config)
633 .config_disabled_functions_reg->disabled_functions);
634
635 ret |= hook_functions_regexp(
636 SNUFFLEUPAGUS_G(config)
637 .config_disabled_functions_reg_ret->disabled_functions);
581 638
582 if (NULL != SNUFFLEUPAGUS_G(config).config_eval->blacklist) { 639 if (NULL != SNUFFLEUPAGUS_G(config).config_eval->blacklist) {
583 sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->blacklist; 640 sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->blacklist;
584 641
585 while (it) { 642 while (it) {
586 hook_function((char*)it->data, 643 hook_function(ZSTR_VAL((zend_string*)it->data),
587 SNUFFLEUPAGUS_G(sp_eval_blacklist_functions_hook), 644 SNUFFLEUPAGUS_G(sp_eval_blacklist_functions_hook),
588 PHP_FN(eval_blacklist_callback)); 645 PHP_FN(eval_blacklist_callback));
589 it = it->next; 646 it = it->next;
590 } 647 }
591 } 648 }
592
593 return ret; 649 return ret;
594} 650}
diff --git a/src/sp_disabled_functions.h b/src/sp_disabled_functions.h
index f80c9c2..4e795a1 100644
--- a/src/sp_disabled_functions.h
+++ b/src/sp_disabled_functions.h
@@ -2,8 +2,11 @@
2#define __SP_DISABLE_FUNCTIONS_H 2#define __SP_DISABLE_FUNCTIONS_H
3 3
4int hook_disabled_functions(); 4int hook_disabled_functions();
5bool should_disable(zend_execute_data *, const char *, const char *, 5bool should_disable(zend_execute_data *, const char *, const zend_string *,
6 const char *); 6 const char *, const sp_list_node *, const zend_string *);
7bool should_drop_on_ret(zval *, const zend_execute_data *const); 7bool should_disable_ht(zend_execute_data *, const char *, const zend_string *,
8 const char *, const sp_list_node *, const HashTable *);
9bool should_drop_on_ret_ht(zval *, const zend_execute_data *const, const sp_list_node* config, const HashTable *);
10bool should_drop_on_ret(zval *, const sp_list_node* config, const char *);
8 11
9#endif /* __SP_DISABLE_FUNCTIONS_H */ 12#endif /* __SP_DISABLE_FUNCTIONS_H */
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);
diff --git a/src/sp_execute.h b/src/sp_execute.h
index fcd0e11..d9eeee8 100644
--- a/src/sp_execute.h
+++ b/src/sp_execute.h
@@ -2,6 +2,6 @@
2#define SP_EXECUTE_H 2#define SP_EXECUTE_H
3 3
4int hook_execute(void); 4int hook_execute(void);
5char *get_eval_filename(const char * const filename); 5zend_string *get_eval_filename(const char * const filename);
6 6
7#endif /* SP_EXECUTE_H */ 7#endif /* SP_EXECUTE_H */
diff --git a/src/sp_pcre_compat.h b/src/sp_pcre_compat.h
index a9eb253..6658316 100644
--- a/src/sp_pcre_compat.h
+++ b/src/sp_pcre_compat.h
@@ -27,6 +27,8 @@
27#endif 27#endif
28 28
29sp_pcre* sp_pcre_compile(const char* str); 29sp_pcre* sp_pcre_compile(const char* str);
30#define sp_is_regexp_matching_zend(regexp, zstr) \
31 sp_is_regexp_matching_len(regexp, ZSTR_VAL(zstr), ZSTR_LEN(zstr))
30#define sp_is_regexp_matching(regexp, str) \ 32#define sp_is_regexp_matching(regexp, str) \
31 sp_is_regexp_matching_len(regexp, str, strlen(str)) 33 sp_is_regexp_matching_len(regexp, str, strlen(str))
32bool sp_is_regexp_matching_len(const sp_pcre* regexp, const char* str, size_t len); 34bool sp_is_regexp_matching_len(const sp_pcre* regexp, const char* str, size_t len);
diff --git a/src/sp_session.c b/src/sp_session.c
index ce852ad..ea65f57 100644
--- a/src/sp_session.c
+++ b/src/sp_session.c
@@ -51,7 +51,7 @@ static int sp_hook_s_read(PS_READ_ARGS) {
51 51
52static int sp_hook_s_write(PS_WRITE_ARGS) { 52static int sp_hook_s_write(PS_WRITE_ARGS) {
53 if (ZSTR_LEN(val) > 0 && SNUFFLEUPAGUS_G(config).config_session->encrypt) { 53 if (ZSTR_LEN(val) > 0 && SNUFFLEUPAGUS_G(config).config_session->encrypt) {
54 zend_string *new_val = encrypt_zval(ZSTR_VAL(val), ZSTR_LEN(val)); 54 zend_string *new_val = encrypt_zval(val);
55 return old_s_write(mod_data, key, new_val, maxlifetime); 55 return old_s_write(mod_data, key, new_val, maxlifetime);
56 } 56 }
57 return old_s_write(mod_data, key, val, maxlifetime); 57 return old_s_write(mod_data, key, val, maxlifetime);
@@ -150,4 +150,4 @@ void hook_session() {
150 s_module = NULL; 150 s_module = NULL;
151 151
152 sp_hook_session_module(); 152 sp_hook_session_module();
153} \ No newline at end of file 153}
diff --git a/src/sp_sloppy.c b/src/sp_sloppy.c
index 05d2505..09e79d8 100644
--- a/src/sp_sloppy.c
+++ b/src/sp_sloppy.c
@@ -1,7 +1,9 @@
1#include "sp_sloppy.h" 1#include "sp_sloppy.h"
2 2
3ZEND_API zend_op_array* (*zend_compile_file_default)(zend_file_handle* file_handle, int type) = NULL; 3ZEND_API zend_op_array* (*zend_compile_file_default)(
4ZEND_API zend_op_array* (*zend_compile_string_default)(zval* source_string, char* filename) = NULL; 4 zend_file_handle* file_handle, int type) = NULL;
5ZEND_API zend_op_array* (*zend_compile_string_default)(zval* source_string,
6 char* filename) = NULL;
5 7
6static void modify_opcode(zend_op_array* opline) { 8static void modify_opcode(zend_op_array* opline) {
7 if (NULL != opline) { 9 if (NULL != opline) {
@@ -24,7 +26,8 @@ ZEND_API zend_op_array* sp_compile_string(zval* source_string, char* filename) {
24 return opline; 26 return opline;
25} 27}
26 28
27ZEND_API zend_op_array* sp_compile_file(zend_file_handle* file_handle, int type) { 29ZEND_API zend_op_array* sp_compile_file(zend_file_handle* file_handle,
30 int type) {
28 zend_op_array* opline = zend_compile_file_default(file_handle, type); 31 zend_op_array* opline = zend_compile_file_default(file_handle, type);
29 modify_opcode(opline); 32 modify_opcode(opline);
30 return opline; 33 return opline;
diff --git a/src/sp_unserialize.c b/src/sp_unserialize.c
index 60ef7be..db99389 100644
--- a/src/sp_unserialize.c
+++ b/src/sp_unserialize.c
@@ -18,8 +18,9 @@ PHP_FUNCTION(sp_serialize) {
18 ZVAL_STRING(&func_name, "hash_hmac"); 18 ZVAL_STRING(&func_name, "hash_hmac");
19 ZVAL_STRING(&params[0], "sha256"); 19 ZVAL_STRING(&params[0], "sha256");
20 params[1] = *return_value; 20 params[1] = *return_value;
21 ZVAL_STRING(&params[2], 21 ZVAL_STRING(
22 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key); 22 &params[2],
23 ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key));
23 call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params); 24 call_user_function(CG(function_table), NULL, &func_name, &hmac, 3, params);
24 25
25 size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac); 26 size_t len = Z_STRLEN_P(return_value) + Z_STRLEN(hmac);
@@ -66,8 +67,9 @@ PHP_FUNCTION(sp_unserialize) {
66 zval params[3]; 67 zval params[3];
67 ZVAL_STRING(&params[0], "sha256"); 68 ZVAL_STRING(&params[0], "sha256");
68 ZVAL_STRING(&params[1], serialized_str); 69 ZVAL_STRING(&params[1], serialized_str);
69 ZVAL_STRING(&params[2], 70 ZVAL_STRING(
70 SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key); 71 &params[2],
72 ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key));
71 call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3, 73 call_user_function(CG(function_table), NULL, &func_name, &expected_hmac, 3,
72 params); 74 params);
73 75
@@ -113,4 +115,4 @@ int hook_serialize(void) {
113 PHP_FN(sp_unserialize)); 115 PHP_FN(sp_unserialize));
114 116
115 return SUCCESS; 117 return SUCCESS;
116} \ No newline at end of file 118}
diff --git a/src/sp_upload_validation.c b/src/sp_upload_validation.c
index 73244ae..794c61f 100644
--- a/src/sp_upload_validation.c
+++ b/src/sp_upload_validation.c
@@ -41,12 +41,14 @@ int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra) {
41 char *cmd[3] = {0}; 41 char *cmd[3] = {0};
42 char *env[5] = {0}; 42 char *env[5] = {0};
43 43
44 sp_log_debug("Filename: %s\nTmpname: %s\nSize: %d\nError: %d\nScript: %s", 44 sp_log_debug(
45 filename, tmp_name, filesize, 45 "Filename: %s\nTmpname: %s\nSize: %d\nError: %d\nScript: %s",
46 Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "error", 5)), 46 filename, tmp_name, filesize,
47 SNUFFLEUPAGUS_G(config).config_upload_validation->script); 47 Z_LVAL_P(zend_hash_str_find(Z_ARRVAL_P(file), "error", 5)),
48 ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_upload_validation->script));
48 49
49 cmd[0] = SNUFFLEUPAGUS_G(config).config_upload_validation->script; 50 cmd[0] =
51 ZSTR_VAL(SNUFFLEUPAGUS_G(config).config_upload_validation->script);
50 cmd[1] = tmp_name; 52 cmd[1] = tmp_name;
51 cmd[2] = NULL; 53 cmd[2] = NULL;
52 54
@@ -58,11 +60,15 @@ int sp_rfc1867_callback(unsigned int event, void *event_data, void **extra) {
58 env[4] = NULL; 60 env[4] = NULL;
59 61
60 if ((pid = fork()) == 0) { 62 if ((pid = fork()) == 0) {
61 if (execve(SNUFFLEUPAGUS_G(config).config_upload_validation->script, 63 if (execve(
62 cmd, env) == -1) { 64 ZSTR_VAL(
63 sp_log_err("upload_validation", "Could not call '%s' : %s", 65 SNUFFLEUPAGUS_G(config).config_upload_validation->script),
64 SNUFFLEUPAGUS_G(config).config_upload_validation->script, 66 cmd, env) == -1) {
65 strerror(errno)); 67 sp_log_err(
68 "upload_validation", "Could not call '%s' : %s",
69 ZSTR_VAL(
70 SNUFFLEUPAGUS_G(config).config_upload_validation->script),
71 strerror(errno));
66 EFREE_3(env); 72 EFREE_3(env);
67 exit(1); 73 exit(1);
68 } 74 }
diff --git a/src/sp_utils.c b/src/sp_utils.c
index 8b7ce49..a94ab2a 100644
--- a/src/sp_utils.c
+++ b/src/sp_utils.c
@@ -19,6 +19,14 @@ static inline void _sp_log_err(const char* fmt, ...) {
19 php_log_err(msg); 19 php_log_err(msg);
20} 20}
21 21
22static bool sp_zend_string_equals(const zend_string* s1,
23 const zend_string* s2) {
24 // We can't use `zend_string_equals` here because it doesn't work on
25 // `const` zend_string.
26 return ZSTR_LEN(s1) == ZSTR_LEN(s2) &&
27 !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
28}
29
22void sp_log_msg(char const* feature, char const* level, const char* fmt, ...) { 30void sp_log_msg(char const* feature, char const* level, const char* fmt, ...) {
23 char* msg; 31 char* msg;
24 va_list args; 32 va_list args;
@@ -56,14 +64,15 @@ int compute_hash(const char* const filename, char* file_hash) {
56 return SUCCESS; 64 return SUCCESS;
57} 65}
58 66
59static int construct_filename(char* filename, const char* folder, 67static int construct_filename(char* filename, const zend_string* folder,
60 const char* textual) { 68 const zend_string* textual) {
61 PHP_SHA256_CTX context; 69 PHP_SHA256_CTX context;
62 unsigned char digest[SHA256_SIZE] = {0}; 70 unsigned char digest[SHA256_SIZE] = {0};
63 char strhash[65] = {0}; 71 char strhash[65] = {0};
64 72
65 if (-1 == mkdir(folder, 0700) && errno != EEXIST) { 73 if (-1 == mkdir(ZSTR_VAL(folder), 0700) && errno != EEXIST) {
66 sp_log_err("request_logging", "Unable to create the folder '%s'.", folder); 74 sp_log_err("request_logging", "Unable to create the folder '%s'.",
75 ZSTR_VAL(folder));
67 return -1; 76 return -1;
68 } 77 }
69 78
@@ -71,15 +80,17 @@ static int construct_filename(char* filename, const char* folder,
71 * as filename, in order to only have one dump per rule, to migitate 80 * as filename, in order to only have one dump per rule, to migitate
72 * DoS attacks. */ 81 * DoS attacks. */
73 PHP_SHA256Init(&context); 82 PHP_SHA256Init(&context);
74 PHP_SHA256Update(&context, (const unsigned char*)textual, strlen(textual)); 83 PHP_SHA256Update(&context, (const unsigned char*)ZSTR_VAL(textual),
84 ZSTR_LEN(textual));
75 PHP_SHA256Final(digest, &context); 85 PHP_SHA256Final(digest, &context);
76 make_digest_ex(strhash, digest, SHA256_SIZE); 86 make_digest_ex(strhash, digest, SHA256_SIZE);
77 snprintf(filename, PATH_MAX - 1, "%s/sp_dump.%s", folder, strhash); 87 snprintf(filename, PATH_MAX - 1, "%s/sp_dump.%s", ZSTR_VAL(folder), strhash);
78 88
79 return 0; 89 return 0;
80} 90}
81 91
82int sp_log_request(const char* folder, const char* text_repr, char* from) { 92int sp_log_request(const zend_string* folder, const zend_string* text_repr,
93 char* from) {
83 FILE* file; 94 FILE* file;
84 const char* current_filename = zend_get_executed_filename(TSRMLS_C); 95 const char* current_filename = zend_get_executed_filename(TSRMLS_C);
85 const int current_line = zend_get_executed_lineno(TSRMLS_C); 96 const int current_line = zend_get_executed_lineno(TSRMLS_C);
@@ -100,7 +111,7 @@ int sp_log_request(const char* folder, const char* text_repr, char* from) {
100 return -1; 111 return -1;
101 } 112 }
102 113
103 fprintf(file, "RULE: sp%s%s\n", from, text_repr); 114 fprintf(file, "RULE: sp%s%s\n", from, ZSTR_VAL(text_repr));
104 115
105 fprintf(file, "FILE: %s:%d\n", current_filename, current_line); 116 fprintf(file, "FILE: %s:%d\n", current_filename, current_line);
106 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) { 117 for (size_t i = 0; i < (sizeof(zones) / sizeof(zones[0])) - 1; i++) {
@@ -130,57 +141,65 @@ int sp_log_request(const char* folder, const char* text_repr, char* from) {
130 return 0; 141 return 0;
131} 142}
132 143
133static char* zv_str_to_char(zval* zv) { 144static char* zend_string_to_char(const zend_string* zs) {
134 zval copy; 145 // Remove \0 from the middle of a string
146 char* copy = emalloc(ZSTR_LEN(zs) + 1);
135 147
136 ZVAL_ZVAL(&copy, zv, 1, 0); 148 copy[ZSTR_LEN(zs)] = 0;
137 for (size_t i = 0; i < Z_STRLEN(copy); i++) { 149 for (size_t i = 0; i < ZSTR_LEN(zs); i++) {
138 if (Z_STRVAL(copy)[i] == '\0') { 150 if (ZSTR_VAL(zs)[i] == '\0') {
139 Z_STRVAL(copy)[i] = '0'; 151 copy[i] = '0';
152 } else {
153 copy[i] = ZSTR_VAL(zs)[i];
140 } 154 }
141 } 155 }
142 return estrdup(Z_STRVAL(copy)); 156 return copy;
143} 157}
144 158
145char* sp_convert_to_string(zval* zv) { 159const zend_string* sp_zval_to_zend_string(zval* zv) {
146 switch (Z_TYPE_P(zv)) { 160 switch (Z_TYPE_P(zv)) {
147 case IS_FALSE:
148 return estrdup("FALSE");
149 case IS_TRUE:
150 return estrdup("TRUE");
151 case IS_NULL:
152 return estrdup("NULL");
153 case IS_LONG: { 161 case IS_LONG: {
154 char* msg; 162 char* msg;
155 spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv)); 163 spprintf(&msg, 0, ZEND_LONG_FMT, Z_LVAL_P(zv));
156 return msg; 164 zend_string* zs = zend_string_init(msg, strlen(msg), 0);
165 efree(msg);
166 return zs;
157 } 167 }
158 case IS_DOUBLE: { 168 case IS_DOUBLE: {
159 char* msg; 169 char* msg;
160 spprintf(&msg, 0, "%f", Z_DVAL_P(zv)); 170 spprintf(&msg, 0, "%f", Z_DVAL_P(zv));
161 return msg; 171 zend_string* zs = zend_string_init(msg, strlen(msg), 0);
172 efree(msg);
173 return zs;
162 } 174 }
163 case IS_STRING: { 175 case IS_STRING: {
164 return zv_str_to_char(zv); 176 return Z_STR_P(zv);
165 } 177 }
178 case IS_FALSE:
179 return zend_string_init("FALSE", 5, 0);
180 case IS_TRUE:
181 return zend_string_init("TRUE", 4, 0);
182 case IS_NULL:
183 return zend_string_init("NULL", 4, 0);
166 case IS_OBJECT: 184 case IS_OBJECT:
167 return estrdup("OBJECT"); 185 return zend_string_init("OBJECT", 6, 0);
168 case IS_ARRAY: 186 case IS_ARRAY:
169 return estrdup("ARRAY"); 187 return zend_string_init("ARRAY", 5, 0);
170 case IS_RESOURCE: 188 case IS_RESOURCE:
171 return estrdup("RESOURCE"); 189 return zend_string_init("RESOURCE", 8, 0);
172 } 190 }
173 return estrdup(""); 191 return zend_string_init("", 0, 0);
174} 192}
175 193
176bool sp_match_value(const char* value, const char* to_match, 194bool sp_match_value(const zend_string* value, const zend_string* to_match,
177 const sp_pcre* rx) { 195 const sp_pcre* rx) {
178 if (to_match) { 196 if (to_match) {
179 if (0 == strcmp(to_match, value)) { 197 return (sp_zend_string_equals(to_match, value));
180 return true;
181 }
182 } else if (rx) { 198 } else if (rx) {
183 return sp_is_regexp_matching(rx, value); 199 char* tmp = zend_string_to_char(value);
200 bool ret = sp_is_regexp_matching(rx, tmp);
201 efree(tmp);
202 return ret;
184 } else { 203 } else {
185 return true; 204 return true;
186 } 205 }
@@ -188,35 +207,41 @@ bool sp_match_value(const char* value, const char* to_match,
188} 207}
189 208
190void sp_log_disable(const char* restrict path, const char* restrict arg_name, 209void sp_log_disable(const char* restrict path, const char* restrict arg_name,
191 const char* restrict arg_value, 210 const zend_string* restrict arg_value,
192 const sp_disabled_function* config_node, unsigned int line, 211 const sp_disabled_function* config_node, unsigned int line,
193 const char* restrict filename) { 212 const char* restrict filename) {
194 const char* dump = config_node->dump; 213 const zend_string* dump = config_node->dump;
195 const char* alias = config_node->alias; 214 const zend_string* alias = config_node->alias;
196 const int sim = config_node->simulation; 215 const int sim = config_node->simulation;
197 216
198 filename = filename ? filename : zend_get_executed_filename(TSRMLS_C); 217 filename = filename ? filename : zend_get_executed_filename(TSRMLS_C);
199 line = line ? line : zend_get_executed_lineno(TSRMLS_C); 218 line = line ? line : zend_get_executed_lineno(TSRMLS_C);
200 219
201 if (arg_name) { 220 if (arg_name) {
221 char* char_repr = NULL;
222 if (arg_value) {
223 char_repr = zend_string_to_char(arg_value);
224 }
202 if (alias) { 225 if (alias) {
203 sp_log_msg( 226 sp_log_msg(
204 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 227 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
205 "Aborted execution on call of the function '%s' in %s:%d, " 228 "Aborted execution on call of the function '%s' in %s:%d, "
206 "because its argument '%s' content (%s) matched the rule '%s'.", 229 "because its argument '%s' content (%s) matched the rule '%s'.",
207 path, filename, line, arg_name, arg_value ? arg_value : "?", alias); 230 path, filename, line, arg_name, char_repr ? char_repr : "?",
231 ZSTR_VAL(alias));
208 } else { 232 } else {
209 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 233 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
210 "Aborted execution on call of the function '%s' in %s:%d, " 234 "Aborted execution on call of the function '%s' in %s:%d, "
211 "because its argument '%s' content (%s) matched a rule.", 235 "because its argument '%s' content (%s) matched a rule.",
212 path, filename, line, arg_name, arg_value ? arg_value : "?"); 236 path, filename, line, arg_name, char_repr ? char_repr : "?");
213 } 237 }
238 efree(char_repr);
214 } else { 239 } else {
215 if (alias) { 240 if (alias) {
216 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 241 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
217 "Aborted execution on call of the function '%s' in %s:%d, " 242 "Aborted execution on call of the function '%s' in %s:%d, "
218 "because of the the rule '%s'.", 243 "because of the the rule '%s'.",
219 path, filename, line, alias); 244 path, filename, line, ZSTR_VAL(alias));
220 } else { 245 } else {
221 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 246 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
222 "Aborted execution on call of the function '%s' in %s:%d.", 247 "Aborted execution on call of the function '%s' in %s:%d.",
@@ -230,45 +255,53 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name,
230} 255}
231 256
232void sp_log_disable_ret(const char* restrict path, 257void sp_log_disable_ret(const char* restrict path,
233 const char* restrict ret_value, 258 const zend_string* restrict ret_value,
234 const sp_disabled_function* config_node) { 259 const sp_disabled_function* config_node) {
235 const char* dump = config_node->dump; 260 const zend_string* dump = config_node->dump;
236 const char* alias = config_node->alias; 261 const zend_string* alias = config_node->alias;
237 const int sim = config_node->simulation; 262 const int sim = config_node->simulation;
263 char* char_repr = NULL;
264
265 if (ret_value) {
266 char_repr = zend_string_to_char(ret_value);
267 }
238 if (alias) { 268 if (alias) {
239 sp_log_msg( 269 sp_log_msg(
240 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 270 "disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
241 "Aborted execution on return of the function '%s' in %s:%d, " 271 "Aborted execution on return of the function '%s' in %s:%d, "
242 "because the function returned '%s', which matched the rule '%s'.", 272 "because the function returned '%s', which matched the rule '%s'.",
243 path, zend_get_executed_filename(TSRMLS_C), 273 path, zend_get_executed_filename(TSRMLS_C),
244 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?", alias); 274 zend_get_executed_lineno(TSRMLS_C), char_repr ? char_repr : "?",
275 ZSTR_VAL(alias));
245 } else { 276 } else {
246 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP, 277 sp_log_msg("disabled_function", sim ? SP_LOG_SIMULATION : SP_LOG_DROP,
247 "Aborted execution on return of the function '%s' in %s:%d, " 278 "Aborted execution on return of the function '%s' in %s:%d, "
248 "because the function returned '%s', which matched a rule.", 279 "because the function returned '%s', which matched a rule.",
249 path, zend_get_executed_filename(TSRMLS_C), 280 path, zend_get_executed_filename(TSRMLS_C),
250 zend_get_executed_lineno(TSRMLS_C), ret_value ? ret_value : "?"); 281 zend_get_executed_lineno(TSRMLS_C), char_repr ? char_repr : "?");
251 } 282 }
283 efree(char_repr);
252 if (dump) { 284 if (dump) {
253 sp_log_request(dump, config_node->textual_representation, 285 sp_log_request(dump, config_node->textual_representation,
254 SP_TOKEN_DISABLE_FUNC); 286 SP_TOKEN_DISABLE_FUNC);
255 } 287 }
256} 288}
257 289
258bool sp_match_array_key(const zval* zv, const char* to_match, 290bool sp_match_array_key(const zval* zv, const zend_string* to_match,
259 const sp_pcre* rx) { 291 const sp_pcre* rx) {
260 zend_string* key; 292 zend_string* key;
261 zend_ulong idx; 293 zend_ulong idx;
262 294
263 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(zv), idx, key) { 295 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(zv), idx, key) {
264 if (key) { 296 if (key) {
265 if (sp_match_value(ZSTR_VAL(key), to_match, rx)) { 297 if (sp_match_value(key, to_match, rx)) {
266 return true; 298 return true;
267 } 299 }
268 } else { 300 } else {
269 char* idx_str = NULL; 301 char* idx_str = NULL;
270 spprintf(&idx_str, 0, "%lu", idx); 302 spprintf(&idx_str, 0, "%lu", idx);
271 if (sp_match_value(idx_str, to_match, rx)) { 303 zend_string* tmp = zend_string_init(idx_str, strlen(idx_str), 0);
304 if (sp_match_value(tmp, to_match, rx)) {
272 efree(idx_str); 305 efree(idx_str);
273 return true; 306 return true;
274 } 307 }
@@ -279,18 +312,16 @@ bool sp_match_array_key(const zval* zv, const char* to_match,
279 return false; 312 return false;
280} 313}
281 314
282bool sp_match_array_value(const zval* arr, const char* to_match, 315bool sp_match_array_value(const zval* arr, const zend_string* to_match,
283 const sp_pcre* rx) { 316 const sp_pcre* rx) {
284 zval* value; 317 zval* value;
285 318
286 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) { 319 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), value) {
287 if (Z_TYPE_P(value) != IS_ARRAY) { 320 if (Z_TYPE_P(value) != IS_ARRAY) {
288 char* value_str = sp_convert_to_string(value); 321 const zend_string* value_str = sp_zval_to_zend_string(value);
289 if (sp_match_value(value_str, to_match, rx)) { 322 if (sp_match_value(value_str, to_match, rx)) {
290 efree(value_str);
291 return true; 323 return true;
292 } else { 324 } else {
293 efree(value_str);
294 } 325 }
295 } else if (sp_match_array_value(value, to_match, rx)) { 326 } else if (sp_match_array_value(value, to_match, rx)) {
296 return true; 327 return true;
@@ -303,6 +334,7 @@ bool sp_match_array_value(const zval* arr, const char* to_match,
303int hook_function(const char* original_name, HashTable* hook_table, 334int hook_function(const char* original_name, HashTable* hook_table,
304 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS)) { 335 void (*new_function)(INTERNAL_FUNCTION_PARAMETERS)) {
305 zend_internal_function* func; 336 zend_internal_function* func;
337 bool ret = FAILURE;
306 338
307 /* The `mb` module likes to hook functions, like strlen->mb_strlen, 339 /* The `mb` module likes to hook functions, like strlen->mb_strlen,
308 * so we have to hook both of them. */ 340 * so we have to hook both of them. */
@@ -317,6 +349,7 @@ int hook_function(const char* original_name, HashTable* hook_table,
317 return FAILURE; 349 return FAILURE;
318 } 350 }
319 func->handler = new_function; 351 func->handler = new_function;
352 ret = SUCCESS;
320 } else { 353 } else {
321 return SUCCESS; 354 return SUCCESS;
322 } 355 }
@@ -326,7 +359,7 @@ int hook_function(const char* original_name, HashTable* hook_table,
326 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; 359 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
327 if (zend_hash_str_find(CG(function_table), 360 if (zend_hash_str_find(CG(function_table),
328 VAR_AND_LEN(original_name + 3))) { 361 VAR_AND_LEN(original_name + 3))) {
329 hook_function(original_name + 3, hook_table, new_function); 362 return hook_function(original_name + 3, hook_table, new_function);
330 } 363 }
331 } else { // TODO this can be moved somewhere else to gain some marginal perfs 364 } else { // TODO this can be moved somewhere else to gain some marginal perfs
332 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN; 365 CG(compiler_options) |= ZEND_COMPILE_NO_BUILTIN_STRLEN;
@@ -334,11 +367,11 @@ int hook_function(const char* original_name, HashTable* hook_table,
334 memcpy(mb_name, "mb_", 3); 367 memcpy(mb_name, "mb_", 3);
335 memcpy(mb_name + 3, VAR_AND_LEN(original_name)); 368 memcpy(mb_name + 3, VAR_AND_LEN(original_name));
336 if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) { 369 if (zend_hash_str_find(CG(function_table), VAR_AND_LEN(mb_name))) {
337 hook_function(mb_name, hook_table, new_function); 370 return hook_function(mb_name, hook_table, new_function);
338 } 371 }
339 } 372 }
340 373
341 return SUCCESS; 374 return ret;
342} 375}
343 376
344int hook_regexp(const sp_pcre* regexp, HashTable* hook_table, 377int hook_regexp(const sp_pcre* regexp, HashTable* hook_table,
@@ -356,7 +389,7 @@ int hook_regexp(const sp_pcre* regexp, HashTable* hook_table,
356 return SUCCESS; 389 return SUCCESS;
357} 390}
358 391
359bool check_is_in_eval_whitelist(const char* const function_name) { 392bool check_is_in_eval_whitelist(const zend_string* const function_name) {
360 const sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->whitelist; 393 const sp_list_node* it = SNUFFLEUPAGUS_G(config).config_eval->whitelist;
361 394
362 if (!it) { 395 if (!it) {
@@ -366,7 +399,7 @@ bool check_is_in_eval_whitelist(const char* const function_name) {
366 /* yes, we could use a HashTable instead, but since the list is pretty 399 /* yes, we could use a HashTable instead, but since the list is pretty
367 * small, it doesn't maka a difference in practise. */ 400 * small, it doesn't maka a difference in practise. */
368 while (it && it->data) { 401 while (it && it->data) {
369 if (0 == strcmp(function_name, (char*)(it->data))) { 402 if (sp_zend_string_equals(function_name, (const zend_string*)(it->data))) {
370 /* We've got a match, the function is whiteslited. */ 403 /* We've got a match, the function is whiteslited. */
371 return true; 404 return true;
372 } 405 }
diff --git a/src/sp_utils.h b/src/sp_utils.h
index 61a23f9..a21a4b0 100644
--- a/src/sp_utils.h
+++ b/src/sp_utils.h
@@ -44,20 +44,20 @@
44 44
45void sp_log_msg(char const *feature, char const *level, const char *fmt, ...); 45void sp_log_msg(char const *feature, char const *level, const char *fmt, ...);
46int compute_hash(const char *const filename, char *file_hash); 46int compute_hash(const char *const filename, char *file_hash);
47char *sp_convert_to_string(zval *); 47const zend_string* sp_zval_to_zend_string(zval *);
48bool sp_match_value(const char *, const char *, const sp_pcre *); 48bool sp_match_value(const zend_string *, const zend_string *, const sp_pcre *);
49bool sp_match_array_key(const zval *, const char *, const sp_pcre *); 49bool sp_match_array_key(const zval *, const zend_string *, const sp_pcre *);
50bool sp_match_array_value(const zval *, const char *, const sp_pcre *); 50bool sp_match_array_value(const zval *, const zend_string *, const sp_pcre *);
51void sp_log_disable(const char *restrict, const char *restrict, 51void sp_log_disable(const char *restrict, const char *restrict,
52 const char *restrict, const sp_disabled_function *, 52 const zend_string *restrict, const sp_disabled_function *,
53 unsigned int, const char*restrict); 53 unsigned int, const char*restrict);
54void sp_log_disable_ret(const char *restrict, const char *restrict, 54void sp_log_disable_ret(const char *restrict, const zend_string *restrict,
55 const sp_disabled_function *); 55 const sp_disabled_function *);
56int hook_function(const char *, HashTable *, 56int hook_function(const char *, HashTable *,
57 void (*)(INTERNAL_FUNCTION_PARAMETERS)); 57 void (*)(INTERNAL_FUNCTION_PARAMETERS));
58int hook_regexp(const sp_pcre *, HashTable *, 58int hook_regexp(const sp_pcre *, HashTable *,
59 void (*)(INTERNAL_FUNCTION_PARAMETERS)); 59 void (*)(INTERNAL_FUNCTION_PARAMETERS));
60bool check_is_in_eval_whitelist(const char * const function_name); 60bool check_is_in_eval_whitelist(const zend_string * const function_name);
61int sp_log_request(const char* folder, const char* text_repr, char* from); 61int sp_log_request(const zend_string* folder, const zend_string* text_repr, char* from);
62 62
63#endif /* SP_UTILS_H */ 63#endif /* SP_UTILS_H */
diff --git a/src/sp_var_value.c b/src/sp_var_value.c
index 7ed8dfa..e91c3d8 100644
--- a/src/sp_var_value.c
+++ b/src/sp_var_value.c
@@ -107,10 +107,9 @@ static zval *get_array_value(zend_execute_data *ed, zval *zvalue,
107 } 107 }
108 108
109 if (Z_TYPE_P(zvalue) == IS_ARRAY) { 109 if (Z_TYPE_P(zvalue) == IS_ARRAY) {
110 char *idx = sp_convert_to_string(idx_value); 110 const zend_string *idx = sp_zval_to_zend_string(idx_value);
111 zval *ret = get_entry_hashtable(Z_ARRVAL_P(zvalue), idx, strlen(idx)); 111 return get_entry_hashtable(Z_ARRVAL_P(zvalue), ZSTR_VAL(idx),
112 efree(idx); 112 ZSTR_LEN(idx));
113 return ret;
114 } 113 }
115 114
116 return NULL; 115 return NULL;