xref: /PHP-8.4/Zend/zend_attributes.c (revision 5a18279b)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Benjamin Eberlei <kontakt@beberlei.de>                      |
16    |          Martin Schröder <m.schroeder2007@gmail.com>                 |
17    +----------------------------------------------------------------------+
18 */
19 
20 #include "zend.h"
21 #include "zend_API.h"
22 #include "zend_attributes.h"
23 #include "zend_attributes_arginfo.h"
24 #include "zend_exceptions.h"
25 #include "zend_smart_str.h"
26 
27 ZEND_API zend_class_entry *zend_ce_attribute;
28 ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute;
29 ZEND_API zend_class_entry *zend_ce_allow_dynamic_properties;
30 ZEND_API zend_class_entry *zend_ce_sensitive_parameter;
31 ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
32 ZEND_API zend_class_entry *zend_ce_override;
33 ZEND_API zend_class_entry *zend_ce_deprecated;
34 
35 static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
36 
37 static HashTable internal_attributes;
38 
zend_attribute_attribute_get_flags(zend_attribute * attr,zend_class_entry * scope)39 uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope)
40 {
41 	// TODO: More proper signature validation: Too many args, incorrect arg names.
42 	if (attr->argc > 0) {
43 		zval flags;
44 
45 		if (FAILURE == zend_get_attribute_value(&flags, attr, 0, scope)) {
46 			ZEND_ASSERT(EG(exception));
47 			return 0;
48 		}
49 
50 		if (Z_TYPE(flags) != IS_LONG) {
51 			zend_throw_error(NULL,
52 				"Attribute::__construct(): Argument #1 ($flags) must be of type int, %s given",
53 				zend_zval_value_name(&flags)
54 			);
55 			zval_ptr_dtor(&flags);
56 			return 0;
57 		}
58 
59 		uint32_t flags_l = Z_LVAL(flags);
60 		if (flags_l & ~ZEND_ATTRIBUTE_FLAGS) {
61 			zend_throw_error(NULL, "Invalid attribute flags specified");
62 			return 0;
63 		}
64 
65 		return flags_l;
66 	}
67 
68 	return ZEND_ATTRIBUTE_TARGET_ALL;
69 }
70 
validate_allow_dynamic_properties(zend_attribute * attr,uint32_t target,zend_class_entry * scope)71 static void validate_allow_dynamic_properties(
72 		zend_attribute *attr, uint32_t target, zend_class_entry *scope)
73 {
74 	if (scope->ce_flags & ZEND_ACC_TRAIT) {
75 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to trait %s",
76 			ZSTR_VAL(scope->name)
77 		);
78 	}
79 	if (scope->ce_flags & ZEND_ACC_INTERFACE) {
80 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to interface %s",
81 			ZSTR_VAL(scope->name)
82 		);
83 	}
84 	if (scope->ce_flags & ZEND_ACC_READONLY_CLASS) {
85 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to readonly class %s",
86 			ZSTR_VAL(scope->name)
87 		);
88 	}
89 	if (scope->ce_flags & ZEND_ACC_ENUM) {
90 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to enum %s",
91 			ZSTR_VAL(scope->name)
92 		);
93 	}
94 	scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
95 }
96 
ZEND_METHOD(Attribute,__construct)97 ZEND_METHOD(Attribute, __construct)
98 {
99 	zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
100 
101 	ZEND_PARSE_PARAMETERS_START(0, 1)
102 		Z_PARAM_OPTIONAL
103 		Z_PARAM_LONG(flags)
104 	ZEND_PARSE_PARAMETERS_END();
105 
106 	ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
107 }
108 
ZEND_METHOD(ReturnTypeWillChange,__construct)109 ZEND_METHOD(ReturnTypeWillChange, __construct)
110 {
111 	ZEND_PARSE_PARAMETERS_NONE();
112 }
113 
ZEND_METHOD(AllowDynamicProperties,__construct)114 ZEND_METHOD(AllowDynamicProperties, __construct)
115 {
116 	ZEND_PARSE_PARAMETERS_NONE();
117 }
118 
ZEND_METHOD(SensitiveParameter,__construct)119 ZEND_METHOD(SensitiveParameter, __construct)
120 {
121 	ZEND_PARSE_PARAMETERS_NONE();
122 }
123 
ZEND_METHOD(SensitiveParameterValue,__construct)124 ZEND_METHOD(SensitiveParameterValue, __construct)
125 {
126 	zval *value;
127 
128 	ZEND_PARSE_PARAMETERS_START(1, 1)
129 		Z_PARAM_ZVAL(value)
130 	ZEND_PARSE_PARAMETERS_END();
131 
132 	zend_update_property_ex(zend_ce_sensitive_parameter_value, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_VALUE), value);
133 }
134 
ZEND_METHOD(SensitiveParameterValue,getValue)135 ZEND_METHOD(SensitiveParameterValue, getValue)
136 {
137 	ZEND_PARSE_PARAMETERS_NONE();
138 
139 	ZVAL_COPY(return_value, OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0));
140 }
141 
ZEND_METHOD(SensitiveParameterValue,__debugInfo)142 ZEND_METHOD(SensitiveParameterValue, __debugInfo)
143 {
144 	ZEND_PARSE_PARAMETERS_NONE();
145 
146 	RETURN_EMPTY_ARRAY();
147 }
148 
attributes_sensitive_parameter_value_get_properties_for(zend_object * zobj,zend_prop_purpose purpose)149 static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_object *zobj, zend_prop_purpose purpose)
150 {
151 	return NULL;
152 }
153 
ZEND_METHOD(Override,__construct)154 ZEND_METHOD(Override, __construct)
155 {
156 	ZEND_PARSE_PARAMETERS_NONE();
157 }
158 
ZEND_METHOD(Deprecated,__construct)159 ZEND_METHOD(Deprecated, __construct)
160 {
161 	zend_string *message = NULL;
162 	zend_string *since = NULL;
163 	zval value;
164 
165 	ZEND_PARSE_PARAMETERS_START(0, 2)
166 		Z_PARAM_OPTIONAL
167 		Z_PARAM_STR_OR_NULL(message)
168 		Z_PARAM_STR_OR_NULL(since)
169 	ZEND_PARSE_PARAMETERS_END();
170 
171 	if (message) {
172 		ZVAL_STR(&value, message);
173 	} else {
174 		ZVAL_NULL(&value);
175 	}
176 	zend_update_property_ex(zend_ce_deprecated, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);
177 
178 	/* The assignment might fail due to 'readonly'. */
179 	if (UNEXPECTED(EG(exception))) {
180 		RETURN_THROWS();
181 	}
182 
183 	if (since) {
184 		ZVAL_STR(&value, since);
185 	} else {
186 		ZVAL_NULL(&value);
187 	}
188 	zend_update_property_ex(zend_ce_deprecated, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_SINCE), &value);
189 
190 	/* The assignment might fail due to 'readonly'. */
191 	if (UNEXPECTED(EG(exception))) {
192 		RETURN_THROWS();
193 	}
194 }
195 
get_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)196 static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
197 {
198 	if (attributes) {
199 		zend_attribute *attr;
200 
201 		ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) {
202 			if (attr->offset == offset && zend_string_equals(attr->lcname, lcname)) {
203 				return attr;
204 			}
205 		} ZEND_HASH_FOREACH_END();
206 	}
207 
208 	return NULL;
209 }
210 
get_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)211 static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
212 {
213 	if (attributes) {
214 		zend_attribute *attr;
215 
216 		ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) {
217 			if (attr->offset == offset && zend_string_equals_cstr(attr->lcname, str, len)) {
218 				return attr;
219 			}
220 		} ZEND_HASH_FOREACH_END();
221 	}
222 
223 	return NULL;
224 }
225 
zend_get_attribute(HashTable * attributes,zend_string * lcname)226 ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname)
227 {
228 	return get_attribute(attributes, lcname, 0);
229 }
230 
zend_get_attribute_str(HashTable * attributes,const char * str,size_t len)231 ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len)
232 {
233 	return get_attribute_str(attributes, str, len, 0);
234 }
235 
zend_get_parameter_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)236 ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
237 {
238 	return get_attribute(attributes, lcname, offset + 1);
239 }
240 
zend_get_parameter_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)241 ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
242 {
243 	return get_attribute_str(attributes, str, len, offset + 1);
244 }
245 
zend_get_attribute_value(zval * ret,zend_attribute * attr,uint32_t i,zend_class_entry * scope)246 ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope)
247 {
248 	if (i >= attr->argc) {
249 		return FAILURE;
250 	}
251 
252 	ZVAL_COPY_OR_DUP(ret, &attr->args[i].value);
253 
254 	if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
255 		if (SUCCESS != zval_update_constant_ex(ret, scope)) {
256 			zval_ptr_dtor(ret);
257 			return FAILURE;
258 		}
259 	}
260 
261 	return SUCCESS;
262 }
263 
zend_get_attribute_object(zval * obj,zend_class_entry * attribute_ce,zend_attribute * attribute_data,zend_class_entry * scope,zend_string * filename)264 ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename)
265 {
266 	zend_execute_data *call = NULL;
267 
268 	if (filename) {
269 		/* Set up dummy call frame that makes it look like the attribute was invoked
270 		 * from where it occurs in the code. */
271 		zend_function dummy_func;
272 		zend_op *opline;
273 
274 		memset(&dummy_func, 0, sizeof(zend_function));
275 
276 		call = zend_vm_stack_push_call_frame_ex(
277 			ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) +
278 			ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) +
279 			ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)),
280 			0, &dummy_func, 0, NULL);
281 
282 		opline = (zend_op*)(call + 1);
283 		memset(opline, 0, sizeof(zend_op));
284 		opline->opcode = ZEND_DO_FCALL;
285 		opline->lineno = attribute_data->lineno;
286 
287 		call->opline = opline;
288 		call->call = NULL;
289 		call->return_value = NULL;
290 		call->func = (zend_function*)(call->opline + 1);
291 		call->prev_execute_data = EG(current_execute_data);
292 
293 		memset(call->func, 0, sizeof(zend_function));
294 		call->func->type = ZEND_USER_FUNCTION;
295 		call->func->op_array.fn_flags =
296 			attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0;
297 		call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE;
298 		call->func->op_array.filename = filename;
299 
300 		EG(current_execute_data) = call;
301 	}
302 
303 	zval *args = NULL;
304 	HashTable *named_params = NULL;
305 
306 	zend_result result = FAILURE;
307 
308 	uint32_t argc = 0;
309 	if (attribute_data->argc) {
310 		args = emalloc(attribute_data->argc * sizeof(zval));
311 
312 		for (uint32_t i = 0; i < attribute_data->argc; i++) {
313 			zval val;
314 			if (FAILURE == zend_get_attribute_value(&val, attribute_data, i, scope)) {
315 				result = FAILURE;
316 				goto out;
317 			}
318 			if (attribute_data->args[i].name) {
319 				if (!named_params) {
320 					named_params = zend_new_array(0);
321 				}
322 				zend_hash_add_new(named_params, attribute_data->args[i].name, &val);
323 			} else {
324 				ZVAL_COPY_VALUE(&args[i], &val);
325 				argc++;
326 			}
327 		}
328 	}
329 
330 	result = object_init_with_constructor(obj, attribute_ce, argc, args, named_params);
331 
332  out:
333 	for (uint32_t i = 0; i < argc; i++) {
334 		zval_ptr_dtor(&args[i]);
335 	}
336 
337 	efree(args);
338 
339 	if (named_params) {
340 		zend_array_destroy(named_params);
341 	}
342 
343 	if (filename) {
344 		EG(current_execute_data) = call->prev_execute_data;
345 		zend_vm_stack_free_call_frame(call);
346 	}
347 
348 	return result;
349 }
350 
351 static const char *target_names[] = {
352 	"class",
353 	"function",
354 	"method",
355 	"property",
356 	"class constant",
357 	"parameter"
358 };
359 
zend_get_attribute_target_names(uint32_t flags)360 ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
361 {
362 	smart_str str = { 0 };
363 
364 	for (uint32_t i = 0; i < (sizeof(target_names) / sizeof(char *)); i++) {
365 		if (flags & (1 << i)) {
366 			if (smart_str_get_len(&str)) {
367 				smart_str_appends(&str, ", ");
368 			}
369 
370 			smart_str_appends(&str, target_names[i]);
371 		}
372 	}
373 
374 	return smart_str_extract(&str);
375 }
376 
zend_is_attribute_repeated(HashTable * attributes,zend_attribute * attr)377 ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
378 {
379 	zend_attribute *other;
380 
381 	ZEND_HASH_PACKED_FOREACH_PTR(attributes, other) {
382 		if (other != attr && other->offset == attr->offset) {
383 			if (zend_string_equals(other->lcname, attr->lcname)) {
384 				return 1;
385 			}
386 		}
387 	} ZEND_HASH_FOREACH_END();
388 
389 	return 0;
390 }
391 
attr_free(zval * v)392 static void attr_free(zval *v)
393 {
394 	zend_attribute *attr = Z_PTR_P(v);
395 	bool persistent = attr->flags & ZEND_ATTRIBUTE_PERSISTENT;
396 
397 	zend_string_release(attr->name);
398 	zend_string_release(attr->lcname);
399 
400 	for (uint32_t i = 0; i < attr->argc; i++) {
401 		if (attr->args[i].name) {
402 			zend_string_release(attr->args[i].name);
403 		}
404 		if (persistent) {
405 			zval_internal_ptr_dtor(&attr->args[i].value);
406 		} else {
407 			zval_ptr_dtor(&attr->args[i].value);
408 		}
409 	}
410 
411 	pefree(attr, persistent);
412 }
413 
zend_add_attribute(HashTable ** attributes,zend_string * name,uint32_t argc,uint32_t flags,uint32_t offset,uint32_t lineno)414 ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno)
415 {
416 	bool persistent = flags & ZEND_ATTRIBUTE_PERSISTENT;
417 	if (*attributes == NULL) {
418 		*attributes = pemalloc(sizeof(HashTable), persistent);
419 		zend_hash_init(*attributes, 8, NULL, attr_free, persistent);
420 	}
421 
422 	zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent);
423 
424 	if (persistent == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) {
425 		attr->name = zend_string_copy(name);
426 	} else {
427 		attr->name = zend_string_dup(name, persistent);
428 	}
429 
430 	attr->lcname = zend_string_tolower_ex(attr->name, persistent);
431 	attr->flags = flags;
432 	attr->lineno = lineno;
433 	attr->offset = offset;
434 	attr->argc = argc;
435 
436 	/* Initialize arguments to avoid partial initialization in case of fatal errors. */
437 	for (uint32_t i = 0; i < argc; i++) {
438 		attr->args[i].name = NULL;
439 		ZVAL_UNDEF(&attr->args[i].value);
440 	}
441 
442 	zend_hash_next_index_insert_ptr(*attributes, attr);
443 
444 	return attr;
445 }
446 
free_internal_attribute(zval * v)447 static void free_internal_attribute(zval *v)
448 {
449 	pefree(Z_PTR_P(v), 1);
450 }
451 
zend_mark_internal_attribute(zend_class_entry * ce)452 ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce)
453 {
454 	zend_internal_attribute *internal_attr;
455 	zend_attribute *attr;
456 
457 	if (ce->type != ZEND_INTERNAL_CLASS) {
458 		zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute");
459 	}
460 
461 	ZEND_HASH_FOREACH_PTR(ce->attributes, attr) {
462 		if (zend_string_equals(attr->name, zend_ce_attribute->name)) {
463 			internal_attr = pemalloc(sizeof(zend_internal_attribute), 1);
464 			internal_attr->ce = ce;
465 			internal_attr->flags = Z_LVAL(attr->args[0].value);
466 			internal_attr->validator = NULL;
467 
468 			zend_string *lcname = zend_string_tolower_ex(ce->name, 1);
469 			zend_hash_update_ptr(&internal_attributes, lcname, internal_attr);
470 			zend_string_release(lcname);
471 
472 			return internal_attr;
473 		}
474 	} ZEND_HASH_FOREACH_END();
475 
476 	zend_error_noreturn(E_ERROR, "Classes must be first marked as attribute before being able to be registered as internal attribute class");
477 }
478 
zend_internal_attribute_register(zend_class_entry * ce,uint32_t flags)479 ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags)
480 {
481 	zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1);
482 	ZVAL_LONG(&attr->args[0].value, flags);
483 
484 	return zend_mark_internal_attribute(ce);
485 }
486 
zend_internal_attribute_get(zend_string * lcname)487 ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname)
488 {
489 	return zend_hash_find_ptr(&internal_attributes, lcname);
490 }
491 
zend_register_attribute_ce(void)492 void zend_register_attribute_ce(void)
493 {
494 	zend_internal_attribute *attr;
495 
496 	zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
497 
498 	zend_ce_attribute = register_class_Attribute();
499 	attr = zend_mark_internal_attribute(zend_ce_attribute);
500 
501 	zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange();
502 	zend_mark_internal_attribute(zend_ce_return_type_will_change_attribute);
503 
504 	zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties();
505 	attr = zend_mark_internal_attribute(zend_ce_allow_dynamic_properties);
506 	attr->validator = validate_allow_dynamic_properties;
507 
508 	zend_ce_sensitive_parameter = register_class_SensitiveParameter();
509 	zend_mark_internal_attribute(zend_ce_sensitive_parameter);
510 
511 	memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers));
512 	attributes_object_handlers_sensitive_parameter_value.get_properties_for = attributes_sensitive_parameter_value_get_properties_for;
513 
514 	/* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */
515 	zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue();
516 	zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value;
517 
518 	zend_ce_override = register_class_Override();
519 	zend_mark_internal_attribute(zend_ce_override);
520 
521 	zend_ce_deprecated = register_class_Deprecated();
522 	attr = zend_mark_internal_attribute(zend_ce_deprecated);
523 }
524 
zend_attributes_shutdown(void)525 void zend_attributes_shutdown(void)
526 {
527 	zend_hash_destroy(&internal_attributes);
528 }
529