Coverage Report

Created: 2025-11-16 06:23

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
3.27k
static inline bool zend_is_whitespace(char c) {
36
3.27k
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
37
3.27k
}
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
71.6k
{
53
71.6k
  zend_result result = FAILURE;
54
55
71.6k
  if (ini_entry->modified) {
56
71.6k
    if (ini_entry->on_modify) {
57
71.6k
      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
71.6k
        result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage);
62
71.6k
      } zend_end_try();
63
71.6k
    }
64
71.6k
    if (stage == ZEND_INI_STAGE_RUNTIME && result == FAILURE) {
65
      /* runtime failure is OK */
66
0
      return FAILURE;
67
0
    }
68
71.6k
    if (ini_entry->value != ini_entry->orig_value) {
69
619
      zend_string_release(ini_entry->value);
70
619
    }
71
71.6k
    ini_entry->value = ini_entry->orig_value;
72
71.6k
    ini_entry->modifiable = ini_entry->orig_modifiable;
73
71.6k
    ini_entry->modified = 0;
74
71.6k
    ini_entry->orig_value = NULL;
75
71.6k
    ini_entry->orig_modifiable = 0;
76
71.6k
  }
77
71.6k
  return SUCCESS;
78
71.6k
}
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
247k
{
132
247k
  if (EG(modified_ini_directives)) {
133
71.0k
    zend_ini_entry *ini_entry;
134
135
285k
    ZEND_HASH_MAP_FOREACH_PTR(EG(modified_ini_directives), ini_entry) {
136
285k
      zend_restore_ini_entry_cb(ini_entry, ZEND_INI_STAGE_DEACTIVATE);
137
285k
    } ZEND_HASH_FOREACH_END();
138
71.0k
    zend_hash_destroy(EG(modified_ini_directives));
139
71.0k
    FREE_HASHTABLE(EG(modified_ini_directives));
140
71.0k
    EG(modified_ini_directives) = NULL;
141
71.0k
  }
142
247k
}
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
7.81k
{
178
7.81k
  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
7.81k
  } else if (!f->key) { /* f is numeric, s is not */
186
0
    return -1;
187
7.81k
  } else if (!s->key) { /* s is numeric, f is not */
188
0
    return 1;
189
7.81k
  } else { /* both strings */
190
7.81k
    return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
191
7.81k
  }
192
7.81k
}
193
/* }}} */
194
195
ZEND_API void zend_ini_sort_entries(void) /* {{{ */
196
8
{
197
8
  zend_hash_sort(EG(ini_directives), ini_key_compare, 0);
198
8
}
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), 1);
228
2.84k
    p->def = ini_entry;
229
2.84k
    p->name = zend_string_init_interned(ini_entry->name, ini_entry->name_length, 1);
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 = 0;
240
2.84k
    p->modified = 0;
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, 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
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, 1) : 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
70.9k
{
341
70.9k
  zend_result ret;
342
70.9k
  zend_string *new_value;
343
344
70.9k
  new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST));
345
70.9k
  ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, false);
346
70.9k
  zend_string_release(new_value);
347
70.9k
  return ret;
348
70.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, 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
71.9k
{
365
71.9k
  zend_ini_entry *ini_entry;
366
71.9k
  zend_string *duplicate;
367
71.9k
  uint8_t modifiable;
368
71.9k
  bool modified;
369
370
71.9k
  if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) {
371
543
    return FAILURE;
372
543
  }
373
374
71.3k
  modifiable = ini_entry->modifiable;
375
71.3k
  modified = ini_entry->modified;
376
377
71.3k
  if (stage == ZEND_INI_STAGE_ACTIVATE && modify_type == ZEND_INI_SYSTEM) {
378
0
    ini_entry->modifiable = ZEND_INI_SYSTEM;
379
0
  }
380
381
71.3k
  if (!force_change) {
382
71.3k
    if (!(ini_entry->modifiable & modify_type)) {
383
0
      return FAILURE;
384
0
    }
385
71.3k
  }
386
387
71.3k
  if (!EG(modified_ini_directives)) {
388
70.9k
    ALLOC_HASHTABLE(EG(modified_ini_directives));
389
70.9k
    zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
390
70.9k
  }
391
71.3k
  if (!modified) {
392
71.2k
    ini_entry->orig_value = ini_entry->value;
393
71.2k
    ini_entry->orig_modifiable = modifiable;
394
71.2k
    ini_entry->modified = 1;
395
71.2k
    zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry);
396
71.2k
  }
397
398
71.3k
  zend_string *prev_value = ini_entry->value;
399
71.3k
  duplicate = zend_string_copy(new_value);
400
401
71.3k
  if (!ini_entry->on_modify
402
71.3k
    || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
403
395
    if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */
404
112
      zend_string_release(prev_value);
405
112
    }
406
    /* Skip assigning the value if the handler has already done so. */
407
395
    if (ini_entry->value == prev_value) {
408
395
      ini_entry->value = duplicate;
409
395
    } else {
410
0
      zend_string_release(duplicate);
411
0
    }
412
70.9k
  } else {
413
70.9k
    zend_string_release(duplicate);
414
70.9k
    return FAILURE;
415
70.9k
  }
416
417
395
  return SUCCESS;
418
71.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) == 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
1.02k
{
462
1.02k
  zend_ini_entry *ini_entry;
463
464
1.02k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
465
1.02k
  if (ini_entry) {
466
1.02k
    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.02k
    } else {
469
1.02k
      return (ini_entry->value      ? ZEND_STRTOL(ZSTR_VAL(ini_entry->value), NULL, 0)      : 0);
470
1.02k
    }
471
1.02k
  }
472
473
0
  return 0;
474
1.02k
}
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
92.3k
{
496
92.3k
  zend_string *str = zend_ini_str_ex(name, name_length, orig, exists);
497
498
92.3k
  return str ? ZSTR_VAL(str) : NULL;
499
92.3k
}
500
/* }}} */
501
502
ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */
503
23
{
504
23
  zend_string *str = zend_ini_str(name, name_length, orig);
505
506
23
  return str ? ZSTR_VAL(str) : NULL;
507
23
}
508
/* }}} */
509
510
511
ZEND_API zend_string *zend_ini_str_ex(const char *name, size_t name_length, bool orig, bool *exists) /* {{{ */
512
92.4k
{
513
92.4k
  zend_ini_entry *ini_entry;
514
515
92.4k
  ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length);
516
92.4k
  if (ini_entry) {
517
92.4k
    if (exists) {
518
23
      *exists = true;
519
23
    }
520
521
92.4k
    if (orig && ini_entry->modified) {
522
0
      return ini_entry->orig_value ? ini_entry->orig_value : NULL;
523
92.4k
    } else {
524
92.4k
      return ini_entry->value ? ini_entry->value : NULL;
525
92.4k
    }
526
92.4k
  } else {
527
0
    if (exists) {
528
0
      *exists = false;
529
0
    }
530
0
    return NULL;
531
0
  }
532
92.4k
}
533
/* }}} */
534
535
ZEND_API zend_string *zend_ini_str(const char *name, size_t name_length, bool orig) /* {{{ */
536
23
{
537
23
  bool exists = true;
538
23
  zend_string *return_value;
539
540
23
  return_value = zend_ini_str_ex(name, name_length, orig, &exists);
541
23
  if (!exists) {
542
0
    return NULL;
543
23
  } else if (!return_value) {
544
0
    return_value = ZSTR_EMPTY_ALLOC();
545
0
  }
546
23
  return return_value;
547
23
}
548
/* }}} */
549
550
ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */
551
961
{
552
961
  zend_ini_entry *ini_entry;
553
554
961
  ini_entry = zend_hash_find_ptr(EG(ini_directives), name);
555
961
  if (ini_entry) {
556
385
    return ini_entry->value ? ini_entry->value : ZSTR_EMPTY_ALLOC();
557
576
  } else {
558
576
    return NULL;
559
576
  }
560
961
}
561
/* }}} */
562
563
ZEND_API bool zend_ini_parse_bool(zend_string *str)
564
1.82k
{
565
1.82k
  if (zend_string_equals_literal_ci(str, "true")
566
1.82k
      || zend_string_equals_literal_ci(str, "yes")
567
1.82k
      || zend_string_equals_literal_ci(str, "on")
568
1.82k
  ) {
569
0
    return 1;
570
1.82k
  } else {
571
1.82k
    return atoi(ZSTR_VAL(str)) != 0;
572
1.82k
  }
573
1.82k
}
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
221
static const char *zend_ini_consume_quantity_prefix(const char *const digits, const char *const str_end, int base) {
581
221
  const char *digits_consumed = digits;
582
  /* Ignore leading whitespace. */
583
238
  while (digits_consumed < str_end && zend_is_whitespace(*digits_consumed)) {++digits_consumed;}
584
221
  if (digits_consumed[0] == '+' || digits_consumed[0] == '-') {
585
30
    ++digits_consumed;
586
30
  }
587
588
221
  if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
589
    /* Value is just 0 */
590
127
    if ((digits_consumed+1) == str_end) {
591
47
      return digits_consumed;
592
47
    }
593
594
80
    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
40
      case 'b':
602
76
      case 'B':
603
76
        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
76
        break;
608
80
    }
609
80
  }
610
174
  return digits_consumed;
611
221
}
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
1.22k
{
615
1.22k
  char *digits_end = NULL;
616
1.22k
  char *str = ZSTR_VAL(value);
617
1.22k
  char *str_end = &str[ZSTR_LEN(value)];
618
1.22k
  char *digits = str;
619
1.22k
  bool overflow = false;
620
1.22k
  zend_ulong factor;
621
1.22k
  smart_str invalid = {0};
622
1.22k
  smart_str interpreted = {0};
623
1.22k
  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.26k
  while (digits < str_end && zend_is_whitespace(*digits)) {++digits;}
628
629
  /* Ignore trailing whitespace */
630
1.42k
  while (digits < str_end && zend_is_whitespace(*(str_end-1))) {--str_end;}
631
632
1.22k
  if (digits == str_end) {
633
7
    *errstr = NULL;
634
7
    return 0;
635
7
  }
636
637
1.22k
  bool is_negative = false;
638
1.21k
  if (digits[0] == '+') {
639
5
    ++digits;
640
1.20k
  } else if (digits[0] == '-') {
641
117
    is_negative = true;
642
117
    ++digits;
643
117
  }
644
645
  /* if there is no digit after +/- */
646
1.21k
  if (!isdigit(digits[0])) {
647
    /* Escape the string to avoid null bytes and to make non-printable chars
648
     * visible */
649
14
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
650
14
    smart_str_0(&invalid);
651
652
14
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no valid leading digits, interpreting as \"0\" for backwards compatibility",
653
14
            ZSTR_VAL(invalid.s));
654
655
14
    smart_str_free(&invalid);
656
14
    return 0;
657
14
  }
658
659
1.19k
  int base = 0;
660
1.19k
  if (digits[0] == '0' && !isdigit(digits[1])) {
661
    /* Value is just 0 */
662
389
    if ((digits+1) == str_end) {
663
168
      *errstr = NULL;
664
168
      return 0;
665
168
    }
666
667
221
    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
191
      case 'x':
677
191
      case 'X':
678
191
        base = 16;
679
191
        break;
680
15
      case 'o':
681
15
      case 'O':
682
15
        base = 8;
683
15
        break;
684
15
      case 'b':
685
15
      case 'B':
686
15
        base = 2;
687
15
        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
221
        }
693
221
        digits += 2;
694
    /* STRTOULL may silently ignore a prefix of whitespace, sign, and base prefix, which would be invalid at this position */
695
221
    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
47
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
699
47
      smart_str_0(&invalid);
700
701
47
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": no digits after base prefix, interpreting as \"0\" for backwards compatibility",
702
47
              ZSTR_VAL(invalid.s));
703
704
47
      smart_str_free(&invalid);
705
47
      return 0;
706
47
    }
707
221
  }
708
984
  evaluation:
709
710
984
  errno = 0;
711
984
  zend_ulong retval = ZEND_STRTOUL(digits, &digits_end, base);
712
713
984
  if (errno == ERANGE) {
714
4
    overflow = true;
715
980
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_UNSIGNED) {
716
148
    if (is_negative) {
717
      /* Ignore "-1" as it is commonly used as max value, for instance in memory_limit=-1. */
718
16
      if (retval == 1 && digits_end == str_end) {
719
16
        retval = -1;
720
16
      } else {
721
0
        overflow = true;
722
0
      }
723
16
    }
724
832
  } else if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
725
    /* Handle PHP_INT_MIN case */
726
832
    if (is_negative && retval == ((zend_ulong)ZEND_LONG_MAX +1)) {
727
0
      retval = 0u - retval;
728
832
    } else if ((zend_long) retval < 0) {
729
0
      overflow = true;
730
832
    } else if (is_negative) {
731
99
      retval = 0u - retval;
732
99
    }
733
832
  }
734
735
984
  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
1.13k
  while (digits_end < str_end && zend_is_whitespace(*digits_end)) ++digits_end;
752
753
  /* No exponent suffix. */
754
978
  if (digits_end == str_end) {
755
760
    goto end;
756
760
  }
757
758
218
  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
157
    case 'M':
765
157
      factor = 1<<20;
766
157
      break;
767
0
    case 'k':
768
16
    case 'K':
769
16
      factor = 1<<10;
770
16
      break;
771
45
    default:
772
      /* Unknown suffix */
773
45
      smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
774
45
      smart_str_0(&invalid);
775
45
      smart_str_append_escaped(&interpreted, str, digits_end - str);
776
45
      smart_str_0(&interpreted);
777
45
      smart_str_append_escaped(&chr, str_end-1, 1);
778
45
      smart_str_0(&chr);
779
780
45
      *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": unknown multiplier \"%s\", interpreting as \"%s\" for backwards compatibility",
781
45
            ZSTR_VAL(invalid.s), ZSTR_VAL(chr.s), ZSTR_VAL(interpreted.s));
782
783
45
      smart_str_free(&invalid);
784
45
      smart_str_free(&interpreted);
785
45
      smart_str_free(&chr);
786
787
45
      return retval;
788
218
  }
789
790
173
  if (!overflow) {
791
171
    if (signed_result == ZEND_INI_PARSE_QUANTITY_SIGNED) {
792
64
      zend_long sretval = (zend_long)retval;
793
64
      if (sretval > 0) {
794
64
        overflow = (zend_long)retval > ZEND_LONG_MAX / (zend_long)factor;
795
64
      } else {
796
0
        overflow = (zend_long)retval < ZEND_LONG_MIN / (zend_long)factor;
797
0
      }
798
107
    } else {
799
107
      overflow = retval > ZEND_ULONG_MAX / factor;
800
107
    }
801
171
  }
802
803
173
  retval *= factor;
804
805
173
  if (UNEXPECTED(digits_end != str_end-1)) {
806
    /* More than one character in suffix */
807
3
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
808
3
    smart_str_0(&invalid);
809
3
    smart_str_append_escaped(&interpreted, str, digits_end - str);
810
3
    smart_str_0(&interpreted);
811
3
    smart_str_append_escaped(&chr, str_end-1, 1);
812
3
    smart_str_0(&chr);
813
814
3
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\", interpreting as \"%s%s\" for backwards compatibility",
815
3
            ZSTR_VAL(invalid.s), ZSTR_VAL(interpreted.s), ZSTR_VAL(chr.s));
816
817
3
    smart_str_free(&invalid);
818
3
    smart_str_free(&interpreted);
819
3
    smart_str_free(&chr);
820
821
3
    return retval;
822
3
  }
823
824
930
end:
825
930
  if (UNEXPECTED(overflow)) {
826
6
    smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value));
827
6
    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
6
    *errstr = zend_strpprintf(0, "Invalid quantity \"%s\": value is out of range, using overflow result for backwards compatibility",
833
6
            ZSTR_VAL(invalid.s));
834
835
6
    smart_str_free(&invalid);
836
6
    smart_str_free(&interpreted);
837
6
    smart_str_free(&chr);
838
839
6
    return retval;
840
6
  }
841
842
924
  *errstr = NULL;
843
924
  return retval;
844
930
}
845
/* }}} */
846
847
ZEND_API zend_long zend_ini_parse_quantity(zend_string *value, zend_string **errstr) /* {{{ */
848
1.04k
{
849
1.04k
  return (zend_long) zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_SIGNED, errstr);
850
1.04k
}
851
/* }}} */
852
853
ZEND_API zend_ulong zend_ini_parse_uquantity(zend_string *value, zend_string **errstr) /* {{{ */
854
172
{
855
172
  return zend_ini_parse_quantity_internal(value, ZEND_INI_PARSE_QUANTITY_UNSIGNED, errstr);
856
172
}
857
/* }}} */
858
859
ZEND_API zend_long zend_ini_parse_quantity_warn(zend_string *value, zend_string *setting) /* {{{ */
860
828
{
861
828
  zend_string *errstr;
862
828
  zend_long retval = zend_ini_parse_quantity(value, &errstr);
863
864
828
  if (errstr) {
865
5
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
866
5
    zend_string_release(errstr);
867
5
  }
868
869
828
  return retval;
870
828
}
871
/* }}} */
872
873
ZEND_API zend_ulong zend_ini_parse_uquantity_warn(zend_string *value, zend_string *setting) /* {{{ */
874
172
{
875
172
  zend_string *errstr;
876
172
  zend_ulong retval = zend_ini_parse_uquantity(value, &errstr);
877
878
172
  if (errstr) {
879
30
    zend_error(E_WARNING, "Invalid \"%s\" setting. %s", ZSTR_VAL(setting), ZSTR_VAL(errstr));
880
30
    zend_string_release(errstr);
881
30
  }
882
883
172
  return retval;
884
172
}
885
/* }}} */
886
887
ZEND_INI_DISP(zend_ini_boolean_displayer_cb) /* {{{ */
888
752
{
889
752
  int value;
890
752
  zend_string *tmp_value;
891
892
752
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
893
0
    tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL );
894
752
  } else if (ini_entry->value) {
895
752
    tmp_value = ini_entry->value;
896
752
  } else {
897
0
    tmp_value = NULL;
898
0
  }
899
900
752
  if (tmp_value) {
901
752
    value = zend_ini_parse_bool(tmp_value);
902
752
  } else {
903
0
    value = 0;
904
0
  }
905
906
752
  if (value) {
907
320
    ZEND_PUTS("On");
908
432
  } else {
909
432
    ZEND_PUTS("Off");
910
432
  }
911
752
}
912
/* }}} */
913
914
ZEND_INI_DISP(zend_ini_color_displayer_cb) /* {{{ */
915
80
{
916
80
  char *value;
917
918
80
  if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) {
919
0
    value = ZSTR_VAL(ini_entry->orig_value);
920
80
  } else if (ini_entry->value) {
921
80
    value = ZSTR_VAL(ini_entry->value);
922
80
  } else {
923
0
    value = NULL;
924
0
  }
925
80
  if (value) {
926
80
    if (zend_uv.html_errors) {
927
80
      zend_printf("<span style=\"color: %s\">%s</span>", value, value);
928
80
    } else {
929
0
      ZEND_PUTS(value);
930
0
    }
931
80
  } 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
80
}
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
684
{
966
684
  bool *p = (bool *) ZEND_INI_GET_ADDR();
967
684
  *p = zend_ini_parse_bool(new_value);
968
684
  return SUCCESS;
969
684
}
970
/* }}} */
971
972
ZEND_API ZEND_INI_MH(OnUpdateLong) /* {{{ */
973
432
{
974
432
  zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
975
432
  *p = zend_ini_parse_quantity_warn(new_value, entry->name);
976
432
  return SUCCESS;
977
432
}
978
/* }}} */
979
980
ZEND_API ZEND_INI_MH(OnUpdateLongGEZero) /* {{{ */
981
32
{
982
32
  zend_long tmp = zend_ini_parse_quantity_warn(new_value, entry->name);
983
32
  if (tmp < 0) {
984
0
    return FAILURE;
985
0
  }
986
987
32
  zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
988
32
  *p = tmp;
989
990
32
  return SUCCESS;
991
32
}
992
/* }}} */
993
994
ZEND_API ZEND_INI_MH(OnUpdateReal) /* {{{ */
995
16
{
996
16
  double *p = (double *) ZEND_INI_GET_ADDR();
997
16
  *p = zend_strtod(ZSTR_VAL(new_value), NULL);
998
16
  return SUCCESS;
999
16
}
1000
/* }}} */
1001
1002
ZEND_API ZEND_INI_MH(OnUpdateString) /* {{{ */
1003
512
{
1004
512
  char **p = (char **) ZEND_INI_GET_ADDR();
1005
512
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1006
512
  return SUCCESS;
1007
512
}
1008
/* }}} */
1009
1010
ZEND_API ZEND_INI_MH(OnUpdateStringUnempty) /* {{{ */
1011
190
{
1012
190
  if (new_value && !ZSTR_VAL(new_value)[0]) {
1013
48
    return FAILURE;
1014
48
  }
1015
1016
142
  char **p = (char **) ZEND_INI_GET_ADDR();
1017
142
  *p = new_value ? ZSTR_VAL(new_value) : NULL;
1018
142
  return SUCCESS;
1019
190
}
1020
/* }}} */
1021
1022
ZEND_API ZEND_INI_MH(OnUpdateStr) /* {{{ */
1023
16
{
1024
16
  zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1025
16
  *p = new_value;
1026
16
  return SUCCESS;
1027
16
}
1028
/* }}} */
1029
1030
ZEND_API ZEND_INI_MH(OnUpdateStrNotEmpty) /* {{{ */
1031
44
{
1032
44
  if (new_value && ZSTR_LEN(new_value) == 0) {
1033
6
    return FAILURE;
1034
6
  }
1035
1036
38
  zend_string **p = (zend_string **) ZEND_INI_GET_ADDR();
1037
38
  *p = new_value;
1038
38
  return SUCCESS;
1039
44
}
1040
/* }}} */