php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #39367 Entries from the realpath cache should be refreshed after unlink() and rename()
Submitted: 2006-11-04 00:09 UTC Modified: 2007-02-09 07:50 UTC
Votes:5
Avg. Score:4.0 ± 0.6
Reproduced:5 of 5 (100.0%)
Same Version:2 (40.0%)
Same OS:1 (20.0%)
From: j at pureftpd dot org Assigned: dmitry (profile)
Status: Closed Package: Filesystem function related
PHP Version: 5.2.0 OS: Any
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: j at pureftpd dot org
New email:
PHP Version: OS:

 

 [2006-11-04 00:09 UTC] j at pureftpd dot org
Description:
------------
realpath() on a symlink returns the final file the link 
points to.

Caching symbolic links through the realpath cache can cause 
unexpected behaviors.

Here's a fix against PHP 5.2.0

--- TSRM/tsrm_virtual_cwd.c.orig        Sat Nov  4 00:56:05 
2006
+++ TSRM/tsrm_virtual_cwd.c     Sat Nov  4 00:58:03 2006
@@ -562,7 +562,11 @@
        }
 
        if (use_realpath && CWDG(realpath_cache_size_limit)) 
{
-               realpath_cache_add(path, path_length, state-
>cwd, state->cwd_length, t TSRMLS_CC);
+               struct stat buf;
+               
+               if (lstat(path, &buf) == 0 && !S_ISLNK
(buf.st_mode)) {          +                       
realpath_cache_add(path, path_length, state->cwd, state-
>cwd_length, t TSRMLS_CC);
+               }
        }
 
        if (verify_path && verify_path(state)) {


Reproduce code:
---------------
See bug #36555, or that code :

<?php

@unlink('/tmp/1link');
@unlink('/tmp/1tmp');
@unlink('/tmp/testfile1');

file_put_contents('/tmp/testfile1', '42');
symlink('/tmp/testfile1', '/tmp/1tmp');
rename('/tmp/1tmp', '/tmp/1link');
$a = file_get_contents('/tmp/1link');
var_dump($a);

unlink('/tmp/1link');

clearstatcache();
$a = file_get_contents('/tmp/1link');
var_dump($a);

?>

Expected result:
----------------
The second file_get_contents() should fail because /tmp/1link 
has been unlinked.

But it doesn't.

Actual result:
--------------
42
42


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-11-04 02:00 UTC] j at pureftpd dot org
I think the real bug is that unlink(), rename() and rmdir() 
don't refresh the related realpath cache entries.

Here's a patch that fixes this, also available from ftp://
ftp.c9x.org/misc/
php_sync_realpath_cache_with_unlink_and_rename.diff



--- TSRM/tsrm_virtual_cwd.c.orig        Sat Nov  4 00:56:05 
2006
+++ TSRM/tsrm_virtual_cwd.c     Sat Nov  4 02:53:13 2006
@@ -361,6 +361,23 @@
 }
 
 
+CWD_API int realpath_cache_delete(const char *path, int 
path_len) {
+       unsigned long key = realpath_cache_key(path, 
path_len);
+       unsigned long n = key % (sizeof(CWDG
(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
+       realpath_cache_bucket **bucket = &CWDG
(realpath_cache)[n];
+
+       while (*bucket != NULL) {
+               if (key == (*bucket)->key && path_len == 
(*bucket)->path_len &&
+                          memcmp(path, (*bucket)->path, 
path_len) == 0) {
+                       realpath_cache_bucket *r = *bucket;
+                       *bucket = (*bucket)->next;
+                       CWDG(realpath_cache_size) -= sizeof
(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len 
+ 1;
+                       free(r);
+               }
+       }
+       return 0;
+}
+
 /* Resolve path relatively to state and put the real path 
into state */
 /* returns 0 for ok, 1 for error */
 CWD_API int virtual_file_ex(cwd_state *state, const char 
*path, verify_path_func verify_path, int use_realpath)
--- TSRM/tsrm_virtual_cwd.h.orig        Sat Nov  4 02:39:04 
2006
+++ TSRM/tsrm_virtual_cwd.h     Sat Nov  4 02:48:04 2006
@@ -232,14 +232,14 @@
 #define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, 
virtual_chdir TSRMLS_CC)
 #define VCWD_GETWD(buf)
 #define VCWD_REALPATH(path, real_path) virtual_realpath
(path, real_path TSRMLS_CC)
-#define VCWD_RENAME(oldname, newname) virtual_rename
(oldname, newname TSRMLS_CC)
+#define VCWD_RENAME(oldname, newname) (virtual_rename
(oldname, newname TSRMLS_CC) + realpath_cache_delete
(oldname, strlen(oldname)) + realpath_cache_delete(newname, 
strlen(newname)))
 #define VCWD_STAT(path, buff) virtual_stat(path, buff 
TSRMLS_CC)
 #if !defined(TSRM_WIN32)
 #define VCWD_LSTAT(path, buff) virtual_lstat(path, buff 
TSRMLS_CC)
 #endif
-#define VCWD_UNLINK(path) virtual_unlink(path TSRMLS_CC)
+#define VCWD_UNLINK(path) (virtual_unlink(path TSRMLS_CC) + 
realpath_cache_delete(path, strlen(path)))
 #define VCWD_MKDIR(pathname, mode) virtual_mkdir(pathname, 
mode TSRMLS_CC)
-#define VCWD_RMDIR(pathname) virtual_rmdir(pathname 
TSRMLS_CC)
+#define VCWD_RMDIR(pathname) (virtual_rmdir(pathname 
TSRMLS_CC) + realpath_cache_delete(pathname, strlen
(pathname)))
 #define VCWD_OPENDIR(pathname) virtual_opendir(pathname 
TSRMLS_CC)
 #define VCWD_POPEN(command, type) virtual_popen(command, 
type TSRMLS_CC)
 #define VCWD_ACCESS(pathname, mode) virtual_access
(pathname, mode TSRMLS_CC)
@@ -261,15 +261,15 @@
 #define VCWD_OPEN(path, flags) open(path, flags)
 #define VCWD_OPEN_MODE(path, flags, mode)      open(path, 
flags, mode)
 #define VCWD_CREAT(path, mode) creat(path, mode)
-#define VCWD_RENAME(oldname, newname) rename(oldname, 
newname)
+#define VCWD_RENAME(oldname, newname) (rename(oldname, 
newname) + realpath_cache_delete(oldname, strlen(oldname)) + 
realpath_cache_delete(newname, strlen(newname)))
 #define VCWD_CHDIR(path) chdir(path)
 #define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, 
chdir)
 #define VCWD_GETWD(buf) getwd(buf)
 #define VCWD_STAT(path, buff) stat(path, buff)
 #define VCWD_LSTAT(path, buff) lstat(path, buff)
-#define VCWD_UNLINK(path) unlink(path)
+#define VCWD_UNLINK(path) (unlink(path) + 
realpath_cache_delete(path, strlen(path)))
 #define VCWD_MKDIR(pathname, mode) mkdir(pathname, mode)
-#define VCWD_RMDIR(pathname) rmdir(pathname)
+#define VCWD_RMDIR(pathname) (rmdir(pathname) + 
realpath_cache_delete(pathname, strlen(pathname)))
 #define VCWD_OPENDIR(pathname) opendir(pathname)
 #define VCWD_POPEN(command, type) popen(command, type)
 #if defined(TSRM_WIN32)
 [2006-11-04 12:33 UTC] j at pureftpd dot org
Another fix is to have clearstatcache() also clear the 
realpath cache.

Patch follows. Also available from ftp://ftp.c9x.org/misc/
php_clearstatcache_should_clear_realpath_cache.diff

--- ext/standard/filestat.c.orig        Sat Nov  4 13:14:10 
2006
+++ ext/standard/filestat.c     Sat Nov  4 13:14:40 2006
@@ -633,6 +633,7 @@
                efree(BG(CurrentLStatFile));
                BG(CurrentLStatFile) = NULL;
        }
+       realpath_cache_empty();
 }
 /* }}} */
 
--- TSRM/tsrm_virtual_cwd.c.orig        Sat Nov  4 00:56:05 
2006
+++ TSRM/tsrm_virtual_cwd.c     Sat Nov  4 13:28:29 2006
@@ -360,6 +360,24 @@
        return NULL;
 }
 
+CWD_API int realpath_cache_empty(void)
+{
+       int i;
+       
+       for (i = 0; i < sizeof(CWDG(realpath_cache)) / 
sizeof(CWDG(realpath_cache)[0]); i++) {
+               realpath_cache_bucket *bucket = CWDG
(realpath_cache)[i];
+
+               while (bucket != NULL) {
+                       realpath_cache_bucket *r = bucket;
+                       bucket = bucket->next;
+                       free(r);
+               }
+               CWDG(realpath_cache)[i] = NULL;
+       }
+       CWDG(realpath_cache_size) = 0;
+       
+       return 0;
+}
 
 /* Resolve path relatively to state and put the real path 
into state */
 /* returns 0 for ok, 1 for error */
--- TSRM/tsrm_virtual_cwd.h.orig        Sat Nov  4 02:39:04 
2006
+++ TSRM/tsrm_virtual_cwd.h     Sat Nov  4 13:12:50 2006
@@ -156,6 +156,7 @@
 CWD_API DIR *virtual_opendir(const char *pathname 
TSRMLS_DC);
 CWD_API FILE *virtual_popen(const char *command, const char 
*type TSRMLS_DC);
 CWD_API int virtual_access(const char *pathname, int mode 
TSRMLS_DC);
+CWD_API int realpath_cache_empty(void);
 #if defined(TSRM_WIN32)
 /* these are not defined in win32 headers */
 #ifndef W_OK
 [2007-01-22 09:36 UTC] dmitry@php.net
The last patch is applied, so you have to use clearstatcache().

rename() and unlink() may rename/unlink directory and it may break several nested entries from realpath cache.

Fixed in CVS HEAD and PHP_5_2.
 [2007-02-09 07:50 UTC] j at pureftpd dot org
Thank you!

-Frank.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 26 23:01:28 2024 UTC