Coverage Report

Created: 2025-07-23 06:33

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