facebook / hhvm

A virtual machine for executing programs written in Hack.
https://hhvm.com
Other
18.13k stars 2.99k forks source link

putenv() doesn't propagate into shell_exec() #7378

Open nazar-pc opened 7 years ago

nazar-pc commented 7 years ago

HHVM Version

HipHop VM 3.16.0-dev (rel)
Compiler: heads/master-0-ge97dcd8e6821b1f70173c0ba106e01c3f239d1e1
Repo schema: bbcd692673eb9a01ff839c7aefeff5b0cbcc89cb

PHP Version

PHP 7.0.8-3ubuntu2 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.8-3ubuntu2, Copyright (c) 1999-2016, by Zend Technologies
    with Xdebug v2.4.0, Copyright (c) 2002-2016, by Derick Rethans

Standalone code, or other way to reproduce the problem

<?php
// test.php
putenv('xyz=xyz');
echo shell_exec(PHP_BINARY.' -d variables_order=EGPCS echo.php');
<?php
// echo.php
var_dump($_ENV['xyz']);

Expected result

> php test.php
string(3) "xyz"

Actual result

> hhvm test.php
Notice: Undefined index: xyz in %s/echo.php on line 2
NULL
fredemmott commented 7 years ago

The complication here is that putenv needs some level of fakery in HHVM - it can't directly back on to the libc call because HHVM is multithreaded.

cmuellner commented 7 years ago

A possible solution would be to warp certain libc calls (e.g. shell_exec() and mktime()) and propagate the environment before calling the actual libc function().

This could be realised by ld(2)'s --wrap option [1]:

--wrap=symbol Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to "wrap_ symbol ". Any undefined reference to "_real symbol " will be resolved to symbol. This can be used to provide a wrapper for a system function. The wrapper function should be called "wrap_ symbol ". If it wishes to call the system function, it should call "_real symbol ".

Here is a trivial example:

void *
__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "wrap_malloc" instead. The call to "__real_malloc" in "wrap_malloc" will call the real "malloc" function. You may wish to provide a "real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".

That approach would be good enough for #7378 (this) and #7533.

[1] https://linux.die.net/man/1/ld

RalphCorderoy commented 5 years ago

Is this also the cause of PHP's putenv() not affecting the following passthru()? That's what's biting me. Environment variables are a commonly used method of passing values through to another command without having the bother of escaping them to become command-line arguments.

$ env -i FOO=foo PHP_BAR=bar php -r 'putenv("XYZZY=xyzzy"); passthru("env | sort");'
FOO=foo
GLIBCPP_FORCE_NEW=1
GLIBCXX_FORCE_NEW=1
PHP_BAR=bar
PWD=/home/ralph
UNW_RBP_ALWAYS_VALID=1
$