xref: /php-src/Zend/zend_attributes.c (revision 49ef6e20)
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 
validate_attribute(zend_attribute * attr,uint32_t target,zend_class_entry * scope)38 void validate_attribute(zend_attribute *attr, uint32_t target, 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 		/* As this is run in the middle of compilation, fetch the attribute value without
45 		 * specifying a scope. The class is not fully linked yet, and we may seen an
46 		 * inconsistent state. */
47 		if (FAILURE == zend_get_attribute_value(&flags, attr, 0, NULL)) {
48 			return;
49 		}
50 
51 		if (Z_TYPE(flags) != IS_LONG) {
52 			zend_error_noreturn(E_ERROR,
53 				"Attribute::__construct(): Argument #1 ($flags) must be of type int, %s given",
54 				zend_zval_value_name(&flags)
55 			);
56 		}
57 
58 		if (Z_LVAL(flags) & ~ZEND_ATTRIBUTE_FLAGS) {
59 			zend_error_noreturn(E_ERROR, "Invalid attribute flags specified");
60 		}
61 
62 		zval_ptr_dtor(&flags);
63 	}
64 }
65 
validate_allow_dynamic_properties(zend_attribute * attr,uint32_t target,zend_class_entry * scope)66 static void validate_allow_dynamic_properties(
67 		zend_attribute *attr, uint32_t target, zend_class_entry *scope)
68 {
69 	if (scope->ce_flags & ZEND_ACC_TRAIT) {
70 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to trait");
71 	}
72 	if (scope->ce_flags & ZEND_ACC_INTERFACE) {
73 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to interface");
74 	}
75 	if (scope->ce_flags & ZEND_ACC_READONLY_CLASS) {
76 		zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to readonly class %s",
77 			ZSTR_VAL(scope->name)
78 		);
79 	}
80 	scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
81 }
82 
ZEND_METHOD(Attribute,__construct)83 ZEND_METHOD(Attribute, __construct)
84 {
85 	zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
86 
87 	ZEND_PARSE_PARAMETERS_START(0, 1)
88 		Z_PARAM_OPTIONAL
89 		Z_PARAM_LONG(flags)
90 	ZEND_PARSE_PARAMETERS_END();
91 
92 	ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
93 }
94 
ZEND_METHOD(ReturnTypeWillChange,__construct)95 ZEND_METHOD(ReturnTypeWillChange, __construct)
96 {
97 	ZEND_PARSE_PARAMETERS_NONE();
98 }
99 
ZEND_METHOD(AllowDynamicProperties,__construct)100 ZEND_METHOD(AllowDynamicProperties, __construct)
101 {
102 	ZEND_PARSE_PARAMETERS_NONE();
103 }
104 
ZEND_METHOD(SensitiveParameter,__construct)105 ZEND_METHOD(SensitiveParameter, __construct)
106 {
107 	ZEND_PARSE_PARAMETERS_NONE();
108 }
109 
ZEND_METHOD(SensitiveParameterValue,__construct)110 ZEND_METHOD(SensitiveParameterValue, __construct)
111 {
112 	zval *value;
113 
114 	ZEND_PARSE_PARAMETERS_START(1, 1)
115 		Z_PARAM_ZVAL(value)
116 	ZEND_PARSE_PARAMETERS_END();
117 
118 	zend_update_property_ex(zend_ce_sensitive_parameter_value, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_VALUE), value);
119 }
120 
ZEND_METHOD(SensitiveParameterValue,getValue)121 ZEND_METHOD(SensitiveParameterValue, getValue)
122 {
123 	ZEND_PARSE_PARAMETERS_NONE();
124 
125 	ZVAL_COPY(return_value, OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0));
126 }
127 
ZEND_METHOD(SensitiveParameterValue,__debugInfo)128 ZEND_METHOD(SensitiveParameterValue, __debugInfo)
129 {
130 	ZEND_PARSE_PARAMETERS_NONE();
131 
132 	RETURN_EMPTY_ARRAY();
133 }
134 
attributes_sensitive_parameter_value_get_properties_for(zend_object * zobj,zend_prop_purpose purpose)135 static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_object *zobj, zend_prop_purpose purpose)
136 {
137 	return NULL;
138 }
139 
ZEND_METHOD(Override,__construct)140 ZEND_METHOD(Override, __construct)
141 {
142 	ZEND_PARSE_PARAMETERS_NONE();
143 }
144 
get_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)145 static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
146 {
147 	if (attributes) {
148 		zend_attribute *attr;
149 
150 		ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) {
151 			if (attr->offset == offset && zend_string_equals(attr->lcname, lcname)) {
152 				return attr;
153 			}
154 		} ZEND_HASH_FOREACH_END();
155 	}
156 
157 	return NULL;
158 }
159 
get_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)160 static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
161 {
162 	if (attributes) {
163 		zend_attribute *attr;
164 
165 		ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) {
166 			if (attr->offset == offset && zend_string_equals_cstr(attr->lcname, str, len)) {
167 				return attr;
168 			}
169 		} ZEND_HASH_FOREACH_END();
170 	}
171 
172 	return NULL;
173 }
174 
zend_get_attribute(HashTable * attributes,zend_string * lcname)175 ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname)
176 {
177 	return get_attribute(attributes, lcname, 0);
178 }
179 
zend_get_attribute_str(HashTable * attributes,const char * str,size_t len)180 ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len)
181 {
182 	return get_attribute_str(attributes, str, len, 0);
183 }
184 
zend_get_parameter_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)185 ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
186 {
187 	return get_attribute(attributes, lcname, offset + 1);
188 }
189 
zend_get_parameter_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)190 ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
191 {
192 	return get_attribute_str(attributes, str, len, offset + 1);
193 }
194 
zend_get_attribute_value(zval * ret,zend_attribute * attr,uint32_t i,zend_class_entry * scope)195 ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope)
196 {
197 	if (i >= attr->argc) {
198 		return FAILURE;
199 	}
200 
201 	ZVAL_COPY_OR_DUP(ret, &attr->args[i].value);
202 
203 	if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
204 		if (SUCCESS != zval_update_constant_ex(ret, scope)) {
205 			zval_ptr_dtor(ret);
206 			return FAILURE;
207 		}
208 	}
209 
210 	return SUCCESS;
211 }
212 
213 static const char *target_names[] = {
214 	"class",
215 	"function",
216 	"method",
217 	"property",
218 	"class constant",
219 	"parameter"
220 };
221 
zend_get_attribute_target_names(uint32_t flags)222 ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
223 {
224 	smart_str str = { 0 };
225 
226 	for (uint32_t i = 0; i < (sizeof(target_names) / sizeof(char *)); i++) {
227 		if (flags & (1 << i)) {
228 			if (smart_str_get_len(&str)) {
229 				smart_str_appends(&str, ", ");
230 			}
231 
232 			smart_str_appends(&str, target_names[i]);
233 		}
234 	}
235 
236 	return smart_str_extract(&str);
237 }
238 
zend_is_attribute_repeated(HashTable * attributes,zend_attribute * attr)239 ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
240 {
241 	zend_attribute *other;
242 
243 	ZEND_HASH_PACKED_FOREACH_PTR(attributes, other) {
244 		if (other != attr && other->offset == attr->offset) {
245 			if (zend_string_equals(other->lcname, attr->lcname)) {
246 				return 1;
247 			}
248 		}
249 	} ZEND_HASH_FOREACH_END();
250 
251 	return 0;
252 }
253 
attr_free(zval * v)254 static void attr_free(zval *v)
255 {
256 	zend_attribute *attr = Z_PTR_P(v);
257 	bool persistent = attr->flags & ZEND_ATTRIBUTE_PERSISTENT;
258 
259 	zend_string_release(attr->name);
260 	zend_string_release(attr->lcname);
261 
262 	for (uint32_t i = 0; i < attr->argc; i++) {
263 		if (attr->args[i].name) {
264 			zend_string_release(attr->args[i].name);
265 		}
266 		if (persistent) {
267 			zval_internal_ptr_dtor(&attr->args[i].value);
268 		} else {
269 			zval_ptr_dtor(&attr->args[i].value);
270 		}
271 	}
272 
273 	pefree(attr, persistent);
274 }
275 
zend_add_attribute(HashTable ** attributes,zend_string * name,uint32_t argc,uint32_t flags,uint32_t offset,uint32_t lineno)276 ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno)
277 {
278 	bool persistent = flags & ZEND_ATTRIBUTE_PERSISTENT;
279 	if (*attributes == NULL) {
280 		*attributes = pemalloc(sizeof(HashTable), persistent);
281 		zend_hash_init(*attributes, 8, NULL, attr_free, persistent);
282 	}
283 
284 	zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent);
285 
286 	if (persistent == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) {
287 		attr->name = zend_string_copy(name);
288 	} else {
289 		attr->name = zend_string_dup(name, persistent);
290 	}
291 
292 	attr->lcname = zend_string_tolower_ex(attr->name, persistent);
293 	attr->flags = flags;
294 	attr->lineno = lineno;
295 	attr->offset = offset;
296 	attr->argc = argc;
297 
298 	/* Initialize arguments to avoid partial initialization in case of fatal errors. */
299 	for (uint32_t i = 0; i < argc; i++) {
300 		attr->args[i].name = NULL;
301 		ZVAL_UNDEF(&attr->args[i].value);
302 	}
303 
304 	zend_hash_next_index_insert_ptr(*attributes, attr);
305 
306 	return attr;
307 }
308 
free_internal_attribute(zval * v)309 static void free_internal_attribute(zval *v)
310 {
311 	pefree(Z_PTR_P(v), 1);
312 }
313 
zend_mark_internal_attribute(zend_class_entry * ce)314 ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce)
315 {
316 	zend_internal_attribute *internal_attr;
317 	zend_attribute *attr;
318 
319 	if (ce->type != ZEND_INTERNAL_CLASS) {
320 		zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute");
321 	}
322 
323 	ZEND_HASH_FOREACH_PTR(ce->attributes, attr) {
324 		if (zend_string_equals(attr->name, zend_ce_attribute->name)) {
325 			internal_attr = pemalloc(sizeof(zend_internal_attribute), 1);
326 			internal_attr->ce = ce;
327 			internal_attr->flags = Z_LVAL(attr->args[0].value);
328 			internal_attr->validator = NULL;
329 
330 			zend_string *lcname = zend_string_tolower_ex(ce->name, 1);
331 			zend_hash_update_ptr(&internal_attributes, lcname, internal_attr);
332 			zend_string_release(lcname);
333 
334 			return internal_attr;
335 		}
336 	} ZEND_HASH_FOREACH_END();
337 
338 	zend_error_noreturn(E_ERROR, "Classes must be first marked as attribute before being able to be registered as internal attribute class");
339 }
340 
zend_internal_attribute_register(zend_class_entry * ce,uint32_t flags)341 ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags)
342 {
343 	zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1);
344 	ZVAL_LONG(&attr->args[0].value, flags);
345 
346 	return zend_mark_internal_attribute(ce);
347 }
348 
zend_internal_attribute_get(zend_string * lcname)349 ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname)
350 {
351 	return zend_hash_find_ptr(&internal_attributes, lcname);
352 }
353 
zend_register_attribute_ce(void)354 void zend_register_attribute_ce(void)
355 {
356 	zend_internal_attribute *attr;
357 
358 	zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
359 
360 	zend_ce_attribute = register_class_Attribute();
361 	attr = zend_mark_internal_attribute(zend_ce_attribute);
362 	attr->validator = validate_attribute;
363 
364 	zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange();
365 	zend_mark_internal_attribute(zend_ce_return_type_will_change_attribute);
366 
367 	zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties();
368 	attr = zend_mark_internal_attribute(zend_ce_allow_dynamic_properties);
369 	attr->validator = validate_allow_dynamic_properties;
370 
371 	zend_ce_sensitive_parameter = register_class_SensitiveParameter();
372 	zend_mark_internal_attribute(zend_ce_sensitive_parameter);
373 
374 	memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers));
375 	attributes_object_handlers_sensitive_parameter_value.get_properties_for = attributes_sensitive_parameter_value_get_properties_for;
376 
377 	/* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */
378 	zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue();
379 	zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value;
380 
381 	zend_ce_override = register_class_Override();
382 	zend_mark_internal_attribute(zend_ce_override);
383 }
384 
zend_attributes_shutdown(void)385 void zend_attributes_shutdown(void)
386 {
387 	zend_hash_destroy(&internal_attributes);
388 }
389