Coverage Report

Created: 2026-04-01 06:49

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 (c) Zend Technologies Ltd. (http://www.zend.com)           |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 2.00 of the Zend license,     |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | http://www.zend.com/license/2_00.txt.                                |
11
   | If you did not receive a copy of the Zend license and are unable to  |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@zend.com so we can mail you a copy immediately.              |
14
   +----------------------------------------------------------------------+
15
   | Author: Zeev Suraski <zeev@php.net>                                  |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#include "zend.h"
20
#include "zend_sort.h"
21
#include "zend_API.h"
22
#include "zend_ini.h"
23
#include "zend_alloc.h"
24
#include "zend_operators.h"
25
#include "zend_strtod.h"
26
#include "zend_modules.h"
27
#include "zend_smart_str.h"
28
#include <ctype.h>
29
30
static HashTable *registered_zend_ini_directives;
31
32
#define NO_VALUE_PLAINTEXT    "no value"
33
#define NO_VALUE_HTML     "<i>no value</i>"
34
35
4.22k
static inline bool zend_is_whitespace(char c) {
36
4.22k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
37
4.22k
}
38
39
/*
40
 * hash_apply functions
41
 */
42
static int zend_remove_ini_entries(zval *el, void *arg) /* {{{ */
43
0
{
44
0
  const zend_ini_entry *ini_entry = (zend_ini_entry *)Z_PTR_P(el);
45
0
  int module_number = *(int *)arg;
46
47
0
  return ini_entry->module_number == module_number;
48
0
}
49
/* }}} */
50
51
static zend_result zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int stage) /* {{{ */
52
56.5k
{
53
56.5k
  zend_result result = FAILURE;
54
55
56.5k
  if (ini_entry->modified) {
56
56.5k
    if (ini_entry->on_modify) {
57
56.5k
      zend_try {
58
      /* even if on_modify bails out, we have to continue on with restoring,
59
        since there can be allocated variables that would be freed on MM shutdown
60
        and would lead to memory corruption later ini entry is modified again */
61
56.5k
        result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage);
62
56.5k
      } zend_end_try();
63
56.5k
    }
64
56.5k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
65
      /* runtime failure is OK */
66
0
      return FAILURE;
67
0
    }
68
56.5k
    if (ini_entry->value != ini_entry->orig_value) {
69
634
      zend_string_release(ini_entry->value);
70
634
    }
71
56.5k
    ini_entry->value = ini_entry->orig_value;
72
56.5k
    ini_entry->modifiable = ini_entry->orig_modifiable;
73
56.5k
    ini_entry->modified = false;
74
56.5k
    ini_entry->orig_value = NULL;
75
56.5k
    ini_entry->orig_modifiable = false;
76
56.5k
  }
77
56.5k
  return SUCCESS;
78
56.5k
}
79
/* }}} */
80
81
static void free_ini_entry(zval *zv) /* {{{ */
82
0
{
83
0
  zend_ini_entry *entry = (zend_ini_entry*)Z_PTR_P(zv);
84
85
0
  zend_string_release_ex(entry->name, true);
86
0
  if (entry->value) {
87
0
    zend_string_release(entry->value);
88
0
  }
89
0
  if (entry->orig_value) {
90
0
    zend_string_release_ex(entry->orig_value, true);
91
0
  }
92
0
  free(entry);
93
0
}
94
/* }}} */
95
96
/*
97
 * Startup / shutdown
98
 */
99
ZEND_API void zend_ini_startup(void) /* {{{ */
100
16
{
101
16
  registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable));
102
103
16
  EG(ini_directives) = registered_zend_ini_directives;
104
16
  EG(modified_ini_directives) = NULL;
105
16
  EG(error_reporting_ini_entry) = NULL;
106
16
  zend_hash_init(registered_zend_ini_directives, 128, NULL, free_ini_entry, true);
107
16
}
108
/* }}} */
109
110
ZEND_API void zend_ini_shutdown(void) /* {{{ */
111
0
{
112
0
  zend_ini_dtor(EG(ini_directives));
113
0
}
114
/* }}} */
115
116
ZEND_API void zend_ini_dtor(HashTable *ini_directives) /* {{{ */
117
0
{
118
0
  zend_hash_destroy(ini_directives);
119
0
  free(ini_directives);
120
0
}
121
/* }}} */
122
123
ZEND_API void zend_ini_global_shutdown(void) /* {{{ */
124
0
{
125
0
  zend_hash_destroy(registered_zend_ini_directives);
126
0
  free(registered_zend_ini_directives);
127
0
}
128
/* }}} */
129
130
ZEND_API void zend_ini_deactivate(void) /* {{{ */
131
224k
{
132
224k
  if (EG(modified_ini_directives)) {
133
56.0k
    zend_ini_entry *ini_entry;
134
135
225k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
136
225k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
137
225k
    } ZEND_HASH_FOREACH_END();
138
56.0k
    zend_hash_destroy(EG(modified_ini_directives));
139
56.0k
    FREE_HASHTABLE(EG(modified_ini_directives));
140
56.0k
    EG(modified_ini_directives) = NULL;
141
56.0k
  }
142
224k
}
143
/* }}} */
144
145
#ifdef ZTS
146
static void copy_ini_entry(zval *zv) /* {{{ */
147
{
148
  zend_ini_entry *old_entry = (zend_ini_entry*)Z_PTR_P(zv);
149
  zend_ini_entry *new_entry = pemalloc(sizeof(zend_ini_entry), true);
150
151
  Z_PTR_P(zv) = new_entry;
152
  memcpy(new_entry, old_entry, sizeof(zend_ini_entry));
153
  if (old_entry->name) {
154
    new_entry->name = zend_string_dup(old_entry->name, true);
155
  }
156
  if (old_entry->value) {
157
    new_entry->value = zend_string_dup(old_entry->value, true);
158
  }
159
  if (old_entry->orig_value) {
160
    new_entry->orig_value = zend_string_dup(old_entry->orig_value, true);
161
  }
162
}
163
/* }}} */
164
165
ZEND_API void zend_copy_ini_directives(void) /* {{{ */
166
{
167
  EG(modified_ini_directives) = NULL;
168
  EG(error_reporting_ini_entry) = NULL;
169
  EG(ini_directives) = (HashTable *) malloc(sizeof(HashTable));
170
  zend_hash_init(EG(ini_directives), registered_zend_ini_directives->nNumOfElements, NULL, free_ini_entry, true);
171
  zend_hash_copy(EG(ini_directives), registered_zend_ini_directives, copy_ini_entry);
172
}
173
/* }}} */
174
#endif
175
176
static int ini_key_compare(Bucket *f, Bucket *s) /* {{{ */
177
4.10k
{
178
4.10k
  if (!f->key && !s->key) { /* both numeric */
179
0
    if (f->h > s->h) {
180
0
      return -1;
181
0
    } else if (f->h < s->h) {
182
0
      return 1;
183
0
    }
184
0
    return 0;
185
4.10k
  } else if (!f->key) { /* f is numeric, s is not */
186
0
    return -1;
187
4.10k
  } else if (!s->key) { /* s is numeric, f is not */
188
0
    return 1;
189
4.10k
  } else { /* both strings */
190
4.10k
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
191
4.10k
  }
192
4.10k
}
193
/* }}} */
194
195
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
196
4
{
197
4
  zend_hash_sort(EG(ini_directives), ini_key_compare, false);
198
4
}
199
/* }}} */
200
201
/*
202
 * Registration / unregistration
203
 */
204
ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_entry, int module_number, int module_type) /* {{{ */
205
160
{
206
160
  zend_ini_entry *p;
207
160
  zval *default_value;
208
160
  HashTable *directives = registered_zend_ini_directives;
209
210
#ifdef ZTS
211
  /* if we are called during the request, eg: from dl(),
212
   * then we should not touch the global directives table,
213
   * and should update the per-(request|thread) version instead.
214
   * This solves two problems: one is that ini entries for dl()'d
215
   * extensions will now work, and the second is that updating the
216
   * global hash here from dl() is not mutex protected and can
217
   * lead to death.
218
   */
219
  if (directives != EG(ini_directives)) {
220
    directives = EG(ini_directives);
221
  } else {
222
    ZEND_ASSERT(module_type == MODULE_PERSISTENT);
223
  }
224
#endif
225
226
3.00k
  while (ini_entry->name) {
227
2.84k
    p = pemalloc(sizeof(zend_ini_entry), true);
228
2.84k
    p->def = ini_entry;
229
2.84k
    p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, true);
230
2.84k
    p->on_modify = ini_entry->on_modify;
231
2.84k
    p->mh_arg1 = ini_entry->mh_arg1;
232
2.84k
    p->mh_arg2 = ini_entry->mh_arg2;
233
2.84k
    p->mh_arg3 = ini_entry->mh_arg3;
234
2.84k
    p->value = NULL;
235
2.84k
    p->orig_value = NULL;
236
2.84k
    p->displayer = ini_entry->displayer;
237
2.84k
    p->modifiable = ini_entry->modifiable;
238
239
2.84k
    p->orig_modifiable = false;
240
2.84k
    p->modified = false;
241
2.84k
    p->module_number = module_number;
242
243
2.84k
    if (zend_hash_add_ptr(directives, p->name, (void*)p) == NULL) {
244
0
      if (p->name) {
245
0
        zend_string_release_ex(p->name, true);
246
0
      }
247
0
      pefree(p, true);
248
0
      zend_unregister_ini_entries_ex(module_number, module_type);
249
0
      return FAILURE;
250
0
    }
251
252
2.84k
    zend_string *prev_value = p->value;
253
254
2.84k
    if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
255
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)) {
256
257
      /* Skip assigning the value if the handler has already done so. */
258
176
      if (p->value == prev_value) {
259
176
        p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
260
176
      }
261
2.67k
    } else {
262
2.67k
      p->value = ini_entry->value ?
263
2.67k
        zend_string_init_interned(ini_entry->value, ini_entry->value_length, true) : NULL;
264
265
2.67k
      if (p->on_modify) {
266
2.43k
        p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
267
2.43k
      }
268
2.67k
    }
269
2.84k
    ini_entry++;
270
2.84k
  }
271
160
  return SUCCESS;
272
160
}
273
/* }}} */
274
275
ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
276
0
{
277
0
  zend_module_entry *module;
278
279
  /* Module is likely to be the last one in the list */
280
0
  ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
281
0
    if (module->module_number == module_number) {
282
0
      return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
283
0
    }
284
0
  } ZEND_HASH_FOREACH_END();
285
286
0
  return FAILURE;
287
0
}
288
/* }}} */
289
290
ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
291
0
{
292
0
  static HashTable *ini_directives;
293
294
0
  if (module_type == MODULE_TEMPORARY) {
295
0
    ini_directives = EG(ini_directives);
296
0
  } else {
297
0
    ini_directives = registered_zend_ini_directives;
298
0
  }
299
300
0
  zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
301
0
}
302
/* }}} */
303
304
ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
305
0
{
306
0
  zend_module_entry *module;
307
308
  /* Module is likely to be the last one in the list */
309
0
  ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
310
0
    if (module->module_number == module_number) {
311
0
      zend_unregister_ini_entries_ex(module_number, module->type);
312
0
      return;
313
0
    }
314
0
  } ZEND_HASH_FOREACH_END();
315
0
}
316
/* }}} */
317
318
#ifdef ZTS
319
ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
320
{
321
  zend_ini_entry *p;
322
323
  ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
324
    if (p->on_modify) {
325
      p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
326
    }
327
  } ZEND_HASH_FOREACH_END();
328
}
329
/* }}} */
330
#endif
331
332
ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
333
0
{
334
335
0
  return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
336
0
}
337
/* }}} */
338
339
ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
340
55.9k
{
341
55.9k
  zend_result ret;
342
55.9k
  zend_string *new_value;
343
344
55.9k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
345
55.9k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
346
55.9k
  zend_string_release(new_value);
347
55.9k
  return ret;
348
55.9k
}
349
/* }}} */
350
351
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) /* {{{ */
352
0
{
353
0
  zend_result ret;
354
0
  zend_string *new_value;
355
356
0
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
357
0
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
358
0
  zend_string_release(new_value);
359
0
  return ret;
360
0
}
361
/* }}} */
362
363
ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
364
56.9k
{
365
56.9k
  zend_ini_entry *ini_entry;
366
56.9k
  zend_string *duplicate;
367
56.9k
  uint8_t modifiable;
368
56.9k
  bool modified;
369
370
56.9k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
371
547
    return FAILURE;
372
547
  }
373
374
56.3k
  modifiable = ini_entry->modifiable;
375
56.3k
  modified = ini_entry->modified;
376
377
56.3k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
378
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
379
0
  }
380
381
56.3k
  if (!force_change) {
382
56.3k
    if (!(ini_entry->modifiable & modify_type)) {
383
0
      return FAILURE;
384
0
    }
385
56.3k
  }
386
387
56.3k
  if (!EG(modified_ini_directives)) {
388
55.9k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
389
55.9k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, false);
390
55.9k
  }
391
56.3k
  if (!modified) {
392
56.2k
    ini_entry->orig_value = ini_entry->value;
393
56.2k
    ini_entry->orig_modifiable = modifiable;
394
56.2k
    ini_entry->modified = true;
395
56.2k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
396
56.2k
  }
397
398
56.3k
  zend_string *prev_value = ini_entry->value;
399
56.3k
  duplicate = zend_string_copy(new_value);
400
401
56.3k
  if (!ini_entry->on_modify
402
56.3k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
403
410
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
404
116
      zend_string_release(prev_value);
405
116
    }
406
    /* Skip assigning the value if the handler has already done so. */
407
410
    if (ini_entry->value == prev_value) {
408
410
      ini_entry->value = duplicate;
409
410
    } else {
410
0
      zend_string_release(duplicate);
411
0
    }
412
55.9k
  } else {
413
55.9k
    zend_string_release(duplicate);
414
55.9k
    return FAILURE;
415
55.9k
  }
416
417
410
  return SUCCESS;
418
56.3k
}
419
/* }}} */
420
421
ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
422
0
{
423
0
  zend_ini_entry *ini_entry;
424
425
0
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
426
0
    (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
427
0
    return FAILURE;
428
0
  }
429
430
0
  if (EG(modified_ini_directives)) {
431
0
    if (zend_restore_ini_entry_cb(ini_entry, stage) == SUCCESS) {
432
0
      zend_hash_del(EG(modified_ini_directives), name);
433
0
    } else {
434
0
      return FAILURE;
435
0
    }
436
0
  }
437
438
0
  return SUCCESS;
439
0
}
440
/* }}} */
441
442
ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
443
0
{
444
0
  zend_ini_entry *ini_entry;
445
446
0
  ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
447
0
  if (ini_entry == NULL) {
448
0
    return FAILURE;
449
0
  }
450
451
0
  ini_entry->displayer = displayer;
452
0
  return SUCCESS;
453
0
}
454
/* }}} */
455
456
/*
457
 * Data retrieval
458
 */
459
460
ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, bool orig) /* {{{ */
461
1.12k
{
462
1.12k
  zend_ini_entry *ini_entry;
463
464
1.12k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
465
1.12k
  if (ini_entry) {
466
1.12k
    if (orig && ini_entry->modified) {
467
0
      return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
468
1.12k
    } else {
469
1.12k
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
470
1.12k
    }
471
1.12k
  }
472
473
0
  return 0;
474
1.12k
}
475
/* }}} */
476
477
ZEND_API double zend_ini_double(const char *name, size_t name_length, bool orig) /* {{{ */
478
0
{
479
0
  zend_ini_entry *ini_entry;
480
481
0
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
482
0
  if (ini_entry) {
483
0
    if (orig && ini_entry->modified) {
484
0
      return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
485
0
    } else {
486
0
      return (double) (ini_entry->value      ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL)      : 0.0);
487
0
    }
488
0
  }
489
490
0
  return 0.0;
491
0
}
492
/* }}} */
493
494
ZEND_API const char *zend_ini_string_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
495
0
{
496
0
  zend_string *str = zend_ini_str_ex(name, name_length, orig, exists);
497
498
0
  return str ? ZSTR_VAL(str) : NULL;
499
0
}
500
/* }}} */
501
502
ZEND_API const char *zend_ini_string(const char *name, size_t name_length, bool orig) /* {{{ */
503
165k
{
504
165k
  zend_string *str = zend_ini_str(name, name_length, orig);
505
506
165k
  return str ? ZSTR_VAL(str) : NULL;
507
165k
}
508
/* }}} */
509
510
511
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
512
167k
{
513
167k
  zend_ini_entry *ini_entry;
514
515
167k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
516
167k
  if (ini_entry) {
517
167k
    if (exists) {
518
167k
      *exists = true;
519
167k
    }
520
521
167k
    if (orig && ini_entry->modified) {
522
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
523
167k
    } else {
524
167k
      return ini_entry->value ? ini_entry->value : NULL;
525
167k
    }
526
167k
  } else {
527
0
    if (exists) {
528
0
      *exists = false;
529
0
    }
530
0
    return NULL;
531
0
  }
532
167k
}
533
/* }}} */
534
535
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
536
167k
{
537
167k
  bool exists = true;
538
167k
  zend_string *return_value;
539
540
167k
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
541
167k
  if (!exists) {
542
0
    return NULL;
543
167k
  } else if (!return_value) {
544
1.05k
    return_value = ZSTR_EMPTY_ALLOC();
545
1.05k
  }
546
167k
  return return_value;
547
167k
}
548
/* }}} */
549
550
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
551
969
{
552
969
  zend_ini_entry *ini_entry;
553
554
969
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
555
969
  if (ini_entry) {
556
386
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
557
583
  } else {
558
583
    return NULL;
559
583
  }
560
969
}
561
/* }}} */
562
563
ZEND_API bool zend_ini_parse_bool(const zend_string *str)
564
1.41k
{
565
1.41k
  if (zend_string_equals_literal_ci(str, "true")
566
1.41k
      || zend_string_equals_literal_ci(str, "yes")
567
1.41k
      || zend_string_equals_literal_ci(str, "on")
568
1.41k
  ) {
569
0
    return true;
570
1.41k
  } else {
571
1.41k
    return atoi(ZSTR_VAL(str)) != 0;
572
1.41k
  }
573
1.41k
}
574
575
typedef enum {
576
  ZEND_INI_PARSE_QUANTITY_SIGNED,
577
  ZEND_INI_PARSE_QUANTITY_UNSIGNED,
578
} zend_ini_parse_quantity_signed_result_t;
579
580
344
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
581
344
  const char *digits_consumed = digits;
582
  /* Ignore leading whitespace. */
583
431
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
584
344
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
585
135
    ++digits_consumed;
586
135
  }
587
588
344
  if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
589
    /* Value is just 0 */
590
274
    if ((digits_consumed+1) == str_end) {
591
189
      return digits_consumed;
592
189
    }
593
594
85
    switch (digits_consumed[1]) {
595
0
      case 'x':
596
0
      case 'X':
597
0
      case 'o':
598
0
      case 'O':
599
0
        digits_consumed += 2;
600
0
        break;
601
23
      case 'b':
602
49
      case 'B':
603
49
        if (base != 16) {
604
          /* 0b or 0B is valid in base 16, but not in the other supported bases. */
605
0
          digits_consumed += 2;
606
0
        }
607
49
        break;
608
85
    }
609
85
  }
610
155
  return digits_consumed;
611
344
}
612
613
static zend_ulong zend_ini_parse_quantity_internal(const zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
614
1.39k
{
615
1.39k
  char *digits_end = NULL;
616
1.39k
  const char *str = ZSTR_VAL(value);
617
1.39k
  const char *str_end = &str[ZSTR_LEN(value)];
618
1.39k
  const char *digits = str;
619
1.39k
  bool overflow = false;
620
1.39k
  zend_ulong factor;
621
1.39k
  smart_str invalid = {0};
622
1.39k
  smart_str interpreted = {0};
623
1.39k
  smart_str chr = {0};
624
625
  /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
626
   * but we need the position of the first non-whitespace later. */
627
1.44k
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
628
629
  /* Ignore trailing whitespace */
630
1.74k
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
631
632
1.39k
  if (digits == str_end) {
633
7
    *errstr = NULL;
634
7
    return 0;
635
7
  }
636
637
1.39k
  bool is_negative = false;
638
1.39k
  if (digits[0] == '+') {
639
5
    ++digits;
640
1.38k
  } else if (digits[0] == '-') {
641
106
    is_negative = true;
642
106
    ++digits;
643
106
  }
644
645
  /* if there is no digit after +/- */
646
1.39k
  if (!isdigit(digits[0])) {
647
    /* Escape the string to avoid null bytes and to make non-printable chars
648
     * visible */
649
18
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
650
18
    smart_str_0(&invalid);
651
652
18
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
653
18
            ZSTR_VAL(invalid.s));
654
655
18
    smart_str_free(&invalid);
656
18
    return 0;
657
18
  }
658
659
1.37k
  int base = 0;
660
1.37k
  if (digits[0] == '0' && !isdigit(digits[1])) {
661
    /* Value is just 0 */
662
521
    if ((digits+1) == str_end) {
663
173
      *errstr = NULL;
664
173
      return 0;
665
173
    }
666
667
348
    switch (digits[1]) {
668
      /* Multiplier suffixes */
669
0
      case 'g':
670
0
      case 'G':
671
0
      case 'm':
672
0
      case 'M':
673
1
      case 'k':
674
1
      case 'K':
675
1
        goto evaluation;
676
181
      case 'x':
677
181
      case 'X':
678
181
        base = 16;
679
181
        break;
680
49
      case 'o':
681
49
      case 'O':
682
49
        base = 8;
683
49
        break;
684
114
      case 'b':
685
114
      case 'B':
686
114
        base = 2;
687
114
        break;
688
3
      default:
689
3
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
690
3
          digits[1]);
691
3
        return 0;
692
348
        }
693
344
        digits += 2;
694
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
695
344
    if (UNEXPECTED(digits == str_end || digits != zend_ini_consume_quantity_prefix(digits, str_end, base))) {
696
      /* Escape the string to avoid null bytes and to make non-printable chars
697
       * visible */
698
222
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
699
222
      smart_str_0(&invalid);
700
701
222
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
702
222
              ZSTR_VAL(invalid.s));
703
704
222
      smart_str_free(&invalid);
705
222
      return 0;
706
222
    }
707
344
  }
708
976
  evaluation:
709
710
976
  errno = 0;
711
976
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
712
713
976
  if (errno == ERANGE) {
714
8
    overflow = true;
715
968
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
716
187
    if (is_negative) {
717
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
718
17
      if (retval == 1 && digits_end == str_end) {
719
16
        retval = -1;
720
16
      } else {
721
1
        overflow = true;
722
1
      }
723
17
    }
724
781
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
725
    /* Handle PHP_INT_MIN case */
726
781
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
727
0
      retval = 0u - retval;
728
781
    } else if ((zend_long) retval < 0) {
729
0
      overflow = true;
730
781
    } else if (is_negative) {
731
87
      retval = 0u - retval;
732
87
    }
733
781
  }
734
735
976
  if (UNEXPECTED(digits_end == digits)) {
736
    /* No leading digits */
737
738
    /* Escape the string to avoid null bytes and to make non-printable chars
739
     * visible */
740
5
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
741
5
    smart_str_0(&invalid);
742
743
5
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
744
5
            ZSTR_VAL(invalid.s));
745
746
5
    smart_str_free(&invalid);
747
5
    return 0;
748
5
  }
749
750
  /* Allow for whitespace between integer portion and any suffix character */
751
1.33k
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
752
753
  /* No exponent suffix. */
754
971
  if (digits_end == str_end) {
755
717
    goto end;
756
717
  }
757
758
254
  switch (*(str_end-1)) {
759
0
    case 'g':
760
2
    case 'G':
761
2
      factor = 1<<30;
762
2
      break;
763
2
    case 'm':
764
197
    case 'M':
765
197
      factor = 1<<20;
766
197
      break;
767
0
    case 'k':
768
16
    case 'K':
769
16
      factor = 1<<10;
770
16
      break;
771
39
    default:
772
      /* Unknown suffix */
773
39
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
774
39
      smart_str_0(&invalid);
775
39
      smart_str_append_escaped(&interpreted, str, digits_end - str);
776
39
      smart_str_0(&interpreted);
777
39
      smart_str_append_escaped(&chr, str_end-1, 1);
778
39
      smart_str_0(&chr);
779
780
39
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
781
39
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
782
783
39
      smart_str_free(&invalid);
784
39
      smart_str_free(&interpreted);
785
39
      smart_str_free(&chr);
786
787
39
      return retval;
788
254
  }
789
790
215
  if (!overflow) {
791
211
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
792
66
      zend_long sretval = (zend_long)retval;
793
66
      if (sretval > 0) {
794
66
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
795
66
      } else {
796
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
797
0
      }
798
145
    } else {
799
145
      overflow = retval > ZEND_ULONG_MAX / factor;
800
145
    }
801
211
  }
802
803
215
  retval *= factor;
804
805
215
  if (UNEXPECTED(digits_end != str_end-1)) {
806
    /* More than one character in suffix */
807
19
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
808
19
    smart_str_0(&invalid);
809
19
    smart_str_append_escaped(&interpreted, str, digits_end - str);
810
19
    smart_str_0(&interpreted);
811
19
    smart_str_append_escaped(&chr, str_end-1, 1);
812
19
    smart_str_0(&chr);
813
814
19
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
815
19
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
816
817
19
    smart_str_free(&invalid);
818
19
    smart_str_free(&interpreted);
819
19
    smart_str_free(&chr);
820
821
19
    return retval;
822
19
  }
823
824
913
end:
825
913
  if (UNEXPECTED(overflow)) {
826
7
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
827
7
    smart_str_0(&invalid);
828
829
    /* Not specifying the resulting value here because the caller may make
830
     * additional conversions. Not specifying the allowed range
831
     * because the caller may do narrower range checks. */
832
7
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
833
7
            ZSTR_VAL(invalid.s));
834
835
7
    smart_str_free(&invalid);
836
7
    smart_str_free(&interpreted);
837
7
    smart_str_free(&chr);
838
839
7
    return retval;
840
7
  }
841
842
906
  *errstr = NULL;
843
906
  return retval;
844
913
}
845
/* }}} */
846
847
ZEND_API zend_long zend_ini_parse_quantity(const zend_string *value, zend_string **errstr) /* {{{ */
848
1.17k
{
849
1.17k
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
850
1.17k
}
851
/* }}} */
852
853
ZEND_API zend_ulong zend_ini_parse_uquantity(const zend_string *value, zend_string **errstr) /* {{{ */
854
222
{
855
222
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
856
222
}
857
/* }}} */
858
859
ZEND_API zend_long zend_ini_parse_quantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
860
819
{
861
819
  zend_string *errstr;
862
819
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
863
864
819
  if (errstr) {
865
0
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
866
0
    zend_string_release(errstr);
867
0
  }
868
869
819
  return retval;
870
819
}
871
/* }}} */
872
873
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(const zend_string *value, zend_string *setting) /* {{{ */
874
222
{
875
222
  zend_string *errstr;
876
222
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
877
878
222
  if (errstr) {
879
55
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
880
55
    zend_string_release(errstr);
881
55
  }
882
883
222
  return retval;
884
222
}
885
/* }}} */
886
887
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
888
376
{
889
376
  bool value;
890
891
376
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
892
0
    value = zend_ini_parse_bool(ini_entry->orig_value);
893
376
  } else if (ini_entry->value) {
894
376
    value = zend_ini_parse_bool(ini_entry->value);
895
376
  } else {
896
0
    value = false;
897
0
  }
898
899
376
  if (value) {
900
160
    ZEND_PUTS("On");
901
216
  } else {
902
216
    ZEND_PUTS("Off");
903
216
  }
904
376
}
905
/* }}} */
906
907
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
908
40
{
909
40
  const char *value;
910
911
40
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
912
0
    value = ZSTR_VAL(ini_entry->orig_value);
913
40
  } else if (ini_entry->value) {
914
40
    value = ZSTR_VAL(ini_entry->value);
915
40
  } else {
916
0
    value = NULL;
917
0
  }
918
40
  if (value) {
919
40
    if (zend_uv.html_errors) {
920
40
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
921
40
    } else {
922
0
      ZEND_PUTS(value);
923
0
    }
924
40
  } else {
925
0
    if (zend_uv.html_errors) {
926
0
      ZEND_PUTS(NO_VALUE_HTML);
927
0
    } else {
928
0
      ZEND_PUTS(NO_VALUE_PLAINTEXT);
929
0
    }
930
0
  }
931
40
}
932
/* }}} */
933
934
ZEND_INI_DISP(display_link_numbers) /* {{{ */
935
0
{
936
0
  const char *value;
937
938
0
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
939
0
    value = ZSTR_VAL(ini_entry->orig_value);
940
0
  } else if (ini_entry->value) {
941
0
    value = ZSTR_VAL(ini_entry->value);
942
0
  } else {
943
0
    value = NULL;
944
0
  }
945
946
0
  if (value) {
947
0
    if (atoi(value) == -1) {
948
0
      ZEND_PUTS("Unlimited");
949
0
    } else {
950
0
      zend_printf("%s", value);
951
0
    }
952
0
  }
953
0
}
954
/* }}} */
955
956
/* Standard message handlers */
957
ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
958
684
{
959
684
  bool *p = ZEND_INI_GET_ADDR();
960
684
  *p = zend_ini_parse_bool(new_value);
961
684
  return SUCCESS;
962
684
}
963
/* }}} */
964
965
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
966
432
{
967
432
  zend_long *p = ZEND_INI_GET_ADDR();
968
432
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
969
432
  return SUCCESS;
970
432
}
971
/* }}} */
972
973
ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
974
32
{
975
32
  zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
976
32
  if (tmp < 0) {
977
0
    return FAILURE;
978
0
  }
979
980
32
  zend_long *p = ZEND_INI_GET_ADDR();
981
32
  *p = tmp;
982
983
32
  return SUCCESS;
984
32
}
985
/* }}} */
986
987
ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
988
16
{
989
16
  double *p = ZEND_INI_GET_ADDR();
990
16
  *p = zend_strtod(ZSTR_VAL(new_value), NULL);
991
16
  return SUCCESS;
992
16
}
993
/* }}} */
994
995
ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
996
512
{
997
512
  char **p = ZEND_INI_GET_ADDR();
998
512
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
999
512
  return SUCCESS;
1000
512
}
1001
/* }}} */
1002
1003
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1004
220
{
1005
220
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1006
48
    return FAILURE;
1007
48
  }
1008
1009
172
  char **p = ZEND_INI_GET_ADDR();
1010
172
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1011
172
  return SUCCESS;
1012
220
}
1013
/* }}} */
1014
1015
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1016
16
{
1017
16
  zend_string **p = ZEND_INI_GET_ADDR();
1018
16
  *p = new_value;
1019
16
  return SUCCESS;
1020
16
}
1021
/* }}} */
1022
1023
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1024
42
{
1025
42
  if (new_value && ZSTR_LEN(new_value) == 0) {
1026
4
    return FAILURE;
1027
4
  }
1028
1029
38
  zend_string **p = ZEND_INI_GET_ADDR();
1030
38
  *p = new_value;
1031
38
  return SUCCESS;
1032
42
}
1033
/* }}} */