php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #44801 Invalid escaping for passthru() in CLI
Submitted: 2008-04-22 19:16 UTC Modified: 2008-11-07 09:52 UTC
From: twm at twmacinta dot com Assigned:
Status: Closed Package: Documentation problem
PHP Version: 5.2.5 OS: Red Hat Enterprise Linux ES 3
Private report: No CVE-ID: None
 [2008-04-22 19:16 UTC] twm at twmacinta dot com
Description:
------------
When I run a PHP script which uses passthru() to execute a command with characters that need escaping, the escaping is incorrect when the script is run from the command line, but it is fine when the script is run from within Apache.  This was causing the script "ext/standard/tests/file/bug22414.phpt" to fail when I attempted to run "make test" after building PHP 5.2.5 on RHEL3.  I have created a simplified script for this report to demonstrate the problem, though I will note that I can also reproduce it reliably with "bug22414.phpt".

I should mention that I also tried my test script from the command line in Mac OS X 10.5 running PHP 5.2.5 and Ubuntu 7.10 running PHP 5.2.3 and it worked correctly in both cases.  So, my suspicion is that there is an older library on RHEL3 which is causing the parsing the be incorrect.  It passes all the requirements checks made by './configure', though.

I ran the test code below using the '-n' option to PHP so as to eliminate my "php.ini" as the problem.


Reproduce code:
---------------
header("Content-Type: text/plain");
$textEscaped = escapeshellarg("Tim's Test");
print("Before: {$textEscaped}\n");
print("After: ");
passthru("/bin/echo {$textEscaped}");


Expected result:
----------------
Before: 'Tim'\''s Test'
After: Tim's Test


Actual result:
--------------
Before: 'Tim'\''s Test'
After: Tim\s Test'


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2008-04-23 00:58 UTC] felipe@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5.2-latest.tar.gz
 
For Windows (zip):
 
  http://snaps.php.net/win32/php5.2-win32-latest.zip

For Windows (installer):

  http://snaps.php.net/win32/php5.2-win32-installer-latest.msi

This looks has been fixed. Works fine for me using 5.2.6-RC4.
 [2008-04-23 14:13 UTC] twm at twmacinta dot com
Thank you for the fast reply.  I have tried the latest CVS snapshot (php5.2-200804231230) and I still get the same incorrect output.  It sounds like some library in RHEL3 which PHP uses is behaving differently than on newer platforms.

If it helps, I have another server running the same version of RHEL3 which has the default version of PHP still on it (updated via 'up2date'), rather than one that I compiled.  I know you don't support PHP 4.3 any longer, I'm just pointing it out in case it helps to know that this test case has been broken on RHEL3 for awhile, it's not a problem unique to my compiled version, and the problem didn't just appear in PHP 5.2.  I do get slightly different output there, but it is still incorrect.
 [2008-04-24 11:09 UTC] jani@php.net
]$ php t.php
Before: 'Tim'\''s Test'
After: Tim's Test

So it works fine using latest CVS (for me). 
You're using /bin/echo there, what if you simply do this:

<?php
header("Content-Type: text/plain");
$textEscaped = escapeshellarg("Tim's Test");
print("Before: {$textEscaped}\n");
print("After: ");
passthru($textEscaped);
?>

That would eliminate one "moving part" here..
Just check the resulting error message what it has. :)

 [2008-04-24 17:23 UTC] twm at twmacinta dot com
Good idea.  I modified my test script as suggested.  Here's the output on RHEL3 with the CVS snapshot php5.2-20080423123:

----
Before: 'Tim'\''s Test'
After: sh: line 1: /var/tmp2/php5_take2/targ/bin/Tim\s: No such file or directory
----


In case you're wondering about the path, I set "./configure --prefix" to "/var/tmp2/php5_take2/targ" so that I could install it there and test it before overwriting my old PHP 4 installation.  PHP 4 on the same machine gives the same output as above, except that it's the usual "/usr/bin/" path.

For comparison, here's the output on my other RHEL3 server which has the latest, default version of PHP 4.3 from Red Hat:

----
Before: 'Tim'\''s Test'
After: sh: line 1: /usr/bin/'Tim'\''s: No such file or directory
----


I also ran the revised test on Mac OS X 10.5, which has PHP 5.2.5, to get what is the correct output (i.e., what you got):

----
Before: 'Tim'\''s Test'
After: sh: Tim's Test: command not found
----
 [2008-04-24 17:44 UTC] jani@php.net
Next obvious question is: How did you build PHP? ie. What configure line, etc. Also to eliminate every last moving parts: run the script like this:

# php -n script.php

 [2008-04-24 17:52 UTC] twm at twmacinta dot com
I was actually using the "-n" flag from the start, so that moving part was already eliminated.

Here are my "./configure" and "make install" commands:

----

CONF_OLD_PREFIX=/usr
CONF_PREFIX=/var/tmp2/php5_take2/targ
CONF_SYSCONFDIR=${CONF_PREFIX}/etc
CONF_BINDIR=${CONF_PREFIX}/bin
./configure \
	--prefix=${CONF_PREFIX} \
	--with-config-file-path=${CONF_SYSCONFDIR} \
	--enable-force-cgi-redirect \
	--enable-fastcgi \
	--disable-debug \
	--enable-pic \
	--disable-rpath \
	--enable-inline-optimization \
	--with-bz2 \
	--with-curl \
	--with-dom=${CONF_PREFIX} \
	--with-exec-dir=${CONF_BINDIR} \
	--with-freetype-dir=${CONF_PREFIX} \
	--with-png-dir=${CONF_PREFIX} \
	--with-gd \
	--enable-gd-native-ttf \
	--with-ttf \
	--with-gdbm \
	--with-gettext \
	--with-db4 \
	--with-ncurses \
	--with-gmp \
	--with-iconv \
	--with-jpeg-dir=${CONF_PREFIX} \
	--with-mm \
	--with-openssl \
	--with-png \
	--with-pspell \
	--with-regex=system \
	--with-xml \
	--with-domxml \
	--with-expat-dir=${CONF_PREFIX} \
	--with-zlib \
	--with-layout=GNU \
	--enable-mcal \
	--enable-bcmath \
	--enable-debugger \
	--enable-exif \
	--enable-ftp \
	--enable-magic-quotes \
	--enable-safe-mode \
	--enable-sockets \
	--enable-sysvsem \
	--enable-sysvshm \
	--enable-discard-path \
	--enable-track-vars \
	--enable-trans-sid \
	--enable-yp \
	--enable-wddx \
	--without-oci8 \
	--with-imap=shared \
	--with-mcrypt \
	--with-imap-ssl \
	--with-kerberos=/usr/kerberos \
	--with-ldap=shared \
	--with-mysql=shared,${CONF_PREFIX} \
	--with-pgsql=shared \
	--with-snmp=shared,${CONF_PREFIX} \
	--with-snmp=shared \
	--enable-net-snmp-hack \
	--with-unixODBC=shared,${CONF_OLD_PREFIX} \
	--enable-memory-limit \
	--enable-bcmath \
	--enable-shmop \
	--enable-versioning \
	--enable-calendar \
	--enable-dbx \
	--enable-dio \
	--enable-mbstring \
	--enable-mbstr-enc-trans
make install INSTALL_ROOT=/var/tmp2/php5_take2/targ
 [2008-04-24 18:01 UTC] jani@php.net
Can you try with this instead:

# rm -f config.cache
# ./configure --disable-all --disable-cgi

ie. Eliminate everything but the core. :)
 [2008-04-24 19:48 UTC] twm at twmacinta dot com
Aha, that set off a spark.  I think I found something useful...

The test script actually worked when I tried this:

  ./configure --disable-all --disable-cgi

Then, I tried no flags at all, and it still worked:

  ./configure

So I went through all of the flags I used originally and discovered that the problem appears when all I add is the safe-mode flag:

  ./configure --enable-safe-mode

Here's the output:

----
Before: 'Tim'\''s Test'
After: sh: line 1: /usr/local/php/bin/echo: No such file or directory
----

Note that it was looking for "echo" at a different path.  When I created the directory that it was looking for and copied "echo" there, then I got the same incorrect output as before:

----
Before: 'Tim'\''s Test'
After: Tim\s Test'
----

So the problem occurs when safe-mode is compiled into the executable even though I am not using safe mode when running the script.  I'm using "php -n" which should avoid safe mode (right?) and my "php.ini" also turns safe mode off.  I checked the other versions of PHP which I reported on earlier, and the versions which behaved incorrectly had safe mode compiled in but turned off, and those that behaved correctly did not have safe mode compiled in at all.
 [2008-04-25 16:29 UTC] jani@php.net
Using --enable-safe-mode makes the default be "on". (without this configure option it defaults to "off").

And in the manual it is mentioned that: "Warning:
With safe mode enabled, the command string is escaped with escapeshellcmd(). Thus, echo y | echo x becomes echo y \| echo x."

The question remains: Did you really turn off safe-mode in php.ini and was it really turned off? (check with phpinfo())

 [2008-04-27 13:44 UTC] twm at twmacinta dot com
OK, that's the problem.  But given that it is the problem, the test script "bug22414.phpt", which is part of "make test", is bound to fail any time safe mode is compiled in.  It makes nested calls to the PHP binary with the "-n" option, which apparently causes safe mode to be turned on since it ignores the test script's custom "php.ini" in that case.  So in that respect, maybe this is a bug in "bug22414.phpt"?

I'd like to suggest that the manual be annotated to reflect the changing behavior of the safe mode default.  Currently, http://www.php.net/manual/en/ini.php says that default value for "safe_mode" in "php.ini" is 0.  There is no mention that the default changes depending on how the binary was compiled.  In fact, I had assumed that the default of 0 only applied when safe mode was compiled into the binary since it would be meaningless otherwise.

This page on safe mode also indicates that the safe mode features aren't applied to command line scripts.  http://www.php.net/manual/en/features.safe-mode.php says "Warning: These PHP restrictions are not valid in executed binaries, of course."  That's doesn't seem entirely correct given that it was affecting passthru() in the command line scripts referenced in this bug.
 [2008-04-28 12:31 UTC] jani@php.net
Test is fixed, but the docs need to be fixed too. 
 [2008-11-05 17:18 UTC] vrana@php.net
Please explain what exactly should be documented.
 [2008-11-05 17:35 UTC] twm at twmacinta dot com
I think jani was referring to this portion of my note from 27 Apr 1:44pm UTC:

I'd like to suggest that the manual be annotated to reflect the changing behavior of the safe mode default.  Currently, http://www.php.net/manual/en/ini.php says that the default value for "safe_mode" in "php.ini" is 0.  There is no mention that the default changes depending on how the binary was compiled.  In fact, I had assumed that the default of 0 only applied when safe mode was compiled into the binary since it would be meaningless otherwise.

This page on safe mode also indicates that the safe mode features aren't applied to command line scripts.  http://www.php.net/manual/en/features.safe-mode.php says "Warning: These PHP restrictions are not valid in executed binaries, of course."  That doesn't seem entirely correct given that it was affecting passthru() in the command line scripts referenced in this bug.
 [2008-11-07 09:52 UTC] vrana@php.net
This bug has been fixed in the documentation's XML sources. Since the
online and downloadable versions of the documentation need some time
to get updated, we would like to ask you to be a bit patient.

Thank you for the report, and for helping us make our documentation better.

"If PHP is compiled with --enable-safe-mode then defaults to On, otherwise Off."

Executed binaries are not affected by safe_mode, only the parameters passed which is properly documented.
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Thu Jul 10 06:01:34 2025 UTC