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
1.17k
static inline bool zend_is_whitespace(char c) {
35
1.17k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
36
1.17k
}
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
25.9k
{
52
25.9k
  zend_result result = FAILURE;
53
54
25.9k
  if (ini_entry->modified) {
55
25.9k
    if (ini_entry->on_modify) {
56
25.9k
      zend_try {
57
      /* even if on_modify bails out, we have to continue on with restoring,
58
        since there can be allocated variables that would be freed on MM shutdown
59
        and would lead to memory corruption later ini entry is modified again */
60
25.9k
        result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage);
61
25.9k
      } zend_end_try();
62
25.9k
    }
63
25.9k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
64
      /* runtime failure is OK */
65
0
      return FAILURE;
66
0
    }
67
25.9k
    if (ini_entry->value != ini_entry->orig_value) {
68
218
      zend_string_release(ini_entry->value);
69
218
    }
70
25.9k
    ini_entry->value = ini_entry->orig_value;
71
25.9k
    ini_entry->modifiable = ini_entry->orig_modifiable;
72
25.9k
    ini_entry->modified = false;
73
25.9k
    ini_entry->orig_value = NULL;
74
25.9k
    ini_entry->orig_modifiable = false;
75
25.9k
  }
76
25.9k
  return SUCCESS;
77
25.9k
}
78
/* }}} */
79
80
static void free_ini_entry(zval *zv) /* {{{ */
81
0
{
82
0
  zend_ini_entry *entry = (zend_ini_entry*)Z_PTR_P(zv);
83
84
0
  zend_string_release_ex(entry->name, true);
85
0
  if (entry->value) {
86
0
    zend_string_release(entry->value);
87
0
  }
88
0
  if (entry->orig_value) {
89
0
    zend_string_release_ex(entry->orig_value, true);
90
0
  }
91
0
  free(entry);
92
0
}
93
/* }}} */
94
95
/*
96
 * Startup / shutdown
97
 */
98
ZEND_API void zend_ini_startup(void) /* {{{ */
99
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
25.7k
{
131
25.7k
  if (EG(modified_ini_directives)) {
132
25.7k
    zend_ini_entry *ini_entry;
133
134
103k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
135
103k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
136
103k
    } ZEND_HASH_FOREACH_END();
137
25.7k
    zend_hash_destroy(EG(modified_ini_directives));
138
25.7k
    FREE_HASHTABLE(EG(modified_ini_directives));
139
25.7k
    EG(modified_ini_directives) = NULL;
140
25.7k
  }
141
25.7k
}
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
26
        (!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
26
      if (p->value == prev_value) {
258
26
        p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
259
26
      }
260
332
    } else {
261
332
      p->value = ini_entry->value ?
262
332
        zend_string_init_interned(ini_entry->value, ini_entry->value_length, true) : NULL;
263
264
332
      if (p->on_modify) {
265
302
        p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
266
302
      }
267
332
    }
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
25.7k
{
340
25.7k
  zend_result ret;
341
25.7k
  zend_string *new_value;
342
343
25.7k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
344
25.7k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
345
25.7k
  zend_string_release(new_value);
346
25.7k
  return ret;
347
25.7k
}
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
25.8k
{
364
25.8k
  zend_ini_entry *ini_entry;
365
25.8k
  zend_string *duplicate;
366
25.8k
  uint8_t modifiable;
367
25.8k
  bool modified;
368
369
25.8k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
370
17
    return FAILURE;
371
17
  }
372
373
25.8k
  modifiable = ini_entry->modifiable;
374
25.8k
  modified = ini_entry->modified;
375
376
25.8k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
377
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
378
0
  }
379
380
25.8k
  if (!force_change) {
381
25.8k
    if (!(ini_entry->modifiable & modify_type)) {
382
0
      return FAILURE;
383
0
    }
384
25.8k
  }
385
386
25.8k
  if (!EG(modified_ini_directives)) {
387
25.7k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
388
25.7k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
389
25.7k
  }
390
25.8k
  if (!modified) {
391
25.8k
    ini_entry->orig_value = ini_entry->value;
392
25.8k
    ini_entry->orig_modifiable = modifiable;
393
25.8k
    ini_entry->modified = true;
394
25.8k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
395
25.8k
  }
396
397
25.8k
  zend_string *prev_value = ini_entry->value;
398
25.8k
  duplicate = zend_string_copy(new_value);
399
400
25.8k
  if (!ini_entry->on_modify
401
25.8k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
402
134
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
403
30
      zend_string_release(prev_value);
404
30
    }
405
    /* Skip assigning the value if the handler has already done so. */
406
134
    if (ini_entry->value == prev_value) {
407
134
      ini_entry->value = duplicate;
408
134
    } else {
409
0
      zend_string_release(duplicate);
410
0
    }
411
25.7k
  } else {
412
25.7k
    zend_string_release(duplicate);
413
25.7k
    return FAILURE;
414
25.7k
  }
415
416
134
  return SUCCESS;
417
25.8k
}
418
/* }}} */
419
420
ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
421
0
{
422
0
  zend_ini_entry *ini_entry;
423
424
0
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
425
0
    (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
426
0
    return FAILURE;
427
0
  }
428
429
0
  if (EG(modified_ini_directives)) {
430
0
    if (zend_restore_ini_entry_cb(ini_entry, stage) == SUCCESS) {
431
0
      zend_hash_del(EG(modified_ini_directives), name);
432
0
    } else {
433
0
      return FAILURE;
434
0
    }
435
0
  }
436
437
0
  return SUCCESS;
438
0
}
439
/* }}} */
440
441
ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
442
0
{
443
0
  zend_ini_entry *ini_entry;
444
445
0
  ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
446
0
  if (ini_entry == NULL) {
447
0
    return FAILURE;
448
0
  }
449
450
0
  ini_entry->displayer = displayer;
451
0
  return SUCCESS;
452
0
}
453
/* }}} */
454
455
/*
456
 * Data retrieval
457
 */
458
459
ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, bool orig) /* {{{ */
460
277
{
461
277
  zend_ini_entry *ini_entry;
462
463
277
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
464
277
  if (ini_entry) {
465
277
    if (orig && ini_entry->modified) {
466
0
      return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
467
277
    } else {
468
277
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
469
277
    }
470
277
  }
471
472
0
  return 0;
473
277
}
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
87.0k
{
503
87.0k
  zend_string *str = zend_ini_str(name, name_length, orig);
504
505
87.0k
  return str ? ZSTR_VAL(str) : NULL;
506
87.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
87.3k
{
512
87.3k
  zend_ini_entry *ini_entry;
513
514
87.3k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
515
87.3k
  if (ini_entry) {
516
87.3k
    if (exists) {
517
87.3k
      *exists = true;
518
87.3k
    }
519
520
87.3k
    if (orig && ini_entry->modified) {
521
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
522
87.3k
    } else {
523
87.3k
      return ini_entry->value ? ini_entry->value : NULL;
524
87.3k
    }
525
87.3k
  } else {
526
0
    if (exists) {
527
0
      *exists = false;
528
0
    }
529
0
    return NULL;
530
0
  }
531
87.3k
}
532
/* }}} */
533
534
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
535
87.3k
{
536
87.3k
  bool exists = true;
537
87.3k
  zend_string *return_value;
538
539
87.3k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
540
87.3k
  if (!exists) {
541
0
    return NULL;
542
87.3k
  } else if (!return_value) {
543
474
    return_value = ZSTR_EMPTY_ALLOC();
544
474
  }
545
87.3k
  return return_value;
546
87.3k
}
547
/* }}} */
548
549
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
550
150
{
551
150
  zend_ini_entry *ini_entry;
552
553
150
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
554
150
  if (ini_entry) {
555
133
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
556
133
  } else {
557
17
    return NULL;
558
17
  }
559
150
}
560
/* }}} */
561
562
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
563
174
{
564
174
  if (zend_string_equals_literal_ci(str, "true")
565
174
      || zend_string_equals_literal_ci(str, "yes")
566
174
      || zend_string_equals_literal_ci(str, "on")
567
174
  ) {
568
0
    return true;
569
174
  } else {
570
174
    return atoi(ZSTR_VAL(str)) != 0;
571
174
  }
572
174
}
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
230
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
580
230
  const char *digits_consumed = digits;
581
  /* Ignore leading whitespace. */
582
304
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
583
230
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
584
112
    ++digits_consumed;
585
112
  }
586
587
230
  if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
588
    /* Value is just 0 */
589
206
    if ((digits_consumed+1) == str_end) {
590
152
      return digits_consumed;
591
152
    }
592
593
54
    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
8
      case 'b':
601
18
      case 'B':
602
18
        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
18
        break;
607
54
    }
608
54
  }
609
78
  return digits_consumed;
610
230
}
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
410
{
614
410
  char *digits_end = NULL;
615
410
  const char *str = ZSTR_VAL(value);
616
410
  const char *str_end = &str[ZSTR_LEN(value)];
617
410
  const char *digits = str;
618
410
  bool overflow = false;
619
410
  zend_ulong factor;
620
410
  smart_str invalid = {0};
621
410
  smart_str interpreted = {0};
622
410
  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
410
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
627
628
  /* Ignore trailing whitespace */
629
412
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
630
631
410
  if (digits == str_end) {
632
4
    *errstr = NULL;
633
4
    return 0;
634
4
  }
635
636
410
  bool is_negative = false;
637
406
  if (digits[0] == '+') {
638
2
    ++digits;
639
404
  } else if (digits[0] == '-') {
640
26
    is_negative = true;
641
26
    ++digits;
642
26
  }
643
644
  /* if there is no digit after +/- */
645
406
  if (!isdigit((unsigned char)digits[0])) {
646
    /* Escape the string to avoid null bytes and to make non-printable chars
647
     * visible */
648
8
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
649
8
    smart_str_0(&invalid);
650
651
8
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
652
8
            ZSTR_VAL(invalid.s));
653
654
8
    smart_str_free(&invalid);
655
8
    return 0;
656
8
  }
657
658
398
  int base = 0;
659
398
  if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) {
660
    /* Value is just 0 */
661
262
    if ((digits+1) == str_end) {
662
30
      *errstr = NULL;
663
30
      return 0;
664
30
    }
665
666
232
    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
86
      case 'x':
676
86
      case 'X':
677
86
        base = 16;
678
86
        break;
679
40
      case 'o':
680
40
      case 'O':
681
40
        base = 8;
682
40
        break;
683
104
      case 'b':
684
104
      case 'B':
685
104
        base = 2;
686
104
        break;
687
2
      default:
688
2
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
689
2
          digits[1]);
690
2
        return 0;
691
232
        }
692
230
        digits += 2;
693
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
694
230
    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
186
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
698
186
      smart_str_0(&invalid);
699
700
186
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
701
186
              ZSTR_VAL(invalid.s));
702
703
186
      smart_str_free(&invalid);
704
186
      return 0;
705
186
    }
706
230
  }
707
180
  evaluation:
708
709
180
  errno = 0;
710
180
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
711
712
180
  if (errno == ERANGE) {
713
2
    overflow = true;
714
178
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
715
44
    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
134
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
724
    /* Handle PHP_INT_MIN case */
725
134
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
726
0
      retval = 0u - retval;
727
134
    } else if ((zend_long) retval < 0) {
728
0
      overflow = true;
729
134
    } else if (is_negative) {
730
24
      retval = 0u - retval;
731
24
    }
732
134
  }
733
734
180
  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
180
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
751
752
  /* No exponent suffix. */
753
180
  if (digits_end == str_end) {
754
122
    goto end;
755
122
  }
756
757
58
  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
48
    case 'M':
764
48
      factor = 1<<20;
765
48
      break;
766
0
    case 'k':
767
2
    case 'K':
768
2
      factor = 1<<10;
769
2
      break;
770
8
    default:
771
      /* Unknown suffix */
772
8
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
773
8
      smart_str_0(&invalid);
774
8
      smart_str_append_escaped(&interpreted, str, digits_end - str);
775
8
      smart_str_0(&interpreted);
776
8
      smart_str_append_escaped(&chr, str_end-1, 1);
777
8
      smart_str_0(&chr);
778
779
8
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
780
8
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
781
782
8
      smart_str_free(&invalid);
783
8
      smart_str_free(&interpreted);
784
8
      smart_str_free(&chr);
785
786
8
      return retval;
787
58
  }
788
789
50
  if (!overflow) {
790
48
    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
40
    } else {
798
40
      overflow = retval > ZEND_ULONG_MAX / factor;
799
40
    }
800
48
  }
801
802
50
  retval *= factor;
803
804
50
  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
172
end:
824
172
  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
170
  *errstr = NULL;
842
170
  return retval;
843
172
}
844
/* }}} */
845
846
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
847
354
{
848
354
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
849
354
}
850
/* }}} */
851
852
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
853
56
{
854
56
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
855
56
}
856
/* }}} */
857
858
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
859
120
{
860
120
  zend_string *errstr;
861
120
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
862
863
120
  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
120
  return retval;
869
120
}
870
/* }}} */
871
872
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
873
56
{
874
56
  zend_string *errstr;
875
56
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
876
877
56
  if (errstr) {
878
12
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
879
12
    zend_string_release(errstr);
880
12
  }
881
882
56
  return retval;
883
56
}
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
90
{
958
90
  bool *p = ZEND_INI_GET_ADDR();
959
90
  *p = zend_ini_parse_bool(new_value);
960
90
  return SUCCESS;
961
90
}
962
/* }}} */
963
964
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
965
58
{
966
58
  zend_long *p = ZEND_INI_GET_ADDR();
967
58
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
968
58
  return SUCCESS;
969
58
}
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
48
{
1004
48
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1005
6
    return FAILURE;
1006
6
  }
1007
1008
42
  char **p = ZEND_INI_GET_ADDR();
1009
42
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1010
42
  return SUCCESS;
1011
48
}
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
/* }}} */