Doc #79704 [ReO->Csd]: proc_open failure with Windows
| From: | phpdocbot@php.net | Date: | Thu, 18 Jun 2020 08:54:10 +0000 |
| Subject: | Doc #79704 [ReO->Csd]: proc_open failure with Windows | ||
| References: | 1 | Groups: | php.doc.bugs |
| Request: | Send a blank email to doc-bugs+get-17660@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=79704&edit=1
ID: 79704
Updated by: phpdocbot@php.net
Reported by: svnpenn at gmail dot com
Summary: proc_open failure with Windows
-Status: Re-Opened
+Status: Closed
Type: Documentation Problem
Package: Program Execution
Operating System: Windows 10
PHP Version: 7.4.7
Assigned To: cmb
Block user comment: N
Private report: N
New Comment:
Automatic comment on behalf of cmb
Revision: http://git.php.net/?p=doc/en.git;a=commit;h=24143af06cb3df496e671ae710af6b54398b2f74
Log: Fix #79704: proc_open failure with Windows
Previous Comments:
------------------------------------------------------------------------
[2020-06-17 07:55:52] cmb@php.net
If $cmd is an array, the bypass_shell option is implicitly set, so
there is no shell (COMSPEC) involved. Thus, shell escaping does
not apply. Instead, the arguments are escaped in conformance to
how parsing C++ command line arguments is documented[1]. This
works fine, if the executed command actually uses the runtime
supplied argv[]; msiexec.exe unfortunately does not.
Admittedly, the escaping applied to the $cmd array elements, as
well as that of escapeshellarg() does more than necessary, but
that can't be changed for BC reasons (at least not for stable PHP
branches).
[1] <https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#parsing-c-command-line-arguments>
------------------------------------------------------------------------
[2020-06-16 23:20:57] svnpenn at gmail dot com
> proc_open() with the $cmd as an array will quote all the arguments.
With a POSIX shell, this is fine. As with non special characters, a quoted
string is the same as a non quoted string. So with a POSIX shell, these would
be the same:
/a
'/a'
but the Windows shell is not POSIX. And quoting should only be used when it is
needed. A command like this will succeed:
msiexec.exe TargetDir=C:\TargetDir /qb /a a.msi
but add quotes and it fails:
msiexec.exe TargetDir=C:\TargetDir /qb "/a" a.msi
So it seems strange that "proc_open" assumes this is the correct thing to do for
all platforms. As detailed in the original post, other languages dont do that.
To make it worse, "escapeshellarg" is broken on Windows as well:
> On Windows, escapeshellarg() instead replaces percent signs, exclamation marks
> (delayed variable substitution) and double quotes with spaces and adds double
> quotes around the string.
https://php.net/function.escapeshellarg
From a security standpoint that might be fine, but makes it impossible to run
some valid external commands from PHP on Windows platform. As a workaround, I
wrote this function:
function f_escape_sh($s_in) {
$s_out = preg_replace('/"/', '""', $s_in);
if ($s_out != $s_in) {
return '"' . $s_out . '"';
}
$n_mat = preg_match('/[ &>^-]/', $s_in);
if ($n_mat == 1) {
return '"' . $s_out . '"';
}
return $s_out;
}
------------------------------------------------------------------------
[2020-06-16 08:21:49] cmb@php.net
Apparently, msiexec.exe does not use the supplied argv[1], but
rather GetCommandLine() doing its own parsing of the arguments.
Other commands may do this as well, so we should at least document
this issue.
[1] <https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#parsing-c-command-line-arguments>
------------------------------------------------------------------------
[2020-06-16 02:46:28] requinix@php.net
proc_open() with the $cmd as an array will quote all the arguments.
Try running the resultant command yourself:
C:\>"C:\Windows\System32\msiexec.exe" "TargetDir=C:\TargetDir"
"/qb" "/a" "a.msi"
What you need to do is
$a = "C:\\Windows\\System32\\msiexec.exe TargetDir=C:\\TargetDir /qb /a a.msi";
The important thing is that you do not quote the TargetDir option or the flags. If the filename is
variable then you may quote it (using escapeshellarg) - this works because msiexec specifically
supports it.
------------------------------------------------------------------------
[2020-06-16 02:30:42] svnpenn at gmail dot com
Description:
------------
proc_open failure with Windows
If I run this Python script:
import subprocess
a = [
r'C:\Windows\System32\msiexec.exe',
r'TargetDir=C:\TargetDir',
'/qb',
'/a',
'a.msi'
]
subprocess.run(a)
Everything is fine, but if I run this PHP script:
<?php
declare(strict_types = 1);
$a1 = [
'C:\\Windows\\System32\\msiexec.exe',
'TargetDir=C:\\TargetDir',
'/qb',
'/a',
'a.msi'
];
$a2 = [];
$a3 = [];
proc_open($a1, $a2, $a3);
I just get the usage page for Windows Installer. The documentation and tests
show that you can use an array:
- https://github.com/php/php-src/blob/master/ext/standard/tests/general_functions/proc_open_array.phpt
- https://www.php.net/manual/en/function.proc-open.php
but it seems PHP is doing something funny with the parsing.
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=79704&edit=1