Coverage Report

Created: 2025-12-31 07:28

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