1 #include "php_snuffleupagus.h"
2 
get_param_var(zend_execute_data * ed,const char * var_name)3 static zval *get_param_var(zend_execute_data *ed, const char *var_name) {
4   unsigned int nb_param = ed->func->common.num_args;
5 
6   for (unsigned int i = 0; i < nb_param; i++) {
7     const char *arg_name;
8     if (ZEND_USER_CODE(ed->func->type)) {
9       arg_name = ZSTR_VAL(ed->func->common.arg_info[i].name);
10     } else {
11       arg_name = ed->func->internal_function.arg_info[i].name;
12     }
13     if (0 == strcmp(arg_name, var_name)) {
14       return ZEND_CALL_VAR_NUM(ed, i);
15     }
16   }
17   return NULL;
18 }
19 
get_local_var(zend_execute_data * ed,const char * var_name)20 static zval *get_local_var(zend_execute_data *ed, const char *var_name) {
21   zend_execute_data *orig_execute_data = ed;
22   zend_execute_data *current = ed;
23 
24   while (current) {
25     zval *value = NULL;
26     zend_string *key = NULL;
27     EG(current_execute_data) = current;
28     zend_array *symtable = zend_rebuild_symbol_table();
29 
30     if (UNEXPECTED(symtable == NULL)) {
31       EG(current_execute_data) = orig_execute_data;
32       return NULL;
33     }
34 
35     ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, key, value) {
36       if (0 == strcmp(var_name, key->val)) {
37         if (Z_TYPE_P(value) == IS_INDIRECT) {
38           value = Z_INDIRECT_P(value);
39         }
40         EG(current_execute_data) = orig_execute_data;
41         return value;
42       }
43     }
44     ZEND_HASH_FOREACH_END();
45     current = current->prev_execute_data;
46   }
47   EG(current_execute_data) = orig_execute_data;
48   return NULL;
49 }
50 
get_constant(const char * value)51 static zval *get_constant(const char *value) {
52   zend_string *name = zend_string_init(value, strlen(value), 0);
53   zval *zvalue = zend_get_constant_ex(name, NULL, ZEND_FETCH_CLASS_SILENT);
54 
55   zend_string_release(name);
56   return zvalue;
57 }
58 
get_var_value(zend_execute_data * ed,const char * var_name,bool is_param)59 static zval *get_var_value(zend_execute_data *ed, const char *var_name,
60                            bool is_param) {
61   if (!var_name) {
62     return NULL;  // LCOV_EXCL_LINE
63   }
64 
65   if (*var_name != VARIABLE_TOKEN) {
66     return get_constant(var_name);
67   } else {
68     var_name++;
69   }
70 
71   if (is_param) {
72     zval *zvalue = get_param_var(ed, var_name);
73     if (!zvalue) {
74       return get_local_var(ed, var_name);
75     }
76     return zvalue;
77   }
78 
79   return get_local_var(ed, var_name);
80 }
81 
get_entry_hashtable(const HashTable * ht,const char * entry,size_t entry_len)82 static void *get_entry_hashtable(const HashTable *ht, const char *entry,
83                                  size_t entry_len) {
84   zval *zvalue = zend_hash_str_find(ht, entry, entry_len);
85 
86   if (!zvalue) {
87     zvalue = zend_hash_index_find(ht, atol(entry));
88   }
89 
90   while (zvalue &&
91          (Z_TYPE_P(zvalue) == IS_INDIRECT || Z_TYPE_P(zvalue) == IS_PTR)) {
92     if (Z_TYPE_P(zvalue) == IS_INDIRECT) {
93       zvalue = Z_INDIRECT_P(zvalue);
94     } else {
95       zvalue = Z_PTR_P(zvalue);
96     }
97   }
98   return zvalue;
99 }
100 
get_array_value(zend_execute_data * ed,zval * zvalue,const sp_tree * tree)101 static zval *get_array_value(zend_execute_data *ed, zval *zvalue,
102                              const sp_tree *tree) {
103   zval *idx_value = sp_get_var_value(ed, tree->idx, false);
104 
105   if (!zvalue || !idx_value) {
106     return NULL;
107   }
108 
109   if (Z_TYPE_P(zvalue) == IS_ARRAY) {
110     const zend_string *idx = sp_zval_to_zend_string(idx_value);
111     return get_entry_hashtable(Z_ARRVAL_P(zvalue), ZSTR_VAL(idx),
112                                ZSTR_LEN(idx));
113   }
114 
115   return NULL;
116 }
117 
get_object_property(zend_execute_data * ed,zval * object,const char * property,bool is_param)118 static zval *get_object_property(zend_execute_data *ed, zval *object,
119                                  const char *property, bool is_param) {
120   char *class_name = object->value.obj->ce->name->val;
121   HashTable *array = Z_OBJPROP_P(object);
122   zval *zvalue = NULL;
123   zval *property_val = get_var_value(ed, property, is_param);
124   size_t len;
125 
126   if (property_val) {
127     if (Z_TYPE_P(property_val) != IS_STRING) {
128       return NULL;
129     } else {
130       property = Z_STRVAL_P(property_val);
131     }
132   }
133   zvalue = get_entry_hashtable(array, property, strlen(property));
134   // TODO do we want to log overflow?
135   if (!zvalue) {
136     len = strlen(property) + 4;
137     char *protected_property = emalloc(len);
138     snprintf(protected_property, len, PROTECTED_PROP_FMT, 0, 0, property);
139     zvalue = get_entry_hashtable(array, protected_property, len - 1);
140     efree(protected_property);
141   }
142   if (!zvalue) {
143     len = strlen(class_name) + 3 + strlen(property);
144     char *private_property = emalloc(len);
145     snprintf(private_property, len, PRIVATE_PROP_FMT, 0, class_name, 0,
146              property);
147     zvalue = get_entry_hashtable(array, private_property, len - 1);
148     efree(private_property);
149   }
150   return zvalue;
151 }
152 
get_class(const char * value)153 static zend_class_entry *get_class(const char *value) {
154   zend_string *name = zend_string_init(value, strlen(value), 0);
155   zend_class_entry *ce = zend_lookup_class(name);
156   zend_string_release(name);
157   return ce;
158 }
159 
get_unknown_type(const char * restrict value,zval * zvalue,zend_class_entry * ce,zend_execute_data * ed,const sp_tree * tree,bool is_param)160 static zval *get_unknown_type(const char *restrict value, zval *zvalue,
161                               zend_class_entry *ce, zend_execute_data *ed,
162                               const sp_tree *tree, bool is_param) {
163   if (ce) {
164     zvalue = get_entry_hashtable(&ce->constants_table, value, strlen(value));
165     ce = NULL;
166   } else if (zvalue && Z_TYPE_P(zvalue) == IS_OBJECT) {
167     zvalue = get_object_property(ed, zvalue, value, is_param);
168   } else if (!zvalue) {
169     if (tree->type == CONSTANT) {
170       zvalue = get_constant(value);
171     }
172     if (!zvalue) {
173       zvalue = emalloc(sizeof(zval));
174       ZVAL_PSTRING(zvalue, value);
175     }
176   } else {
177     return NULL;
178   }
179   return zvalue;
180 }
181 
sp_get_var_value(zend_execute_data * ed,const sp_tree * tree,bool is_param)182 zval *sp_get_var_value(zend_execute_data *ed, const sp_tree *tree,
183                        bool is_param) {
184   zval *zvalue = NULL;
185   zend_class_entry *ce = NULL;
186 
187   while (tree) {
188     switch (tree->type) {
189       case ARRAY:
190         if (ce) {
191           zvalue = get_entry_hashtable(&ce->constants_table, tree->value,
192                                        strlen(tree->value));
193           ce = NULL;
194         } else if (!zvalue) {
195           zvalue = get_var_value(ed, tree->value, is_param);
196         } else if (Z_TYPE_P(zvalue) == IS_OBJECT) {
197           zvalue = get_object_property(ed, zvalue, tree->value, is_param);
198         }
199         zvalue = get_array_value(ed, zvalue, tree);
200         break;
201       case VAR:
202         if (zvalue && Z_TYPE_P(zvalue) == IS_OBJECT) {
203           zvalue = get_object_property(ed, zvalue, tree->value, is_param);
204         } else {
205           zvalue = get_var_value(ed, tree->value, is_param);
206         }
207         break;
208       case OBJECT:
209         if (!zvalue) {
210           zvalue = get_var_value(ed, tree->value, is_param);
211         } else if (Z_TYPE_P(zvalue) == IS_OBJECT) {
212           if (0 != strlen(tree->value)) {
213             zvalue = get_object_property(ed, zvalue, tree->value, is_param);
214           }
215         } else {
216           return NULL;
217         }
218         break;
219       case CLASS:
220         ce = get_class(tree->value);
221         zvalue = NULL;
222         break;
223       default:
224         zvalue = get_unknown_type(tree->value, zvalue, ce, ed, tree, is_param);
225         ce = NULL;
226         break;
227     }
228     if (!zvalue && !ce) {
229       return NULL;
230     }
231     tree = tree->next;
232   }
233   return zvalue;
234 }
235