Coverage Report

Created: 2026-06-13 07:01

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.56k
static inline bool zend_is_whitespace(char c) {
35
4.56k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
4.56k
}
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
58.9k
{
52
58.9k
  zend_result result = FAILURE;
53
54
58.9k
  if (ini_entry->modified) {
55
58.9k
    if (ini_entry->on_modify) {
56
58.9k
      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
58.9k
        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
58.9k
      } zend_end_try();
62
58.9k
    }
63
58.9k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
58.9k
    if (ini_entry->value != ini_entry->orig_value) {
68
701
      zend_string_release(ini_entry->value);
69
701
    }
70
58.9k
    ini_entry->value = ini_entry->orig_value;
71
58.9k
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
58.9k
    ini_entry->modified = false;
73
58.9k
    ini_entry->orig_value = NULL;
74
58.9k
    ini_entry->orig_modifiable = false;
75
58.9k
  }
76
58.9k
  return SUCCESS;
77
58.9k
}
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
229k
{
131
229k
  if (EG(modified_ini_directives)) {
132
58.3k
    zend_ini_entry *ini_entry;
133
134
234k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
234k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
234k
    } ZEND_HASH_FOREACH_END();
137
58.3k
    zend_hash_destroy(EG(modified_ini_directives));
138
58.3k
    FREE_HASHTABLE(EG(modified_ini_directives));
139
58.3k
    EG(modified_ini_directives) = NULL;
140
58.3k
  }
141
229k
}
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
4.06k
{
177
4.06k
  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
4.06k
  } else if (!f->key) { /* f is numeric, s is not */
185
0
    return -1;
186
4.06k
  } else if (!s->key) { /* s is numeric, f is not */
187
0
    return 1;
188
4.06k
  } else { /* both strings */
189
4.06k
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
190
4.06k
  }
191
4.06k
}
192
/* }}} */
193
194
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
195
4
{
196
4
  zend_hash_sort(EG(ini_directives), ini_key_compare, false);
197
4
}
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
58.1k
{
340
58.1k
  zend_result ret;
341
58.1k
  zend_string *new_value;
342
343
58.1k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
58.1k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
58.1k
  zend_string_release(new_value);
346
58.1k
  return ret;
347
58.1k
}
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
59.2k
{
364
59.2k
  zend_ini_entry *ini_entry;
365
59.2k
  zend_string *duplicate;
366
59.2k
  uint8_t modifiable;
367
59.2k
  bool modified;
368
369
59.2k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
561
    return FAILURE;
371
561
  }
372
373
58.6k
  modifiable = ini_entry->modifiable;
374
58.6k
  modified = ini_entry->modified;
375
376
58.6k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
58.6k
  if (!force_change) {
381
58.6k
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
58.6k
  }
385
386
58.6k
  if (!EG(modified_ini_directives)) {
387
58.2k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
58.2k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
58.2k
  }
390
58.6k
  if (!modified) {
391
58.4k
    ini_entry->orig_value = ini_entry->value;
392
58.4k
    ini_entry->orig_modifiable = modifiable;
393
58.4k
    ini_entry->modified = true;
394
58.4k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
58.4k
  }
396
397
58.6k
  zend_string *prev_value = ini_entry->value;
398
58.6k
  duplicate = zend_string_copy(new_value);
399
400
58.6k
  if (!ini_entry->on_modify
401
58.6k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
465
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
122
      zend_string_release(prev_value);
404
122
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
465
    if (ini_entry->value == prev_value) {
407
465
      ini_entry->value = duplicate;
408
465
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
58.1k
  } else {
412
58.1k
    zend_string_release(duplicate);
413
58.1k
    return FAILURE;
414
58.1k
  }
415
416
465
  return SUCCESS;
417
58.6k
}
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.08k
{
461
1.08k
  zend_ini_entry *ini_entry;
462
463
1.08k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
1.08k
  if (ini_entry) {
465
1.08k
    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.08k
    } else {
468
1.08k
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
1.08k
    }
470
1.08k
  }
471
472
0
  return 0;
473
1.08k
}
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
128k
{
503
128k
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
128k
  return str ? ZSTR_VAL(str) : NULL;
506
128k
}
507
/* }}} */
508
509
510
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
511
129k
{
512
129k
  zend_ini_entry *ini_entry;
513
514
129k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
129k
  if (ini_entry) {
516
129k
    if (exists) {
517
129k
      *exists = true;
518
129k
    }
519
520
129k
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
129k
    } else {
523
129k
      return ini_entry->value ? ini_entry->value : NULL;
524
129k
    }
525
129k
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
129k
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
129k
{
536
129k
  bool exists = true;
537
129k
  zend_string *return_value;
538
539
129k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
129k
  if (!exists) {
541
0
    return NULL;
542
129k
  } else if (!return_value) {
543
1.12k
    return_value = ZSTR_EMPTY_ALLOC();
544
1.12k
  }
545
129k
  return return_value;
546
129k
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
1.05k
{
551
1.05k
  zend_ini_entry *ini_entry;
552
553
1.05k
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
1.05k
  if (ini_entry) {
555
450
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
603
  } else {
557
603
    return NULL;
558
603
  }
559
1.05k
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
1.45k
{
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.45k
  if (UNEXPECTED(str == NULL)) {
567
0
    return false;
568
0
  }
569
1.45k
  if (zend_string_equals_literal_ci(str, "true")
570
1.45k
      || zend_string_equals_literal_ci(str, "yes")
571
1.45k
      || zend_string_equals_literal_ci(str, "on")
572
1.45k
  ) {
573
0
    return true;
574
1.45k
  } else {
575
1.45k
    return atoi(ZSTR_VAL(str)) != 0;
576
1.45k
  }
577
1.45k
}
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
387
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
585
387
  const char *digits_consumed = digits;
586
  /* Ignore leading whitespace. */
587
490
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
588
387
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
589
153
    ++digits_consumed;
590
153
  }
591
592
387
  if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
593
    /* Value is just 0 */
594
319
    if ((digits_consumed+1) == str_end) {
595
222
      return digits_consumed;
596
222
    }
597
598
97
    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
23
      case 'b':
606
54
      case 'B':
607
54
        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
54
        break;
612
97
    }
613
97
  }
614
165
  return digits_consumed;
615
387
}
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.49k
{
619
1.49k
  char *digits_end = NULL;
620
1.49k
  const char *str = ZSTR_VAL(value);
621
1.49k
  const char *str_end = &str[ZSTR_LEN(value)];
622
1.49k
  const char *digits = str;
623
1.49k
  bool overflow = false;
624
1.49k
  zend_ulong factor;
625
1.49k
  smart_str invalid = {0};
626
1.49k
  smart_str interpreted = {0};
627
1.49k
  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.56k
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
632
633
  /* Ignore trailing whitespace */
634
1.82k
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
635
636
1.49k
  if (digits == str_end) {
637
9
    *errstr = NULL;
638
9
    return 0;
639
9
  }
640
641
1.49k
  bool is_negative = false;
642
1.48k
  if (digits[0] == '+') {
643
3
    ++digits;
644
1.48k
  } else if (digits[0] == '-') {
645
111
    is_negative = true;
646
111
    ++digits;
647
111
  }
648
649
  /* if there is no digit after +/- */
650
1.48k
  if (!isdigit((unsigned char)digits[0])) {
651
    /* Escape the string to avoid null bytes and to make non-printable chars
652
     * visible */
653
23
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
654
23
    smart_str_0(&invalid);
655
656
23
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
657
23
            ZSTR_VAL(invalid.s));
658
659
23
    smart_str_free(&invalid);
660
23
    return 0;
661
23
  }
662
663
1.46k
  int base = 0;
664
1.46k
  if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) {
665
    /* Value is just 0 */
666
576
    if ((digits+1) == str_end) {
667
180
      *errstr = NULL;
668
180
      return 0;
669
180
    }
670
671
396
    switch (digits[1]) {
672
      /* Multiplier suffixes */
673
0
      case 'g':
674
0
      case 'G':
675
0
      case 'm':
676
0
      case 'M':
677
1
      case 'k':
678
1
      case 'K':
679
1
        goto evaluation;
680
200
      case 'x':
681
200
      case 'X':
682
200
        base = 16;
683
200
        break;
684
55
      case 'o':
685
55
      case 'O':
686
55
        base = 8;
687
55
        break;
688
132
      case 'b':
689
132
      case 'B':
690
132
        base = 2;
691
132
        break;
692
8
      default:
693
8
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
694
8
          digits[1]);
695
8
        return 0;
696
396
        }
697
387
        digits += 2;
698
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
699
387
    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
256
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
703
256
      smart_str_0(&invalid);
704
705
256
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
706
256
              ZSTR_VAL(invalid.s));
707
708
256
      smart_str_free(&invalid);
709
256
      return 0;
710
256
    }
711
387
  }
712
1.01k
  evaluation:
713
714
1.01k
  errno = 0;
715
1.01k
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
716
717
1.01k
  if (errno == ERANGE) {
718
5
    overflow = true;
719
1.01k
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
720
220
    if (is_negative) {
721
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
722
17
      if (retval == 1 && digits_end == str_end) {
723
16
        retval = -1;
724
16
      } else {
725
1
        overflow = true;
726
1
      }
727
17
    }
728
794
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
729
    /* Handle PHP_INT_MIN case */
730
794
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
731
0
      retval = 0u - retval;
732
794
    } else if ((zend_long) retval < 0) {
733
0
      overflow = true;
734
794
    } else if (is_negative) {
735
94
      retval = 0u - retval;
736
94
    }
737
794
  }
738
739
1.01k
  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
4
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
745
4
    smart_str_0(&invalid);
746
747
4
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
748
4
            ZSTR_VAL(invalid.s));
749
750
4
    smart_str_free(&invalid);
751
4
    return 0;
752
4
  }
753
754
  /* Allow for whitespace between integer portion and any suffix character */
755
1.42k
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
756
757
  /* No exponent suffix. */
758
1.01k
  if (digits_end == str_end) {
759
722
    goto end;
760
722
  }
761
762
293
  switch (*(str_end-1)) {
763
2
    case 'g':
764
4
    case 'G':
765
4
      factor = 1<<30;
766
4
      break;
767
5
    case 'm':
768
225
    case 'M':
769
225
      factor = 1<<20;
770
225
      break;
771
0
    case 'k':
772
16
    case 'K':
773
16
      factor = 1<<10;
774
16
      break;
775
48
    default:
776
      /* Unknown suffix */
777
48
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
778
48
      smart_str_0(&invalid);
779
48
      smart_str_append_escaped(&interpreted, str, digits_end - str);
780
48
      smart_str_0(&interpreted);
781
48
      smart_str_append_escaped(&chr, str_end-1, 1);
782
48
      smart_str_0(&chr);
783
784
48
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
785
48
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
786
787
48
      smart_str_free(&invalid);
788
48
      smart_str_free(&interpreted);
789
48
      smart_str_free(&chr);
790
791
48
      return retval;
792
293
  }
793
794
245
  if (!overflow) {
795
240
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
796
66
      zend_long sretval = (zend_long)retval;
797
66
      if (sretval > 0) {
798
66
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
799
66
      } else {
800
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
801
0
      }
802
174
    } else {
803
174
      overflow = retval > ZEND_ULONG_MAX / factor;
804
174
    }
805
240
  }
806
807
245
  retval *= factor;
808
809
245
  if (UNEXPECTED(digits_end != str_end-1)) {
810
    /* More than one character in suffix */
811
24
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
812
24
    smart_str_0(&invalid);
813
24
    smart_str_append_escaped(&interpreted, str, digits_end - str);
814
24
    smart_str_0(&interpreted);
815
24
    smart_str_append_escaped(&chr, str_end-1, 1);
816
24
    smart_str_0(&chr);
817
818
24
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
819
24
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
820
821
24
    smart_str_free(&invalid);
822
24
    smart_str_free(&interpreted);
823
24
    smart_str_free(&chr);
824
825
24
    return retval;
826
24
  }
827
828
943
end:
829
943
  if (UNEXPECTED(overflow)) {
830
8
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
831
8
    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
8
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
837
8
            ZSTR_VAL(invalid.s));
838
839
8
    smart_str_free(&invalid);
840
8
    smart_str_free(&interpreted);
841
8
    smart_str_free(&chr);
842
843
8
    return retval;
844
8
  }
845
846
935
  *errstr = NULL;
847
935
  return retval;
848
943
}
849
/* }}} */
850
851
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
852
1.23k
{
853
1.23k
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
854
1.23k
}
855
/* }}} */
856
857
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
858
256
{
859
256
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
860
256
}
861
/* }}} */
862
863
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
864
831
{
865
831
  zend_string *errstr;
866
831
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
867
868
831
  if (errstr) {
869
0
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
870
0
    zend_string_release(errstr);
871
0
  }
872
873
831
  return retval;
874
831
}
875
/* }}} */
876
877
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
878
256
{
879
256
  zend_string *errstr;
880
256
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
881
882
256
  if (errstr) {
883
68
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
884
68
    zend_string_release(errstr);
885
68
  }
886
887
256
  return retval;
888
256
}
889
/* }}} */
890
891
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
892
384
{
893
384
  bool value;
894
895
384
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
896
0
    value = zend_ini_parse_bool(ini_entry->orig_value);
897
384
  } else if (ini_entry->value) {
898
384
    value = zend_ini_parse_bool(ini_entry->value);
899
384
  } else {
900
0
    value = false;
901
0
  }
902
903
384
  if (value) {
904
160
    ZEND_PUTS("On");
905
224
  } else {
906
224
    ZEND_PUTS("Off");
907
224
  }
908
384
}
909
/* }}} */
910
911
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
912
40
{
913
40
  const char *value;
914
915
40
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
916
0
    value = ZSTR_VAL(ini_entry->orig_value);
917
40
  } else if (ini_entry->value) {
918
40
    value = ZSTR_VAL(ini_entry->value);
919
40
  } else {
920
0
    value = NULL;
921
0
  }
922
40
  if (value) {
923
40
    if (zend_uv.html_errors) {
924
40
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
925
40
    } else {
926
0
      ZEND_PUTS(value);
927
0
    }
928
40
  } 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
40
}
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
704
{
963
704
  bool *p = ZEND_INI_GET_ADDR();
964
704
  *p = zend_ini_parse_bool(new_value);
965
704
  return SUCCESS;
966
704
}
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
512
{
1001
512
  char **p = ZEND_INI_GET_ADDR();
1002
512
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1003
512
  return SUCCESS;
1004
512
}
1005
/* }}} */
1006
1007
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1008
252
{
1009
252
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1010
48
    return FAILURE;
1011
48
  }
1012
1013
204
  char **p = ZEND_INI_GET_ADDR();
1014
204
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1015
204
  return SUCCESS;
1016
252
}
1017
/* }}} */
1018
1019
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1020
16
{
1021
16
  zend_string **p = ZEND_INI_GET_ADDR();
1022
16
  *p = new_value;
1023
16
  return SUCCESS;
1024
16
}
1025
/* }}} */
1026
1027
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1028
42
{
1029
42
  if (new_value && ZSTR_LEN(new_value) == 0) {
1030
4
    return FAILURE;
1031
4
  }
1032
1033
38
  zend_string **p = ZEND_INI_GET_ADDR();
1034
38
  *p = new_value;
1035
38
  return SUCCESS;
1036
42
}
1037
/* }}} */