Doc #79704 [ReO->Csd]: proc_open failure with Windows

From: 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

« previous php.doc.bugs (#17660) next »