1 /*
2    +----------------------------------------------------------------------+
3    | Xdebug                                                               |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2002-2021 Derick Rethans                               |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 1.01 of the Xdebug license,   |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available at through the world-wide-web at                           |
10    | https://xdebug.org/license.php                                       |
11    | If you did not receive a copy of the Xdebug license and are unable   |
12    | to obtain it through the world-wide-web, please send a note to       |
13    | derick@xdebug.org so we can mail you a copy immediately.             |
14    +----------------------------------------------------------------------+
15  */
16 
17 #include "php.h"
18 #include "ext/standard/php_string.h"
19 #include "ext/standard/url.h"
20 #include "zend.h"
21 #include "zend_exceptions.h"
22 #include "zend_extensions.h"
23 #include "ext/standard/php_smart_string.h"
24 #include "zend_smart_str.h"
25 
26 #include "php_xdebug.h"
27 
28 #include "lib/compat.h"
29 #include "lib/lib_private.h"
30 #include "lib/mm.h"
31 #include "lib/var.h"
32 #include "lib/var_export_html.h"
33 #include "lib/var_export_line.h"
34 #include "lib/xml.h"
35 
ZEND_EXTERN_MODULE_GLOBALS(xdebug)36 ZEND_EXTERN_MODULE_GLOBALS(xdebug)
37 
38 static inline int object_or_ancestor_is_internal(zval dzval)
39 {
40 	zend_class_entry *tmp_ce = Z_OBJCE(dzval);
41 
42 	do {
43 		if (tmp_ce->type == ZEND_INTERNAL_CLASS) {
44 			return 1;
45 		}
46 		tmp_ce = tmp_ce->parent;
47 	} while (tmp_ce);
48 
49 	return 0;
50 }
51 #if PHP_VERSION_ID >= 70400
xdebug_objdebug_pp(zval ** zval_pp,int flags)52 HashTable *xdebug_objdebug_pp(zval **zval_pp, int flags)
53 #else
54 HashTable *xdebug_objdebug_pp(zval **zval_pp, int *is_tmp, int flags)
55 #endif
56 {
57 	zval dzval = **zval_pp;
58 	HashTable *tmp;
59 
60 	if (
61 		!XG_BASE(in_debug_info) &&
62 		(object_or_ancestor_is_internal(dzval) || (flags & XDEBUG_VAR_OBJDEBUG_USE_DEBUGINFO)) &&
63 		Z_OBJ_HANDLER(dzval, get_debug_info)
64 	) {
65 		void        *original_trace_context;
66 		zend_object *orig_exception;
67 
68 		xdebug_tracing_save_trace_context(&original_trace_context);
69 		XG_BASE(in_debug_info) = 1;
70 		orig_exception = EG(exception);
71 		EG(exception) = NULL;
72 
73 #if PHP_VERSION_ID >= 70400
74 		tmp = zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_DEBUG);
75 #else
76 		tmp = Z_OBJ_HANDLER(dzval, get_debug_info)(&dzval, is_tmp);
77 #endif
78 		XG_BASE(in_debug_info) = 0;
79 		xdebug_tracing_restore_trace_context(original_trace_context);
80 		EG(exception) = orig_exception;
81 
82 		return tmp;
83 	} else {
84 #if PHP_VERSION_ID >= 70400
85 		return zend_get_properties_for(&dzval, ZEND_PROP_PURPOSE_VAR_EXPORT);
86 #else
87 		*is_tmp = 0;
88 		if (Z_OBJ_HANDLER(dzval, get_properties)) {
89 			return Z_OBJPROP(dzval);
90 		}
91 #endif
92 	}
93 	return NULL;
94 }
95 
xdebug_error_type_simple(int type)96 char* xdebug_error_type_simple(int type)
97 {
98 	switch (type) {
99 		case E_ERROR:
100 		case E_CORE_ERROR:
101 		case E_COMPILE_ERROR:
102 		case E_USER_ERROR:
103 			return xdstrdup("fatal-error");
104 			break;
105 		case E_RECOVERABLE_ERROR:
106 			return xdstrdup("recoverable-fatal-error");
107 			break;
108 		case E_WARNING:
109 		case E_CORE_WARNING:
110 		case E_COMPILE_WARNING:
111 		case E_USER_WARNING:
112 			return xdstrdup("warning");
113 			break;
114 		case E_PARSE:
115 			return xdstrdup("parse-error");
116 			break;
117 		case E_NOTICE:
118 		case E_USER_NOTICE:
119 			return xdstrdup("notice");
120 			break;
121 		case E_STRICT:
122 			return xdstrdup("strict-standards");
123 			break;
124 		case E_DEPRECATED:
125 		case E_USER_DEPRECATED:
126 			return xdstrdup("deprecated");
127 			break;
128 		case 0:
129 			return xdstrdup("xdebug");
130 			break;
131 		default:
132 			return xdstrdup("unknown-error");
133 			break;
134 	}
135 }
136 
xdebug_error_type(int type)137 char* xdebug_error_type(int type)
138 {
139 	switch (type) {
140 		case E_ERROR:
141 		case E_CORE_ERROR:
142 		case E_COMPILE_ERROR:
143 		case E_USER_ERROR:
144 			return xdstrdup("Fatal error");
145 			break;
146 		case E_RECOVERABLE_ERROR:
147 			return xdstrdup("Recoverable fatal error");
148 			break;
149 		case E_WARNING:
150 		case E_CORE_WARNING:
151 		case E_COMPILE_WARNING:
152 		case E_USER_WARNING:
153 			return xdstrdup("Warning");
154 			break;
155 		case E_PARSE:
156 			return xdstrdup("Parse error");
157 			break;
158 		case E_NOTICE:
159 		case E_USER_NOTICE:
160 			return xdstrdup("Notice");
161 			break;
162 		case E_STRICT:
163 			return xdstrdup("Strict standards");
164 			break;
165 		case E_DEPRECATED:
166 		case E_USER_DEPRECATED:
167 			return xdstrdup("Deprecated");
168 			break;
169 		case 0:
170 			return xdstrdup("Xdebug");
171 			break;
172 		default:
173 			return xdstrdup("Unknown error");
174 			break;
175 	}
176 }
177 
178 /*************************************************************************************************************************************/
179 #define T(offset) (*(union _temp_variable *)((char*)zdata->current_execute_data->Ts + offset))
180 
xdebug_get_zval_with_opline(zend_execute_data * zdata,const zend_op * opline,int node_type,const znode_op * node,int * is_var)181 zval *xdebug_get_zval_with_opline(zend_execute_data *zdata, const zend_op *opline, int node_type, const znode_op *node, int *is_var)
182 {
183 #if PHP_VERSION_ID >= 80000
184 	return zend_get_zval_ptr(opline, node_type, node, zdata);
185 #else
186 	zend_free_op should_free;
187 
188 # if PHP_VERSION_ID >= 70300
189 	return zend_get_zval_ptr(opline, node_type, node, zdata, &should_free, BP_VAR_IS);
190 # else
191 	return zend_get_zval_ptr(node_type, node, zdata, &should_free, BP_VAR_IS);
192 # endif
193 #endif
194 }
195 
xdebug_get_zval(zend_execute_data * zdata,int node_type,const znode_op * node,int * is_var)196 zval *xdebug_get_zval(zend_execute_data *zdata, int node_type, const znode_op *node, int *is_var)
197 {
198 	return xdebug_get_zval_with_opline(zdata, zdata->opline, node_type, node, is_var);
199 }
200 
201 
202 /*****************************************************************************
203 ** PHP Variable related utility functions
204 */
205 
206 /*****************************************************************************
207 ** Data returning functions
208 */
209 #define XF_ST_ROOT                 0
210 #define XF_ST_ARRAY_INDEX_NUM      1
211 #define XF_ST_ARRAY_INDEX_ASSOC    2
212 #define XF_ST_OBJ_PROPERTY         3
213 #define XF_ST_STATIC_ROOT          4
214 #define XF_ST_STATIC_PROPERTY      5
215 #define XF_ST_ESCAPED_OBJ_PROPERTY 6
216 
fetch_ht_from_zval(zval * z)217 inline static HashTable *fetch_ht_from_zval(zval *z)
218 {
219 	switch (Z_TYPE_P(z)) {
220 		case IS_ARRAY:
221 			return Z_ARRVAL_P(z);
222 			break;
223 		case IS_OBJECT:
224 			return Z_OBJPROP_P(z);
225 			break;
226 	}
227 	return NULL;
228 }
229 
fetch_classname_from_zval(zval * z,int * length,zend_class_entry ** ce)230 inline static char *fetch_classname_from_zval(zval *z, int *length, zend_class_entry **ce)
231 {
232 	zend_string *class_name;
233 
234 	if (Z_TYPE_P(z) == IS_INDIRECT) {
235 		z = z->value.zv;
236 	}
237 	if (Z_TYPE_P(z) == IS_REFERENCE) {
238 		z = &z->value.ref->val;
239 	}
240 
241 	if (Z_TYPE_P(z) != IS_OBJECT) {
242 		return NULL;
243 	}
244 
245 	class_name = Z_OBJ_HANDLER_P(z, get_class_name)(Z_OBJ_P(z));
246 
247 	*ce = Z_OBJCE_P(z);
248 	*length = class_name->len;
249 
250 	return estrdup(class_name->val);
251 }
252 
prepare_search_key(char * name,unsigned int * name_length,const char * prefix,int prefix_length)253 static char* prepare_search_key(char *name, unsigned int *name_length, const char *prefix, int prefix_length)
254 {
255 	char *element;
256 	int   extra_length = 0;
257 
258 	if (prefix_length) {
259 		if (prefix[0] == '*') {
260 			extra_length = 3;
261 		} else {
262 			extra_length = 2 + prefix_length;
263 		}
264 	}
265 
266 	element = malloc(*name_length + 1 + extra_length);
267 	memset(element, 0, *name_length + 1 + extra_length);
268 	if (extra_length) {
269 		memcpy(element + 1, prefix, extra_length - 2);
270 	}
271 	memcpy(element + extra_length, name, *name_length);
272 	*name_length += extra_length;
273 
274 	return element;
275 }
276 
replace_star_by_null(const char * name,int name_length)277 char *replace_star_by_null(const char *name, int name_length)
278 {
279 	char *tmp = xdstrdup(name);
280 	int   i;
281 
282 	for (i = 0; i < name_length; i++) {
283 		if (tmp[i] == '*') {
284 			tmp[i] = '\0';
285 		}
286 	}
287 
288 	return tmp;
289 }
290 
291 #if PHP_VERSION_ID >= 70400
get_splobjectstorage_storage(zval * parent,HashTable ** properties)292 static zval *get_splobjectstorage_storage(zval *parent, HashTable **properties)
293 {
294 	*properties = zend_get_properties_for(parent, ZEND_PROP_PURPOSE_DEBUG);
295 	return zend_hash_str_find(*properties, "\0SplObjectStorage\0storage", sizeof("*SplObjectStorage*storage") - 1);
296 }
297 #else
get_splobjectstorage_storage(zval * parent,HashTable ** properties,int * is_temp)298 static zval *get_splobjectstorage_storage(zval *parent, HashTable **properties, int *is_temp)
299 {
300 	*properties = Z_OBJDEBUG_P(parent, *is_temp);
301 	return zend_hash_str_find(*properties, "\0SplObjectStorage\0storage", sizeof("*SplObjectStorage*storage") - 1);
302 }
303 #endif
304 
305 #if PHP_VERSION_ID < 70400
xdebug_var_maybe_destroy_ht(HashTable * ht,int is_temp)306 void xdebug_var_maybe_destroy_ht(HashTable *ht, int is_temp)
307 {
308 	if (ht && is_temp) {
309 		zend_hash_destroy(ht);
310 		FREE_HASHTABLE(ht);
311 	}
312 }
313 #endif
314 
fetch_zval_from_symbol_table(zval * value_in,char * name,unsigned int name_length,int type,char * ccn,int ccnl,zend_class_entry * cce)315 static void fetch_zval_from_symbol_table(
316 		zval *value_in, char *name, unsigned int name_length,
317 		int type, char* ccn, int ccnl, zend_class_entry *cce)
318 {
319 	HashTable *ht = NULL;
320 	char  *element = NULL;
321 	unsigned int element_length = name_length;
322 	zend_property_info *zpp;
323 #if PHP_VERSION_ID < 70400
324 	int is_temp = 0;
325 #endif
326 	int free_duplicated_name = 0;
327 	HashTable *myht = NULL;
328 	zval *orig_value_in = value_in;
329 	zval tmp_retval;
330 
331 	ZVAL_UNDEF(&tmp_retval);
332 
333 	if (Z_TYPE_P(value_in) == IS_INDIRECT) {
334 		value_in = Z_INDIRECT_P(value_in);
335 	}
336 	ZVAL_DEREF(value_in);
337 
338 	ht = fetch_ht_from_zval(value_in);
339 
340 	switch (type) {
341 		case XF_ST_STATIC_ROOT:
342 		case XF_ST_STATIC_PROPERTY:
343 			/* First we try a public,private,protected property */
344 #if PHP_VERSION_ID >= 70400
345 			if (cce && (cce->type == ZEND_INTERNAL_CLASS || (cce->ce_flags & ZEND_ACC_IMMUTABLE))) {
346 				zend_class_init_statics(cce);
347 			}
348 #endif
349 			element = prepare_search_key(name, &element_length, "", 0);
350 			if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL) && cce->default_static_members_count && CE_STATIC_MEMBERS(cce)) {
351 				ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]);
352 				goto cleanup;
353 			}
354 			element_length = name_length;
355 
356 			/* Then we try to see whether the first char is * and use the part between * and * as class name for the private property */
357 			if (name[0] == '*') {
358 				char *secondStar;
359 
360 				secondStar = strstr(name + 1, "*");
361 				if (secondStar) {
362 					free(element);
363 					element_length = name_length - (secondStar + 1 - name);
364 					element = prepare_search_key(secondStar + 1, &element_length, "", 0);
365 					if (cce && ((zpp = zend_hash_str_find_ptr(&cce->properties_info, element, element_length)) != NULL)) {
366 						ZVAL_COPY(&tmp_retval, &CE_STATIC_MEMBERS(cce)[zpp->offset]);
367 						goto cleanup;
368 					}
369 				}
370 			}
371 
372 			break;
373 
374 		case XF_ST_ROOT:
375 			/* Check for compiled vars */
376 			element = prepare_search_key(name, &element_length, "", 0);
377 			if (xdebug_lib_has_active_data() && xdebug_lib_has_active_function()) {
378 				int i = 0;
379 				zend_ulong hash_value = zend_inline_hash_func(element, element_length);
380 				zend_op_array *opa = xdebug_lib_get_active_func_oparray();
381 				zval **CV;
382 
383 				while (i < opa->last_var) {
384 					if (ZSTR_H(opa->vars[i]) == hash_value &&
385 						ZSTR_LEN(opa->vars[i]) == element_length &&
386 						strncmp(STR_NAME_VAL(opa->vars[i]), element, element_length) == 0)
387 					{
388 						zval *CV_z = ZEND_CALL_VAR_NUM(xdebug_lib_get_active_data(), i);
389 						CV = &CV_z;
390 						if (CV) {
391 							ZVAL_COPY(&tmp_retval, *CV);
392 							goto cleanup;
393 						}
394 					}
395 					i++;
396 				}
397 			}
398 			free(element);
399 			ht = xdebug_lib_get_active_symbol_table();
400 
401 			XDEBUG_BREAK_INTENTIONALLY_MISSING
402 
403 		case XF_ST_ARRAY_INDEX_ASSOC:
404 			element = prepare_search_key(name, &name_length, "", 0);
405 			xdebug_stripcslashes(element, (int *) &name_length);
406 
407 			/* Handle "this" in a different way */
408 			if (type == XF_ST_ROOT && strcmp("this", element) == 0) {
409 				if (xdebug_lib_has_active_object()) {
410 					ZVAL_COPY(&tmp_retval, xdebug_lib_get_active_object());
411 				} else {
412 					ZVAL_NULL(&tmp_retval);
413 				}
414 				goto cleanup;
415 			}
416 
417 			if (ht) {
418 				zval *tmp = zend_hash_str_find(ht, element, name_length);
419 				if (tmp != NULL) {
420 					ZVAL_COPY(&tmp_retval, tmp);
421 					goto cleanup;
422 				}
423 			}
424 			break;
425 
426 		case XF_ST_ARRAY_INDEX_NUM:
427 			element = prepare_search_key(name, &name_length, "", 0);
428 			if (ht) {
429 				zval *tmp = zend_hash_index_find(ht, strtoull(element, NULL, 10));
430 				if (tmp != NULL) {
431 					ZVAL_COPY(&tmp_retval, tmp);
432 					goto cleanup;
433 				}
434 			}
435 			break;
436 
437 		case XF_ST_ESCAPED_OBJ_PROPERTY:
438 			name = xdstrndup(name, name_length);
439 			free_duplicated_name = 1;
440 			xdebug_stripcslashes(name, (int *) &name_length);
441 
442 			XDEBUG_BREAK_INTENTIONALLY_MISSING
443 
444 		case XF_ST_OBJ_PROPERTY:
445 			/* Let's see if there is a debug handler */
446 			if (value_in && Z_TYPE_P(value_in) == IS_OBJECT) {
447 #if PHP_VERSION_ID >= 70400
448 				myht = xdebug_objdebug_pp(&value_in, XDEBUG_VAR_OBJDEBUG_DEFAULT);
449 #else
450 				myht = xdebug_objdebug_pp(&value_in, &is_temp, XDEBUG_VAR_OBJDEBUG_DEFAULT);
451 #endif
452 				if (myht) {
453 					/* As a normal (public) property */
454 					zval *tmp = zend_symtable_str_find(myht, name, name_length);
455 					if (tmp != NULL) {
456 						ZVAL_COPY(&tmp_retval, tmp);
457 #if PHP_VERSION_ID >= 70400
458 						zend_release_properties(myht);
459 #else
460 						xdebug_var_maybe_destroy_ht(myht, is_temp);
461 #endif
462 						goto cleanup;
463 					}
464 
465 					/* As a private property */
466 					{
467 						char *unmangled = replace_star_by_null(name, name_length);
468 						zval *tmp = zend_symtable_str_find(myht, unmangled, name_length);
469 						if (tmp != NULL) {
470 							ZVAL_COPY(&tmp_retval, tmp);
471 #if PHP_VERSION_ID >= 70400
472 							zend_release_properties(myht);
473 #else
474 							xdebug_var_maybe_destroy_ht(myht, is_temp);
475 #endif
476 							xdfree(unmangled);
477 							goto cleanup;
478 						}
479 						xdfree(unmangled);
480 					}
481 
482 #if PHP_VERSION_ID >= 70400
483 					zend_release_properties(myht);
484 #else
485 					xdebug_var_maybe_destroy_ht(myht, is_temp);
486 #endif
487 				}
488 			}
489 			/* First we try an object handler */
490 			if (cce) {
491 				zval *tmp_val;
492 #if PHP_VERSION_ID >= 80000
493 				tmp_val = zend_read_property(cce, Z_OBJ_P(value_in), name, name_length, 1, &tmp_retval);
494 #else
495 				tmp_val = zend_read_property(cce, value_in, name, name_length, 1, &tmp_retval);
496 #endif
497 				if (tmp_val != &tmp_retval && tmp_val != &EG(uninitialized_zval)) {
498 					ZVAL_COPY(&tmp_retval, tmp_val);
499 					goto cleanup;
500 				}
501 
502 				if (EG(exception)) {
503 					zend_clear_exception();
504 				}
505 			}
506 
507 			/* Then we try a public property */
508 			element = prepare_search_key(name, &element_length, "", 0);
509 			if (ht) {
510 				zval *tmp = zend_symtable_str_find(ht, element, element_length);
511 				if (tmp != NULL) {
512 					ZVAL_COPY(&tmp_retval, tmp);
513 					goto cleanup;
514 				}
515 			}
516 			element_length = name_length;
517 
518 			/* Then we try it again as protected property */
519 			free(element);
520 			element = prepare_search_key(name, &element_length, "*", 1);
521 			if (ht) {
522 				zval *tmp = zend_hash_str_find(ht, element, element_length);
523 				if (tmp != NULL) {
524 					ZVAL_COPY(&tmp_retval, tmp);
525 					goto cleanup;
526 				}
527 			}
528 			element_length = name_length;
529 
530 			/* Then we try it again as private property */
531 			free(element);
532 			element = prepare_search_key(name, &element_length, ccn, ccnl);
533 			if (ht) {
534 				zval *tmp = zend_hash_str_find(ht, element, element_length);
535 				if (tmp != NULL) {
536 					ZVAL_COPY(&tmp_retval, tmp);
537 					goto cleanup;
538 				}
539 			}
540 			element_length = name_length;
541 
542 			/* All right, time for a mega hack. It's SplObjectStorage access time! */
543 			if (strncmp(ccn, "SplObjectStorage", ccnl) == 0 && strncmp(name, "storage", name_length) == 0) {
544 #if PHP_VERSION_ID >= 70400
545 				zval *tmp = get_splobjectstorage_storage(value_in, &myht);
546 #else
547 				zval *tmp = get_splobjectstorage_storage(value_in, &myht, &is_temp);
548 #endif
549 				element = NULL;
550 				if (tmp != NULL) {
551 					ZVAL_COPY(&tmp_retval, tmp);
552 #if PHP_VERSION_ID >= 70400
553 					zend_release_properties(myht);
554 #else
555 					xdebug_var_maybe_destroy_ht(myht, is_temp);
556 #endif
557 					goto cleanup;
558 				}
559 #if PHP_VERSION_ID >= 70400
560 				zend_release_properties(myht);
561 #else
562 				xdebug_var_maybe_destroy_ht(myht, is_temp);
563 #endif
564 			}
565 
566 			break;
567 	}
568 
569 cleanup:
570 	if (element) {
571 		free(element);
572 	}
573 	if (free_duplicated_name && name) {
574 		xdfree(name);
575 	}
576 
577 	zval_ptr_dtor_nogc(orig_value_in);
578 	ZVAL_COPY_VALUE(orig_value_in, &tmp_retval);
579 }
580 
is_objectish(zval * value)581 inline static int is_objectish(zval *value)
582 {
583 	switch (Z_TYPE_P(value)) {
584 		case IS_OBJECT:
585 			return 1;
586 
587 		case IS_INDIRECT:
588 			if (Z_TYPE_P(Z_INDIRECT_P(value)) == IS_OBJECT) {
589 				return 1;
590 			}
591 			break;
592 
593 		case IS_REFERENCE:
594 			if (Z_TYPE_P(Z_REFVAL_P(value)) == IS_OBJECT) {
595 				return 1;
596 			}
597 			break;
598 	}
599 
600 	return 0;
601 }
602 
xdebug_get_php_symbol(zval * retval,xdebug_str * name)603 void xdebug_get_php_symbol(zval *retval, xdebug_str* name)
604 {
605 	int        found = -1;
606 	int        state = 0;
607 	char      *ptr = name->d;
608 	size_t     ctr = 0;
609 	char      *keyword = NULL, *keyword_end = NULL;
610 	int        type = XF_ST_ROOT;
611 	char      *current_classname = NULL;
612 	zend_class_entry *current_ce = NULL;
613 	int        cc_length = 0;
614 	char       quotechar = 0;
615 
616 	ZVAL_UNDEF(retval);
617 
618 	do {
619 		if (ctr == name->l) {
620 			found = 0;
621 		} else {
622 			switch (state) {
623 				case 0:
624 					if (ptr[ctr] == '$') {
625 						keyword = &ptr[ctr] + 1;
626 						break;
627 					}
628 					if (ptr[ctr] == ':') { /* special tricks */
629 						keyword = &ptr[ctr];
630 						state = 7;
631 						break;
632 					}
633 					keyword = &ptr[ctr];
634 					state = 1;
635 
636 					XDEBUG_BREAK_INTENTIONALLY_MISSING
637 
638 				case 1:
639 					if (ptr[ctr] == '[') {
640 						keyword_end = &ptr[ctr];
641 						if (keyword) {
642 							fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
643 							if (current_classname) {
644 								efree(current_classname);
645 							}
646 							current_classname = NULL;
647 							cc_length = 0;
648 							current_ce = NULL;
649 							keyword = NULL;
650 						}
651 						state = 3;
652 					} else if (ptr[ctr] == '-') {
653 						keyword_end = &ptr[ctr];
654 						if (keyword) {
655 							fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
656 							if (current_classname) {
657 								efree(current_classname);
658 							}
659 							current_classname = NULL;
660 							cc_length = 0;
661 							current_ce = NULL;
662 							if (is_objectish(retval)) {
663 								current_classname = fetch_classname_from_zval(retval, &cc_length, &current_ce);
664 							}
665 							keyword = NULL;
666 						}
667 						state = 2;
668 						type = XF_ST_OBJ_PROPERTY;
669 					} else if (ptr[ctr] == ':') {
670 						keyword_end = &ptr[ctr];
671 						if (keyword) {
672 							fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
673 							if (current_classname) {
674 								efree(current_classname);
675 							}
676 							current_classname = NULL;
677 							cc_length = 0;
678 							if (is_objectish(retval)) {
679 								current_classname = fetch_classname_from_zval(retval, &cc_length, &current_ce);
680 							}
681 							keyword = NULL;
682 						}
683 						state = 8;
684 						type = XF_ST_STATIC_PROPERTY;
685 					}
686 					break;
687 				case 2:
688 					if (ptr[ctr] != '>') {
689 						if (ptr[ctr] == '{') {
690 							state = 11;
691 						} else {
692 							keyword = &ptr[ctr];
693 							state = 1;
694 						}
695 					}
696 					break;
697 				case 8:
698 					if (ptr[ctr] != ':') {
699 						keyword = &ptr[ctr];
700 						state = 1;
701 					}
702 					break;
703 				case 3: /* Parsing in [...] */
704 					/* Associative arrays */
705 					if (ptr[ctr] == '\'' || ptr[ctr] == '"') {
706 						state = 4;
707 						keyword = &ptr[ctr] + 1;
708 						quotechar = ptr[ctr];
709 						type = XF_ST_ARRAY_INDEX_ASSOC;
710 					}
711 					/* Numerical index */
712 					if (ptr[ctr] >= '0' && ptr[ctr] <= '9') {
713 						cc_length = 0;
714 						state = 6;
715 						keyword = &ptr[ctr];
716 						type = XF_ST_ARRAY_INDEX_NUM;
717 					}
718 					/* Numerical index starting with a - */
719 					if (ptr[ctr] == '-') {
720 						state = 9;
721 						keyword = &ptr[ctr];
722 					}
723 					break;
724 				case 9:
725 					/* Numerical index starting with a - */
726 					if (ptr[ctr] >= '0' && ptr[ctr] <= '9') {
727 						state = 6;
728 						type = XF_ST_ARRAY_INDEX_NUM;
729 					}
730 					break;
731 				case 4:
732 					if (ptr[ctr] == '\\') {
733 						state = 10; /* Escaped character */
734 					} else if (ptr[ctr] == quotechar) {
735 						quotechar = 0;
736 						state = 5;
737 						keyword_end = &ptr[ctr];
738 						fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
739 						if (current_classname) {
740 							efree(current_classname);
741 						}
742 						current_classname = NULL;
743 						cc_length = 0;
744 						if (is_objectish(retval)) {
745 							current_classname = fetch_classname_from_zval(retval, &cc_length, &current_ce);
746 						}
747 						keyword = NULL;
748 					}
749 					break;
750 				case 10: /* Escaped character */
751 					state = 4;
752 					break;
753 				case 5:
754 					if (ptr[ctr] == ']') {
755 						state = 1;
756 					}
757 					break;
758 				case 6:
759 					if (ptr[ctr] == ']') {
760 						state = 1;
761 						keyword_end = &ptr[ctr];
762 						fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
763 						if (current_classname) {
764 							efree(current_classname);
765 						}
766 						current_classname = NULL;
767 						cc_length = 0;
768 						if (is_objectish(retval)) {
769 							current_classname = fetch_classname_from_zval(retval, &cc_length, &current_ce);
770 						}
771 						keyword = NULL;
772 					}
773 					break;
774 				case 7: /* special cases, started with a ":" */
775 					if (ptr[ctr] == ':') {
776 						function_stack_entry *active_fse = xdebug_lib_get_active_stack_entry();
777 						state = 1;
778 						keyword_end = &ptr[ctr];
779 
780 						if (strncmp(keyword, "::", 2) == 0 && active_fse->function.object_class) { /* static class properties */
781 							zend_class_entry *ce = zend_fetch_class(active_fse->function.object_class, ZEND_FETCH_CLASS_SELF);
782 
783 							current_classname = estrdup(STR_NAME_VAL(ce->name));
784 							cc_length = strlen(STR_NAME_VAL(ce->name));
785 							current_ce = ce;
786 							keyword = &ptr[ctr] + 1;
787 
788 							type = XF_ST_STATIC_ROOT;
789 						} else {
790 							keyword = NULL;
791 						}
792 					}
793 					break;
794 
795 				case 11:
796 					if (ptr[ctr] == '\'' || ptr[ctr] == '"') {
797 						state = 12;
798 						keyword = &ptr[ctr] + 1;
799 						quotechar = ptr[ctr];
800 						type = XF_ST_ESCAPED_OBJ_PROPERTY;
801 					}
802 					break;
803 
804 				case 12: /* Inside {" */
805 					if (ptr[ctr] == '\\') {
806 						state = 13; /* Escaped character */
807 					} else if (ptr[ctr] == quotechar) {
808 						quotechar = 0;
809 						state = 14;
810 						keyword_end = &ptr[ctr];
811 						fetch_zval_from_symbol_table(retval, keyword, keyword_end - keyword, type, current_classname, cc_length, current_ce);
812 						if (current_classname) {
813 							efree(current_classname);
814 						}
815 						current_classname = NULL;
816 						cc_length = 0;
817 						if (is_objectish(retval)) {
818 							current_classname = fetch_classname_from_zval(retval, &cc_length, &current_ce);
819 						}
820 						keyword = NULL;
821 					}
822 					break;
823 				case 13: /* Escaped character */
824 					state = 12;
825 					break;
826 				case 14:
827 					if (ptr[ctr] == '}') {
828 						state = 1;
829 					}
830 					break;
831 			}
832 			ctr++;
833 		}
834 	} while (found < 0);
835 	if (keyword != NULL) {
836 		fetch_zval_from_symbol_table(retval, keyword, &ptr[ctr] - keyword, type, current_classname, cc_length, current_ce);
837 	}
838 	if (current_classname) {
839 		efree(current_classname);
840 	}
841 }
842 
843 
844 #if PHP_VERSION_ID >= 70400
xdebug_get_property_type(zval * object,zval * val)845 xdebug_str* xdebug_get_property_type(zval* object, zval *val)
846 {
847 	xdebug_str         *type_str = NULL;
848 	zend_property_info *info;
849 
850 	if (Z_TYPE_P(val) != IS_INDIRECT) {
851 		return NULL;
852 	}
853 	val = Z_INDIRECT_P(val);
854 
855 	info = zend_get_typed_property_info_for_slot(Z_OBJ_P(object), val);
856 
857 	if (info) {
858 #if PHP_VERSION_ID >= 80000
859 		zend_string *type_info = zend_type_to_string(info->type);
860 		type_str = xdebug_str_new();
861 
862 # if PHP_VERSION_ID >= 80100
863 		if (info->flags & ZEND_ACC_READONLY) {
864 			xdebug_str_add_literal(type_str, "readonly ");
865 		}
866 # endif
867 
868 		xdebug_str_add_zstr(type_str, type_info);
869 
870 		zend_string_release(type_info);
871 
872 #else
873 		type_str = xdebug_str_new();
874 
875 		if (ZEND_TYPE_ALLOW_NULL(info->type)) {
876 			xdebug_str_addc(type_str, '?');
877 		}
878 		if (ZEND_TYPE_IS_CLASS(info->type)) {
879 			xdebug_str_add(
880 				type_str,
881 				ZSTR_VAL(
882 					ZEND_TYPE_IS_CE(info->type) ? ZEND_TYPE_CE(info->type)->name : ZEND_TYPE_NAME(info->type)
883 				),
884 				0
885 			);
886 		} else {
887 			xdebug_str_add(type_str, zend_get_type_by_const(ZEND_TYPE_CODE(info->type)), 0);
888 		}
889 #endif
890 	}
891 
892 	return type_str;
893 }
894 #endif
895 
xdebug_get_property_info(char * mangled_property,int mangled_len,const char ** modifier,char ** class_name)896 xdebug_str* xdebug_get_property_info(char *mangled_property, int mangled_len, const char **modifier, char **class_name)
897 {
898 	const char *cls_name, *tmp_prop_name;
899 	size_t      tmp_prop_name_len;
900 	xdebug_str *property_name;
901 
902 	zend_string *i_mangled = zend_string_init(mangled_property, mangled_len - 1, 0);
903 	zend_unmangle_property_name_ex(i_mangled, &cls_name, &tmp_prop_name, &tmp_prop_name_len);
904 	property_name = xdebug_str_create((char*) tmp_prop_name, tmp_prop_name_len);
905 	*class_name = cls_name ? xdstrdup(cls_name) : NULL;
906 	zend_string_release(i_mangled);
907 
908 	if (*class_name) {
909 		if (*class_name[0] == '*') {
910 			*modifier = "protected";
911 		} else {
912 			*modifier = "private";
913 		}
914 	} else {
915 		*modifier = "public";
916 	}
917 
918 	return property_name;
919 }
920 
921 #define XDEBUG_MAX_INT 2147483647
922 
xdebug_var_export_options_from_ini(void)923 xdebug_var_export_options* xdebug_var_export_options_from_ini(void)
924 {
925 	xdebug_var_export_options *options;
926 	options = xdmalloc(sizeof(xdebug_var_export_options));
927 
928 	options->max_children = XINI_LIB(display_max_children);
929 	options->max_data = XINI_LIB(display_max_data);
930 	options->max_depth = XINI_LIB(display_max_depth);
931 	options->show_hidden = 0;
932 	options->show_location = 1;
933 	options->extended_properties = 0;
934 	options->encode_as_extended_property = 0;
935 
936 	if (options->max_children == -1 || options->max_children > XDEBUG_MAX_INT) {
937 		options->max_children = XDEBUG_MAX_INT;
938 	} else if (options->max_children < 1) {
939 		options->max_children = 0;
940 	}
941 
942 	if (options->max_data == -1 || options->max_data > XDEBUG_MAX_INT) {
943 		options->max_data = XDEBUG_MAX_INT;
944 	} else if (options->max_data < 1) {
945 		options->max_data = 0;
946 	}
947 
948 	if (options->max_depth == -1 || options->max_depth > 1023) {
949 		options->max_depth = 1023;
950 	} else if (options->max_depth < 1) {
951 		options->max_depth = 0;
952 	}
953 
954 	options->runtime = (xdebug_var_runtime_page*) xdmalloc((options->max_depth + 1) * sizeof(xdebug_var_runtime_page));
955 	options->no_decoration = 0;
956 
957 	return options;
958 }
959 
960 xdebug_var_export_options xdebug_var_nolimit_options = { XDEBUG_MAX_INT, XDEBUG_MAX_INT, 1023, 1, 0, 0, 0, NULL, 0 };
961 
xdebug_var_get_nolimit_options(void)962 xdebug_var_export_options* xdebug_var_get_nolimit_options(void)
963 {
964 	return &xdebug_var_nolimit_options;
965 }
966 
967 /*****************************************************************************
968 ** Normal variable printing routines
969 */
970 
971 #define XDEBUG_VAR_ATTR_TEXT 0
972 #define XDEBUG_VAR_ATTR_FANCY 1
973 
xdebug_add_variable_attributes(xdebug_str * str,zval * struc,zend_bool html)974 void xdebug_add_variable_attributes(xdebug_str *str, zval *struc, zend_bool html)
975 {
976 	if (html) {
977 		xdebug_str_add_literal(str, "<i>(");
978 	} else {
979 		xdebug_str_add_literal(str, "(");
980 	}
981 
982 	if (Z_TYPE_P(struc) >= IS_STRING && Z_TYPE_P(struc) != IS_INDIRECT) {
983 		if (Z_TYPE_P(struc) == IS_STRING && ZSTR_IS_INTERNED(Z_STR_P(struc))) {
984 			xdebug_str_add_literal(str, "interned");
985 		} else if (Z_TYPE_P(struc) == IS_ARRAY && (GC_FLAGS(Z_ARRVAL_P(struc)) & IS_ARRAY_IMMUTABLE)) {
986 			xdebug_str_add_literal(str, "immutable");
987 		} else {
988 			xdebug_str_add_fmt(str, "refcount=%d", Z_REFCOUNT_P(struc));
989 		}
990 		xdebug_str_add_fmt(str, ", is_ref=%d", Z_TYPE_P(struc) == IS_REFERENCE);
991 	} else {
992 		xdebug_str_add_literal(str, "refcount=0, is_ref=0");
993 	}
994 
995 	if (html) {
996 		xdebug_str_add_literal(str, ")</i>");
997 	} else {
998 		xdebug_str_add_literal(str, ")=");
999 	}
1000 }
1001 
1002 
1003 /*****************************************************************************
1004 ** XML encoding function
1005 */
1006 static const char xml_encode_count[256] = {
1007 	4, 1, 1, 1,  1, 1, 1, 1,  1, 1, 5, 1,  1, 5, 1, 1,
1008 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1009 	1, 1, 6, 1,  1, 1, 5, 5,  1, 1, 1, 1,  1, 1, 1, 1,
1010 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  4, 1, 4, 1,
1011 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1012 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1013 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1014 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1015 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1016 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1017 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1018 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1019 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1020 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1021 	1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
1022 };
1023 
1024 static const char* xml_encode_map[64] = {
1025 	"&#0;", NULL, NULL,     NULL,  NULL, NULL, NULL,    NULL,     NULL, NULL, "&#10;", NULL,  NULL,   "&#13;", NULL,   NULL,
1026 	NULL,   NULL, NULL,     NULL,  NULL, NULL, NULL,    NULL,     NULL, NULL, NULL,    NULL,  NULL,   NULL,    NULL,   NULL,
1027 	NULL,   NULL, "&quot;", NULL,  NULL, NULL, "&amp;", "&#39;",  NULL, NULL, NULL,    NULL,  NULL,   NULL,    NULL,   NULL,
1028 	NULL,   NULL, NULL,     NULL,  NULL, NULL, NULL,    NULL,     NULL, NULL, NULL,    NULL,  "&lt;", NULL,    "&gt;", NULL,
1029 };
1030 
xdebug_xmlize(char * s_string,size_t len,size_t * newlen)1031 char* xdebug_xmlize(char *s_string, size_t len, size_t *newlen)
1032 {
1033 	int i, w_pos;
1034 	int encoded_string_length = 0;
1035 	char *new_string;
1036 	const unsigned char *string = (unsigned char*) s_string;
1037 
1038 	/* Quick bailout for empty strings */
1039 	if (!len) {
1040 		*newlen = 0;
1041 		return estrdup("");
1042 	}
1043 
1044 	/* Calculate new memory requirement */
1045 	for (i = 0; i < len; i++) {
1046 		encoded_string_length += xml_encode_count[string[i]];
1047 	}
1048 
1049 	/* No characters need to be encoded, so just duplicate and return */
1050 	if (encoded_string_length == len) {
1051 		*newlen = len;
1052 		return estrdup(s_string);
1053 	}
1054 
1055 	new_string = emalloc(encoded_string_length + 1);
1056 	w_pos = 0;
1057 	for (i = 0; i < len; i++) {
1058 		int replacement_length = xml_encode_count[string[i]];
1059 
1060 		if (replacement_length != 1) {
1061 			int j;
1062 
1063 			for (j = 0; j < replacement_length; j++) {
1064 				new_string[w_pos] = xml_encode_map[string[i]][j];
1065 				w_pos++;
1066 			}
1067 			continue;
1068 		}
1069 
1070 		new_string[w_pos] = string[i];
1071 		w_pos++;
1072 	}
1073 
1074 	new_string[w_pos] = '\0';
1075 	*newlen = encoded_string_length; /* remove one for null byte */
1076 
1077 	return new_string;
1078 }
1079 
1080 /*****************************************************************************
1081 ** Function name printing function
1082 */
xdebug_create_doc_link(xdebug_func f)1083 static char* xdebug_create_doc_link(xdebug_func f)
1084 {
1085 	char *tmp_target = NULL, *p, *retval;
1086 
1087 	switch (f.type) {
1088 		case XFUNC_NORMAL: {
1089 			tmp_target = xdebug_sprintf("function.%s", f.function);
1090 			break;
1091 		}
1092 
1093 		case XFUNC_STATIC_MEMBER:
1094 		case XFUNC_MEMBER: {
1095 			if (strcmp(f.function, "__construct") == 0) {
1096 				tmp_target = xdebug_sprintf("%s.construct", ZSTR_VAL(f.object_class));
1097 			} else {
1098 				tmp_target = xdebug_sprintf("%s.%s", ZSTR_VAL(f.object_class), f.function);
1099 			}
1100 			break;
1101 		}
1102 	}
1103 
1104 	while ((p = strchr(tmp_target, '_')) != NULL) {
1105 		*p = '-';
1106 	}
1107 
1108 	retval = xdebug_sprintf("<a href='%s%s%s' target='_new'>%s</a>",
1109 		(PG(docref_root) && PG(docref_root)[0]) ? PG(docref_root) : "http://www.php.net/",
1110 		tmp_target, PG(docref_ext), f.function);
1111 
1112 	xdfree(tmp_target);
1113 
1114 	return retval;
1115 }
1116 
xdebug_show_fname(xdebug_func f,int flags)1117 char* xdebug_show_fname(xdebug_func f, int flags)
1118 {
1119 	switch (f.type) {
1120 		case XFUNC_NORMAL: {
1121 			if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) {
1122 				return xdebug_create_doc_link(f);
1123 			} else {
1124 				return xdstrdup(f.function);
1125 			}
1126 			break;
1127 		}
1128 
1129 		case XFUNC_STATIC_MEMBER:
1130 		case XFUNC_MEMBER: {
1131 			if (PG(html_errors) && (flags & XDEBUG_SHOW_FNAME_ALLOW_HTML) && f.internal) {
1132 				return xdebug_create_doc_link(f);
1133 			} else {
1134 				if (f.scope_class && !(flags & XDEBUG_SHOW_FNAME_IGNORE_SCOPE)) {
1135 					return xdebug_sprintf("%s%s%s",
1136 						ZSTR_VAL(f.scope_class),
1137 						f.type == XFUNC_STATIC_MEMBER ? "::" : "->",
1138 						f.function ? f.function : "?"
1139 					);
1140 				}
1141 
1142 				return xdebug_sprintf("%s%s%s",
1143 					f.object_class ? ZSTR_VAL(f.object_class) : "?",
1144 					f.type == XFUNC_STATIC_MEMBER ? "::" : "->",
1145 					f.function ? f.function : "?"
1146 				);
1147 			}
1148 			break;
1149 		}
1150 
1151 		case XFUNC_EVAL:
1152 			return xdstrdup("eval");
1153 			break;
1154 
1155 		case XFUNC_INCLUDE:
1156 			return xdstrdup("include");
1157 			break;
1158 
1159 		case XFUNC_INCLUDE_ONCE:
1160 			return xdstrdup("include_once");
1161 			break;
1162 
1163 		case XFUNC_REQUIRE:
1164 			return xdstrdup("require");
1165 			break;
1166 
1167 		case XFUNC_REQUIRE_ONCE:
1168 			return xdstrdup("require_once");
1169 			break;
1170 
1171 		case XFUNC_MAIN:
1172 			return xdstrdup("{main}");
1173 			break;
1174 
1175 		case XFUNC_ZEND_PASS:
1176 			return xdstrdup("{zend_pass}");
1177 			break;
1178 
1179 #if PHP_VERSION_ID >= 80100
1180 		case XFUNC_FIBER:
1181 			return xdstrdup(f.function);
1182 			break;
1183 #endif
1184 
1185 		default:
1186 			return xdstrdup("{unknown}");
1187 	}
1188 }
1189