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