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