Coverage Report

Created: 2025-12-14 06:09

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