Coverage Report

Created: 2026-09-14 06:25

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