xref: /PHP-8.4/Zend/zend_ini.c (revision 8c19efdc)
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    | Author: Zeev Suraski <zeev@php.net>                                  |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "zend.h"
20 #include "zend_sort.h"
21 #include "zend_API.h"
22 #include "zend_ini.h"
23 #include "zend_alloc.h"
24 #include "zend_operators.h"
25 #include "zend_strtod.h"
26 #include "zend_modules.h"
27 #include "zend_smart_str.h"
28 #include <ctype.h>
29 
30 static HashTable *registered_zend_ini_directives;
31 
32 #define NO_VALUE_PLAINTEXT		"no value"
33 #define NO_VALUE_HTML			"<i>no value</i>"
34 
zend_is_whitespace(char c)35 static inline bool zend_is_whitespace(char c) {
36 	return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
37 }
38 
39 /*
40  * hash_apply functions
41  */
zend_remove_ini_entries(zval * el,void * arg)42 static int zend_remove_ini_entries(zval *el, void *arg) /* {{{ */
43 {
44 	zend_ini_entry *ini_entry = (zend_ini_entry *)Z_PTR_P(el);
45 	int module_number = *(int *)arg;
46 
47 	return ini_entry->module_number == module_number;
48 }
49 /* }}} */
50 
zend_restore_ini_entry_cb(zend_ini_entry * ini_entry,int stage)51 static zend_result zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage) /* {{{ */
52 {
53 	zend_result result = FAILURE;
54 
55 	if (ini_entry->modified) {
56 		if (ini_entry->on_modify) {
57 			zend_try {
58 			/* even if on_modify bails out, we have to continue on with restoring,
59 				since there can be allocated variables that would be freed on MM shutdown
60 				and would lead to memory corruption later ini entry is modified again */
61 				result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage);
62 			} zend_end_try();
63 		}
64 		if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
65 			/* runtime failure is OK */
66 			return FAILURE;
67 		}
68 		if (ini_entry->value != ini_entry->orig_value) {
69 			zend_string_release(ini_entry->value);
70 		}
71 		ini_entry->value = ini_entry->orig_value;
72 		ini_entry->modifiable = ini_entry->orig_modifiable;
73 		ini_entry->modified = 0;
74 		ini_entry->orig_value = NULL;
75 		ini_entry->orig_modifiable = 0;
76 	}
77 	return SUCCESS;
78 }
79 /* }}} */
80 
free_ini_entry(zval * zv)81 static void free_ini_entry(zval *zv) /* {{{ */
82 {
83 	zend_ini_entry *entry = (zend_ini_entry*)Z_PTR_P(zv);
84 
85 	zend_string_release_ex(entry->name, 1);
86 	if (entry->value) {
87 		zend_string_release(entry->value);
88 	}
89 	if (entry->orig_value) {
90 		zend_string_release_ex(entry->orig_value, 1);
91 	}
92 	free(entry);
93 }
94 /* }}} */
95 
96 /*
97  * Startup / shutdown
98  */
zend_ini_startup(void)99 ZEND_API void zend_ini_startup(void) /* {{{ */
100 {
101 	registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable));
102 
103 	EG(ini_directives) = registered_zend_ini_directives;
104 	EG(modified_ini_directives) = NULL;
105 	EG(error_reporting_ini_entry) = NULL;
106 	zend_hash_init(registered_zend_ini_directives, 128, NULL, free_ini_entry, 1);
107 }
108 /* }}} */
109 
zend_ini_shutdown(void)110 ZEND_API void zend_ini_shutdown(void) /* {{{ */
111 {
112 	zend_ini_dtor(EG(ini_directives));
113 }
114 /* }}} */
115 
zend_ini_dtor(HashTable * ini_directives)116 ZEND_API void zend_ini_dtor(HashTable *ini_directives) /* {{{ */
117 {
118 	zend_hash_destroy(ini_directives);
119 	free(ini_directives);
120 }
121 /* }}} */
122 
zend_ini_global_shutdown(void)123 ZEND_API void zend_ini_global_shutdown(void) /* {{{ */
124 {
125 	zend_hash_destroy(registered_zend_ini_directives);
126 	free(registered_zend_ini_directives);
127 }
128 /* }}} */
129 
zend_ini_deactivate(void)130 ZEND_API void zend_ini_deactivate(void) /* {{{ */
131 {
132 	if (EG(modified_ini_directives)) {
133 		zend_ini_entry *ini_entry;
134 
135 		ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
136 			zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
137 		} ZEND_HASH_FOREACH_END();
138 		zend_hash_destroy(EG(modified_ini_directives));
139 		FREE_HASHTABLE(EG(modified_ini_directives));
140 		EG(modified_ini_directives) = NULL;
141 	}
142 }
143 /* }}} */
144 
145 #ifdef ZTS
copy_ini_entry(zval * zv)146 static void copy_ini_entry(zval *zv) /* {{{ */
147 {
148 	zend_ini_entry *old_entry = (zend_ini_entry*)Z_PTR_P(zv);
149 	zend_ini_entry *new_entry = pemalloc(sizeof(zend_ini_entry), 1);
150 
151 	Z_PTR_P(zv) = new_entry;
152 	memcpy(new_entry, old_entry, sizeof(zend_ini_entry));
153 	if (old_entry->name) {
154 		new_entry->name = zend_string_dup(old_entry->name, 1);
155 	}
156 	if (old_entry->value) {
157 		new_entry->value = zend_string_dup(old_entry->value, 1);
158 	}
159 	if (old_entry->orig_value) {
160 		new_entry->orig_value = zend_string_dup(old_entry->orig_value, 1);
161 	}
162 }
163 /* }}} */
164 
zend_copy_ini_directives(void)165 ZEND_API void zend_copy_ini_directives(void) /* {{{ */
166 {
167 	EG(modified_ini_directives) = NULL;
168 	EG(error_reporting_ini_entry) = NULL;
169 	EG(ini_directives) = (HashTable *) malloc(sizeof(HashTable));
170 	zend_hash_init(EG(ini_directives), registered_zend_ini_directives->nNumOfElements, NULL, free_ini_entry, 1);
171 	zend_hash_copy(EG(ini_directives), registered_zend_ini_directives, copy_ini_entry);
172 }
173 /* }}} */
174 #endif
175 
ini_key_compare(Bucket * f,Bucket * s)176 static int ini_key_compare(Bucket *f, Bucket *s) /* {{{ */
177 {
178 	if (!f->key && !s->key) { /* both numeric */
179 		if (f->h > s->h) {
180 			return -1;
181 		} else if (f->h < s->h) {
182 			return 1;
183 		}
184 		return 0;
185 	} else if (!f->key) { /* f is numeric, s is not */
186 		return -1;
187 	} else if (!s->key) { /* s is numeric, f is not */
188 		return 1;
189 	} else { /* both strings */
190 		return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
191 	}
192 }
193 /* }}} */
194 
zend_ini_sort_entries(void)195 ZEND_API void zend_ini_sort_entries(void) /* {{{ */
196 {
197 	zend_hash_sort(EG(ini_directives), ini_key_compare, 0);
198 }
199 /* }}} */
200 
201 /*
202  * Registration / unregistration
203  */
zend_register_ini_entries_ex(const zend_ini_entry_def * ini_entry,int module_number,int module_type)204 ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_entry, int module_number, int module_type) /* {{{ */
205 {
206 	zend_ini_entry *p;
207 	zval *default_value;
208 	HashTable *directives = registered_zend_ini_directives;
209 
210 #ifdef ZTS
211 	/* if we are called during the request, eg: from dl(),
212 	 * then we should not touch the global directives table,
213 	 * and should update the per-(request|thread) version instead.
214 	 * This solves two problems: one is that ini entries for dl()'d
215 	 * extensions will now work, and the second is that updating the
216 	 * global hash here from dl() is not mutex protected and can
217 	 * lead to death.
218 	 */
219 	if (directives != EG(ini_directives)) {
220 		directives = EG(ini_directives);
221 	} else {
222 		ZEND_ASSERT(module_type == MODULE_PERSISTENT);
223 	}
224 #endif
225 
226 	while (ini_entry->name) {
227 		p = pemalloc(sizeof(zend_ini_entry), 1);
228 		p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, 1);
229 		p->on_modify = ini_entry->on_modify;
230 		p->mh_arg1 = ini_entry->mh_arg1;
231 		p->mh_arg2 = ini_entry->mh_arg2;
232 		p->mh_arg3 = ini_entry->mh_arg3;
233 		p->value = NULL;
234 		p->orig_value = NULL;
235 		p->displayer = ini_entry->displayer;
236 		p->modifiable = ini_entry->modifiable;
237 
238 		p->orig_modifiable = 0;
239 		p->modified = 0;
240 		p->module_number = module_number;
241 
242 		if (zend_hash_add_ptr(directives, p->name, (void*)p) == NULL) {
243 			if (p->name) {
244 				zend_string_release_ex(p->name, 1);
245 			}
246 			pefree(p, true);
247 			zend_unregister_ini_entries_ex(module_number, module_type);
248 			return FAILURE;
249 		}
250 		if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
251 		    (!p->on_modify || p->on_modify(p, Z_STR_P(default_value), p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS)) {
252 
253 			p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
254 		} else {
255 			p->value = ini_entry->value ?
256 				zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
257 
258 			if (p->on_modify) {
259 				p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
260 			}
261 		}
262 		ini_entry++;
263 	}
264 	return SUCCESS;
265 }
266 /* }}} */
267 
zend_register_ini_entries(const zend_ini_entry_def * ini_entry,int module_number)268 ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
269 {
270 	zend_module_entry *module;
271 
272 	/* Module is likely to be the last one in the list */
273 	ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
274 		if (module->module_number == module_number) {
275 			return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
276 		}
277 	} ZEND_HASH_FOREACH_END();
278 
279 	return FAILURE;
280 }
281 /* }}} */
282 
zend_unregister_ini_entries_ex(int module_number,int module_type)283 ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
284 {
285 	static HashTable *ini_directives;
286 
287 	if (module_type == MODULE_TEMPORARY) {
288 		ini_directives = EG(ini_directives);
289 	} else {
290 		ini_directives = registered_zend_ini_directives;
291 	}
292 
293 	zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
294 }
295 /* }}} */
296 
zend_unregister_ini_entries(int module_number)297 ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
298 {
299 	zend_module_entry *module;
300 
301 	/* Module is likely to be the last one in the list */
302 	ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
303 		if (module->module_number == module_number) {
304 			zend_unregister_ini_entries_ex(module_number, module->type);
305 			return;
306 		}
307 	} ZEND_HASH_FOREACH_END();
308 }
309 /* }}} */
310 
311 #ifdef ZTS
zend_ini_refresh_caches(int stage)312 ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
313 {
314 	zend_ini_entry *p;
315 
316 	ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
317 		if (p->on_modify) {
318 			p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
319 		}
320 	} ZEND_HASH_FOREACH_END();
321 }
322 /* }}} */
323 #endif
324 
zend_alter_ini_entry(zend_string * name,zend_string * new_value,int modify_type,int stage)325 ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
326 {
327 
328 	return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
329 }
330 /* }}} */
331 
zend_alter_ini_entry_chars(zend_string * name,const char * value,size_t value_length,int modify_type,int stage)332 ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
333 {
334 	zend_result ret;
335 	zend_string *new_value;
336 
337 	new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
338 	ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
339 	zend_string_release(new_value);
340 	return ret;
341 }
342 /* }}} */
343 
zend_alter_ini_entry_chars_ex(zend_string * name,const char * value,size_t value_length,int modify_type,int stage,int force_change)344 ZEND_API zend_result zend_alter_ini_entry_chars_ex(zend_string *name, const char *value, size_t value_length, int modify_type, int stage, int force_change) /* {{{ */
345 {
346 	zend_result ret;
347 	zend_string *new_value;
348 
349 	new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
350 	ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
351 	zend_string_release(new_value);
352 	return ret;
353 }
354 /* }}} */
355 
zend_alter_ini_entry_ex(zend_string * name,zend_string * new_value,int modify_type,int stage,bool force_change)356 ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
357 {
358 	zend_ini_entry *ini_entry;
359 	zend_string *duplicate;
360 	uint8_t modifiable;
361 	bool modified;
362 
363 	if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
364 		return FAILURE;
365 	}
366 
367 	modifiable = ini_entry->modifiable;
368 	modified = ini_entry->modified;
369 
370 	if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
371 		ini_entry->modifiable = ZEND_INI_SYSTEM;
372 	}
373 
374 	if (!force_change) {
375 		if (!(ini_entry->modifiable & modify_type)) {
376 			return FAILURE;
377 		}
378 	}
379 
380 	if (!EG(modified_ini_directives)) {
381 		ALLOC_HASHTABLE(EG(modified_ini_directives));
382 		zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
383 	}
384 	if (!modified) {
385 		ini_entry->orig_value = ini_entry->value;
386 		ini_entry->orig_modifiable = modifiable;
387 		ini_entry->modified = 1;
388 		zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
389 	}
390 
391 	duplicate = zend_string_copy(new_value);
392 
393 	if (!ini_entry->on_modify
394 		|| ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
395 		if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
396 			zend_string_release(ini_entry->value);
397 		}
398 		ini_entry->value = duplicate;
399 	} else {
400 		zend_string_release(duplicate);
401 		return FAILURE;
402 	}
403 
404 	return SUCCESS;
405 }
406 /* }}} */
407 
zend_restore_ini_entry(zend_string * name,int stage)408 ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
409 {
410 	zend_ini_entry *ini_entry;
411 
412 	if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
413 		(stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
414 		return FAILURE;
415 	}
416 
417 	if (EG(modified_ini_directives)) {
418 		if (zend_restore_ini_entry_cb(ini_entry, stage) == 0) {
419 			zend_hash_del(EG(modified_ini_directives), name);
420 		} else {
421 			return FAILURE;
422 		}
423 	}
424 
425 	return SUCCESS;
426 }
427 /* }}} */
428 
zend_ini_register_displayer(const char * name,uint32_t name_length,void (* displayer)(zend_ini_entry * ini_entry,int type))429 ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
430 {
431 	zend_ini_entry *ini_entry;
432 
433 	ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
434 	if (ini_entry == NULL) {
435 		return FAILURE;
436 	}
437 
438 	ini_entry->displayer = displayer;
439 	return SUCCESS;
440 }
441 /* }}} */
442 
443 /*
444  * Data retrieval
445  */
446 
zend_ini_long(const char * name,size_t name_length,int orig)447 ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig) /* {{{ */
448 {
449 	zend_ini_entry *ini_entry;
450 
451 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
452 	if (ini_entry) {
453 		if (orig && ini_entry->modified) {
454 			return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
455 		} else {
456 			return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
457 		}
458 	}
459 
460 	return 0;
461 }
462 /* }}} */
463 
zend_ini_double(const char * name,size_t name_length,int orig)464 ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) /* {{{ */
465 {
466 	zend_ini_entry *ini_entry;
467 
468 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
469 	if (ini_entry) {
470 		if (orig && ini_entry->modified) {
471 			return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
472 		} else {
473 			return (double) (ini_entry->value      ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL)      : 0.0);
474 		}
475 	}
476 
477 	return 0.0;
478 }
479 /* }}} */
480 
zend_ini_string_ex(const char * name,size_t name_length,int orig,bool * exists)481 ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
482 {
483 	zend_ini_entry *ini_entry;
484 
485 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
486 	if (ini_entry) {
487 		if (exists) {
488 			*exists = 1;
489 		}
490 
491 		if (orig && ini_entry->modified) {
492 			return ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : NULL;
493 		} else {
494 			return ini_entry->value ? ZSTR_VAL(ini_entry->value) : NULL;
495 		}
496 	} else {
497 		if (exists) {
498 			*exists = 0;
499 		}
500 		return NULL;
501 	}
502 }
503 /* }}} */
504 
zend_ini_string(const char * name,size_t name_length,int orig)505 ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
506 {
507 	bool exists = 1;
508 	char *return_value;
509 
510 	return_value = zend_ini_string_ex(name, name_length, orig, &exists);
511 	if (!exists) {
512 		return NULL;
513 	} else if (!return_value) {
514 		return_value = "";
515 	}
516 	return return_value;
517 }
518 /* }}} */
519 
520 
zend_ini_str_ex(const char * name,size_t name_length,bool orig,bool * exists)521 ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
522 {
523 	zend_ini_entry *ini_entry;
524 
525 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
526 	if (ini_entry) {
527 		if (exists) {
528 			*exists = 1;
529 		}
530 
531 		if (orig && ini_entry->modified) {
532 			return ini_entry->orig_value ? ini_entry->orig_value : NULL;
533 		} else {
534 			return ini_entry->value ? ini_entry->value : NULL;
535 		}
536 	} else {
537 		if (exists) {
538 			*exists = 0;
539 		}
540 		return NULL;
541 	}
542 }
543 /* }}} */
544 
zend_ini_str(const char * name,size_t name_length,bool orig)545 ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
546 {
547 	bool exists = 1;
548 	zend_string *return_value;
549 
550 	return_value = zend_ini_str_ex(name, name_length, orig, &exists);
551 	if (!exists) {
552 		return NULL;
553 	} else if (!return_value) {
554 		return_value = ZSTR_EMPTY_ALLOC();
555 	}
556 	return return_value;
557 }
558 /* }}} */
559 
zend_ini_get_value(zend_string * name)560 ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
561 {
562 	zend_ini_entry *ini_entry;
563 
564 	ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
565 	if (ini_entry) {
566 		return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
567 	} else {
568 		return NULL;
569 	}
570 }
571 /* }}} */
572 
zend_ini_parse_bool(zend_string * str)573 ZEND_API bool zend_ini_parse_bool(zend_string *str)
574 {
575 	if (zend_string_equals_literal_ci(str, "true")
576 			|| zend_string_equals_literal_ci(str, "yes")
577 			|| zend_string_equals_literal_ci(str, "on")
578 	) {
579 		return 1;
580 	} else {
581 		return atoi(ZSTR_VAL(str)) != 0;
582 	}
583 }
584 
585 typedef enum {
586 	ZEND_INI_PARSE_QUANTITY_SIGNED,
587 	ZEND_INI_PARSE_QUANTITY_UNSIGNED,
588 } zend_ini_parse_quantity_signed_result_t;
589 
zend_ini_consume_quantity_prefix(const char * const digits,const char * const str_end)590 static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
591 	const char *digits_consumed = digits;
592 	/* Ignore leading whitespace. */
593 	while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
594 	if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
595 		++digits_consumed;
596 	}
597 
598 	if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
599 		/* Value is just 0 */
600 		if ((digits_consumed+1) == str_end) {
601 			return digits;
602 		}
603 
604 		switch (digits_consumed[1]) {
605 			case 'x':
606 			case 'X':
607 			case 'o':
608 			case 'O':
609 			case 'b':
610 			case 'B':
611 				digits_consumed += 2;
612 				break;
613 		}
614 	}
615 	return digits_consumed;
616 }
617 
zend_ini_parse_quantity_internal(zend_string * value,zend_ini_parse_quantity_signed_result_t signed_result,zend_string ** errstr)618 static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
619 {
620 	char *digits_end = NULL;
621 	char *str = ZSTR_VAL(value);
622 	char *str_end = &str[ZSTR_LEN(value)];
623 	char *digits = str;
624 	bool overflow = false;
625 	zend_ulong factor;
626 	smart_str invalid = {0};
627 	smart_str interpreted = {0};
628 	smart_str chr = {0};
629 
630 	/* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
631 	 * but we need the position of the first non-whitespace later. */
632 	while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
633 
634 	/* Ignore trailing whitespace */
635 	while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
636 
637 	if (digits == str_end) {
638 		*errstr = NULL;
639 		return 0;
640 	}
641 
642 	bool is_negative = false;
643 	if (digits[0] == '+') {
644 		++digits;
645 	} else if (digits[0] == '-') {
646 		is_negative = true;
647 		++digits;
648 	}
649 
650 	/* if there is no digit after +/- */
651 	if (!isdigit(digits[0])) {
652 		/* Escape the string to avoid null bytes and to make non-printable chars
653 		 * visible */
654 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
655 		smart_str_0(&invalid);
656 
657 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
658 						ZSTR_VAL(invalid.s));
659 
660 		smart_str_free(&invalid);
661 		return 0;
662 	}
663 
664 	int base = 0;
665 	if (digits[0] == '0' && !isdigit(digits[1])) {
666 		/* Value is just 0 */
667 		if ((digits+1) == str_end) {
668 			*errstr = NULL;
669 			return 0;
670 		}
671 
672 		switch (digits[1]) {
673 			/* Multiplier suffixes */
674 			case 'g':
675 			case 'G':
676 			case 'm':
677 			case 'M':
678 			case 'k':
679 			case 'K':
680 				goto evaluation;
681 			case 'x':
682 			case 'X':
683 				base = 16;
684 				break;
685 			case 'o':
686 			case 'O':
687 				base = 8;
688 				break;
689 			case 'b':
690 			case 'B':
691 				base = 2;
692 				break;
693 			default:
694 				*errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
695 					digits[1]);
696 				return 0;
697         }
698         digits += 2;
699 		if (UNEXPECTED(digits == str_end)) {
700 			/* Escape the string to avoid null bytes and to make non-printable chars
701 			 * visible */
702 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
703 			smart_str_0(&invalid);
704 
705 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
706 							ZSTR_VAL(invalid.s));
707 
708 			smart_str_free(&invalid);
709 			return 0;
710 		}
711 		if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
712 			/* Escape the string to avoid null bytes and to make non-printable chars
713 			 * visible */
714 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
715 			smart_str_0(&invalid);
716 
717 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
718 							ZSTR_VAL(invalid.s));
719 
720 			smart_str_free(&invalid);
721 			return 0;
722 		}
723 	}
724 	evaluation:
725 
726 	errno = 0;
727 	zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
728 
729 	if (errno == ERANGE) {
730 		overflow = true;
731 	} else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
732 		if (is_negative) {
733 			/* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
734 			if (retval == 1 && digits_end == str_end) {
735 				retval = -1;
736 			} else {
737 				overflow = true;
738 			}
739 		}
740 	} else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
741 		/* Handle PHP_INT_MIN case */
742 		if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
743 			retval = 0u - retval;
744 		} else if ((zend_long) retval < 0) {
745 			overflow = true;
746 		} else if (is_negative) {
747 			retval = 0u - retval;
748 		}
749 	}
750 
751 	if (UNEXPECTED(digits_end == digits)) {
752 		/* No leading digits */
753 
754 		/* Escape the string to avoid null bytes and to make non-printable chars
755 		 * visible */
756 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
757 		smart_str_0(&invalid);
758 
759 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
760 						ZSTR_VAL(invalid.s));
761 
762 		smart_str_free(&invalid);
763 		return 0;
764 	}
765 
766 	/* Allow for whitespace between integer portion and any suffix character */
767 	while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
768 
769 	/* No exponent suffix. */
770 	if (digits_end == str_end) {
771 		goto end;
772 	}
773 
774 	switch (*(str_end-1)) {
775 		case 'g':
776 		case 'G':
777 			factor = 1<<30;
778 			break;
779 		case 'm':
780 		case 'M':
781 			factor = 1<<20;
782 			break;
783 		case 'k':
784 		case 'K':
785 			factor = 1<<10;
786 			break;
787 		default:
788 			/* Unknown suffix */
789 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
790 			smart_str_0(&invalid);
791 			smart_str_append_escaped(&interpreted, str, digits_end - str);
792 			smart_str_0(&interpreted);
793 			smart_str_append_escaped(&chr, str_end-1, 1);
794 			smart_str_0(&chr);
795 
796 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
797 						ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
798 
799 			smart_str_free(&invalid);
800 			smart_str_free(&interpreted);
801 			smart_str_free(&chr);
802 
803 			return retval;
804 	}
805 
806 	if (!overflow) {
807 		if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
808 			zend_long sretval = (zend_long)retval;
809 			if (sretval > 0) {
810 				overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
811 			} else {
812 				overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
813 			}
814 		} else {
815 			overflow = retval > ZEND_ULONG_MAX / factor;
816 		}
817 	}
818 
819 	retval *= factor;
820 
821 	if (UNEXPECTED(digits_end != str_end-1)) {
822 		/* More than one character in suffix */
823 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
824 		smart_str_0(&invalid);
825 		smart_str_append_escaped(&interpreted, str, digits_end - str);
826 		smart_str_0(&interpreted);
827 		smart_str_append_escaped(&chr, str_end-1, 1);
828 		smart_str_0(&chr);
829 
830 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
831 						ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
832 
833 		smart_str_free(&invalid);
834 		smart_str_free(&interpreted);
835 		smart_str_free(&chr);
836 
837 		return retval;
838 	}
839 
840 end:
841 	if (UNEXPECTED(overflow)) {
842 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
843 		smart_str_0(&invalid);
844 
845 		/* Not specifying the resulting value here because the caller may make
846 		 * additional conversions. Not specifying the allowed range
847 		 * because the caller may do narrower range checks. */
848 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
849 						ZSTR_VAL(invalid.s));
850 
851 		smart_str_free(&invalid);
852 		smart_str_free(&interpreted);
853 		smart_str_free(&chr);
854 
855 		return retval;
856 	}
857 
858 	*errstr = NULL;
859 	return retval;
860 }
861 /* }}} */
862 
zend_ini_parse_quantity(zend_string * value,zend_string ** errstr)863 ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
864 {
865 	return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
866 }
867 /* }}} */
868 
zend_ini_parse_uquantity(zend_string * value,zend_string ** errstr)869 ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
870 {
871 	return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
872 }
873 /* }}} */
874 
zend_ini_parse_quantity_warn(zend_string * value,zend_string * setting)875 ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
876 {
877 	zend_string *errstr;
878 	zend_long retval = zend_ini_parse_quantity(value, &errstr);
879 
880 	if (errstr) {
881 		zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
882 		zend_string_release(errstr);
883 	}
884 
885 	return retval;
886 }
887 /* }}} */
888 
zend_ini_parse_uquantity_warn(zend_string * value,zend_string * setting)889 ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
890 {
891 	zend_string *errstr;
892 	zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
893 
894 	if (errstr) {
895 		zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
896 		zend_string_release(errstr);
897 	}
898 
899 	return retval;
900 }
901 /* }}} */
902 
ZEND_INI_DISP(zend_ini_boolean_displayer_cb)903 ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
904 {
905 	int value;
906 	zend_string *tmp_value;
907 
908 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
909 		tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
910 	} else if (ini_entry->value) {
911 		tmp_value = ini_entry->value;
912 	} else {
913 		tmp_value = NULL;
914 	}
915 
916 	if (tmp_value) {
917 		value = zend_ini_parse_bool(tmp_value);
918 	} else {
919 		value = 0;
920 	}
921 
922 	if (value) {
923 		ZEND_PUTS("On");
924 	} else {
925 		ZEND_PUTS("Off");
926 	}
927 }
928 /* }}} */
929 
ZEND_INI_DISP(zend_ini_color_displayer_cb)930 ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
931 {
932 	char *value;
933 
934 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
935 		value = ZSTR_VAL(ini_entry->orig_value);
936 	} else if (ini_entry->value) {
937 		value = ZSTR_VAL(ini_entry->value);
938 	} else {
939 		value = NULL;
940 	}
941 	if (value) {
942 		if (zend_uv.html_errors) {
943 			zend_printf("<span style=\"color: %s\">%s</span>", value, value);
944 		} else {
945 			ZEND_PUTS(value);
946 		}
947 	} else {
948 		if (zend_uv.html_errors) {
949 			ZEND_PUTS(NO_VALUE_HTML);
950 		} else {
951 			ZEND_PUTS(NO_VALUE_PLAINTEXT);
952 		}
953 	}
954 }
955 /* }}} */
956 
ZEND_INI_DISP(display_link_numbers)957 ZEND_INI_DISP(display_link_numbers) /* {{{ */
958 {
959 	char *value;
960 
961 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
962 		value = ZSTR_VAL(ini_entry->orig_value);
963 	} else if (ini_entry->value) {
964 		value = ZSTR_VAL(ini_entry->value);
965 	} else {
966 		value = NULL;
967 	}
968 
969 	if (value) {
970 		if (atoi(value) == -1) {
971 			ZEND_PUTS("Unlimited");
972 		} else {
973 			zend_printf("%s", value);
974 		}
975 	}
976 }
977 /* }}} */
978 
979 /* Standard message handlers */
ZEND_INI_MH(OnUpdateBool)980 ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
981 {
982 	bool *p = (bool *) ZEND_INI_GET_ADDR();
983 	*p = zend_ini_parse_bool(new_value);
984 	return SUCCESS;
985 }
986 /* }}} */
987 
ZEND_INI_MH(OnUpdateLong)988 ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
989 {
990 	zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
991 	*p = zend_ini_parse_quantity_warn(new_value, entry->name);
992 	return SUCCESS;
993 }
994 /* }}} */
995 
ZEND_INI_MH(OnUpdateLongGEZero)996 ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
997 {
998 	zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
999 	if (tmp < 0) {
1000 		return FAILURE;
1001 	}
1002 
1003 	zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
1004 	*p = tmp;
1005 
1006 	return SUCCESS;
1007 }
1008 /* }}} */
1009 
ZEND_INI_MH(OnUpdateReal)1010 ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
1011 {
1012 	double *p = (double *) ZEND_INI_GET_ADDR();
1013 	*p = zend_strtod(ZSTR_VAL(new_value), NULL);
1014 	return SUCCESS;
1015 }
1016 /* }}} */
1017 
ZEND_INI_MH(OnUpdateString)1018 ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
1019 {
1020 	char **p = (char **) ZEND_INI_GET_ADDR();
1021 	*p = new_value ? ZSTR_VAL(new_value) : NULL;
1022 	return SUCCESS;
1023 }
1024 /* }}} */
1025 
ZEND_INI_MH(OnUpdateStringUnempty)1026 ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1027 {
1028 	if (new_value && !ZSTR_VAL(new_value)[0]) {
1029 		return FAILURE;
1030 	}
1031 
1032 	char **p = (char **) ZEND_INI_GET_ADDR();
1033 	*p = new_value ? ZSTR_VAL(new_value) : NULL;
1034 	return SUCCESS;
1035 }
1036 /* }}} */
1037 
ZEND_INI_MH(OnUpdateStr)1038 ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1039 {
1040 	zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1041 	*p = new_value;
1042 	return SUCCESS;
1043 }
1044 /* }}} */
1045 
ZEND_INI_MH(OnUpdateStrNotEmpty)1046 ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1047 {
1048 	if (new_value && ZSTR_LEN(new_value) == 0) {
1049 		return FAILURE;
1050 	}
1051 
1052 	zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1053 	*p = new_value;
1054 	return SUCCESS;
1055 }
1056 /* }}} */
1057