Coverage Report

Created: 2026-06-02 06:37

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
196
static inline bool zend_is_whitespace(char c) {
35
196
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
196
}
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
0
{
52
0
  zend_result result = FAILURE;
53
54
0
  if (ini_entry->modified) {
55
0
    if (ini_entry->on_modify) {
56
0
      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
0
        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
0
      } zend_end_try();
62
0
    }
63
0
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
0
    if (ini_entry->value != ini_entry->orig_value) {
68
0
      zend_string_release(ini_entry->value);
69
0
    }
70
0
    ini_entry->value = ini_entry->orig_value;
71
0
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
0
    ini_entry->modified = false;
73
0
    ini_entry->orig_value = NULL;
74
0
    ini_entry->orig_modifiable = false;
75
0
  }
76
0
  return SUCCESS;
77
0
}
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
2
{
100
2
  registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable));
101
102
2
  EG(ini_directives) = registered_zend_ini_directives;
103
2
  EG(modified_ini_directives) = NULL;
104
2
  EG(error_reporting_ini_entry) = NULL;
105
2
  zend_hash_init(registered_zend_ini_directives, 128, NULL, free_ini_entry, true);
106
2
}
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
38.8k
{
131
38.8k
  if (EG(modified_ini_directives)) {
132
0
    zend_ini_entry *ini_entry;
133
134
0
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
0
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
0
    } ZEND_HASH_FOREACH_END();
137
0
    zend_hash_destroy(EG(modified_ini_directives));
138
0
    FREE_HASHTABLE(EG(modified_ini_directives));
139
0
    EG(modified_ini_directives) = NULL;
140
0
  }
141
38.8k
}
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
0
{
177
0
  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
0
  } else if (!f->key) { /* f is numeric, s is not */
185
0
    return -1;
186
0
  } else if (!s->key) { /* s is numeric, f is not */
187
0
    return 1;
188
0
  } else { /* both strings */
189
0
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
190
0
  }
191
0
}
192
/* }}} */
193
194
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
195
0
{
196
0
  zend_hash_sort(EG(ini_directives), ini_key_compare, false);
197
0
}
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
20
{
205
20
  zend_ini_entry *p;
206
20
  zval *default_value;
207
20
  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
378
  while (ini_entry->name) {
226
358
    p = pemalloc(sizeof(zend_ini_entry), true);
227
358
    p->def = ini_entry;
228
358
    p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, true);
229
358
    p->on_modify = ini_entry->on_modify;
230
358
    p->mh_arg1 = ini_entry->mh_arg1;
231
358
    p->mh_arg2 = ini_entry->mh_arg2;
232
358
    p->mh_arg3 = ini_entry->mh_arg3;
233
358
    p->value = NULL;
234
358
    p->orig_value = NULL;
235
358
    p->displayer = ini_entry->displayer;
236
358
    p->modifiable = ini_entry->modifiable;
237
238
358
    p->orig_modifiable = false;
239
358
    p->modified = false;
240
358
    p->module_number = module_number;
241
242
358
    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
358
    zend_string *prev_value = p->value;
252
253
358
    if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
254
18
        (!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
18
      if (p->value == prev_value) {
258
18
        p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
259
18
      }
260
340
    } else {
261
340
      p->value = ini_entry->value ?
262
340
        zend_string_init_interned(ini_entry->value, ini_entry->value_length, true) : NULL;
263
264
340
      if (p->on_modify) {
265
310
        p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
266
310
      }
267
340
    }
268
358
    ini_entry++;
269
358
  }
270
20
  return SUCCESS;
271
20
}
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
0
{
340
0
  zend_result ret;
341
0
  zend_string *new_value;
342
343
0
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
0
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
0
  zend_string_release(new_value);
346
0
  return ret;
347
0
}
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
0
{
364
0
  zend_ini_entry *ini_entry;
365
0
  zend_string *duplicate;
366
0
  uint8_t modifiable;
367
0
  bool modified;
368
369
0
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
0
    return FAILURE;
371
0
  }
372
373
0
  modifiable = ini_entry->modifiable;
374
0
  modified = ini_entry->modified;
375
376
0
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
0
  if (!force_change) {
381
0
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
0
  }
385
386
0
  if (!EG(modified_ini_directives)) {
387
0
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
0
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
0
  }
390
0
  if (!modified) {
391
0
    ini_entry->orig_value = ini_entry->value;
392
0
    ini_entry->orig_modifiable = modifiable;
393
0
    ini_entry->modified = true;
394
0
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
0
  }
396
397
0
  zend_string *prev_value = ini_entry->value;
398
0
  duplicate = zend_string_copy(new_value);
399
400
0
  if (!ini_entry->on_modify
401
0
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
0
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
0
      zend_string_release(prev_value);
404
0
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
0
    if (ini_entry->value == prev_value) {
407
0
      ini_entry->value = duplicate;
408
0
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
0
  } else {
412
0
    zend_string_release(duplicate);
413
0
    return FAILURE;
414
0
  }
415
416
0
  return SUCCESS;
417
0
}
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
0
{
461
0
  zend_ini_entry *ini_entry;
462
463
0
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
0
  if (ini_entry) {
465
0
    if (orig && ini_entry->modified) {
466
0
      return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
467
0
    } else {
468
0
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
0
    }
470
0
  }
471
472
0
  return 0;
473
0
}
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
4
{
503
4
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
4
  return str ? ZSTR_VAL(str) : NULL;
506
4
}
507
/* }}} */
508
509
510
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
511
4
{
512
4
  zend_ini_entry *ini_entry;
513
514
4
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
4
  if (ini_entry) {
516
4
    if (exists) {
517
4
      *exists = true;
518
4
    }
519
520
4
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
4
    } else {
523
4
      return ini_entry->value ? ini_entry->value : NULL;
524
4
    }
525
4
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
4
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
4
{
536
4
  bool exists = true;
537
4
  zend_string *return_value;
538
539
4
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
4
  if (!exists) {
541
0
    return NULL;
542
4
  } else if (!return_value) {
543
2
    return_value = ZSTR_EMPTY_ALLOC();
544
2
  }
545
4
  return return_value;
546
4
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
0
{
551
0
  zend_ini_entry *ini_entry;
552
553
0
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
0
  if (ini_entry) {
555
0
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
0
  } else {
557
0
    return NULL;
558
0
  }
559
0
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
100
{
564
100
  if (zend_string_equals_literal_ci(str, "true")
565
100
      || zend_string_equals_literal_ci(str, "yes")
566
100
      || zend_string_equals_literal_ci(str, "on")
567
100
  ) {
568
0
    return true;
569
100
  } else {
570
100
    return atoi(ZSTR_VAL(str)) != 0;
571
100
  }
572
100
}
573
574
typedef enum {
575
  ZEND_INI_PARSE_QUANTITY_SIGNED,
576
  ZEND_INI_PARSE_QUANTITY_UNSIGNED,
577
} zend_ini_parse_quantity_signed_result_t;
578
579
2
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
580
2
  const char *digits_consumed = digits;
581
  /* Ignore leading whitespace. */
582
2
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
583
2
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
584
0
    ++digits_consumed;
585
0
  }
586
587
2
  if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
588
    /* Value is just 0 */
589
0
    if ((digits_consumed+1) == str_end) {
590
0
      return digits_consumed;
591
0
    }
592
593
0
    switch (digits_consumed[1]) {
594
0
      case 'x':
595
0
      case 'X':
596
0
      case 'o':
597
0
      case 'O':
598
0
        digits_consumed += 2;
599
0
        break;
600
0
      case 'b':
601
0
      case 'B':
602
0
        if (base != 16) {
603
          /* 0b or 0B is valid in base 16, but not in the other supported bases. */
604
0
          digits_consumed += 2;
605
0
        }
606
0
        break;
607
0
    }
608
0
  }
609
2
  return digits_consumed;
610
2
}
611
612
static zend_ulong zend_ini_parse_quantity_internal(const zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
613
92
{
614
92
  char *digits_end = NULL;
615
92
  const char *str = ZSTR_VAL(value);
616
92
  const char *str_end = &str[ZSTR_LEN(value)];
617
92
  const char *digits = str;
618
92
  bool overflow = false;
619
92
  zend_ulong factor;
620
92
  smart_str invalid = {0};
621
92
  smart_str interpreted = {0};
622
92
  smart_str chr = {0};
623
624
  /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
625
   * but we need the position of the first non-whitespace later. */
626
92
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
627
628
  /* Ignore trailing whitespace */
629
92
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
630
631
92
  if (digits == str_end) {
632
0
    *errstr = NULL;
633
0
    return 0;
634
0
  }
635
636
92
  bool is_negative = false;
637
92
  if (digits[0] == '+') {
638
0
    ++digits;
639
92
  } else if (digits[0] == '-') {
640
4
    is_negative = true;
641
4
    ++digits;
642
4
  }
643
644
  /* if there is no digit after +/- */
645
92
  if (!isdigit((unsigned char)digits[0])) {
646
    /* Escape the string to avoid null bytes and to make non-printable chars
647
     * visible */
648
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
649
0
    smart_str_0(&invalid);
650
651
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
652
0
            ZSTR_VAL(invalid.s));
653
654
0
    smart_str_free(&invalid);
655
0
    return 0;
656
0
  }
657
658
92
  int base = 0;
659
92
  if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) {
660
    /* Value is just 0 */
661
18
    if ((digits+1) == str_end) {
662
16
      *errstr = NULL;
663
16
      return 0;
664
16
    }
665
666
2
    switch (digits[1]) {
667
      /* Multiplier suffixes */
668
0
      case 'g':
669
0
      case 'G':
670
0
      case 'm':
671
0
      case 'M':
672
0
      case 'k':
673
0
      case 'K':
674
0
        goto evaluation;
675
2
      case 'x':
676
2
      case 'X':
677
2
        base = 16;
678
2
        break;
679
0
      case 'o':
680
0
      case 'O':
681
0
        base = 8;
682
0
        break;
683
0
      case 'b':
684
0
      case 'B':
685
0
        base = 2;
686
0
        break;
687
0
      default:
688
0
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
689
0
          digits[1]);
690
0
        return 0;
691
2
        }
692
2
        digits += 2;
693
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
694
2
    if (UNEXPECTED(digits == str_end || digits != zend_ini_consume_quantity_prefix(digits, str_end, base))) {
695
      /* Escape the string to avoid null bytes and to make non-printable chars
696
       * visible */
697
0
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
698
0
      smart_str_0(&invalid);
699
700
0
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
701
0
              ZSTR_VAL(invalid.s));
702
703
0
      smart_str_free(&invalid);
704
0
      return 0;
705
0
    }
706
2
  }
707
76
  evaluation:
708
709
76
  errno = 0;
710
76
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
711
712
76
  if (errno == ERANGE) {
713
0
    overflow = true;
714
76
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
715
4
    if (is_negative) {
716
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
717
2
      if (retval == 1 && digits_end == str_end) {
718
2
        retval = -1;
719
2
      } else {
720
0
        overflow = true;
721
0
      }
722
2
    }
723
72
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
724
    /* Handle PHP_INT_MIN case */
725
72
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
726
0
      retval = 0u - retval;
727
72
    } else if ((zend_long) retval < 0) {
728
0
      overflow = true;
729
72
    } else if (is_negative) {
730
2
      retval = 0u - retval;
731
2
    }
732
72
  }
733
734
76
  if (UNEXPECTED(digits_end == digits)) {
735
    /* No leading digits */
736
737
    /* Escape the string to avoid null bytes and to make non-printable chars
738
     * visible */
739
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
740
0
    smart_str_0(&invalid);
741
742
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
743
0
            ZSTR_VAL(invalid.s));
744
745
0
    smart_str_free(&invalid);
746
0
    return 0;
747
0
  }
748
749
  /* Allow for whitespace between integer portion and any suffix character */
750
76
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
751
752
  /* No exponent suffix. */
753
76
  if (digits_end == str_end) {
754
66
    goto end;
755
66
  }
756
757
10
  switch (*(str_end-1)) {
758
0
    case 'g':
759
0
    case 'G':
760
0
      factor = 1<<30;
761
0
      break;
762
0
    case 'm':
763
8
    case 'M':
764
8
      factor = 1<<20;
765
8
      break;
766
0
    case 'k':
767
2
    case 'K':
768
2
      factor = 1<<10;
769
2
      break;
770
0
    default:
771
      /* Unknown suffix */
772
0
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
773
0
      smart_str_0(&invalid);
774
0
      smart_str_append_escaped(&interpreted, str, digits_end - str);
775
0
      smart_str_0(&interpreted);
776
0
      smart_str_append_escaped(&chr, str_end-1, 1);
777
0
      smart_str_0(&chr);
778
779
0
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
780
0
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
781
782
0
      smart_str_free(&invalid);
783
0
      smart_str_free(&interpreted);
784
0
      smart_str_free(&chr);
785
786
0
      return retval;
787
10
  }
788
789
10
  if (!overflow) {
790
10
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
791
8
      zend_long sretval = (zend_long)retval;
792
8
      if (sretval > 0) {
793
8
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
794
8
      } else {
795
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
796
0
      }
797
8
    } else {
798
2
      overflow = retval > ZEND_ULONG_MAX / factor;
799
2
    }
800
10
  }
801
802
10
  retval *= factor;
803
804
10
  if (UNEXPECTED(digits_end != str_end-1)) {
805
    /* More than one character in suffix */
806
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
807
0
    smart_str_0(&invalid);
808
0
    smart_str_append_escaped(&interpreted, str, digits_end - str);
809
0
    smart_str_0(&interpreted);
810
0
    smart_str_append_escaped(&chr, str_end-1, 1);
811
0
    smart_str_0(&chr);
812
813
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
814
0
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
815
816
0
    smart_str_free(&invalid);
817
0
    smart_str_free(&interpreted);
818
0
    smart_str_free(&chr);
819
820
0
    return retval;
821
0
  }
822
823
76
end:
824
76
  if (UNEXPECTED(overflow)) {
825
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
826
0
    smart_str_0(&invalid);
827
828
    /* Not specifying the resulting value here because the caller may make
829
     * additional conversions. Not specifying the allowed range
830
     * because the caller may do narrower range checks. */
831
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
832
0
            ZSTR_VAL(invalid.s));
833
834
0
    smart_str_free(&invalid);
835
0
    smart_str_free(&interpreted);
836
0
    smart_str_free(&chr);
837
838
0
    return retval;
839
0
  }
840
841
76
  *errstr = NULL;
842
76
  return retval;
843
76
}
844
/* }}} */
845
846
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
847
86
{
848
86
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
849
86
}
850
/* }}} */
851
852
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
853
6
{
854
6
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
855
6
}
856
/* }}} */
857
858
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
859
86
{
860
86
  zend_string *errstr;
861
86
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
862
863
86
  if (errstr) {
864
0
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
865
0
    zend_string_release(errstr);
866
0
  }
867
868
86
  return retval;
869
86
}
870
/* }}} */
871
872
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
873
6
{
874
6
  zend_string *errstr;
875
6
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
876
877
6
  if (errstr) {
878
0
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
879
0
    zend_string_release(errstr);
880
0
  }
881
882
6
  return retval;
883
6
}
884
/* }}} */
885
886
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
887
0
{
888
0
  bool value;
889
890
0
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
891
0
    value = zend_ini_parse_bool(ini_entry->orig_value);
892
0
  } else if (ini_entry->value) {
893
0
    value = zend_ini_parse_bool(ini_entry->value);
894
0
  } else {
895
0
    value = false;
896
0
  }
897
898
0
  if (value) {
899
0
    ZEND_PUTS("On");
900
0
  } else {
901
0
    ZEND_PUTS("Off");
902
0
  }
903
0
}
904
/* }}} */
905
906
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
907
0
{
908
0
  const char *value;
909
910
0
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
911
0
    value = ZSTR_VAL(ini_entry->orig_value);
912
0
  } else if (ini_entry->value) {
913
0
    value = ZSTR_VAL(ini_entry->value);
914
0
  } else {
915
0
    value = NULL;
916
0
  }
917
0
  if (value) {
918
0
    if (zend_uv.html_errors) {
919
0
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
920
0
    } else {
921
0
      ZEND_PUTS(value);
922
0
    }
923
0
  } else {
924
0
    if (zend_uv.html_errors) {
925
0
      ZEND_PUTS(NO_VALUE_HTML);
926
0
    } else {
927
0
      ZEND_PUTS(NO_VALUE_PLAINTEXT);
928
0
    }
929
0
  }
930
0
}
931
/* }}} */
932
933
ZEND_INI_DISP(display_link_numbers) /* {{{ */
934
0
{
935
0
  const char *value;
936
937
0
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
938
0
    value = ZSTR_VAL(ini_entry->orig_value);
939
0
  } else if (ini_entry->value) {
940
0
    value = ZSTR_VAL(ini_entry->value);
941
0
  } else {
942
0
    value = NULL;
943
0
  }
944
945
0
  if (value) {
946
0
    if (atoi(value) == -1) {
947
0
      ZEND_PUTS("Unlimited");
948
0
    } else {
949
0
      zend_printf("%s", value);
950
0
    }
951
0
  }
952
0
}
953
/* }}} */
954
955
/* Standard message handlers */
956
ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
957
86
{
958
86
  bool *p = ZEND_INI_GET_ADDR();
959
86
  *p = zend_ini_parse_bool(new_value);
960
86
  return SUCCESS;
961
86
}
962
/* }}} */
963
964
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
965
54
{
966
54
  zend_long *p = ZEND_INI_GET_ADDR();
967
54
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
968
54
  return SUCCESS;
969
54
}
970
/* }}} */
971
972
ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
973
4
{
974
4
  zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
975
4
  if (tmp < 0) {
976
0
    return FAILURE;
977
0
  }
978
979
4
  zend_long *p = ZEND_INI_GET_ADDR();
980
4
  *p = tmp;
981
982
4
  return SUCCESS;
983
4
}
984
/* }}} */
985
986
ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
987
2
{
988
2
  double *p = ZEND_INI_GET_ADDR();
989
2
  *p = zend_strtod(ZSTR_VAL(new_value), NULL);
990
2
  return SUCCESS;
991
2
}
992
/* }}} */
993
994
ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
995
64
{
996
64
  char **p = ZEND_INI_GET_ADDR();
997
64
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
998
64
  return SUCCESS;
999
64
}
1000
/* }}} */
1001
1002
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1003
18
{
1004
18
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1005
6
    return FAILURE;
1006
6
  }
1007
1008
12
  char **p = ZEND_INI_GET_ADDR();
1009
12
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1010
12
  return SUCCESS;
1011
18
}
1012
/* }}} */
1013
1014
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1015
2
{
1016
2
  zend_string **p = ZEND_INI_GET_ADDR();
1017
2
  *p = new_value;
1018
2
  return SUCCESS;
1019
2
}
1020
/* }}} */
1021
1022
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1023
4
{
1024
4
  if (new_value && ZSTR_LEN(new_value) == 0) {
1025
0
    return FAILURE;
1026
0
  }
1027
1028
4
  zend_string **p = ZEND_INI_GET_ADDR();
1029
4
  *p = new_value;
1030
4
  return SUCCESS;
1031
4
}
1032
/* }}} */