php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77782 getenv without params returns keys that aren't accessible with $varname param
Submitted: 2019-03-22 20:52 UTC Modified: 2019-03-23 18:26 UTC
From: matt at mattallan dot me Assigned:
Status: Verified Package: FPM related
PHP Version: 7.3.3 OS: macOs Mojave 10.14
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2019-03-22 20:52 UTC] matt at mattallan dot me
Description:
------------
PHP version:

PHP 7.3.3 (cli) (built: Mar  8 2019 16:40:07) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.3, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.3, Copyright (c) 1999-2018, by Zend Technologies

(installed with Homebrew)

Relevant ini params:

variables_order = "EGPCS"
auto_globals_jit = Off

Overview:

I set the environment variable `FOO` using the fastcgi_param in my Nginx configuration.  If I make a HTTP request to the server I am able to access the environment variable using `getenv()['FOO']` but I am not able to access the environment variable using `getenv('FOO')`.

The same issue occurs with the Caddy server.  If I set the environment variable in the fpm configuration using `env[FOO]=bar` the issue does not occur.  If I run the script from the CLI or the built in web server it works as expected.

Test script:
---------------
<?php

$env = getenv();
var_dump(array_key_exists('FOO', $env));
var_dump($env['FOO']);
var_dump(getenv('FOO'));

Expected result:
----------------
I expect to see the output:

bool(true)
string(3) "bar"
string(3) "bar"

Actual result:
--------------
I see the output:

bool(true)
string(3) "bar"
bool(false)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-03-22 22:51 UTC] danack@php.net
To be clear, are you seeing this with 'FOO' or is foo being used as a metasyntatic name?,


Also, please could you run and show the output of:

$env = getenv();
foreach ($env as $key => $value) {
    var_dump($key);
}

$var = 'FOO';
var_dump(getenv($var));
var_dump($env[$var]);
var_dump($var);

with FOO replaced by the actual name if it isn't FOO.
 [2019-03-22 23:26 UTC] matt at mattallan dot me
Hi,

I literally set the environment variable `FOO` for this reproduction script.  The Nginx config contains `fastcgi_param FOO bar;`.

I get the following output from the script you included:

string(4) "USER"
string(4) "HOME"
string(15) "HTTP_CONNECTION"
string(11) "HTTP_ACCEPT"
string(20) "HTTP_ACCEPT_ENCODING"
string(15) "HTTP_USER_AGENT"
string(9) "HTTP_HOST"
string(15) "REDIRECT_STATUS"
string(11) "SERVER_NAME"
string(11) "SERVER_PORT"
string(11) "SERVER_ADDR"
string(11) "REMOTE_PORT"
string(11) "REMOTE_ADDR"
string(15) "SERVER_SOFTWARE"
string(17) "GATEWAY_INTERFACE"
string(14) "REQUEST_SCHEME"
string(15) "SERVER_PROTOCOL"
string(13) "DOCUMENT_ROOT"
string(12) "DOCUMENT_URI"
string(11) "REQUEST_URI"
string(11) "SCRIPT_NAME"
string(14) "CONTENT_LENGTH"
string(12) "CONTENT_TYPE"
string(14) "REQUEST_METHOD"
string(12) "QUERY_STRING"
string(3) "FOO"
string(15) "SCRIPT_FILENAME"
string(9) "FCGI_ROLE"
string(8) "PHP_SELF"
string(18) "REQUEST_TIME_FLOAT"
string(12) "REQUEST_TIME"
bool(false)
string(3) "bar"
string(3) "FOO"

Using the Caddy webserver with the configuration directive `env FOO bar` I get this output:

string(4) "USER"
string(4) "HOME"
string(11) "REMOTE_ADDR"
string(12) "REMOTE_IDENT"
string(11) "HTTP_ACCEPT"
string(3) "FOO"
string(11) "REQUEST_URI"
string(11) "SERVER_NAME"
string(14) "REQUEST_SCHEME"
string(9) "PATH_INFO"
string(12) "CONTENT_TYPE"
string(15) "SCRIPT_FILENAME"
string(15) "SERVER_PROTOCOL"
string(12) "QUERY_STRING"
string(11) "REMOTE_USER"
string(11) "REMOTE_HOST"
string(12) "DOCUMENT_URI"
string(13) "DOCUMENT_ROOT"
string(15) "SERVER_SOFTWARE"
string(14) "REQUEST_METHOD"
string(17) "GATEWAY_INTERFACE"
string(9) "AUTH_TYPE"
string(15) "HTTP_USER_AGENT"
string(11) "SCRIPT_NAME"
string(9) "HTTP_HOST"
string(11) "SERVER_PORT"
string(11) "REMOTE_PORT"
string(14) "CONTENT_LENGTH"
string(9) "FCGI_ROLE"
string(8) "PHP_SELF"
string(18) "REQUEST_TIME_FLOAT"
string(12) "REQUEST_TIME"
bool(false)
string(3) "bar"
string(3) "FOO"

I created a gist with the exact Caddyfile I am using here: https://gist.github.com/matt-allan/280a0120d7e8467fb384acd80d697edb

Thanks,

Matt
 [2019-03-23 03:30 UTC] danack@php.net
I can't reproduce this. I've setup a test docker-compose https://github.com/Danack/PhpTest

Cloning that, running "docker-compose up --build" and then going to http://localhost:8080/ shows FOO set to bar correctly.

However, it is a little odd that you have things like SERVER_NAME and DOCUMENT_ROOT set in your environment variables apparently...those are not meant to be environment variables, but are passed in a different way for fastcgi. Is there anything funky in your config that is setting those?
 [2019-03-23 15:39 UTC] matt at mattallan dot me
Hi Danack,

I was able to reproduce the issue with the docker-compose setup.  I pushed an updated branch here: https://github.com/matt-allan/PhpTest/tree/reproduce-bug

I had to change two things:
- Don't set the env in fpm.conf.  Only set it in the Caddyfile.
- Set auto_globals_jit to Off, not On.

Thanks for your help,

Matt
 [2019-03-23 18:26 UTC] danack@php.net
-Status: Open +Status: Verified
 [2019-03-23 18:26 UTC] danack@php.net
I can confirm I can see weird stuff happening with the 'reproduce-bug' branch. However the auto_globals_jit doesn't seem to affect anything for me.

The next step would probably try to check what caddy is actually passing to PHP. This should be possible to do with fcgi-debug, https://github.com/lighttpd/fcgi-debug

I have grabbed the output using strace, but interpreting what it is passing is hard as I don't speak CGI.

     0.001020 read(4, "\1\1\0\1\0\10\0\0", 8) = 8
     0.001081 read(4, "\0\1\0\0\0\0\0\0", 8) = 8
     0.001000 read(4, "\1\4\0\1\3\374\4\0", 8) = 8
     0.001575 read(4, "\f\0REMOTE_IDENT\v\0REMOTE_USER\vUHTTP_ACCEPTtext/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\24\21HTTP_ACCEPT_ENCODINGgzip, deflate, br\17\10SERVER_PROTOCOLHTTP/1.1\21\7GATEWAY_INTERFACECGI/1.1\t\16HTTP_HOSTlocalhost:8080\v\4SERVER_PORT8080\17\fSERVER_SOFTWARECaddy/0.11.5\vnHTTP_COOKIEPhpstorm-4f4f8806=d319baf0-4d68-47d4-8f75-aea9d6690dc6; Phpstorm-4f4f8bc7=65ded63d-c4bc-412d-9e55-bffead73b792\22\tHTTP_CACHE_CONTROLmax-age=0\f\0CONTENT_TYPE\v\fREMOTE_ADDR192.168.32.1\16\4REQUEST_SCHEMEhttp\v\0SERVER_NAME\f\nDOCUMENT_URI/index.php\36\1HTTP_UPGRADE_INSECURE_REQUESTS1\17yHTTP_USER_AGENTMozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36\t\0AUTH_TYPE\t\0PATH_INFO\f\0QUERY_STRING\v\1REQUEST_URI/\v\nSCRIPT_NAME/index.php\16\1CONTENT_LENGTH0\v\5REMOTE_PORT55402\16\3REQUEST_METHODGET\r\23DOCUMENT_ROOT/var/app/app/public\17\35SCRIPT_FILENAME/var/app/app/public/index.php\24\32HTTP_ACCEPT_LANGUAGEen-GB,en-US;q=0.9,en;q=0.8\v\fREMOTE_HOST192.168.32.1\3\3FOObar\17\nHTTP_CONNECTIONkeep-alive\0\0\0\0", 1024) = 1024
     0.001293 read(4, "\1\4\0\1\0\0\0\0", 8) = 8



I pushed the changes to have strace to https://github.com/Danack/PhpTest/tree/reproduce-bug apparently strace needs to be invoke inside the container, after ssh'ing in with "docker exec -it phptest_php_fpm_1 bash" as the script I attempted to invoke it from the outside doesn't work.

There is a small chance this is a bug in caddy, as it is quite new software. Checking to see what  fcgi-debug thinks is being passed from caddy to PHP is probably going to be a lot easier than debugging the PHP code.
 [2019-03-26 14:43 UTC] matt at mattallan dot me
> However the auto_globals_jit doesn't seem to affect anything for me.

Yeah, I was disabling it initially to avoid #77783 interfering, but it doesn't seem to matter.

> There is a small chance this is a bug in caddy

I originally ran into this issue using Nginx, I just switched to Caddy for the reproduction steps because it's simpler to install and configure without Docker.  I will see if I can reproduce this in Docker using NGINX to confirm.

Thanks for all your help, I will try to debug the CGI exchange later today.
 [2019-03-27 13:51 UTC] danack@php.net
I realised I forgot to say clearly what the "weird stuff" is.

The variables that are passed as CGI params are duplicated into the environment variables. As the strings are only present once in the data sent via CGI, this probably means either:

i) Caddy is sending malformed cgi data.
ii) PHP-FPM is reading the cgi data incorrectly.
 [2019-03-27 15:10 UTC] matt at mattallan dot me
I inspected the exchange with Wireshark and it seems correct.

The FCGI spec says:

> FCGI_PARAMS is a stream record type used in sending name-value pairs from the Web server to the application. The name-value pairs are sent down the stream one after the other, in no specified order.

> FastCGI transmits a name-value pair as the length of the name, followed by the length of the value, followed by the name, followed by the value. Lengths of 127 bytes and less can be encoded in one byte, while longer lengths are always encoded in four bytes:

And the CGI 1.1 spec says:

> Meta-variables are passed to the script in identically named
      environment variables.  These are accessed by the C library
      routine getenv() or variable environ.

Both Caddy and Nginx are sending `03 03 46 4f 4f 62 61 72` (3 3 FOO BAR), which matches the spec.  I put the output in a gist here: https://gist.github.com/matt-allan/e845317b09765f1a4d4cf9b8743c3680

I read through the code and it looks like calling `getenv` without a param eventually calls fcgi_loadenv with cgi_php_load_env_var, but if you pass varname to getenv it only calls fcgi_getenv.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sun Jun 16 10:01:28 2019 UTC