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