Coverage Report

Created: 2025-06-13 06:43

/src/php-src/Zend/zend_ini.c
Line
Count
Source (jump to first uncovered line)
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
1.83k
static inline bool zend_is_whitespace(char c) {
36
1.83k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
37
1.83k
}
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
81.7k
{
53
81.7k
  zend_result result = FAILURE;
54
55
81.7k
  if (ini_entry->modified) {
56
81.7k
    if (ini_entry->on_modify) {
57
81.7k
      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
81.7k
        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
81.7k
      } zend_end_try();
63
81.7k
    }
64
81.7k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
65
      /* runtime failure is OK */
66
0
      return FAILURE;
67
0
    }
68
81.7k
    if (ini_entry->value != ini_entry->orig_value) {
69
625
      zend_string_release(ini_entry->value);
70
625
    }
71
81.7k
    ini_entry->value = ini_entry->orig_value;
72
81.7k
    ini_entry->modifiable = ini_entry->orig_modifiable;
73
81.7k
    ini_entry->modified = 0;
74
81.7k
    ini_entry->orig_value = NULL;
75
81.7k
    ini_entry->orig_modifiable = 0;
76
81.7k
  }
77
81.7k
  return SUCCESS;
78
81.7k
}
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
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, 1);
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
300k
{
132
300k
  if (EG(modified_ini_directives)) {
133
81.2k
    zend_ini_entry *ini_entry;
134
135
325k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
136
325k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
137
325k
    } ZEND_HASH_FOREACH_END();
138
81.2k
    zend_hash_destroy(EG(modified_ini_directives));
139
81.2k
    FREE_HASHTABLE(EG(modified_ini_directives));
140
81.2k
    EG(modified_ini_directives) = NULL;
141
81.2k
  }
142
300k
}
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
4.84k
{
178
4.84k
  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.84k
  } else if (!f->key) { /* f is numeric, s is not */
186
0
    return -1;
187
4.84k
  } else if (!s->key) { /* s is numeric, f is not */
188
0
    return 1;
189
4.84k
  } else { /* both strings */
190
4.84k
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
191
4.84k
  }
192
4.84k
}
193
/* }}} */
194
195
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
196
5
{
197
5
  zend_hash_sort(EG(ini_directives), ini_key_compare, 0);
198
5
}
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
148
{
206
148
  zend_ini_entry *p;
207
148
  zval *default_value;
208
148
  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
2.33k
  while (ini_entry->name) {
227
2.18k
    p = pemalloc(sizeof(zend_ini_entry), 1);
228
2.18k
    p->def = ini_entry;
229
2.18k
    p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, 1);
230
2.18k
    p->on_modify = ini_entry->on_modify;
231
2.18k
    p->mh_arg1 = ini_entry->mh_arg1;
232
2.18k
    p->mh_arg2 = ini_entry->mh_arg2;
233
2.18k
    p->mh_arg3 = ini_entry->mh_arg3;
234
2.18k
    p->value = NULL;
235
2.18k
    p->orig_value = NULL;
236
2.18k
    p->displayer = ini_entry->displayer;
237
2.18k
    p->modifiable = ini_entry->modifiable;
238
239
2.18k
    p->orig_modifiable = 0;
240
2.18k
    p->modified = 0;
241
2.18k
    p->module_number = module_number;
242
243
2.18k
    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
2.18k
    if (((default_value = zend_get_configuration_directive(p->name)) != NULL) &&
252
2.18k
        (!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)) {
253
254
192
      p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value)));
255
1.99k
    } else {
256
1.99k
      p->value = ini_entry->value ?
257
1.60k
        zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL;
258
259
1.99k
      if (p->on_modify) {
260
1.75k
        p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP);
261
1.75k
      }
262
1.99k
    }
263
2.18k
    ini_entry++;
264
2.18k
  }
265
148
  return SUCCESS;
266
148
}
267
/* }}} */
268
269
ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_entry, int module_number) /* {{{ */
270
0
{
271
0
  zend_module_entry *module;
272
273
  /* Module is likely to be the last one in the list */
274
0
  ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
275
0
    if (module->module_number == module_number) {
276
0
      return zend_register_ini_entries_ex(ini_entry, module_number, module->type);
277
0
    }
278
0
  } ZEND_HASH_FOREACH_END();
279
280
0
  return FAILURE;
281
0
}
282
/* }}} */
283
284
ZEND_API void zend_unregister_ini_entries_ex(int module_number, int module_type) /* {{{ */
285
0
{
286
0
  static HashTable *ini_directives;
287
288
0
  if (module_type == MODULE_TEMPORARY) {
289
0
    ini_directives = EG(ini_directives);
290
0
  } else {
291
0
    ini_directives = registered_zend_ini_directives;
292
0
  }
293
294
0
  zend_hash_apply_with_argument(ini_directives, zend_remove_ini_entries, (void *) &module_number);
295
0
}
296
/* }}} */
297
298
ZEND_API void zend_unregister_ini_entries(int module_number) /* {{{ */
299
0
{
300
0
  zend_module_entry *module;
301
302
  /* Module is likely to be the last one in the list */
303
0
  ZEND_HASH_REVERSE_FOREACH_PTR(&module_registry, module) {
304
0
    if (module->module_number == module_number) {
305
0
      zend_unregister_ini_entries_ex(module_number, module->type);
306
0
      return;
307
0
    }
308
0
  } ZEND_HASH_FOREACH_END();
309
0
}
310
/* }}} */
311
312
#ifdef ZTS
313
ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */
314
{
315
  zend_ini_entry *p;
316
317
  ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), p) {
318
    if (p->on_modify) {
319
      p->on_modify(p, p->value, p->mh_arg1, p->mh_arg2, p->mh_arg3, stage);
320
    }
321
  } ZEND_HASH_FOREACH_END();
322
}
323
/* }}} */
324
#endif
325
326
ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */
327
0
{
328
329
0
  return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
330
0
}
331
/* }}} */
332
333
ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage) /* {{{ */
334
81.1k
{
335
81.1k
  zend_result ret;
336
81.1k
  zend_string *new_value;
337
338
81.1k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
339
81.1k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0);
340
81.1k
  zend_string_release(new_value);
341
81.1k
  return ret;
342
81.1k
}
343
/* }}} */
344
345
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) /* {{{ */
346
10
{
347
10
  zend_result ret;
348
10
  zend_string *new_value;
349
350
10
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
351
10
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change);
352
10
  zend_string_release(new_value);
353
10
  return ret;
354
10
}
355
/* }}} */
356
357
ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */
358
82.0k
{
359
82.0k
  zend_ini_entry *ini_entry;
360
82.0k
  zend_string *duplicate;
361
82.0k
  uint8_t modifiable;
362
82.0k
  bool modified;
363
364
82.0k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
365
539
    return FAILURE;
366
539
  }
367
368
81.5k
  modifiable = ini_entry->modifiable;
369
81.5k
  modified = ini_entry->modified;
370
371
81.5k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
372
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
373
0
  }
374
375
81.5k
  if (!force_change) {
376
81.5k
    if (!(ini_entry->modifiable & modify_type)) {
377
0
      return FAILURE;
378
0
    }
379
81.5k
  }
380
381
81.5k
  if (!EG(modified_ini_directives)) {
382
81.1k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
383
81.1k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
384
81.1k
  }
385
81.5k
  if (!modified) {
386
81.4k
    ini_entry->orig_value = ini_entry->value;
387
81.4k
    ini_entry->orig_modifiable = modifiable;
388
81.4k
    ini_entry->modified = 1;
389
81.4k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
390
81.4k
  }
391
392
81.5k
  duplicate = zend_string_copy(new_value);
393
394
81.5k
  if (!ini_entry->on_modify
395
81.5k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
396
416
    if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */
397
114
      zend_string_release(ini_entry->value);
398
114
    }
399
416
    ini_entry->value = duplicate;
400
81.1k
  } else {
401
81.1k
    zend_string_release(duplicate);
402
81.1k
    return FAILURE;
403
81.1k
  }
404
405
416
  return SUCCESS;
406
81.5k
}
407
/* }}} */
408
409
ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage) /* {{{ */
410
0
{
411
0
  zend_ini_entry *ini_entry;
412
413
0
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL ||
414
0
    (stage == ZEND_INI_STAGE_RUNTIME && (ini_entry->modifiable & ZEND_INI_USER) == 0)) {
415
0
    return FAILURE;
416
0
  }
417
418
0
  if (EG(modified_ini_directives)) {
419
0
    if (zend_restore_ini_entry_cb(ini_entry, stage) == 0) {
420
0
      zend_hash_del(EG(modified_ini_directives), name);
421
0
    } else {
422
0
      return FAILURE;
423
0
    }
424
0
  }
425
426
0
  return SUCCESS;
427
0
}
428
/* }}} */
429
430
ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)) /* {{{ */
431
0
{
432
0
  zend_ini_entry *ini_entry;
433
434
0
  ini_entry = zend_hash_str_find_ptr(registered_zend_ini_directives, name, name_length);
435
0
  if (ini_entry == NULL) {
436
0
    return FAILURE;
437
0
  }
438
439
0
  ini_entry->displayer = displayer;
440
0
  return SUCCESS;
441
0
}
442
/* }}} */
443
444
/*
445
 * Data retrieval
446
 */
447
448
ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig) /* {{{ */
449
723
{
450
723
  zend_ini_entry *ini_entry;
451
452
723
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
453
723
  if (ini_entry) {
454
723
    if (orig && ini_entry->modified) {
455
0
      return (ini_entry->orig_value ? ZEND_STRTOL(ZSTR_VAL(ini_entry->orig_value), NULL, 0) : 0);
456
723
    } else {
457
723
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
458
723
    }
459
723
  }
460
461
0
  return 0;
462
723
}
463
/* }}} */
464
465
ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) /* {{{ */
466
0
{
467
0
  zend_ini_entry *ini_entry;
468
469
0
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
470
0
  if (ini_entry) {
471
0
    if (orig && ini_entry->modified) {
472
0
      return (double) (ini_entry->orig_value ? zend_strtod(ZSTR_VAL(ini_entry->orig_value), NULL) : 0.0);
473
0
    } else {
474
0
      return (double) (ini_entry->value      ? zend_strtod(ZSTR_VAL(ini_entry->value), NULL)      : 0.0);
475
0
    }
476
0
  }
477
478
0
  return 0.0;
479
0
}
480
/* }}} */
481
482
ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */
483
121k
{
484
121k
  zend_string *str = zend_ini_str_ex(name, name_length, orig, exists);
485
486
121k
  return str ? ZSTR_VAL(str) : NULL;
487
121k
}
488
/* }}} */
489
490
ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
491
25
{
492
25
  zend_string *str = zend_ini_str(name, name_length, orig);
493
494
25
  return str ? ZSTR_VAL(str) : NULL;
495
25
}
496
/* }}} */
497
498
499
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
500
121k
{
501
121k
  zend_ini_entry *ini_entry;
502
503
121k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
504
121k
  if (ini_entry) {
505
121k
    if (exists) {
506
25
      *exists = 1;
507
25
    }
508
509
121k
    if (orig && ini_entry->modified) {
510
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
511
121k
    } else {
512
121k
      return ini_entry->value ? ini_entry->value : NULL;
513
121k
    }
514
121k
  } else {
515
0
    if (exists) {
516
0
      *exists = 0;
517
0
    }
518
0
    return NULL;
519
0
  }
520
121k
}
521
/* }}} */
522
523
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
524
25
{
525
25
  bool exists = 1;
526
25
  zend_string *return_value;
527
528
25
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
529
25
  if (!exists) {
530
0
    return NULL;
531
25
  } else if (!return_value) {
532
0
    return_value = ZSTR_EMPTY_ALLOC();
533
0
  }
534
25
  return return_value;
535
25
}
536
/* }}} */
537
538
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
539
929
{
540
929
  zend_ini_entry *ini_entry;
541
542
929
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
543
929
  if (ini_entry) {
544
366
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
545
563
  } else {
546
563
    return NULL;
547
563
  }
548
929
}
549
/* }}} */
550
551
ZEND_API bool zend_ini_parse_bool(zend_string *str)
552
1.32k
{
553
1.32k
  if (zend_string_equals_literal_ci(str, "true")
554
1.32k
      || zend_string_equals_literal_ci(str, "yes")
555
1.32k
      || zend_string_equals_literal_ci(str, "on")
556
1.32k
  ) {
557
0
    return 1;
558
1.32k
  } else {
559
1.32k
    return atoi(ZSTR_VAL(str)) != 0;
560
1.32k
  }
561
1.32k
}
562
563
typedef enum {
564
  ZEND_INI_PARSE_QUANTITY_SIGNED,
565
  ZEND_INI_PARSE_QUANTITY_UNSIGNED,
566
} zend_ini_parse_quantity_signed_result_t;
567
568
140
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
569
140
  const char *digits_consumed = digits;
570
  /* Ignore leading whitespace. */
571
161
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
572
140
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
573
40
    ++digits_consumed;
574
40
  }
575
576
140
  if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
577
    /* Value is just 0 */
578
101
    if ((digits_consumed+1) == str_end) {
579
61
      return digits_consumed;
580
61
    }
581
582
40
    switch (digits_consumed[1]) {
583
0
      case 'x':
584
0
      case 'X':
585
0
      case 'o':
586
0
      case 'O':
587
0
        digits_consumed += 2;
588
0
        break;
589
15
      case 'b':
590
36
      case 'B':
591
36
        if (base != 16) {
592
          /* 0b or 0B is valid in base 16, but not in the other supported bases. */
593
0
          digits_consumed += 2;
594
0
        }
595
36
        break;
596
40
    }
597
40
  }
598
79
  return digits_consumed;
599
140
}
600
601
static zend_ulong zend_ini_parse_quantity_internal(zend_string *value, zend_ini_parse_quantity_signed_result_t signed_result, zend_string **errstr) /* {{{ */
602
769
{
603
769
  char *digits_end = NULL;
604
769
  char *str = ZSTR_VAL(value);
605
769
  char *str_end = &str[ZSTR_LEN(value)];
606
769
  char *digits = str;
607
769
  bool overflow = false;
608
769
  zend_ulong factor;
609
769
  smart_str invalid = {0};
610
769
  smart_str interpreted = {0};
611
769
  smart_str chr = {0};
612
613
  /* Ignore leading whitespace. ZEND_STRTOL() also skips leading whitespaces,
614
   * but we need the position of the first non-whitespace later. */
615
769
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
616
617
  /* Ignore trailing whitespace */
618
769
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
619
620
769
  if (digits == str_end) {
621
5
    *errstr = NULL;
622
5
    return 0;
623
5
  }
624
625
764
  bool is_negative = false;
626
764
  if (digits[0] == '+') {
627
4
    ++digits;
628
760
  } else if (digits[0] == '-') {
629
66
    is_negative = true;
630
66
    ++digits;
631
66
  }
632
633
  /* if there is no digit after +/- */
634
764
  if (!isdigit(digits[0])) {
635
    /* Escape the string to avoid null bytes and to make non-printable chars
636
     * visible */
637
6
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
638
6
    smart_str_0(&invalid);
639
640
6
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
641
6
            ZSTR_VAL(invalid.s));
642
643
6
    smart_str_free(&invalid);
644
6
    return 0;
645
6
  }
646
647
758
  int base = 0;
648
758
  if (digits[0] == '0' && !isdigit(digits[1])) {
649
    /* Value is just 0 */
650
261
    if ((digits+1) == str_end) {
651
119
      *errstr = NULL;
652
119
      return 0;
653
119
    }
654
655
142
    switch (digits[1]) {
656
      /* Multiplier suffixes */
657
0
      case 'g':
658
0
      case 'G':
659
0
      case 'm':
660
0
      case 'M':
661
0
      case 'k':
662
0
      case 'K':
663
0
        goto evaluation;
664
98
      case 'x':
665
98
      case 'X':
666
98
        base = 16;
667
98
        break;
668
21
      case 'o':
669
21
      case 'O':
670
21
        base = 8;
671
21
        break;
672
21
      case 'b':
673
21
      case 'B':
674
21
        base = 2;
675
21
        break;
676
2
      default:
677
2
        *errstr = zend_strpprintf(0, "Invalid prefix \"0%c\", interpreting as \"0\" for backwards compatibility",
678
2
          digits[1]);
679
2
        return 0;
680
142
        }
681
140
        digits += 2;
682
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
683
140
    if (UNEXPECTED(digits == str_end || digits != zend_ini_consume_quantity_prefix(digits, str_end, base))) {
684
      /* Escape the string to avoid null bytes and to make non-printable chars
685
       * visible */
686
61
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
687
61
      smart_str_0(&invalid);
688
689
61
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
690
61
              ZSTR_VAL(invalid.s));
691
692
61
      smart_str_free(&invalid);
693
61
      return 0;
694
61
    }
695
140
  }
696
576
  evaluation:
697
698
576
  errno = 0;
699
576
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
700
701
576
  if (errno == ERANGE) {
702
2
    overflow = true;
703
574
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
704
88
    if (is_negative) {
705
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
706
2
      if (retval == 1 && digits_end == str_end) {
707
0
        retval = -1;
708
2
      } else {
709
2
        overflow = true;
710
2
      }
711
2
    }
712
486
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
713
    /* Handle PHP_INT_MIN case */
714
486
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
715
0
      retval = 0u - retval;
716
486
    } else if ((zend_long) retval < 0) {
717
0
      overflow = true;
718
486
    } else if (is_negative) {
719
62
      retval = 0u - retval;
720
62
    }
721
486
  }
722
723
576
  if (UNEXPECTED(digits_end == digits)) {
724
    /* No leading digits */
725
726
    /* Escape the string to avoid null bytes and to make non-printable chars
727
     * visible */
728
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
729
0
    smart_str_0(&invalid);
730
731
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
732
0
            ZSTR_VAL(invalid.s));
733
734
0
    smart_str_free(&invalid);
735
0
    return 0;
736
0
  }
737
738
  /* Allow for whitespace between integer portion and any suffix character */
739
576
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
740
741
  /* No exponent suffix. */
742
576
  if (digits_end == str_end) {
743
431
    goto end;
744
431
  }
745
746
145
  switch (*(str_end-1)) {
747
0
    case 'g':
748
0
    case 'G':
749
0
      factor = 1<<30;
750
0
      break;
751
0
    case 'm':
752
119
    case 'M':
753
119
      factor = 1<<20;
754
119
      break;
755
0
    case 'k':
756
16
    case 'K':
757
16
      factor = 1<<10;
758
16
      break;
759
10
    default:
760
      /* Unknown suffix */
761
10
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
762
10
      smart_str_0(&invalid);
763
10
      smart_str_append_escaped(&interpreted, str, digits_end - str);
764
10
      smart_str_0(&interpreted);
765
10
      smart_str_append_escaped(&chr, str_end-1, 1);
766
10
      smart_str_0(&chr);
767
768
10
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
769
10
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
770
771
10
      smart_str_free(&invalid);
772
10
      smart_str_free(&interpreted);
773
10
      smart_str_free(&chr);
774
775
10
      return retval;
776
145
  }
777
778
135
  if (!overflow) {
779
131
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
780
52
      zend_long sretval = (zend_long)retval;
781
52
      if (sretval > 0) {
782
52
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
783
52
      } else {
784
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
785
0
      }
786
79
    } else {
787
79
      overflow = retval > ZEND_ULONG_MAX / factor;
788
79
    }
789
131
  }
790
791
135
  retval *= factor;
792
793
135
  if (UNEXPECTED(digits_end != str_end-1)) {
794
    /* More than one character in suffix */
795
0
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
796
0
    smart_str_0(&invalid);
797
0
    smart_str_append_escaped(&interpreted, str, digits_end - str);
798
0
    smart_str_0(&interpreted);
799
0
    smart_str_append_escaped(&chr, str_end-1, 1);
800
0
    smart_str_0(&chr);
801
802
0
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
803
0
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
804
805
0
    smart_str_free(&invalid);
806
0
    smart_str_free(&interpreted);
807
0
    smart_str_free(&chr);
808
809
0
    return retval;
810
0
  }
811
812
566
end:
813
566
  if (UNEXPECTED(overflow)) {
814
4
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
815
4
    smart_str_0(&invalid);
816
817
    /* Not specifying the resulting value here because the caller may make
818
     * additional conversions. Not specifying the allowed range
819
     * because the caller may do narrower range checks. */
820
4
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
821
4
            ZSTR_VAL(invalid.s));
822
823
4
    smart_str_free(&invalid);
824
4
    smart_str_free(&interpreted);
825
4
    smart_str_free(&chr);
826
827
4
    return retval;
828
4
  }
829
830
562
  *errstr = NULL;
831
562
  return retval;
832
566
}
833
/* }}} */
834
835
ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
836
661
{
837
661
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
838
661
}
839
/* }}} */
840
841
ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
842
108
{
843
108
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
844
108
}
845
/* }}} */
846
847
ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
848
523
{
849
523
  zend_string *errstr;
850
523
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
851
852
523
  if (errstr) {
853
9
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
854
9
    zend_string_release(errstr);
855
9
  }
856
857
523
  return retval;
858
523
}
859
/* }}} */
860
861
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
862
108
{
863
108
  zend_string *errstr;
864
108
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
865
866
108
  if (errstr) {
867
7
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
868
7
    zend_string_release(errstr);
869
7
  }
870
871
108
  return retval;
872
108
}
873
/* }}} */
874
875
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
876
470
{
877
470
  int value;
878
470
  zend_string *tmp_value;
879
880
470
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
881
0
    tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
882
470
  } else if (ini_entry->value) {
883
470
    tmp_value = ini_entry->value;
884
470
  } else {
885
0
    tmp_value = NULL;
886
0
  }
887
888
470
  if (tmp_value) {
889
470
    value = zend_ini_parse_bool(tmp_value);
890
470
  } else {
891
0
    value = 0;
892
0
  }
893
894
470
  if (value) {
895
210
    ZEND_PUTS("On");
896
260
  } else {
897
260
    ZEND_PUTS("Off");
898
260
  }
899
470
}
900
/* }}} */
901
902
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
903
50
{
904
50
  char *value;
905
906
50
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
907
0
    value = ZSTR_VAL(ini_entry->orig_value);
908
50
  } else if (ini_entry->value) {
909
50
    value = ZSTR_VAL(ini_entry->value);
910
50
  } else {
911
0
    value = NULL;
912
0
  }
913
50
  if (value) {
914
50
    if (zend_uv.html_errors) {
915
50
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
916
50
    } else {
917
0
      ZEND_PUTS(value);
918
0
    }
919
50
  } else {
920
0
    if (zend_uv.html_errors) {
921
0
      ZEND_PUTS(NO_VALUE_HTML);
922
0
    } else {
923
0
      ZEND_PUTS(NO_VALUE_PLAINTEXT);
924
0
    }
925
0
  }
926
50
}
927
/* }}} */
928
929
ZEND_INI_DISP(display_link_numbers) /* {{{ */
930
0
{
931
0
  char *value;
932
933
0
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
934
0
    value = ZSTR_VAL(ini_entry->orig_value);
935
0
  } else if (ini_entry->value) {
936
0
    value = ZSTR_VAL(ini_entry->value);
937
0
  } else {
938
0
    value = NULL;
939
0
  }
940
941
0
  if (value) {
942
0
    if (atoi(value) == -1) {
943
0
      ZEND_PUTS("Unlimited");
944
0
    } else {
945
0
      zend_printf("%s", value);
946
0
    }
947
0
  }
948
0
}
949
/* }}} */
950
951
/* Standard message handlers */
952
ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */
953
516
{
954
516
  bool *p = (bool *) ZEND_INI_GET_ADDR();
955
516
  *p = zend_ini_parse_bool(new_value);
956
516
  return SUCCESS;
957
516
}
958
/* }}} */
959
960
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
961
276
{
962
276
  zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
963
276
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
964
276
  return SUCCESS;
965
276
}
966
/* }}} */
967
968
ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
969
32
{
970
32
  zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
971
32
  if (tmp < 0) {
972
0
    return FAILURE;
973
0
  }
974
975
32
  zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
976
32
  *p = tmp;
977
978
32
  return SUCCESS;
979
32
}
980
/* }}} */
981
982
ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
983
4
{
984
4
  double *p = (double *) ZEND_INI_GET_ADDR();
985
4
  *p = zend_strtod(ZSTR_VAL(new_value), NULL);
986
4
  return SUCCESS;
987
4
}
988
/* }}} */
989
990
ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
991
472
{
992
472
  char **p = (char **) ZEND_INI_GET_ADDR();
993
472
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
994
472
  return SUCCESS;
995
472
}
996
/* }}} */
997
998
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
999
158
{
1000
158
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1001
12
    return FAILURE;
1002
12
  }
1003
1004
146
  char **p = (char **) ZEND_INI_GET_ADDR();
1005
146
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1006
146
  return SUCCESS;
1007
158
}
1008
/* }}} */
1009
1010
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1011
0
{
1012
0
  zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1013
0
  *p = new_value;
1014
0
  return SUCCESS;
1015
0
}
1016
/* }}} */
1017
1018
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1019
42
{
1020
42
  if (new_value && ZSTR_LEN(new_value) == 0) {
1021
5
    return FAILURE;
1022
5
  }
1023
1024
37
  zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1025
37
  *p = new_value;
1026
37
  return SUCCESS;
1027
42
}
1028
/* }}} */