Coverage Report

Created: 2026-06-02 06:36

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
845
static inline bool zend_is_whitespace(char c) {
35
845
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
845
}
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
85
{
52
85
  zend_result result = FAILURE;
53
54
85
  if (ini_entry->modified) {
55
85
    if (ini_entry->on_modify) {
56
85
      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
85
        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
85
      } zend_end_try();
62
85
    }
63
85
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
85
    if (ini_entry->value != ini_entry->orig_value) {
68
64
      zend_string_release(ini_entry->value);
69
64
    }
70
85
    ini_entry->value = ini_entry->orig_value;
71
85
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
85
    ini_entry->modified = false;
73
85
    ini_entry->orig_value = NULL;
74
85
    ini_entry->orig_modifiable = false;
75
85
  }
76
85
  return SUCCESS;
77
85
}
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
33.5k
{
131
33.5k
  if (EG(modified_ini_directives)) {
132
82
    zend_ini_entry *ini_entry;
133
134
334
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
334
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
334
    } ZEND_HASH_FOREACH_END();
137
82
    zend_hash_destroy(EG(modified_ini_directives));
138
82
    FREE_HASHTABLE(EG(modified_ini_directives));
139
82
    EG(modified_ini_directives) = NULL;
140
82
  }
141
33.5k
}
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
1.19k
{
177
1.19k
  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
1.19k
  } else if (!f->key) { /* f is numeric, s is not */
185
0
    return -1;
186
1.19k
  } else if (!s->key) { /* s is numeric, f is not */
187
0
    return 1;
188
1.19k
  } else { /* both strings */
189
1.19k
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
190
1.19k
  }
191
1.19k
}
192
/* }}} */
193
194
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
195
1
{
196
1
  zend_hash_sort(EG(ini_directives), ini_key_compare, false);
197
1
}
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
11
{
340
11
  zend_result ret;
341
11
  zend_string *new_value;
342
343
11
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
11
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
11
  zend_string_release(new_value);
346
11
  return ret;
347
11
}
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
82
{
364
82
  zend_ini_entry *ini_entry;
365
82
  zend_string *duplicate;
366
82
  uint8_t modifiable;
367
82
  bool modified;
368
369
82
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
8
    return FAILURE;
371
8
  }
372
373
74
  modifiable = ini_entry->modifiable;
374
74
  modified = ini_entry->modified;
375
376
74
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
74
  if (!force_change) {
381
74
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
74
  }
385
386
74
  if (!EG(modified_ini_directives)) {
387
62
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
62
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
62
  }
390
74
  if (!modified) {
391
65
    ini_entry->orig_value = ini_entry->value;
392
65
    ini_entry->orig_modifiable = modifiable;
393
65
    ini_entry->modified = true;
394
65
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
65
  }
396
397
74
  zend_string *prev_value = ini_entry->value;
398
74
  duplicate = zend_string_copy(new_value);
399
400
74
  if (!ini_entry->on_modify
401
74
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
57
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
9
      zend_string_release(prev_value);
404
9
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
57
    if (ini_entry->value == prev_value) {
407
57
      ini_entry->value = duplicate;
408
57
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
57
  } else {
412
17
    zend_string_release(duplicate);
413
17
    return FAILURE;
414
17
  }
415
416
57
  return SUCCESS;
417
74
}
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
289
{
461
289
  zend_ini_entry *ini_entry;
462
463
289
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
289
  if (ini_entry) {
465
289
    if (orig && ini_entry->modified) {
466
0
      return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
467
289
    } else {
468
289
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
289
    }
470
289
  }
471
472
0
  return 0;
473
289
}
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
17.0k
{
503
17.0k
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
17.0k
  return str ? ZSTR_VAL(str) : NULL;
506
17.0k
}
507
/* }}} */
508
509
510
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
511
17.3k
{
512
17.3k
  zend_ini_entry *ini_entry;
513
514
17.3k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
17.3k
  if (ini_entry) {
516
17.3k
    if (exists) {
517
17.3k
      *exists = true;
518
17.3k
    }
519
520
17.3k
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
17.3k
    } else {
523
17.3k
      return ini_entry->value ? ini_entry->value : NULL;
524
17.3k
    }
525
17.3k
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
17.3k
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
17.3k
{
536
17.3k
  bool exists = true;
537
17.3k
  zend_string *return_value;
538
539
17.3k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
17.3k
  if (!exists) {
541
0
    return NULL;
542
17.3k
  } else if (!return_value) {
543
26
    return_value = ZSTR_EMPTY_ALLOC();
544
26
  }
545
17.3k
  return return_value;
546
17.3k
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
71
{
551
71
  zend_ini_entry *ini_entry;
552
553
71
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
71
  if (ini_entry) {
555
58
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
58
  } else {
557
13
    return NULL;
558
13
  }
559
71
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
235
{
564
235
  if (zend_string_equals_literal_ci(str, "true")
565
235
      || zend_string_equals_literal_ci(str, "yes")
566
235
      || zend_string_equals_literal_ci(str, "on")
567
235
  ) {
568
0
    return true;
569
235
  } else {
570
235
    return atoi(ZSTR_VAL(str)) != 0;
571
235
  }
572
235
}
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
169
{
614
169
  char *digits_end = NULL;
615
169
  const char *str = ZSTR_VAL(value);
616
169
  const char *str_end = &str[ZSTR_LEN(value)];
617
169
  const char *digits = str;
618
169
  bool overflow = false;
619
169
  zend_ulong factor;
620
169
  smart_str invalid = {0};
621
169
  smart_str interpreted = {0};
622
169
  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
208
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
627
628
  /* Ignore trailing whitespace */
629
315
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
630
631
169
  if (digits == str_end) {
632
2
    *errstr = NULL;
633
2
    return 0;
634
2
  }
635
636
169
  bool is_negative = false;
637
167
  if (digits[0] == '+') {
638
0
    ++digits;
639
167
  } else if (digits[0] == '-') {
640
8
    is_negative = true;
641
8
    ++digits;
642
8
  }
643
644
  /* if there is no digit after +/- */
645
167
  if (!isdigit((unsigned char)digits[0])) {
646
    /* Escape the string to avoid null bytes and to make non-printable chars
647
     * visible */
648
1
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
649
1
    smart_str_0(&invalid);
650
651
1
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
652
1
            ZSTR_VAL(invalid.s));
653
654
1
    smart_str_free(&invalid);
655
1
    return 0;
656
1
  }
657
658
166
  int base = 0;
659
166
  if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) {
660
    /* Value is just 0 */
661
22
    if ((digits+1) == str_end) {
662
20
      *errstr = NULL;
663
20
      return 0;
664
20
    }
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
146
  evaluation:
708
709
146
  errno = 0;
710
146
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
711
712
146
  if (errno == ERANGE) {
713
3
    overflow = true;
714
143
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
715
63
    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
80
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
724
    /* Handle PHP_INT_MIN case */
725
80
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
726
0
      retval = 0u - retval;
727
80
    } else if ((zend_long) retval < 0) {
728
0
      overflow = true;
729
80
    } else if (is_negative) {
730
6
      retval = 0u - retval;
731
6
    }
732
80
  }
733
734
146
  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
398
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
751
752
  /* No exponent suffix. */
753
146
  if (digits_end == str_end) {
754
74
    goto end;
755
74
  }
756
757
72
  switch (*(str_end-1)) {
758
1
    case 'g':
759
1
    case 'G':
760
1
      factor = 1<<30;
761
1
      break;
762
4
    case 'm':
763
57
    case 'M':
764
57
      factor = 1<<20;
765
57
      break;
766
0
    case 'k':
767
2
    case 'K':
768
2
      factor = 1<<10;
769
2
      break;
770
12
    default:
771
      /* Unknown suffix */
772
12
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
773
12
      smart_str_0(&invalid);
774
12
      smart_str_append_escaped(&interpreted, str, digits_end - str);
775
12
      smart_str_0(&interpreted);
776
12
      smart_str_append_escaped(&chr, str_end-1, 1);
777
12
      smart_str_0(&chr);
778
779
12
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
780
12
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
781
782
12
      smart_str_free(&invalid);
783
12
      smart_str_free(&interpreted);
784
12
      smart_str_free(&chr);
785
786
12
      return retval;
787
72
  }
788
789
60
  if (!overflow) {
790
57
    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
49
    } else {
798
49
      overflow = retval > ZEND_ULONG_MAX / factor;
799
49
    }
800
57
  }
801
802
60
  retval *= factor;
803
804
60
  if (UNEXPECTED(digits_end != str_end-1)) {
805
    /* More than one character in suffix */
806
14
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
807
14
    smart_str_0(&invalid);
808
14
    smart_str_append_escaped(&interpreted, str, digits_end - str);
809
14
    smart_str_0(&interpreted);
810
14
    smart_str_append_escaped(&chr, str_end-1, 1);
811
14
    smart_str_0(&chr);
812
813
14
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
814
14
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
815
816
14
    smart_str_free(&invalid);
817
14
    smart_str_free(&interpreted);
818
14
    smart_str_free(&chr);
819
820
14
    return retval;
821
14
  }
822
823
120
end:
824
120
  if (UNEXPECTED(overflow)) {
825
2
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
826
2
    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
2
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
832
2
            ZSTR_VAL(invalid.s));
833
834
2
    smart_str_free(&invalid);
835
2
    smart_str_free(&interpreted);
836
2
    smart_str_free(&chr);
837
838
2
    return retval;
839
2
  }
840
841
118
  *errstr = NULL;
842
118
  return retval;
843
120
}
844
/* }}} */
845
846
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
847
99
{
848
99
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
849
99
}
850
/* }}} */
851
852
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
853
70
{
854
70
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
855
70
}
856
/* }}} */
857
858
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
859
96
{
860
96
  zend_string *errstr;
861
96
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
862
863
96
  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
96
  return retval;
869
96
}
870
/* }}} */
871
872
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
873
70
{
874
70
  zend_string *errstr;
875
70
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
876
877
70
  if (errstr) {
878
29
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
879
29
    zend_string_release(errstr);
880
29
  }
881
882
70
  return retval;
883
70
}
884
/* }}} */
885
886
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
887
96
{
888
96
  bool value;
889
890
96
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
891
0
    value = zend_ini_parse_bool(ini_entry->orig_value);
892
96
  } else if (ini_entry->value) {
893
96
    value = zend_ini_parse_bool(ini_entry->value);
894
96
  } else {
895
0
    value = false;
896
0
  }
897
898
96
  if (value) {
899
40
    ZEND_PUTS("On");
900
56
  } else {
901
56
    ZEND_PUTS("Off");
902
56
  }
903
96
}
904
/* }}} */
905
906
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
907
10
{
908
10
  const char *value;
909
910
10
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
911
0
    value = ZSTR_VAL(ini_entry->orig_value);
912
10
  } else if (ini_entry->value) {
913
10
    value = ZSTR_VAL(ini_entry->value);
914
10
  } else {
915
0
    value = NULL;
916
0
  }
917
10
  if (value) {
918
10
    if (zend_uv.html_errors) {
919
10
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
920
10
    } else {
921
0
      ZEND_PUTS(value);
922
0
    }
923
10
  } 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
10
}
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
88
{
958
88
  bool *p = ZEND_INI_GET_ADDR();
959
88
  *p = zend_ini_parse_bool(new_value);
960
88
  return SUCCESS;
961
88
}
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
30
{
1004
30
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1005
6
    return FAILURE;
1006
6
  }
1007
1008
24
  char **p = ZEND_INI_GET_ADDR();
1009
24
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1010
24
  return SUCCESS;
1011
30
}
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
8
{
1024
8
  if (new_value && ZSTR_LEN(new_value) == 0) {
1025
1
    return FAILURE;
1026
1
  }
1027
1028
7
  zend_string **p = ZEND_INI_GET_ADDR();
1029
7
  *p = new_value;
1030
7
  return SUCCESS;
1031
8
}
1032
/* }}} */