Page MenuHomePhabricator

Refine checks against "cwd" before "proc_open()"
ClosedPublic

Authored by epriestley on Feb 22 2019, 11:40 PM.
Tags
None
Referenced Files
Unknown Object (File)
Wed, Nov 27, 12:17 PM
Unknown Object (File)
Wed, Nov 27, 9:57 AM
Unknown Object (File)
Wed, Nov 27, 5:50 AM
Unknown Object (File)
Tue, Nov 26, 10:13 PM
Unknown Object (File)
Tue, Nov 26, 10:13 PM
Unknown Object (File)
Tue, Nov 26, 10:13 PM
Unknown Object (File)
Tue, Nov 26, 10:13 PM
Unknown Object (File)
Tue, Nov 26, 10:13 PM
Subscribers
None

Details

Summary

See PHI1094. Previously, see D15785. Fixes T10853. Upstream: https://bugs.php.net/bug.php?id=77656.

PHP allows you to proc_open() into any CWD, including one that does not exist, is not readable, and/or is not executable.

We attempt to catch this, but don't do enough checks, and allow you to attempt execution into a directory which exists but which you can't read or execute.

Add more checks to prevent this. Also, try to make assertExists() more clever about cases where a path really doesn't exist versus cases where we aren't allowed to tell if a path exists or not because of a permissions issue.

(It's technically legal to chdir() into a directory you only have +x on, although this is probably ill-advised.)

Test Plan

Used this script with various arguments:

<?php

require_once '/Users/epriestley/dev/phabricator/scripts/init/init-script.php';

$result = id(new ExecFuture('pwd && ls -alh'))
  ->setCWD($argv[1])
  ->resolvex();

var_dump($result);

In all cases, the directory name describes the permissions. subdir/ exists and is a directory we have "+r" and "+x" on.

epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-readable
[2019-02-22 15:34:14] EXCEPTION: (Exception) Preparing to run a command in directory "/Users/epriestley/scratch/not-readable", but that directory is not readable (the current process does not have "+r" permission). at [<phutil>/src/future/exec/PhutilExecutableFuture.php:156]
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-readable/subdir
array(2) {
  [0]=>
  string(163) "/Users/epriestley/scratch/not-readable/subdir
total 0
drwxr-xr-x  2 epriestley  staff    64B Feb 22 15:33 .
drwxr-x--x  3 root        wheel    96B Feb 22 15:33 ..
"
  [1]=>
  string(0) ""
}
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-executable
[2019-02-22 15:34:24] EXCEPTION: (Exception) Preparing to run a command in directory "/Users/epriestley/scratch/not-executable", but that directory is not executable (the current process does not have "+x" permission). at [<phutil>/src/future/exec/PhutilExecutableFuture.php:165]
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-executable/subdir
[2019-02-22 15:34:26] EXCEPTION: (PhutilProxyException) Unable to run a command in directory "/Users/epriestley/scratch/not-executable/subdir". {>} (FilesystemException) Filesystem path "/Users/epriestley/scratch/not-executable/subdir" can not be accessed because a parent directory ("/Users/epriestley/scratch/not-executable") is not executable (the current process does not have "+x" permission). at [<phutil>/src/filesystem/Filesystem.php:1142]
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-readable-or-executable/
[2019-02-22 15:34:32] EXCEPTION: (Exception) Preparing to run a command in directory "/Users/epriestley/scratch/not-readable-or-executable/", but that directory is not readable (the current process does not have "+r" permission). at [<phutil>/src/future/exec/PhutilExecutableFuture.php:156]
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/not-readable-or-executable/subdir
[2019-02-22 15:34:33] EXCEPTION: (PhutilProxyException) Unable to run a command in directory "/Users/epriestley/scratch/not-readable-or-executable/subdir". {>} (FilesystemException) Filesystem path "/Users/epriestley/scratch/not-readable-or-executable/subdir" can not be accessed because a parent directory ("/Users/epriestley/scratch/not-readable-or-executable") is not executable (the current process does not have "+x" permission). at [<phutil>/src/filesystem/Filesystem.php:1142]
epriestley@orbital ~/scratch $ php -f pwd2.php /Users/epriestley/scratch/does-not-exist
[2019-02-22 15:34:37] EXCEPTION: (PhutilProxyException) Unable to run a command in directory "/Users/epriestley/scratch/does-not-exist". {>} (FilesystemException) Filesystem path "/Users/epriestley/scratch/does-not-exist" does not exist. at [<phutil>/src/filesystem/Filesystem.php:1153]

Diff Detail

Repository
rPHU libphutil
Branch
subexec1
Lint
Lint Passed
SeverityLocationCodeMessage
Advicesrc/filesystem/Filesystem.php:1145XHP62Class Name Literal
Advicesrc/filesystem/Filesystem.php:1156XHP62Class Name Literal
Unit
Tests Passed
Build Status
Buildable 22098
Build 30188: Run Core Tests
Build 30187: arc lint + arc unit

Event Timeline

Harbormaster returned this revision to the author for changes because remote builds failed.Feb 22 2019, 11:40 PM
Harbormaster failed remote builds in B22097: Diff 48240!

Builds are failing in a curious way so this probably breaks something, let me see if I can figure out what..

src/filesystem/Filesystem.php
1145

Actual example of ">>" working properly, above. ^^^

  • Set my "origin" back to the right value after testing git remote set-url commands with random values.
Harbormaster returned this revision to the author for changes because remote builds failed.Feb 22 2019, 11:50 PM
Harbormaster failed remote builds in B22098: Diff 48241!
  • Remove local code that made "arc diff" change the working copy's origin URL.
This revision is now accepted and ready to land.Feb 23 2019, 2:39 AM

I'm going to cut the release before trying to unleash this, and I also want to make sure is_executable($dir) works in a similar way on Windows.

It's possible the behavior is, say, "always return false", in which case I'll add if (windows) { do nothing } else { check execute } sorts of guards.

On Windows, I believe the call chain is is_executable(...)VCWD_ACCESS(..., X_OK)tsrm_win32_access(..., X_OK)php_win32_ioutil_access_w(..., X_OK)GetBinaryTypeW(...).

This method tests if a path is a .exe: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-getbinarytypew

This suggests is_executable(...) will always return false for directories on Windows. As far as I can tell, this is also the actual behavior.

I'm going to skip the is_executable(..) checks under Windows for now since they definitely don't work as written (as far as I can tell, they always fail). If we discover there's some secret alternative magic in the future, we can specialize things further.

  • On Windows, don't do "+x" checks against directories because these checks seem to always fail and not be meaningful anyway.
This revision was automatically updated to reflect the committed changes.