xref: /php-src/Zend/zend_ini.c (revision f6f1f7c1)
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 			zend_unregister_ini_entries_ex(module_number, module_type);
247 			return FAILURE;
248 		}
249 		if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
250 		    (!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)) {
251 
252 			p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
253 		} else {
254 			p->value = ini_entry->value ?
255 				zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
256 
257 			if (p->on_modify) {
258 				p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
259 			}
260 		}
261 		ini_entry++;
262 	}
263 	return SUCCESS;
264 }
265 /* }}} */
266 
zend_register_ini_entries(const zend_ini_entry_def * ini_entry,int module_number)267 ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
268 {
269 	zend_module_entry *module;
270 
271 	/* Module is likely to be the last one in the list */
272 	ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
273 		if (module->module_number == module_number) {
274 			return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
275 		}
276 	} ZEND_HASH_FOREACH_END();
277 
278 	return FAILURE;
279 }
280 /* }}} */
281 
zend_unregister_ini_entries_ex(int module_number,int module_type)282 ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
283 {
284 	static HashTable *ini_directives;
285 
286 	if (module_type == MODULE_TEMPORARY) {
287 		ini_directives = EG(ini_directives);
288 	} else {
289 		ini_directives = registered_zend_ini_directives;
290 	}
291 
292 	zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
293 }
294 /* }}} */
295 
zend_unregister_ini_entries(int module_number)296 ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
297 {
298 	zend_module_entry *module;
299 
300 	/* Module is likely to be the last one in the list */
301 	ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
302 		if (module->module_number == module_number) {
303 			zend_unregister_ini_entries_ex(module_number, module->type);
304 			return;
305 		}
306 	} ZEND_HASH_FOREACH_END();
307 }
308 /* }}} */
309 
310 #ifdef ZTS
zend_ini_refresh_caches(int stage)311 ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
312 {
313 	zend_ini_entry *p;
314 
315 	ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
316 		if (p->on_modify) {
317 			p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
318 		}
319 	} ZEND_HASH_FOREACH_END();
320 }
321 /* }}} */
322 #endif
323 
zend_alter_ini_entry(zend_string * name,zend_string * new_value,int modify_type,int stage)324 ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
325 {
326 
327 	return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
328 }
329 /* }}} */
330 
zend_alter_ini_entry_chars(zend_string * name,const char * value,size_t value_length,int modify_type,int stage)331 ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
332 {
333 	zend_result ret;
334 	zend_string *new_value;
335 
336 	new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
337 	ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
338 	zend_string_release(new_value);
339 	return ret;
340 }
341 /* }}} */
342 
zend_alter_ini_entry_chars_ex(zend_string * name,const char * value,size_t value_length,int modify_type,int stage,int force_change)343 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) /* {{{ */
344 {
345 	zend_result ret;
346 	zend_string *new_value;
347 
348 	new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
349 	ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
350 	zend_string_release(new_value);
351 	return ret;
352 }
353 /* }}} */
354 
zend_alter_ini_entry_ex(zend_string * name,zend_string * new_value,int modify_type,int stage,bool force_change)355 ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
356 {
357 	zend_ini_entry *ini_entry;
358 	zend_string *duplicate;
359 	uint8_t modifiable;
360 	bool modified;
361 
362 	if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
363 		return FAILURE;
364 	}
365 
366 	modifiable = ini_entry->modifiable;
367 	modified = ini_entry->modified;
368 
369 	if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
370 		ini_entry->modifiable = ZEND_INI_SYSTEM;
371 	}
372 
373 	if (!force_change) {
374 		if (!(ini_entry->modifiable & modify_type)) {
375 			return FAILURE;
376 		}
377 	}
378 
379 	if (!EG(modified_ini_directives)) {
380 		ALLOC_HASHTABLE(EG(modified_ini_directives));
381 		zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
382 	}
383 	if (!modified) {
384 		ini_entry->orig_value = ini_entry->value;
385 		ini_entry->orig_modifiable = modifiable;
386 		ini_entry->modified = 1;
387 		zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
388 	}
389 
390 	duplicate = zend_string_copy(new_value);
391 
392 	if (!ini_entry->on_modify
393 		|| ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
394 		if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
395 			zend_string_release(ini_entry->value);
396 		}
397 		ini_entry->value = duplicate;
398 	} else {
399 		zend_string_release(duplicate);
400 		return FAILURE;
401 	}
402 
403 	return SUCCESS;
404 }
405 /* }}} */
406 
zend_restore_ini_entry(zend_string * name,int stage)407 ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
408 {
409 	zend_ini_entry *ini_entry;
410 
411 	if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
412 		(stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
413 		return FAILURE;
414 	}
415 
416 	if (EG(modified_ini_directives)) {
417 		if (zend_restore_ini_entry_cb(ini_entry, stage) == 0) {
418 			zend_hash_del(EG(modified_ini_directives), name);
419 		} else {
420 			return FAILURE;
421 		}
422 	}
423 
424 	return SUCCESS;
425 }
426 /* }}} */
427 
zend_ini_register_displayer(const char * name,uint32_t name_length,void (* displayer)(zend_ini_entry * ini_entry,int type))428 ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
429 {
430 	zend_ini_entry *ini_entry;
431 
432 	ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
433 	if (ini_entry == NULL) {
434 		return FAILURE;
435 	}
436 
437 	ini_entry->displayer = displayer;
438 	return SUCCESS;
439 }
440 /* }}} */
441 
442 /*
443  * Data retrieval
444  */
445 
zend_ini_long(const char * name,size_t name_length,int orig)446 ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig) /* {{{ */
447 {
448 	zend_ini_entry *ini_entry;
449 
450 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
451 	if (ini_entry) {
452 		if (orig && ini_entry->modified) {
453 			return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
454 		} else {
455 			return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
456 		}
457 	}
458 
459 	return 0;
460 }
461 /* }}} */
462 
zend_ini_double(const char * name,size_t name_length,int orig)463 ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) /* {{{ */
464 {
465 	zend_ini_entry *ini_entry;
466 
467 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
468 	if (ini_entry) {
469 		if (orig && ini_entry->modified) {
470 			return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
471 		} else {
472 			return (double) (ini_entry->value      ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL)      : 0.0);
473 		}
474 	}
475 
476 	return 0.0;
477 }
478 /* }}} */
479 
zend_ini_string_ex(const char * name,size_t name_length,int orig,bool * exists)480 ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
481 {
482 	zend_ini_entry *ini_entry;
483 
484 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
485 	if (ini_entry) {
486 		if (exists) {
487 			*exists = 1;
488 		}
489 
490 		if (orig && ini_entry->modified) {
491 			return ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : NULL;
492 		} else {
493 			return ini_entry->value ? ZSTR_VAL(ini_entry->value) : NULL;
494 		}
495 	} else {
496 		if (exists) {
497 			*exists = 0;
498 		}
499 		return NULL;
500 	}
501 }
502 /* }}} */
503 
zend_ini_string(const char * name,size_t name_length,int orig)504 ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
505 {
506 	bool exists = 1;
507 	char *return_value;
508 
509 	return_value = zend_ini_string_ex(name, name_length, orig, &exists);
510 	if (!exists) {
511 		return NULL;
512 	} else if (!return_value) {
513 		return_value = "";
514 	}
515 	return return_value;
516 }
517 /* }}} */
518 
519 
zend_ini_str_ex(const char * name,size_t name_length,bool orig,bool * exists)520 ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
521 {
522 	zend_ini_entry *ini_entry;
523 
524 	ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
525 	if (ini_entry) {
526 		if (exists) {
527 			*exists = 1;
528 		}
529 
530 		if (orig && ini_entry->modified) {
531 			return ini_entry->orig_value ? ini_entry->orig_value : NULL;
532 		} else {
533 			return ini_entry->value ? ini_entry->value : NULL;
534 		}
535 	} else {
536 		if (exists) {
537 			*exists = 0;
538 		}
539 		return NULL;
540 	}
541 }
542 /* }}} */
543 
zend_ini_str(const char * name,size_t name_length,bool orig)544 ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
545 {
546 	bool exists = 1;
547 	zend_string *return_value;
548 
549 	return_value = zend_ini_str_ex(name, name_length, orig, &exists);
550 	if (!exists) {
551 		return NULL;
552 	} else if (!return_value) {
553 		return_value = ZSTR_EMPTY_ALLOC();
554 	}
555 	return return_value;
556 }
557 /* }}} */
558 
zend_ini_get_value(zend_string * name)559 ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
560 {
561 	zend_ini_entry *ini_entry;
562 
563 	ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
564 	if (ini_entry) {
565 		return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
566 	} else {
567 		return NULL;
568 	}
569 }
570 /* }}} */
571 
zend_ini_parse_bool(zend_string * str)572 ZEND_API bool zend_ini_parse_bool(zend_string *str)
573 {
574 	if (zend_string_equals_literal_ci(str, "true")
575 			|| zend_string_equals_literal_ci(str, "yes")
576 			|| zend_string_equals_literal_ci(str, "on")
577 	) {
578 		return 1;
579 	} else {
580 		return atoi(ZSTR_VAL(str)) != 0;
581 	}
582 }
583 
584 typedef enum {
585 	ZEND_INI_PARSE_QUANTITY_SIGNED,
586 	ZEND_INI_PARSE_QUANTITY_UNSIGNED,
587 } zend_ini_parse_quantity_signed_result_t;
588 
zend_ini_consume_quantity_prefix(const char * const digits,const char * const str_end)589 static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end) {
590 	const char *digits_consumed = digits;
591 	/* Ignore leading whitespace. */
592 	while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
593 	if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
594 		++digits_consumed;
595 	}
596 
597 	if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
598 		/* Value is just 0 */
599 		if ((digits_consumed+1) == str_end) {
600 			return digits;
601 		}
602 
603 		switch (digits_consumed[1]) {
604 			case 'x':
605 			case 'X':
606 			case 'o':
607 			case 'O':
608 			case 'b':
609 			case 'B':
610 				digits_consumed += 2;
611 				break;
612 		}
613 	}
614 	return digits_consumed;
615 }
616 
zend_ini_parse_quantity_internal(zend_string * value,zend_ini_parse_quantity_signed_result_t signed_result,zend_string ** errstr)617 static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
618 {
619 	char *digits_end = NULL;
620 	char *str = ZSTR_VAL(value);
621 	char *str_end = &str[ZSTR_LEN(value)];
622 	char *digits = str;
623 	bool overflow = false;
624 	zend_ulong factor;
625 	smart_str invalid = {0};
626 	smart_str interpreted = {0};
627 	smart_str chr = {0};
628 
629 	/* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
630 	 * but we need the position of the first non-whitespace later. */
631 	while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
632 
633 	/* Ignore trailing whitespace */
634 	while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
635 
636 	if (digits == str_end) {
637 		*errstr = NULL;
638 		return 0;
639 	}
640 
641 	bool is_negative = false;
642 	if (digits[0] == '+') {
643 		++digits;
644 	} else if (digits[0] == '-') {
645 		is_negative = true;
646 		++digits;
647 	}
648 
649 	/* if there is no digit after +/- */
650 	if (!isdigit(digits[0])) {
651 		/* Escape the string to avoid null bytes and to make non-printable chars
652 		 * visible */
653 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
654 		smart_str_0(&invalid);
655 
656 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
657 						ZSTR_VAL(invalid.s));
658 
659 		smart_str_free(&invalid);
660 		return 0;
661 	}
662 
663 	int base = 0;
664 	if (digits[0] == '0' && !isdigit(digits[1])) {
665 		/* Value is just 0 */
666 		if ((digits+1) == str_end) {
667 			*errstr = NULL;
668 			return 0;
669 		}
670 
671 		switch (digits[1]) {
672 			/* Multiplier suffixes */
673 			case 'g':
674 			case 'G':
675 			case 'm':
676 			case 'M':
677 			case 'k':
678 			case 'K':
679 				goto evaluation;
680 			case 'x':
681 			case 'X':
682 				base = 16;
683 				break;
684 			case 'o':
685 			case 'O':
686 				base = 8;
687 				break;
688 			case 'b':
689 			case 'B':
690 				base = 2;
691 				break;
692 			default:
693 				*errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
694 					digits[1]);
695 				return 0;
696         }
697         digits += 2;
698 		if (UNEXPECTED(digits == str_end)) {
699 			/* Escape the string to avoid null bytes and to make non-printable chars
700 			 * visible */
701 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
702 			smart_str_0(&invalid);
703 
704 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
705 							ZSTR_VAL(invalid.s));
706 
707 			smart_str_free(&invalid);
708 			return 0;
709 		}
710 		if (UNEXPECTED(digits != zend_ini_consume_quantity_prefix(digits, str_end))) {
711 			/* Escape the string to avoid null bytes and to make non-printable chars
712 			 * visible */
713 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
714 			smart_str_0(&invalid);
715 
716 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
717 							ZSTR_VAL(invalid.s));
718 
719 			smart_str_free(&invalid);
720 			return 0;
721 		}
722 	}
723 	evaluation:
724 
725 	errno = 0;
726 	zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
727 
728 	if (errno == ERANGE) {
729 		overflow = true;
730 	} else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
731 		if (is_negative) {
732 			/* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
733 			if (retval == 1 && digits_end == str_end) {
734 				retval = -1;
735 			} else {
736 				overflow = true;
737 			}
738 		}
739 	} else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
740 		/* Handle PHP_INT_MIN case */
741 		if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
742 			retval = 0u - retval;
743 		} else if ((zend_long) retval < 0) {
744 			overflow = true;
745 		} else if (is_negative) {
746 			retval = 0u - retval;
747 		}
748 	}
749 
750 	if (UNEXPECTED(digits_end == digits)) {
751 		/* No leading digits */
752 
753 		/* Escape the string to avoid null bytes and to make non-printable chars
754 		 * visible */
755 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
756 		smart_str_0(&invalid);
757 
758 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
759 						ZSTR_VAL(invalid.s));
760 
761 		smart_str_free(&invalid);
762 		return 0;
763 	}
764 
765 	/* Allow for whitespace between integer portion and any suffix character */
766 	while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
767 
768 	/* No exponent suffix. */
769 	if (digits_end == str_end) {
770 		goto end;
771 	}
772 
773 	switch (*(str_end-1)) {
774 		case 'g':
775 		case 'G':
776 			factor = 1<<30;
777 			break;
778 		case 'm':
779 		case 'M':
780 			factor = 1<<20;
781 			break;
782 		case 'k':
783 		case 'K':
784 			factor = 1<<10;
785 			break;
786 		default:
787 			/* Unknown suffix */
788 			smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
789 			smart_str_0(&invalid);
790 			smart_str_append_escaped(&interpreted, str, digits_end - str);
791 			smart_str_0(&interpreted);
792 			smart_str_append_escaped(&chr, str_end-1, 1);
793 			smart_str_0(&chr);
794 
795 			*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
796 						ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
797 
798 			smart_str_free(&invalid);
799 			smart_str_free(&interpreted);
800 			smart_str_free(&chr);
801 
802 			return retval;
803 	}
804 
805 	if (!overflow) {
806 		if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
807 			zend_long sretval = (zend_long)retval;
808 			if (sretval > 0) {
809 				overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
810 			} else {
811 				overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
812 			}
813 		} else {
814 			overflow = retval > ZEND_ULONG_MAX / factor;
815 		}
816 	}
817 
818 	retval *= factor;
819 
820 	if (UNEXPECTED(digits_end != str_end-1)) {
821 		/* More than one character in suffix */
822 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
823 		smart_str_0(&invalid);
824 		smart_str_append_escaped(&interpreted, str, digits_end - str);
825 		smart_str_0(&interpreted);
826 		smart_str_append_escaped(&chr, str_end-1, 1);
827 		smart_str_0(&chr);
828 
829 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
830 						ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
831 
832 		smart_str_free(&invalid);
833 		smart_str_free(&interpreted);
834 		smart_str_free(&chr);
835 
836 		return retval;
837 	}
838 
839 end:
840 	if (UNEXPECTED(overflow)) {
841 		smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
842 		smart_str_0(&invalid);
843 
844 		/* Not specifying the resulting value here because the caller may make
845 		 * additional conversions. Not specifying the allowed range
846 		 * because the caller may do narrower range checks. */
847 		*errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
848 						ZSTR_VAL(invalid.s));
849 
850 		smart_str_free(&invalid);
851 		smart_str_free(&interpreted);
852 		smart_str_free(&chr);
853 
854 		return retval;
855 	}
856 
857 	*errstr = NULL;
858 	return retval;
859 }
860 /* }}} */
861 
zend_ini_parse_quantity(zend_string * value,zend_string ** errstr)862 ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
863 {
864 	return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
865 }
866 /* }}} */
867 
zend_ini_parse_uquantity(zend_string * value,zend_string ** errstr)868 ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
869 {
870 	return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
871 }
872 /* }}} */
873 
zend_ini_parse_quantity_warn(zend_string * value,zend_string * setting)874 ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
875 {
876 	zend_string *errstr;
877 	zend_long retval = zend_ini_parse_quantity(value, &errstr);
878 
879 	if (errstr) {
880 		zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
881 		zend_string_release(errstr);
882 	}
883 
884 	return retval;
885 }
886 /* }}} */
887 
zend_ini_parse_uquantity_warn(zend_string * value,zend_string * setting)888 ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
889 {
890 	zend_string *errstr;
891 	zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
892 
893 	if (errstr) {
894 		zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
895 		zend_string_release(errstr);
896 	}
897 
898 	return retval;
899 }
900 /* }}} */
901 
ZEND_INI_DISP(zend_ini_boolean_displayer_cb)902 ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
903 {
904 	int value;
905 	zend_string *tmp_value;
906 
907 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
908 		tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
909 	} else if (ini_entry->value) {
910 		tmp_value = ini_entry->value;
911 	} else {
912 		tmp_value = NULL;
913 	}
914 
915 	if (tmp_value) {
916 		value = zend_ini_parse_bool(tmp_value);
917 	} else {
918 		value = 0;
919 	}
920 
921 	if (value) {
922 		ZEND_PUTS("On");
923 	} else {
924 		ZEND_PUTS("Off");
925 	}
926 }
927 /* }}} */
928 
ZEND_INI_DISP(zend_ini_color_displayer_cb)929 ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
930 {
931 	char *value;
932 
933 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
934 		value = ZSTR_VAL(ini_entry->orig_value);
935 	} else if (ini_entry->value) {
936 		value = ZSTR_VAL(ini_entry->value);
937 	} else {
938 		value = NULL;
939 	}
940 	if (value) {
941 		if (zend_uv.html_errors) {
942 			zend_printf("<span style=\"color: %s\">%s</span>", value, value);
943 		} else {
944 			ZEND_PUTS(value);
945 		}
946 	} else {
947 		if (zend_uv.html_errors) {
948 			ZEND_PUTS(NO_VALUE_HTML);
949 		} else {
950 			ZEND_PUTS(NO_VALUE_PLAINTEXT);
951 		}
952 	}
953 }
954 /* }}} */
955 
ZEND_INI_DISP(display_link_numbers)956 ZEND_INI_DISP(display_link_numbers) /* {{{ */
957 {
958 	char *value;
959 
960 	if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
961 		value = ZSTR_VAL(ini_entry->orig_value);
962 	} else if (ini_entry->value) {
963 		value = ZSTR_VAL(ini_entry->value);
964 	} else {
965 		value = NULL;
966 	}
967 
968 	if (value) {
969 		if (atoi(value) == -1) {
970 			ZEND_PUTS("Unlimited");
971 		} else {
972 			zend_printf("%s", value);
973 		}
974 	}
975 }
976 /* }}} */
977 
978 /* Standard message handlers */
ZEND_INI_MH(OnUpdateBool)979 ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
980 {
981 	bool *p = (bool *) ZEND_INI_GET_ADDR();
982 	*p = zend_ini_parse_bool(new_value);
983 	return SUCCESS;
984 }
985 /* }}} */
986 
ZEND_INI_MH(OnUpdateLong)987 ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
988 {
989 	zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
990 	*p = zend_ini_parse_quantity_warn(new_value, entry->name);
991 	return SUCCESS;
992 }
993 /* }}} */
994 
ZEND_INI_MH(OnUpdateLongGEZero)995 ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
996 {
997 	zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
998 	if (tmp < 0) {
999 		return FAILURE;
1000 	}
1001 
1002 	zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
1003 	*p = tmp;
1004 
1005 	return SUCCESS;
1006 }
1007 /* }}} */
1008 
ZEND_INI_MH(OnUpdateReal)1009 ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
1010 {
1011 	double *p = (double *) ZEND_INI_GET_ADDR();
1012 	*p = zend_strtod(ZSTR_VAL(new_value), NULL);
1013 	return SUCCESS;
1014 }
1015 /* }}} */
1016 
ZEND_INI_MH(OnUpdateString)1017 ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
1018 {
1019 	char **p = (char **) ZEND_INI_GET_ADDR();
1020 	*p = new_value ? ZSTR_VAL(new_value) : NULL;
1021 	return SUCCESS;
1022 }
1023 /* }}} */
1024 
ZEND_INI_MH(OnUpdateStringUnempty)1025 ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1026 {
1027 	if (new_value && !ZSTR_VAL(new_value)[0]) {
1028 		return FAILURE;
1029 	}
1030 
1031 	char **p = (char **) ZEND_INI_GET_ADDR();
1032 	*p = new_value ? ZSTR_VAL(new_value) : NULL;
1033 	return SUCCESS;
1034 }
1035 /* }}} */
1036 
ZEND_INI_MH(OnUpdateStr)1037 ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1038 {
1039 	zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1040 	*p = new_value;
1041 	return SUCCESS;
1042 }
1043 /* }}} */
1044 
ZEND_INI_MH(OnUpdateStrNotEmpty)1045 ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1046 {
1047 	if (new_value && ZSTR_LEN(new_value) == 0) {
1048 		return FAILURE;
1049 	}
1050 
1051 	zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1052 	*p = new_value;
1053 	return SUCCESS;
1054 }
1055 /* }}} */
1056