Coverage Report

Created: 2026-07-25 06:39

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.33k
static inline bool zend_is_whitespace(char c) {
35
4.33k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
4.33k
}
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
75.9k
{
52
75.9k
  zend_result result = FAILURE;
53
54
75.9k
  if (ini_entry->modified) {
55
75.9k
    if (ini_entry->on_modify) {
56
75.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
75.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
75.9k
      } zend_end_try();
62
75.9k
    }
63
75.9k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
75.9k
    if (ini_entry->value != ini_entry->orig_value) {
68
745
      zend_string_release(ini_entry->value);
69
745
    }
70
75.9k
    ini_entry->value = ini_entry->orig_value;
71
75.9k
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
75.9k
    ini_entry->modified = false;
73
75.9k
    ini_entry->orig_value = NULL;
74
75.9k
    ini_entry->orig_modifiable = false;
75
75.9k
  }
76
75.9k
  return SUCCESS;
77
75.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
300k
{
131
300k
  if (EG(modified_ini_directives)) {
132
75.3k
    zend_ini_entry *ini_entry;
133
134
302k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
302k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
302k
    } ZEND_HASH_FOREACH_END();
137
75.3k
    zend_hash_destroy(EG(modified_ini_directives));
138
75.3k
    FREE_HASHTABLE(EG(modified_ini_directives));
139
75.3k
    EG(modified_ini_directives) = NULL;
140
75.3k
  }
141
300k
}
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
75.2k
{
340
75.2k
  zend_result ret;
341
75.2k
  zend_string *new_value;
342
343
75.2k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
75.2k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
75.2k
  zend_string_release(new_value);
346
75.2k
  return ret;
347
75.2k
}
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
76.4k
{
364
76.4k
  zend_ini_entry *ini_entry;
365
76.4k
  zend_string *duplicate;
366
76.4k
  uint8_t modifiable;
367
76.4k
  bool modified;
368
369
76.4k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
578
    return FAILURE;
371
578
  }
372
373
75.8k
  modifiable = ini_entry->modifiable;
374
75.8k
  modified = ini_entry->modified;
375
376
75.8k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
75.8k
  if (!force_change) {
381
75.8k
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
75.8k
  }
385
386
75.8k
  if (!EG(modified_ini_directives)) {
387
75.2k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
75.2k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
75.2k
  }
390
75.8k
  if (!modified) {
391
75.5k
    ini_entry->orig_value = ini_entry->value;
392
75.5k
    ini_entry->orig_modifiable = modifiable;
393
75.5k
    ini_entry->modified = true;
394
75.5k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
75.5k
  }
396
397
75.8k
  zend_string *prev_value = ini_entry->value;
398
75.8k
  duplicate = zend_string_copy(new_value);
399
400
75.8k
  if (!ini_entry->on_modify
401
75.8k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
655
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
250
      zend_string_release(prev_value);
404
250
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
655
    if (ini_entry->value == prev_value) {
407
655
      ini_entry->value = duplicate;
408
655
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
75.2k
  } else {
412
75.2k
    zend_string_release(duplicate);
413
75.2k
    return FAILURE;
414
75.2k
  }
415
416
655
  return SUCCESS;
417
75.8k
}
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.11k
{
461
1.11k
  zend_ini_entry *ini_entry;
462
463
1.11k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
1.11k
  if (ini_entry) {
465
1.11k
    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.11k
    } else {
468
1.11k
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
1.11k
    }
470
1.11k
  }
471
472
0
  return 0;
473
1.11k
}
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
107k
{
503
107k
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
107k
  return str ? ZSTR_VAL(str) : NULL;
506
107k
}
507
/* }}} */
508
509
510
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
511
108k
{
512
108k
  zend_ini_entry *ini_entry;
513
514
108k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
108k
  if (ini_entry) {
516
108k
    if (exists) {
517
108k
      *exists = true;
518
108k
    }
519
520
108k
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
108k
    } else {
523
108k
      return ini_entry->value ? ini_entry->value : NULL;
524
108k
    }
525
108k
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
108k
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
108k
{
536
108k
  bool exists = true;
537
108k
  zend_string *return_value;
538
539
108k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
108k
  if (!exists) {
541
0
    return NULL;
542
108k
  } else if (!return_value) {
543
1.13k
    return_value = ZSTR_EMPTY_ALLOC();
544
1.13k
  }
545
108k
  return return_value;
546
108k
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
1.12k
{
551
1.12k
  zend_ini_entry *ini_entry;
552
553
1.12k
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
1.12k
  if (ini_entry) {
555
511
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
613
  } else {
557
613
    return NULL;
558
613
  }
559
1.12k
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
1.58k
{
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.58k
  if (UNEXPECTED(str == NULL)) {
567
0
    return false;
568
0
  }
569
1.58k
  if (zend_string_equals_literal_ci(str, "true")
570
1.58k
      || zend_string_equals_literal_ci(str, "yes")
571
1.58k
      || zend_string_equals_literal_ci(str, "on")
572
1.58k
  ) {
573
0
    return true;
574
1.58k
  } else {
575
1.58k
    return atoi(ZSTR_VAL(str)) != 0;
576
1.58k
  }
577
1.58k
}
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
373
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
585
373
  const char *digits_consumed = digits;
586
  /* Ignore leading whitespace. */
587
472
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
588
373
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
589
158
    ++digits_consumed;
590
158
  }
591
592
373
  if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
593
    /* Value is just 0 */
594
308
    if ((digits_consumed+1) == str_end) {
595
220
      return digits_consumed;
596
220
    }
597
598
88
    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
17
      case 'b':
606
45
      case 'B':
607
45
        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
45
        break;
612
88
    }
613
88
  }
614
153
  return digits_consumed;
615
373
}
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.58k
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
632
633
  /* Ignore trailing whitespace */
634
1.73k
  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.49k
  if (digits[0] == '+') {
643
2
    ++digits;
644
1.48k
  } else if (digits[0] == '-') {
645
101
    is_negative = true;
646
101
    ++digits;
647
101
  }
648
649
  /* if there is no digit after +/- */
650
1.49k
  if (!isdigit((unsigned char)digits[0])) {
651
    /* Escape the string to avoid null bytes and to make non-printable chars
652
     * visible */
653
24
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
654
24
    smart_str_0(&invalid);
655
656
24
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
657
24
            ZSTR_VAL(invalid.s));
658
659
24
    smart_str_free(&invalid);
660
24
    return 0;
661
24
  }
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
563
    if ((digits+1) == str_end) {
667
179
      *errstr = NULL;
668
179
      return 0;
669
179
    }
670
671
384
    switch (digits[1]) {
672
      /* Multiplier suffixes */
673
0
      case 'g':
674
0
      case 'G':
675
0
      case 'm':
676
0
      case 'M':
677
0
      case 'k':
678
0
      case 'K':
679
0
        goto evaluation;
680
191
      case 'x':
681
191
      case 'X':
682
191
        base = 16;
683
191
        break;
684
52
      case 'o':
685
52
      case 'O':
686
52
        base = 8;
687
52
        break;
688
130
      case 'b':
689
130
      case 'B':
690
130
        base = 2;
691
130
        break;
692
11
      default:
693
11
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
694
11
          digits[1]);
695
11
        return 0;
696
384
        }
697
373
        digits += 2;
698
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
699
373
    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
257
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
703
257
      smart_str_0(&invalid);
704
705
257
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
706
257
              ZSTR_VAL(invalid.s));
707
708
257
      smart_str_free(&invalid);
709
257
      return 0;
710
257
    }
711
373
  }
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
6
    overflow = true;
719
1.01k
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
720
232
    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
781
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
729
    /* Handle PHP_INT_MIN case */
730
781
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
731
0
      retval = 0u - retval;
732
781
    } else if ((zend_long) retval < 0) {
733
2
      overflow = true;
734
779
    } else if (is_negative) {
735
84
      retval = 0u - retval;
736
84
    }
737
781
  }
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
3
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
745
3
    smart_str_0(&invalid);
746
747
3
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
748
3
            ZSTR_VAL(invalid.s));
749
750
3
    smart_str_free(&invalid);
751
3
    return 0;
752
3
  }
753
754
  /* Allow for whitespace between integer portion and any suffix character */
755
1.27k
  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
710
    goto end;
760
710
  }
761
762
306
  switch (*(str_end-1)) {
763
2
    case 'g':
764
2
    case 'G':
765
2
      factor = 1<<30;
766
2
      break;
767
7
    case 'm':
768
235
    case 'M':
769
235
      factor = 1<<20;
770
235
      break;
771
0
    case 'k':
772
16
    case 'K':
773
16
      factor = 1<<10;
774
16
      break;
775
53
    default:
776
      /* Unknown suffix */
777
53
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
778
53
      smart_str_0(&invalid);
779
53
      smart_str_append_escaped(&interpreted, str, digits_end - str);
780
53
      smart_str_0(&interpreted);
781
53
      smart_str_append_escaped(&chr, str_end-1, 1);
782
53
      smart_str_0(&chr);
783
784
53
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
785
53
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
786
787
53
      smart_str_free(&invalid);
788
53
      smart_str_free(&interpreted);
789
53
      smart_str_free(&chr);
790
791
53
      return retval;
792
306
  }
793
794
253
  if (!overflow) {
795
248
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
796
64
      zend_long sretval = (zend_long)retval;
797
64
      if (sretval > 0) {
798
64
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
799
64
      } else {
800
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
801
0
      }
802
184
    } else {
803
184
      overflow = retval > ZEND_ULONG_MAX / factor;
804
184
    }
805
248
  }
806
807
253
  retval *= factor;
808
809
253
  if (UNEXPECTED(digits_end != str_end-1)) {
810
    /* More than one character in suffix */
811
23
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
812
23
    smart_str_0(&invalid);
813
23
    smart_str_append_escaped(&interpreted, str, digits_end - str);
814
23
    smart_str_0(&interpreted);
815
23
    smart_str_append_escaped(&chr, str_end-1, 1);
816
23
    smart_str_0(&chr);
817
818
23
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
819
23
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
820
821
23
    smart_str_free(&invalid);
822
23
    smart_str_free(&interpreted);
823
23
    smart_str_free(&chr);
824
825
23
    return retval;
826
23
  }
827
828
940
end:
829
940
  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
931
  *errstr = NULL;
847
931
  return retval;
848
940
}
849
/* }}} */
850
851
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
852
1.22k
{
853
1.22k
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
854
1.22k
}
855
/* }}} */
856
857
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
858
270
{
859
270
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
860
270
}
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
4
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
870
4
    zend_string_release(errstr);
871
4
  }
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
270
{
879
270
  zend_string *errstr;
880
270
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
881
882
270
  if (errstr) {
883
69
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
884
69
    zend_string_release(errstr);
885
69
  }
886
887
270
  return retval;
888
270
}
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
712
{
963
712
  bool *p = ZEND_INI_GET_ADDR();
964
712
  *p = zend_ini_parse_bool(new_value);
965
712
  return SUCCESS;
966
712
}
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
480
{
1001
480
  char **p = ZEND_INI_GET_ADDR();
1002
480
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1003
480
  return SUCCESS;
1004
480
}
1005
/* }}} */
1006
1007
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1008
298
{
1009
298
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1010
48
    return FAILURE;
1011
48
  }
1012
1013
250
  char **p = ZEND_INI_GET_ADDR();
1014
250
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1015
250
  return SUCCESS;
1016
298
}
1017
/* }}} */
1018
1019
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1020
48
{
1021
48
  zend_string **p = ZEND_INI_GET_ADDR();
1022
48
  *p = new_value;
1023
48
  return SUCCESS;
1024
48
}
1025
/* }}} */
1026
1027
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1028
64
{
1029
64
  if (new_value && ZSTR_LEN(new_value) == 0) {
1030
7
    return FAILURE;
1031
7
  }
1032
1033
57
  zend_string **p = ZEND_INI_GET_ADDR();
1034
57
  *p = new_value;
1035
57
  return SUCCESS;
1036
64
}
1037
/* }}} */