|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2007-07-05 06:34 UTC] geoffwa at cs dot rmit dot edu dot au
Description: ------------ (possibly related to bug #39953 or bug #39351) If a relative path to a file has '..' as a leading path component, and the directory referred to by '..' is not readable by the user, then opening a file using the relative path fails. Using an absolute path or a path with a non-'..' leading component opens said file just fine. Reproduce code: --------------- See http://goanna.cs.rmit.edu.au/~geoffwa/relative_path_bug.php for a large test script. Expected result: ---------------- (Using PHP 5.1.4) Current working directory is: /home/g/geoffwa/test Opened /home/g/geoffwa/test/a/b/file Opened ./a/b/file from ./a using ./b/file Opened ./a/b/file from ./a using ./b/c/../file Opened ./a/b/file from ./a/b/c using ../file Opened ../file from ./a/b/c using ./../file Opened ./a/b/file from ./a/b using ./file Opened ./a/file from ./a/b using ./c/../../file Opened ./a/b/c/file from ./a/b/c using ../c/file Actual result: -------------- (Using PHP 5.2.3 + suhosin patch) Opened /home/g/geoffwa/test/a/b/file Opened ./a/b/file from ./a using ./b/file Opened ./a/b/file from ./a using ./b/c/../file Failed to open ./a/b/file from ./a/b/c using ../file Failed to open ./a/b/file from ./a/b/c using ./../file Opened ./a/b/file from ./a/b using ./file Opened ./a/file from ./a/b using ./c/../../file Failed to open ./a/b/c/file from ./a/b/c using ../c/file PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Nov 18 09:00:01 2025 UTC |
Doing a bit of tracing of expand_filepath nets: (from PHP-5.2.3) expand_filepath(filepath = 0xffbff5f4 "test2.php", real_path = 0xffbfee20 "") called from function php_execute_script expand_filepath returns 0xffbfee20 "/home/g/geoffwa/test/test2.php" expand_filepath(filepath = 0xffbfdec0 "./a", real_path = 0xffbfe2c4 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe2c4 "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe108 "./a/b/file", real_path = 0xffbfe50c "") called from function php_checkuid_ex expand_filepath returns 0xffbfe50c "/home/g/geoffwa/test/a/b/file" expand_filepath(filepath = 0xffbfe098 "./a", real_path = 0xffbfe49c "") called from function php_checkuid_ex expand_filepath returns 0xffbfe49c "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe028 "./a", real_path = 0xffbfe42c "") called from function php_checkuid_ex expand_filepath returns 0xffbfe42c "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe120 "./a/b", real_path = 0xffbfe524 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe524 "/home/g/geoffwa/test/a/b" expand_filepath(filepath = 0xffbfdfd8 "../b/file", real_path = 0xffbfe3dc "") called from function php_checkuid_ex expand_filepath returns (nil) (from PHP 5.2 snap 200707060030) expand_filepath(filepath = 0xffbff5ef "test2.php", real_path = 0xffbfee18 "") called from function php_execute_script expand_filepath returns 0xffbfee18 "/home/g/geoffwa/test/test2.php" expand_filepath(filepath = 0xffbfdeb8 "./a", real_path = 0xffbfe2bc "") called from function php_checkuid_ex expand_filepath returns 0xffbfe2bc "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe100 "./a/b/file", real_path = 0xffbfe504 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe504 "/home/g/geoffwa/test/a/b/file" expand_filepath(filepath = 0xffbfe090 "./a", real_path = 0xffbfe494 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe494 "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe020 "./a", real_path = 0xffbfe424 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe424 "/home/g/geoffwa/test/a" expand_filepath(filepath = 0xffbfe118 "./a/b", real_path = 0xffbfe51c "") called from function php_checkuid_ex expand_filepath returns 0xffbfe51c "/home/g/geoffwa/test/a/b" expand_filepath(filepath = 0xffbfdfd0 "../b/file", real_path = 0xffbfe3d4 "") called from function php_checkuid_ex expand_filepath returns 0xffbfe3d4 "../b/file" expand_filepath(filepath = 0x53d1c0 "../b/file", real_path = (nil)) called from function _php_stream_fopen expand_filepath returns 0x53d398 "../b/file"It's still broken in CVS (my bad - forgot to remove the workaround patch we had). virtual_file_ex() get called several times, with the last invocation being: virtual_file_ex(state = 0xffbfdf9c, path = 0xffbfe018 "../b/file", verify_path = (nil), use_realpath = 1) called from function expand_filepath virtual_file_ex returns 1 Having written a rather grandoise summary of stepping through virtual_file_ex() I think the problem might be in php_checkuid_ex().No idea if this is correct but it fixes it: diff -ur ./php5.2-200707060030/main/safe_mode.c ./php-5.2-snap/main/safe_mode.c --- ./php5.2-200707060030/main/safe_mode.c 2007-01-13 00:30:58.000000000 +1100 +++ ./php-5.2-snap/main/safe_mode.c 2007-07-07 11:42:10.804129000 +1000 @@ -86,7 +86,8 @@ * If that fails, passthrough and check directory... */ if (mode != CHECKUID_ALLOW_ONLY_DIR) { - expand_filepath(filename, path TSRMLS_CC); + // VCWD_STAT() can handle relative paths right? + strlcpy(path, filename, MAXPATHLEN); ret = VCWD_STAT(path, &sb); if (ret < 0) { if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) { diff -ur ./php5.2-200707060030/main/streams/plain_wrapper.c ./php-5.2-snap/main/streams/plain_wrapper.c --- ./php5.2-200707060030/main/streams/plain_wrapper.c 2007-04-19 00:31:35.000000000 +1000 +++ ./php-5.2-snap/main/streams/plain_wrapper.c 2007-07-07 11:58:57.673891000 +1000 @@ -888,9 +888,10 @@ return NULL; } - if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) { - return NULL; - } + //if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) { + // return NULL; + //} + realpath = estrndup(filename, strlen(filename)); if (persistent) { spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath);With php5.2-200710080430 the problem is worse, not better. I can't even mkdir() in my test script any more! <?php ini_set('display_errors', '1'); mkdir("./a/b", 0700, true) or die('mkdir failed'); touch("./a/b/file") or die('touch failed'); chmod("./a", 0300) or die('chmod failed'); chdir("./a/b") or die('chdir failed'); $fp = fopen('../b/file', 'r'); if ($fp) print "SUCCESS\n"; else print "FAILURE\n"; ?> produces: Warning: mkdir(): Unable to access ./a in /home/g/geoffwa/work/test/test2.php on line 3 mkdir failed Looking at truss, the last four syscalls are: getcwd("/home/g/geoffwa/work/test", 1024) = 0 resolvepath("./a", 0xFFBFD238, 1024) Err#2 ENOENT stat("a", 0xFFBFDF20) Err#2 ENOENT stat("a", 0xFFBFDF20) Err#2 ENOENT I'd also like to point out that the Solaris getcwd() works fine: (from the man page) The getcwd() function may fail if: EACCES A parent directory cannot be read to get its name.Geoffwa or ian, could you please send me a .tar with the directory structure and permissions you are using along with instructions on how to reproduce the error? The script works just fine for me in Solaris with the most recent CVS. ------- d--x--x--x 2 rob rob 512 Oct 8 09:24 . drwxr-xr-x 20 rob rob 1536 Oct 8 09:15 .. -rwxr-xr-x 1 rob rob 10620420 Oct 8 09:17 php-cvs -rw-r--r-- 1 rob rob 301 Oct 8 09:17 test.php ------- [0925][rob@opteron:~/mkdirtest]$ ./php-cvs ./test.php Warning: mkdir(): Permission denied in /export/home/rob/mkdirtest/test.php on line 3 mkdir failed [0925][rob@opteron:~/mkdirtest]$ chmod u+w . [0925][rob@opteron:~/mkdirtest]$ ./php-cvs ./test.php SUCCESS [0925][rob@opteron:~/mkdirtest]$Geoffwa, when you get a chance, please let me know if applying the patch below to the current snapshot fixes the issue for you. [2014][rob@opteron:~/mkdirtest]$ ./php-solfix -dsafe_mode=1 ./test2.php Current working directory is: /export/home/rob/mkdirtest Opened /export/home/rob/mkdirtest/a/b/file Opened ./a/b/file from ./a using ./b/file Opened ./a/b/file from ./a using ./b/c/../file Opened ./a/b/file from ./a/b/c using ../file Opened ../file from ./a/b/c using ./../file Opened ./a/b/file from ./a/b using ./file Opened ./a/file from ./a/b using ./c/../../file Opened ./a/b/c/file from ./a/b/c using ../c/file [2014][rob@opteron:~/mkdirtest]$ ./php-solfix -dsafe_mode=1 ./test.php SUCCESS --- ./safe_mode.c.old 2007-09-23 10:19:21.000000000 -0500 +++ ./safe_mode.c 2007-10-09 19:39:44.000000000 -0500 @@ -86,7 +86,15 @@ * If that fails, passthrough and check directory... */ if (mode != CHECKUID_ALLOW_ONLY_DIR) { - expand_filepath(filename, path TSRMLS_CC); + + char filename_test[MAXPATHLEN]; + strcpy(filename_test,filename); + if (VCWD_GETCWD(filename_test, sizeof(filename)) == NULL) { + strcpy(path,filename); + } else { + expand_filepath(filename, path TSRMLS_CC); + } + ret = VCWD_STAT(path, &sb); if (ret < 0) { if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) {PHP5.2-200710080430 + your patch still doesn't work for either test case (also, shouldn't sizeof(filename) be sizeof(filename_test)?). $ ./php5.2-200710080430-fixed -dsafe_mode=1 test2.php Warning: mkdir(): Unable to access ./a in /pathto/test2.php on line 3 mkdir failed (so the initial mkdir("./a/b", 0700, true) call is failing) Stepping through the non-patched PHP5.2-200710080430 the error message is being generated from: if (mode != CHECKUID_ALLOW_ONLY_FILE) { /* check directory */ ret = VCWD_STAT(path, &sb); if (ret < 0) { if ((flags & CHECKUID_NO_ERRORS) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename); } return 0; } (line 147 in safe_mode.c)