Coverage Report

Created: 2026-06-02 06:40

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
3.88k
static inline bool zend_is_whitespace(char c) {
35
3.88k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
3.88k
}
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
57.6k
{
52
57.6k
  zend_result result = FAILURE;
53
54
57.6k
  if (ini_entry->modified) {
55
57.6k
    if (ini_entry->on_modify) {
56
57.6k
      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
57.6k
        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
57.6k
      } zend_end_try();
62
57.6k
    }
63
57.6k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
57.6k
    if (ini_entry->value != ini_entry->orig_value) {
68
646
      zend_string_release(ini_entry->value);
69
646
    }
70
57.6k
    ini_entry->value = ini_entry->orig_value;
71
57.6k
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
57.6k
    ini_entry->modified = false;
73
57.6k
    ini_entry->orig_value = NULL;
74
57.6k
    ini_entry->orig_modifiable = false;
75
57.6k
  }
76
57.6k
  return SUCCESS;
77
57.6k
}
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
228k
{
131
228k
  if (EG(modified_ini_directives)) {
132
57.1k
    zend_ini_entry *ini_entry;
133
134
229k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
229k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
229k
    } ZEND_HASH_FOREACH_END();
137
57.1k
    zend_hash_destroy(EG(modified_ini_directives));
138
57.1k
    FREE_HASHTABLE(EG(modified_ini_directives));
139
57.1k
    EG(modified_ini_directives) = NULL;
140
57.1k
  }
141
228k
}
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
57.0k
{
340
57.0k
  zend_result ret;
341
57.0k
  zend_string *new_value;
342
343
57.0k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
57.0k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
57.0k
  zend_string_release(new_value);
346
57.0k
  return ret;
347
57.0k
}
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
58.0k
{
364
58.0k
  zend_ini_entry *ini_entry;
365
58.0k
  zend_string *duplicate;
366
58.0k
  uint8_t modifiable;
367
58.0k
  bool modified;
368
369
58.0k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
550
    return FAILURE;
371
550
  }
372
373
57.4k
  modifiable = ini_entry->modifiable;
374
57.4k
  modified = ini_entry->modified;
375
376
57.4k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
57.4k
  if (!force_change) {
381
57.4k
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
57.4k
  }
385
386
57.4k
  if (!EG(modified_ini_directives)) {
387
57.0k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
57.0k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
57.0k
  }
390
57.4k
  if (!modified) {
391
57.2k
    ini_entry->orig_value = ini_entry->value;
392
57.2k
    ini_entry->orig_modifiable = modifiable;
393
57.2k
    ini_entry->modified = true;
394
57.2k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
57.2k
  }
396
397
57.4k
  zend_string *prev_value = ini_entry->value;
398
57.4k
  duplicate = zend_string_copy(new_value);
399
400
57.4k
  if (!ini_entry->on_modify
401
57.4k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
436
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
121
      zend_string_release(prev_value);
404
121
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
436
    if (ini_entry->value == prev_value) {
407
436
      ini_entry->value = duplicate;
408
436
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
57.0k
  } else {
412
57.0k
    zend_string_release(duplicate);
413
57.0k
    return FAILURE;
414
57.0k
  }
415
416
436
  return SUCCESS;
417
57.4k
}
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.01k
{
461
1.01k
  zend_ini_entry *ini_entry;
462
463
1.01k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
1.01k
  if (ini_entry) {
465
1.01k
    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.01k
    } else {
468
1.01k
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
1.01k
    }
470
1.01k
  }
471
472
0
  return 0;
473
1.01k
}
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
122k
{
503
122k
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
122k
  return str ? ZSTR_VAL(str) : NULL;
506
122k
}
507
/* }}} */
508
509
510
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
511
123k
{
512
123k
  zend_ini_entry *ini_entry;
513
514
123k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
123k
  if (ini_entry) {
516
123k
    if (exists) {
517
123k
      *exists = true;
518
123k
    }
519
520
123k
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
123k
    } else {
523
123k
      return ini_entry->value ? ini_entry->value : NULL;
524
123k
    }
525
123k
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
123k
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
123k
{
536
123k
  bool exists = true;
537
123k
  zend_string *return_value;
538
539
123k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
123k
  if (!exists) {
541
0
    return NULL;
542
123k
  } else if (!return_value) {
543
1.09k
    return_value = ZSTR_EMPTY_ALLOC();
544
1.09k
  }
545
123k
  return return_value;
546
123k
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
985
{
551
985
  zend_ini_entry *ini_entry;
552
553
985
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
985
  if (ini_entry) {
555
403
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
582
  } else {
557
582
    return NULL;
558
582
  }
559
985
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
1.43k
{
564
1.43k
  if (zend_string_equals_literal_ci(str, "true")
565
1.43k
      || zend_string_equals_literal_ci(str, "yes")
566
1.43k
      || zend_string_equals_literal_ci(str, "on")
567
1.43k
  ) {
568
0
    return true;
569
1.43k
  } else {
570
1.43k
    return atoi(ZSTR_VAL(str)) != 0;
571
1.43k
  }
572
1.43k
}
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
349
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
580
349
  const char *digits_consumed = digits;
581
  /* Ignore leading whitespace. */
582
441
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
583
349
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
584
142
    ++digits_consumed;
585
142
  }
586
587
349
  if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
588
    /* Value is just 0 */
589
290
    if ((digits_consumed+1) == str_end) {
590
200
      return digits_consumed;
591
200
    }
592
593
90
    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
20
      case 'b':
601
48
      case 'B':
602
48
        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
48
        break;
607
90
    }
608
90
  }
609
149
  return digits_consumed;
610
349
}
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
1.38k
{
614
1.38k
  char *digits_end = NULL;
615
1.38k
  const char *str = ZSTR_VAL(value);
616
1.38k
  const char *str_end = &str[ZSTR_LEN(value)];
617
1.38k
  const char *digits = str;
618
1.38k
  bool overflow = false;
619
1.38k
  zend_ulong factor;
620
1.38k
  smart_str invalid = {0};
621
1.38k
  smart_str interpreted = {0};
622
1.38k
  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
1.42k
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
627
628
  /* Ignore trailing whitespace */
629
1.53k
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
630
631
1.38k
  if (digits == str_end) {
632
9
    *errstr = NULL;
633
9
    return 0;
634
9
  }
635
636
1.38k
  bool is_negative = false;
637
1.37k
  if (digits[0] == '+') {
638
2
    ++digits;
639
1.37k
  } else if (digits[0] == '-') {
640
97
    is_negative = true;
641
97
    ++digits;
642
97
  }
643
644
  /* if there is no digit after +/- */
645
1.37k
  if (!isdigit((unsigned char)digits[0])) {
646
    /* Escape the string to avoid null bytes and to make non-printable chars
647
     * visible */
648
18
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
649
18
    smart_str_0(&invalid);
650
651
18
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
652
18
            ZSTR_VAL(invalid.s));
653
654
18
    smart_str_free(&invalid);
655
18
    return 0;
656
18
  }
657
658
1.35k
  int base = 0;
659
1.35k
  if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) {
660
    /* Value is just 0 */
661
529
    if ((digits+1) == str_end) {
662
172
      *errstr = NULL;
663
172
      return 0;
664
172
    }
665
666
357
    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
175
      case 'x':
676
175
      case 'X':
677
175
        base = 16;
678
175
        break;
679
52
      case 'o':
680
52
      case 'O':
681
52
        base = 8;
682
52
        break;
683
122
      case 'b':
684
122
      case 'B':
685
122
        base = 2;
686
122
        break;
687
8
      default:
688
8
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
689
8
          digits[1]);
690
8
        return 0;
691
357
        }
692
349
        digits += 2;
693
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
694
349
    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
234
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
698
234
      smart_str_0(&invalid);
699
700
234
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
701
234
              ZSTR_VAL(invalid.s));
702
703
234
      smart_str_free(&invalid);
704
234
      return 0;
705
234
    }
706
349
  }
707
943
  evaluation:
708
709
943
  errno = 0;
710
943
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
711
712
943
  if (errno == ERANGE) {
713
5
    overflow = true;
714
938
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
715
180
    if (is_negative) {
716
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
717
16
      if (retval == 1 && digits_end == str_end) {
718
16
        retval = -1;
719
16
      } else {
720
0
        overflow = true;
721
0
      }
722
16
    }
723
758
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
724
    /* Handle PHP_INT_MIN case */
725
758
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
726
0
      retval = 0u - retval;
727
758
    } else if ((zend_long) retval < 0) {
728
0
      overflow = true;
729
758
    } else if (is_negative) {
730
81
      retval = 0u - retval;
731
81
    }
732
758
  }
733
734
943
  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
3
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
740
3
    smart_str_0(&invalid);
741
742
3
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
743
3
            ZSTR_VAL(invalid.s));
744
745
3
    smart_str_free(&invalid);
746
3
    return 0;
747
3
  }
748
749
  /* Allow for whitespace between integer portion and any suffix character */
750
1.19k
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
751
752
  /* No exponent suffix. */
753
940
  if (digits_end == str_end) {
754
692
    goto end;
755
692
  }
756
757
248
  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
196
    case 'M':
764
196
      factor = 1<<20;
765
196
      break;
766
0
    case 'k':
767
16
    case 'K':
768
16
      factor = 1<<10;
769
16
      break;
770
35
    default:
771
      /* Unknown suffix */
772
35
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
773
35
      smart_str_0(&invalid);
774
35
      smart_str_append_escaped(&interpreted, str, digits_end - str);
775
35
      smart_str_0(&interpreted);
776
35
      smart_str_append_escaped(&chr, str_end-1, 1);
777
35
      smart_str_0(&chr);
778
779
35
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
780
35
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
781
782
35
      smart_str_free(&invalid);
783
35
      smart_str_free(&interpreted);
784
35
      smart_str_free(&chr);
785
786
35
      return retval;
787
248
  }
788
789
213
  if (!overflow) {
790
208
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
791
64
      zend_long sretval = (zend_long)retval;
792
64
      if (sretval > 0) {
793
64
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
794
64
      } else {
795
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
796
0
      }
797
144
    } else {
798
144
      overflow = retval > ZEND_ULONG_MAX / factor;
799
144
    }
800
208
  }
801
802
213
  retval *= factor;
803
804
213
  if (UNEXPECTED(digits_end != str_end-1)) {
805
    /* More than one character in suffix */
806
17
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
807
17
    smart_str_0(&invalid);
808
17
    smart_str_append_escaped(&interpreted, str, digits_end - str);
809
17
    smart_str_0(&interpreted);
810
17
    smart_str_append_escaped(&chr, str_end-1, 1);
811
17
    smart_str_0(&chr);
812
813
17
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
814
17
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
815
816
17
    smart_str_free(&invalid);
817
17
    smart_str_free(&interpreted);
818
17
    smart_str_free(&chr);
819
820
17
    return retval;
821
17
  }
822
823
888
end:
824
888
  if (UNEXPECTED(overflow)) {
825
8
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
826
8
    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
8
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
832
8
            ZSTR_VAL(invalid.s));
833
834
8
    smart_str_free(&invalid);
835
8
    smart_str_free(&interpreted);
836
8
    smart_str_free(&chr);
837
838
8
    return retval;
839
8
  }
840
841
880
  *errstr = NULL;
842
880
  return retval;
843
888
}
844
/* }}} */
845
846
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
847
1.17k
{
848
1.17k
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
849
1.17k
}
850
/* }}} */
851
852
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
853
214
{
854
214
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
855
214
}
856
/* }}} */
857
858
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
859
807
{
860
807
  zend_string *errstr;
861
807
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
862
863
807
  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
807
  return retval;
869
807
}
870
/* }}} */
871
872
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
873
214
{
874
214
  zend_string *errstr;
875
214
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
876
877
214
  if (errstr) {
878
51
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
879
51
    zend_string_release(errstr);
880
51
  }
881
882
214
  return retval;
883
214
}
884
/* }}} */
885
886
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
887
384
{
888
384
  bool value;
889
890
384
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
891
0
    value = zend_ini_parse_bool(ini_entry->orig_value);
892
384
  } else if (ini_entry->value) {
893
384
    value = zend_ini_parse_bool(ini_entry->value);
894
384
  } else {
895
0
    value = false;
896
0
  }
897
898
384
  if (value) {
899
160
    ZEND_PUTS("On");
900
224
  } else {
901
224
    ZEND_PUTS("Off");
902
224
  }
903
384
}
904
/* }}} */
905
906
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
907
40
{
908
40
  const char *value;
909
910
40
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
911
0
    value = ZSTR_VAL(ini_entry->orig_value);
912
40
  } else if (ini_entry->value) {
913
40
    value = ZSTR_VAL(ini_entry->value);
914
40
  } else {
915
0
    value = NULL;
916
0
  }
917
40
  if (value) {
918
40
    if (zend_uv.html_errors) {
919
40
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
920
40
    } else {
921
0
      ZEND_PUTS(value);
922
0
    }
923
40
  } 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
40
}
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
700
{
958
700
  bool *p = ZEND_INI_GET_ADDR();
959
700
  *p = zend_ini_parse_bool(new_value);
960
700
  return SUCCESS;
961
700
}
962
/* }}} */
963
964
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
965
442
{
966
442
  zend_long *p = ZEND_INI_GET_ADDR();
967
442
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
968
442
  return SUCCESS;
969
442
}
970
/* }}} */
971
972
ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
973
32
{
974
32
  zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
975
32
  if (tmp < 0) {
976
0
    return FAILURE;
977
0
  }
978
979
32
  zend_long *p = ZEND_INI_GET_ADDR();
980
32
  *p = tmp;
981
982
32
  return SUCCESS;
983
32
}
984
/* }}} */
985
986
ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
987
16
{
988
16
  double *p = ZEND_INI_GET_ADDR();
989
16
  *p = zend_strtod(ZSTR_VAL(new_value), NULL);
990
16
  return SUCCESS;
991
16
}
992
/* }}} */
993
994
ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
995
512
{
996
512
  char **p = ZEND_INI_GET_ADDR();
997
512
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
998
512
  return SUCCESS;
999
512
}
1000
/* }}} */
1001
1002
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1003
244
{
1004
244
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1005
48
    return FAILURE;
1006
48
  }
1007
1008
196
  char **p = ZEND_INI_GET_ADDR();
1009
196
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1010
196
  return SUCCESS;
1011
244
}
1012
/* }}} */
1013
1014
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1015
16
{
1016
16
  zend_string **p = ZEND_INI_GET_ADDR();
1017
16
  *p = new_value;
1018
16
  return SUCCESS;
1019
16
}
1020
/* }}} */
1021
1022
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1023
42
{
1024
42
  if (new_value && ZSTR_LEN(new_value) == 0) {
1025
4
    return FAILURE;
1026
4
  }
1027
1028
38
  zend_string **p = ZEND_INI_GET_ADDR();
1029
38
  *p = new_value;
1030
38
  return SUCCESS;
1031
42
}
1032
/* }}} */