Coverage Report

Created: 2022-10-14 11:23

/src/php-src/main/php_variables.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | http://www.php.net/license/3_01.txt                                  |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
14
   |          Zeev Suraski <zeev@php.net>                                 |
15
   +----------------------------------------------------------------------+
16
 */
17
18
#include <stdio.h>
19
#include "php.h"
20
#include "ext/standard/php_standard.h"
21
#include "ext/standard/credits.h"
22
#include "zend_smart_str.h"
23
#include "php_variables.h"
24
#include "php_globals.h"
25
#include "php_content_types.h"
26
#include "SAPI.h"
27
#include "zend_globals.h"
28
29
/* for systems that need to override reading of environment variables */
30
void _php_import_environment_variables(zval *array_ptr);
31
PHPAPI void (*php_import_environment_variables)(zval *array_ptr) = _php_import_environment_variables;
32
33
PHPAPI void php_register_variable(const char *var, const char *strval, zval *track_vars_array)
34
390k
{
35
390k
  php_register_variable_safe(var, strval, strlen(strval), track_vars_array);
36
390k
}
37
38
/* binary-safe version */
39
PHPAPI void php_register_variable_safe(const char *var, const char *strval, size_t str_len, zval *track_vars_array)
40
390k
{
41
390k
  zval new_entry;
42
390k
  assert(strval != NULL);
43
44
390k
  ZVAL_STRINGL_FAST(&new_entry, strval, str_len);
45
46
390k
  php_register_variable_ex(var, &new_entry, track_vars_array);
47
390k
}
48
49
static zend_always_inline void php_register_variable_quick(const char *name, size_t name_len, zval *val, HashTable *ht)
50
13.1k
{
51
13.1k
  zend_string *key = zend_string_init_interned(name, name_len, 0);
52
53
13.1k
  zend_hash_update_ind(ht, key, val);
54
13.1k
  zend_string_release_ex(key, 0);
55
13.1k
}
56
57
PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *track_vars_array)
58
390k
{
59
390k
  char *p = NULL;
60
390k
  char *ip = NULL;    /* index pointer */
61
390k
  char *index;
62
390k
  char *var, *var_orig;
63
390k
  size_t var_len, index_len;
64
390k
  zval gpc_element, *gpc_element_p;
65
390k
  zend_bool is_array = 0;
66
390k
  HashTable *symtable1 = NULL;
67
390k
  ALLOCA_FLAG(use_heap)
68
69
390k
  assert(var_name != NULL);
70
71
390k
  if (track_vars_array && Z_TYPE_P(track_vars_array) == IS_ARRAY) {
72
0
    symtable1 = Z_ARRVAL_P(track_vars_array);
73
0
  }
74
75
390k
  if (!symtable1) {
76
    /* Nothing to do */
77
390k
    zval_ptr_dtor_nogc(val);
78
390k
    return;
79
390k
  }
80
81
82
  /* ignore leading spaces in the variable name */
83
0
  while (*var_name==' ') {
84
0
    var_name++;
85
0
  }
86
87
  /*
88
   * Prepare variable name
89
   */
90
0
  var_len = strlen(var_name);
91
0
  var = var_orig = do_alloca(var_len + 1, use_heap);
92
0
  memcpy(var_orig, var_name, var_len + 1);
93
94
  /* ensure that we don't have spaces or dots in the variable name (not binary safe) */
95
0
  for (p = var; *p; p++) {
96
0
    if (*p == ' ' || *p == '.') {
97
0
      *p='_';
98
0
    } else if (*p == '[') {
99
0
      is_array = 1;
100
0
      ip = p;
101
0
      *p = 0;
102
0
      break;
103
0
    }
104
0
  }
105
0
  var_len = p - var;
106
107
0
  if (var_len==0) { /* empty variable name, or variable name with a space in it */
108
0
    zval_ptr_dtor_nogc(val);
109
0
    free_alloca(var_orig, use_heap);
110
0
    return;
111
0
  }
112
113
0
  if (var_len == sizeof("this")-1 && EG(current_execute_data)) {
114
0
    zend_execute_data *ex = EG(current_execute_data);
115
116
0
    while (ex) {
117
0
      if (ex->func && ZEND_USER_CODE(ex->func->common.type)) {
118
0
        if ((ZEND_CALL_INFO(ex) & ZEND_CALL_HAS_SYMBOL_TABLE)
119
0
            && ex->symbol_table == symtable1) {
120
0
          if (memcmp(var, "this", sizeof("this")-1) == 0) {
121
0
            zend_throw_error(NULL, "Cannot re-assign $this");
122
0
            zval_ptr_dtor_nogc(val);
123
0
            free_alloca(var_orig, use_heap);
124
0
            return;
125
0
          }
126
0
        }
127
0
        break;
128
0
      }
129
0
      ex = ex->prev_execute_data;
130
0
    }
131
0
  }
132
133
  /* GLOBALS hijack attempt, reject parameter */
134
0
  if (symtable1 == &EG(symbol_table) &&
135
0
    var_len == sizeof("GLOBALS")-1 &&
136
0
    !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) {
137
0
    zval_ptr_dtor_nogc(val);
138
0
    free_alloca(var_orig, use_heap);
139
0
    return;
140
0
  }
141
142
0
  index = var;
143
0
  index_len = var_len;
144
145
0
  if (is_array) {
146
0
    int nest_level = 0;
147
0
    while (1) {
148
0
      char *index_s;
149
0
      size_t new_idx_len = 0;
150
151
0
      if(++nest_level > PG(max_input_nesting_level)) {
152
0
        HashTable *ht;
153
        /* too many levels of nesting */
154
155
0
        if (track_vars_array) {
156
0
          ht = Z_ARRVAL_P(track_vars_array);
157
0
          zend_symtable_str_del(ht, var, var_len);
158
0
        }
159
160
0
        zval_ptr_dtor_nogc(val);
161
162
        /* do not output the error message to the screen,
163
         this helps us to to avoid "information disclosure" */
164
0
        if (!PG(display_errors)) {
165
0
          php_error_docref(NULL, E_WARNING, "Input variable nesting level exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level));
166
0
        }
167
0
        free_alloca(var_orig, use_heap);
168
0
        return;
169
0
      }
170
171
0
      ip++;
172
0
      index_s = ip;
173
0
      if (isspace(*ip)) {
174
0
        ip++;
175
0
      }
176
0
      if (*ip==']') {
177
0
        index_s = NULL;
178
0
      } else {
179
0
        ip = strchr(ip, ']');
180
0
        if (!ip) {
181
          /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */
182
0
          *(index_s - 1) = '_';
183
184
0
          index_len = 0;
185
0
          if (index) {
186
0
            index_len = strlen(index);
187
0
          }
188
0
          goto plain_var;
189
0
          return;
190
0
        }
191
0
        *ip = 0;
192
0
        new_idx_len = strlen(index_s);
193
0
      }
194
195
0
      if (!index) {
196
0
        array_init(&gpc_element);
197
0
        if ((gpc_element_p = zend_hash_next_index_insert(symtable1, &gpc_element)) == NULL) {
198
0
          zend_array_destroy(Z_ARR(gpc_element));
199
0
          zval_ptr_dtor_nogc(val);
200
0
          free_alloca(var_orig, use_heap);
201
0
          return;
202
0
        }
203
0
      } else {
204
0
        gpc_element_p = zend_symtable_str_find(symtable1, index, index_len);
205
0
        if (!gpc_element_p) {
206
0
          zval tmp;
207
0
          array_init(&tmp);
208
0
          gpc_element_p = zend_symtable_str_update_ind(symtable1, index, index_len, &tmp);
209
0
        } else {
210
0
          if (Z_TYPE_P(gpc_element_p) == IS_INDIRECT) {
211
0
            gpc_element_p = Z_INDIRECT_P(gpc_element_p);
212
0
          }
213
0
          if (Z_TYPE_P(gpc_element_p) != IS_ARRAY) {
214
0
            zval_ptr_dtor_nogc(gpc_element_p);
215
0
            array_init(gpc_element_p);
216
0
          } else {
217
0
            SEPARATE_ARRAY(gpc_element_p);
218
0
          }
219
0
        }
220
0
      }
221
0
      symtable1 = Z_ARRVAL_P(gpc_element_p);
222
      /* ip pointed to the '[' character, now obtain the key */
223
0
      index = index_s;
224
0
      index_len = new_idx_len;
225
226
0
      ip++;
227
0
      if (*ip == '[') {
228
0
        is_array = 1;
229
0
        *ip = 0;
230
0
      } else {
231
0
        goto plain_var;
232
0
      }
233
0
    }
234
0
  } else {
235
0
plain_var:
236
0
    if (!index) {
237
0
      if (zend_hash_next_index_insert(symtable1, val) == NULL) {
238
0
        zval_ptr_dtor_nogc(val);
239
0
      }
240
0
    } else {
241
0
      zend_ulong idx;
242
243
      /*
244
       * According to rfc2965, more specific paths are listed above the less specific ones.
245
       * If we encounter a duplicate cookie name, we should skip it, since it is not possible
246
       * to have the same (plain text) cookie name for the same path and we should not overwrite
247
       * more specific cookies with the less specific ones.
248
       */
249
0
      if (Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) != IS_UNDEF &&
250
0
        symtable1 == Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]) &&
251
0
        zend_symtable_str_exists(symtable1, index, index_len)) {
252
0
        zval_ptr_dtor_nogc(val);
253
0
      } else if (ZEND_HANDLE_NUMERIC_STR(index, index_len, idx)) {
254
0
        zend_hash_index_update(symtable1, idx, val);
255
0
      } else {
256
0
        php_register_variable_quick(index, index_len, val, symtable1);
257
0
      }
258
0
    }
259
0
  }
260
0
  free_alloca(var_orig, use_heap);
261
0
}
262
263
typedef struct post_var_data {
264
  smart_str str;
265
  char *ptr;
266
  char *end;
267
  uint64_t cnt;
268
269
  /* Bytes in ptr that have already been scanned for '&' */
270
  size_t already_scanned;
271
} post_var_data_t;
272
273
static zend_bool add_post_var(zval *arr, post_var_data_t *var, zend_bool eof)
274
0
{
275
0
  char *start, *ksep, *vsep, *val;
276
0
  size_t klen, vlen;
277
0
  size_t new_vlen;
278
279
0
  if (var->ptr >= var->end) {
280
0
    return 0;
281
0
  }
282
283
0
  start = var->ptr + var->already_scanned;
284
0
  vsep = memchr(start, '&', var->end - start);
285
0
  if (!vsep) {
286
0
    if (!eof) {
287
0
      var->already_scanned = var->end - var->ptr;
288
0
      return 0;
289
0
    } else {
290
0
      vsep = var->end;
291
0
    }
292
0
  }
293
294
0
  ksep = memchr(var->ptr, '=', vsep - var->ptr);
295
0
  if (ksep) {
296
0
    *ksep = '\0';
297
    /* "foo=bar&" or "foo=&" */
298
0
    klen = ksep - var->ptr;
299
0
    vlen = vsep - ++ksep;
300
0
  } else {
301
0
    ksep = "";
302
    /* "foo&" */
303
0
    klen = vsep - var->ptr;
304
0
    vlen = 0;
305
0
  }
306
307
0
  php_url_decode(var->ptr, klen);
308
309
0
  val = estrndup(ksep, vlen);
310
0
  if (vlen) {
311
0
    vlen = php_url_decode(val, vlen);
312
0
  }
313
314
0
  if (sapi_module.input_filter(PARSE_POST, var->ptr, &val, vlen, &new_vlen)) {
315
0
    php_register_variable_safe(var->ptr, val, new_vlen, arr);
316
0
  }
317
0
  efree(val);
318
319
0
  var->ptr = vsep + (vsep != var->end);
320
0
  var->already_scanned = 0;
321
0
  return 1;
322
0
}
323
324
static inline int add_post_vars(zval *arr, post_var_data_t *vars, zend_bool eof)
325
0
{
326
0
  uint64_t max_vars = PG(max_input_vars);
327
328
0
  vars->ptr = ZSTR_VAL(vars->str.s);
329
0
  vars->end = ZSTR_VAL(vars->str.s) + ZSTR_LEN(vars->str.s);
330
0
  while (add_post_var(arr, vars, eof)) {
331
0
    if (++vars->cnt > max_vars) {
332
0
      php_error_docref(NULL, E_WARNING,
333
0
          "Input variables exceeded %" PRIu64 ". "
334
0
          "To increase the limit change max_input_vars in php.ini.",
335
0
          max_vars);
336
0
      return FAILURE;
337
0
    }
338
0
  }
339
340
0
  if (!eof && ZSTR_VAL(vars->str.s) != vars->ptr) {
341
0
    memmove(ZSTR_VAL(vars->str.s), vars->ptr, ZSTR_LEN(vars->str.s) = vars->end - vars->ptr);
342
0
  }
343
0
  return SUCCESS;
344
0
}
345
346
#ifdef PHP_WIN32
347
#define SAPI_POST_HANDLER_BUFSIZ 16384
348
#else
349
0
# define SAPI_POST_HANDLER_BUFSIZ BUFSIZ
350
#endif
351
SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler)
352
0
{
353
0
  zval *arr = (zval *) arg;
354
0
  php_stream *s = SG(request_info).request_body;
355
0
  post_var_data_t post_data;
356
357
0
  if (s && SUCCESS == php_stream_rewind(s)) {
358
0
    memset(&post_data, 0, sizeof(post_data));
359
360
0
    while (!php_stream_eof(s)) {
361
0
      char buf[SAPI_POST_HANDLER_BUFSIZ] = {0};
362
0
      ssize_t len = php_stream_read(s, buf, SAPI_POST_HANDLER_BUFSIZ);
363
364
0
      if (len > 0) {
365
0
        smart_str_appendl(&post_data.str, buf, len);
366
367
0
        if (SUCCESS != add_post_vars(arr, &post_data, 0)) {
368
0
          smart_str_free(&post_data.str);
369
0
          return;
370
0
        }
371
0
      }
372
373
0
      if (len != SAPI_POST_HANDLER_BUFSIZ){
374
0
        break;
375
0
      }
376
0
    }
377
378
0
    if (post_data.str.s) {
379
0
      add_post_vars(arr, &post_data, 1);
380
0
      smart_str_free(&post_data.str);
381
0
    }
382
0
  }
383
0
}
384
#undef SAPI_POST_HANDLER_BUFSIZ
385
386
SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter)
387
0
{
388
  /* TODO: check .ini setting here and apply user-defined input filter */
389
0
  if(new_val_len) *new_val_len = val_len;
390
0
  return 1;
391
0
}
392
393
SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data)
394
781k
{
395
781k
  char *res = NULL, *var, *val, *separator = NULL;
396
781k
  const char *c_var;
397
781k
  zval array;
398
781k
  int free_buffer = 0;
399
781k
  char *strtok_buf = NULL;
400
781k
  zend_long count = 0;
401
402
781k
  ZVAL_UNDEF(&array);
403
781k
  switch (arg) {
404
0
    case PARSE_POST:
405
390k
    case PARSE_GET:
406
781k
    case PARSE_COOKIE:
407
781k
      array_init(&array);
408
781k
      switch (arg) {
409
0
        case PARSE_POST:
410
0
          zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_POST]);
411
0
          ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_POST], &array);
412
0
          break;
413
390k
        case PARSE_GET:
414
390k
          zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_GET]);
415
390k
          ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_GET], &array);
416
390k
          break;
417
390k
        case PARSE_COOKIE:
418
390k
          zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_COOKIE]);
419
390k
          ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_COOKIE], &array);
420
390k
          break;
421
781k
      }
422
781k
      break;
423
0
    default:
424
0
      ZVAL_COPY_VALUE(&array, destArray);
425
0
      break;
426
781k
  }
427
428
781k
  if (arg == PARSE_POST) {
429
0
    sapi_handle_post(&array);
430
0
    return;
431
0
  }
432
433
781k
  if (arg == PARSE_GET) {   /* GET data */
434
390k
    c_var = SG(request_info).query_string;
435
390k
    if (c_var && *c_var) {
436
0
      res = (char *) estrdup(c_var);
437
0
      free_buffer = 1;
438
390k
    } else {
439
390k
      free_buffer = 0;
440
390k
    }
441
390k
  } else if (arg == PARSE_COOKIE) {   /* Cookie data */
442
390k
    c_var = SG(request_info).cookie_data;
443
390k
    if (c_var && *c_var) {
444
0
      res = (char *) estrdup(c_var);
445
0
      free_buffer = 1;
446
390k
    } else {
447
390k
      free_buffer = 0;
448
390k
    }
449
0
  } else if (arg == PARSE_STRING) {   /* String data */
450
0
    res = str;
451
0
    free_buffer = 1;
452
0
  }
453
454
781k
  if (!res) {
455
781k
    return;
456
781k
  }
457
458
0
  switch (arg) {
459
0
    case PARSE_GET:
460
0
    case PARSE_STRING:
461
0
      separator = PG(arg_separator).input;
462
0
      break;
463
0
    case PARSE_COOKIE:
464
0
      separator = ";\0";
465
0
      break;
466
0
  }
467
468
0
  var = php_strtok_r(res, separator, &strtok_buf);
469
470
0
  while (var) {
471
0
    size_t val_len;
472
0
    size_t new_val_len;
473
474
0
    val = strchr(var, '=');
475
476
0
    if (arg == PARSE_COOKIE) {
477
      /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */
478
0
      while (isspace(*var)) {
479
0
        var++;
480
0
      }
481
0
      if (var == val || *var == '\0') {
482
0
        goto next_cookie;
483
0
      }
484
0
    }
485
486
0
    if (++count > PG(max_input_vars)) {
487
0
      php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
488
0
      break;
489
0
    }
490
491
0
    if (val) { /* have a value */
492
493
0
      *val++ = '\0';
494
495
0
      if (arg == PARSE_COOKIE) {
496
0
        val_len = php_raw_url_decode(val, strlen(val));
497
0
      } else {
498
0
        val_len = php_url_decode(val, strlen(val));
499
0
      }
500
0
    } else {
501
0
      val     = "";
502
0
      val_len =  0;
503
0
    }
504
505
0
    val = estrndup(val, val_len);
506
0
    php_url_decode(var, strlen(var));
507
0
    if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len)) {
508
0
      php_register_variable_safe(var, val, new_val_len, &array);
509
0
    }
510
0
    efree(val);
511
0
next_cookie:
512
0
    var = php_strtok_r(NULL, separator, &strtok_buf);
513
0
  }
514
515
0
  if (free_buffer) {
516
0
    efree(res);
517
0
  }
518
0
}
519
520
static zend_always_inline int valid_environment_name(const char *name, const char *end)
521
12.2k
{
522
12.2k
  const char *s;
523
524
176k
  for (s = name; s < end; s++) {
525
164k
    if (*s == ' ' || *s == '.' || *s == '[') {
526
0
      return 0;
527
0
    }
528
164k
  }
529
12.2k
  return 1;
530
12.2k
}
531
532
static zend_always_inline void import_environment_variable(HashTable *ht, char *env)
533
12.2k
{
534
12.2k
  char *p;
535
12.2k
  size_t name_len, len;
536
12.2k
  zval val;
537
12.2k
  zend_ulong idx;
538
539
12.2k
  p = strchr(env, '=');
540
12.2k
  if (!p
541
12.2k
    || p == env
542
12.2k
    || !valid_environment_name(env, p)) {
543
    /* malformed entry? */
544
0
    return;
545
0
  }
546
12.2k
  name_len = p - env;
547
12.2k
  p++;
548
12.2k
  len = strlen(p);
549
12.2k
  ZVAL_STRINGL_FAST(&val, p, len);
550
12.2k
  if (ZEND_HANDLE_NUMERIC_STR(env, name_len, idx)) {
551
0
    zend_hash_index_update(ht, idx, &val);
552
12.2k
  } else {
553
12.2k
    php_register_variable_quick(env, name_len, &val, ht);
554
12.2k
  }
555
12.2k
}
556
557
void _php_import_environment_variables(zval *array_ptr)
558
470
{
559
470
  tsrm_env_lock();
560
561
470
#ifndef PHP_WIN32
562
12.6k
  for (char **env = environ; env != NULL && *env != NULL; env++) {
563
12.2k
    import_environment_variable(Z_ARRVAL_P(array_ptr), *env);
564
12.2k
  }
565
#else
566
  char *environment = GetEnvironmentStringsA();
567
  for (char *env = environment; env != NULL && *env; env += strlen(env) + 1) {
568
    import_environment_variable(Z_ARRVAL_P(array_ptr), env);
569
  }
570
  FreeEnvironmentStringsA(environment);
571
#endif
572
573
470
  tsrm_env_unlock();
574
470
}
575
576
zend_bool php_std_auto_global_callback(char *name, uint32_t name_len)
577
0
{
578
0
  zend_printf("%s\n", name);
579
0
  return 0; /* don't rearm */
580
0
}
581
582
/* {{{ php_build_argv
583
 */
584
PHPAPI void php_build_argv(const char *s, zval *track_vars_array)
585
391k
{
586
391k
  zval arr, argc, tmp;
587
391k
  int count = 0;
588
589
391k
  if (!(SG(request_info).argc || track_vars_array)) {
590
0
    return;
591
0
  }
592
593
391k
  array_init(&arr);
594
595
  /* Prepare argv */
596
391k
  if (SG(request_info).argc) { /* are we in cli sapi? */
597
0
    int i;
598
0
    for (i = 0; i < SG(request_info).argc; i++) {
599
0
      ZVAL_STRING(&tmp, SG(request_info).argv[i]);
600
0
      if (zend_hash_next_index_insert(Z_ARRVAL(arr), &tmp) == NULL) {
601
0
        zend_string_efree(Z_STR(tmp));
602
0
      }
603
0
    }
604
391k
  } else   if (s && *s) {
605
0
    while (1) {
606
0
      const char *space = strchr(s, '+');
607
      /* auto-type */
608
0
      ZVAL_STRINGL(&tmp, s, space ? space - s : strlen(s));
609
0
      count++;
610
0
      if (zend_hash_next_index_insert(Z_ARRVAL(arr), &tmp) == NULL) {
611
0
        zend_string_efree(Z_STR(tmp));
612
0
      }
613
0
      if (!space) {
614
0
        break;
615
0
      }
616
0
      s = space + 1;
617
0
    }
618
0
  }
619
620
  /* prepare argc */
621
391k
  if (SG(request_info).argc) {
622
0
    ZVAL_LONG(&argc, SG(request_info).argc);
623
391k
  } else {
624
391k
    ZVAL_LONG(&argc, count);
625
391k
  }
626
627
391k
  if (SG(request_info).argc) {
628
0
    Z_ADDREF(arr);
629
0
    zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &arr);
630
0
    zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &argc);
631
0
  }
632
391k
  if (track_vars_array && Z_TYPE_P(track_vars_array) == IS_ARRAY) {
633
470
    Z_ADDREF(arr);
634
470
    zend_hash_update(Z_ARRVAL_P(track_vars_array), ZSTR_KNOWN(ZEND_STR_ARGV), &arr);
635
470
    zend_hash_update(Z_ARRVAL_P(track_vars_array), ZSTR_KNOWN(ZEND_STR_ARGC), &argc);
636
470
  }
637
391k
  zval_ptr_dtor_nogc(&arr);
638
391k
}
639
/* }}} */
640
641
/* {{{ php_register_server_variables
642
 */
643
static inline void php_register_server_variables(void)
644
470
{
645
470
  zval tmp;
646
470
  zval *arr = &PG(http_globals)[TRACK_VARS_SERVER];
647
470
  HashTable *ht;
648
649
470
  zval_ptr_dtor_nogc(arr);
650
470
  array_init(arr);
651
652
  /* Server variables */
653
470
  if (sapi_module.register_server_variables) {
654
470
    sapi_module.register_server_variables(arr);
655
470
  }
656
470
  ht = Z_ARRVAL_P(arr);
657
658
  /* PHP Authentication support */
659
470
  if (SG(request_info).auth_user) {
660
0
    ZVAL_STRING(&tmp, SG(request_info).auth_user);
661
0
    php_register_variable_quick("PHP_AUTH_USER", sizeof("PHP_AUTH_USER")-1, &tmp, ht);
662
0
  }
663
470
  if (SG(request_info).auth_password) {
664
0
    ZVAL_STRING(&tmp, SG(request_info).auth_password);
665
0
    php_register_variable_quick("PHP_AUTH_PW", sizeof("PHP_AUTH_PW")-1, &tmp, ht);
666
0
  }
667
470
  if (SG(request_info).auth_digest) {
668
0
    ZVAL_STRING(&tmp, SG(request_info).auth_digest);
669
0
    php_register_variable_quick("PHP_AUTH_DIGEST", sizeof("PHP_AUTH_DIGEST")-1, &tmp, ht);
670
0
  }
671
672
  /* store request init time */
673
470
  ZVAL_DOUBLE(&tmp, sapi_get_request_time());
674
470
  php_register_variable_quick("REQUEST_TIME_FLOAT", sizeof("REQUEST_TIME_FLOAT")-1, &tmp, ht);
675
470
  ZVAL_LONG(&tmp, zend_dval_to_lval(Z_DVAL(tmp)));
676
470
  php_register_variable_quick("REQUEST_TIME", sizeof("REQUEST_TIME")-1, &tmp, ht);
677
470
}
678
/* }}} */
679
680
/* {{{ php_autoglobal_merge
681
 */
682
static void php_autoglobal_merge(HashTable *dest, HashTable *src)
683
0
{
684
0
  zval *src_entry, *dest_entry;
685
0
  zend_string *string_key;
686
0
  zend_ulong num_key;
687
0
  int globals_check = (dest == (&EG(symbol_table)));
688
689
0
  ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
690
0
    if (Z_TYPE_P(src_entry) != IS_ARRAY
691
0
      || (string_key && (dest_entry = zend_hash_find(dest, string_key)) == NULL)
692
0
      || (string_key == NULL && (dest_entry = zend_hash_index_find(dest, num_key)) == NULL)
693
0
      || Z_TYPE_P(dest_entry) != IS_ARRAY) {
694
0
      Z_TRY_ADDREF_P(src_entry);
695
0
      if (string_key) {
696
0
        if (!globals_check || ZSTR_LEN(string_key) != sizeof("GLOBALS") - 1
697
0
            || memcmp(ZSTR_VAL(string_key), "GLOBALS", sizeof("GLOBALS") - 1)) {
698
0
          zend_hash_update(dest, string_key, src_entry);
699
0
        } else {
700
0
          Z_TRY_DELREF_P(src_entry);
701
0
        }
702
0
      } else {
703
0
        zend_hash_index_update(dest, num_key, src_entry);
704
0
      }
705
0
    } else {
706
0
      SEPARATE_ARRAY(dest_entry);
707
0
      php_autoglobal_merge(Z_ARRVAL_P(dest_entry), Z_ARRVAL_P(src_entry));
708
0
    }
709
0
  } ZEND_HASH_FOREACH_END();
710
0
}
711
/* }}} */
712
713
/* {{{ php_hash_environment
714
 */
715
PHPAPI int php_hash_environment(void)
716
390k
{
717
390k
  memset(PG(http_globals), 0, sizeof(PG(http_globals)));
718
390k
  zend_activate_auto_globals();
719
390k
  if (PG(register_argc_argv)) {
720
390k
    php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]);
721
390k
  }
722
390k
  return SUCCESS;
723
390k
}
724
/* }}} */
725
726
static zend_bool php_auto_globals_create_get(zend_string *name)
727
390k
{
728
390k
  if (PG(variables_order) && (strchr(PG(variables_order),'G') || strchr(PG(variables_order),'g'))) {
729
390k
    sapi_module.treat_data(PARSE_GET, NULL, NULL);
730
0
  } else {
731
0
    zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_GET]);
732
0
    array_init(&PG(http_globals)[TRACK_VARS_GET]);
733
0
  }
734
735
390k
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_GET]);
736
390k
  Z_ADDREF(PG(http_globals)[TRACK_VARS_GET]);
737
738
390k
  return 0; /* don't rearm */
739
390k
}
740
741
static zend_bool php_auto_globals_create_post(zend_string *name)
742
390k
{
743
390k
  if (PG(variables_order) &&
744
390k
      (strchr(PG(variables_order),'P') || strchr(PG(variables_order),'p')) &&
745
390k
    !SG(headers_sent) &&
746
390k
    SG(request_info).request_method &&
747
0
    !strcasecmp(SG(request_info).request_method, "POST")) {
748
0
    sapi_module.treat_data(PARSE_POST, NULL, NULL);
749
390k
  } else {
750
390k
    zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_POST]);
751
390k
    array_init(&PG(http_globals)[TRACK_VARS_POST]);
752
390k
  }
753
754
390k
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_POST]);
755
390k
  Z_ADDREF(PG(http_globals)[TRACK_VARS_POST]);
756
757
390k
  return 0; /* don't rearm */
758
390k
}
759
760
static zend_bool php_auto_globals_create_cookie(zend_string *name)
761
390k
{
762
390k
  if (PG(variables_order) && (strchr(PG(variables_order),'C') || strchr(PG(variables_order),'c'))) {
763
390k
    sapi_module.treat_data(PARSE_COOKIE, NULL, NULL);
764
0
  } else {
765
0
    zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_COOKIE]);
766
0
    array_init(&PG(http_globals)[TRACK_VARS_COOKIE]);
767
0
  }
768
769
390k
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_COOKIE]);
770
390k
  Z_ADDREF(PG(http_globals)[TRACK_VARS_COOKIE]);
771
772
390k
  return 0; /* don't rearm */
773
390k
}
774
775
static zend_bool php_auto_globals_create_files(zend_string *name)
776
390k
{
777
390k
  if (Z_TYPE(PG(http_globals)[TRACK_VARS_FILES]) == IS_UNDEF) {
778
390k
    array_init(&PG(http_globals)[TRACK_VARS_FILES]);
779
390k
  }
780
781
390k
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_FILES]);
782
390k
  Z_ADDREF(PG(http_globals)[TRACK_VARS_FILES]);
783
784
390k
  return 0; /* don't rearm */
785
390k
}
786
787
/* Upgly hack to fix HTTP_PROXY issue, see bug #72573 */
788
static void check_http_proxy(HashTable *var_table)
789
470
{
790
470
  if (zend_hash_str_exists(var_table, "HTTP_PROXY", sizeof("HTTP_PROXY")-1)) {
791
0
    char *local_proxy = getenv("HTTP_PROXY");
792
793
0
    if (!local_proxy) {
794
0
      zend_hash_str_del(var_table, "HTTP_PROXY", sizeof("HTTP_PROXY")-1);
795
0
    } else {
796
0
      zval local_zval;
797
0
      ZVAL_STRING(&local_zval, local_proxy);
798
0
      zend_hash_str_update(var_table, "HTTP_PROXY", sizeof("HTTP_PROXY")-1, &local_zval);
799
0
    }
800
0
  }
801
470
}
802
803
static zend_bool php_auto_globals_create_server(zend_string *name)
804
470
{
805
470
  if (PG(variables_order) && (strchr(PG(variables_order),'S') || strchr(PG(variables_order),'s'))) {
806
470
    php_register_server_variables();
807
808
470
    if (PG(register_argc_argv)) {
809
470
      if (SG(request_info).argc) {
810
0
        zval *argc, *argv;
811
812
0
        if ((argc = zend_hash_find_ex_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), 1)) != NULL &&
813
0
          (argv = zend_hash_find_ex_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), 1)) != NULL) {
814
0
          Z_ADDREF_P(argv);
815
0
          zend_hash_update(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZSTR_KNOWN(ZEND_STR_ARGV), argv);
816
0
          zend_hash_update(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZSTR_KNOWN(ZEND_STR_ARGC), argc);
817
0
        }
818
470
      } else {
819
470
        php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]);
820
470
      }
821
470
    }
822
823
0
  } else {
824
0
    zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_SERVER]);
825
0
    array_init(&PG(http_globals)[TRACK_VARS_SERVER]);
826
0
  }
827
828
470
  check_http_proxy(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]));
829
470
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_SERVER]);
830
470
  Z_ADDREF(PG(http_globals)[TRACK_VARS_SERVER]);
831
832
  /* TODO: TRACK_VARS_SERVER is modified in a number of places (e.g. phar) past this point,
833
   * where rc>1 due to the $_SERVER global. Ideally this shouldn't happen, but for now we
834
   * ignore this issue, as it would probably require larger changes. */
835
470
  HT_ALLOW_COW_VIOLATION(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]));
836
837
470
  return 0; /* don't rearm */
838
470
}
839
840
static zend_bool php_auto_globals_create_env(zend_string *name)
841
0
{
842
0
  zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_ENV]);
843
0
  array_init(&PG(http_globals)[TRACK_VARS_ENV]);
844
845
0
  if (PG(variables_order) && (strchr(PG(variables_order),'E') || strchr(PG(variables_order),'e'))) {
846
0
    php_import_environment_variables(&PG(http_globals)[TRACK_VARS_ENV]);
847
0
  }
848
849
0
  check_http_proxy(Z_ARRVAL(PG(http_globals)[TRACK_VARS_ENV]));
850
0
  zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_ENV]);
851
0
  Z_ADDREF(PG(http_globals)[TRACK_VARS_ENV]);
852
853
0
  return 0; /* don't rearm */
854
0
}
855
856
static zend_bool php_auto_globals_create_request(zend_string *name)
857
0
{
858
0
  zval form_variables;
859
0
  unsigned char _gpc_flags[3] = {0, 0, 0};
860
0
  char *p;
861
862
0
  array_init(&form_variables);
863
864
0
  if (PG(request_order) != NULL) {
865
0
    p = PG(request_order);
866
0
  } else {
867
0
    p = PG(variables_order);
868
0
  }
869
870
0
  for (; p && *p; p++) {
871
0
    switch (*p) {
872
0
      case 'g':
873
0
      case 'G':
874
0
        if (!_gpc_flags[0]) {
875
0
          php_autoglobal_merge(Z_ARRVAL(form_variables), Z_ARRVAL(PG(http_globals)[TRACK_VARS_GET]));
876
0
          _gpc_flags[0] = 1;
877
0
        }
878
0
        break;
879
0
      case 'p':
880
0
      case 'P':
881
0
        if (!_gpc_flags[1]) {
882
0
          php_autoglobal_merge(Z_ARRVAL(form_variables), Z_ARRVAL(PG(http_globals)[TRACK_VARS_POST]));
883
0
          _gpc_flags[1] = 1;
884
0
        }
885
0
        break;
886
0
      case 'c':
887
0
      case 'C':
888
0
        if (!_gpc_flags[2]) {
889
0
          php_autoglobal_merge(Z_ARRVAL(form_variables), Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]));
890
0
          _gpc_flags[2] = 1;
891
0
        }
892
0
        break;
893
0
    }
894
0
  }
895
896
0
  zend_hash_update(&EG(symbol_table), name, &form_variables);
897
0
  return 0;
898
0
}
899
900
void php_startup_auto_globals(void)
901
3.47k
{
902
3.47k
  zend_register_auto_global(zend_string_init_interned("_GET", sizeof("_GET")-1, 1), 0, php_auto_globals_create_get);
903
3.47k
  zend_register_auto_global(zend_string_init_interned("_POST", sizeof("_POST")-1, 1), 0, php_auto_globals_create_post);
904
3.47k
  zend_register_auto_global(zend_string_init_interned("_COOKIE", sizeof("_COOKIE")-1, 1), 0, php_auto_globals_create_cookie);
905
3.47k
  zend_register_auto_global(zend_string_init_interned("_SERVER", sizeof("_SERVER")-1, 1), PG(auto_globals_jit), php_auto_globals_create_server);
906
3.47k
  zend_register_auto_global(zend_string_init_interned("_ENV", sizeof("_ENV")-1, 1), PG(auto_globals_jit), php_auto_globals_create_env);
907
3.47k
  zend_register_auto_global(zend_string_init_interned("_REQUEST", sizeof("_REQUEST")-1, 1), PG(auto_globals_jit), php_auto_globals_create_request);
908
3.47k
  zend_register_auto_global(zend_string_init_interned("_FILES", sizeof("_FILES")-1, 1), 0, php_auto_globals_create_files);
909
3.47k
}