diff --git a/ext/standard/dir.c b/ext/standard/dir.c index 2eb9d853eeb16..8e9f2b53b4443 100644 --- a/ext/standard/dir.c +++ b/ext/standard/dir.c @@ -400,13 +400,34 @@ PHP_FUNCTION(getcwd) /* }}} */ /* {{{ Find pathnames matching a pattern */ +#if defined(ZTS) && defined(PHP_GLOB_ALTDIRFUNC) +static void *php_glob_opendir_wrapper(const char *path) +{ + return VCWD_OPENDIR(path); +} + +static void php_glob_closedir_wrapper(void *dir) +{ + (void) closedir(dir); +} + +static int php_glob_lstat_wrapper(const char *buf, zend_stat_t *sb) +{ + return VCWD_LSTAT(buf, sb); +} + +static int php_glob_stat_wrapper(const char *buf, zend_stat_t *sb) +{ + return VCWD_STAT(buf, sb); +} +#endif + PHP_FUNCTION(glob) { size_t cwd_skip = 0; -#ifdef ZTS +#if defined(ZTS) && !defined(PHP_GLOB_ALTDIRFUNC) char cwd[MAXPATHLEN]; char work_pattern[MAXPATHLEN]; - char *result; #endif char *pattern = NULL; size_t pattern_len; @@ -433,9 +454,28 @@ PHP_FUNCTION(glob) RETURN_FALSE; } + memset(&globbuf, 0, sizeof(globbuf)); + + int passed_glob_flags = flags & PHP_GLOB_FLAGMASK; + #ifdef ZTS if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) { - result = VCWD_GETCWD(cwd, MAXPATHLEN); + /* System glob uses the current work directory which is not thread safe. + * The first fix is to override the functions used to open/read/... paths + * with the VCWD ones used in PHP. + * If that functionality is unavailable for whatever reason, fall back + * to prepending the current working directory to the passed path. + * However, that comes with limitations regarding meta characters + * that is not solvable in general (GH-13204). */ +#ifdef PHP_GLOB_ALTDIRFUNC + globbuf.gl_opendir = php_glob_opendir_wrapper; + globbuf.gl_readdir = (struct dirent *(*)(void *)) readdir; + globbuf.gl_closedir = php_glob_closedir_wrapper; + globbuf.gl_lstat = php_glob_lstat_wrapper; + globbuf.gl_stat = php_glob_stat_wrapper; + passed_glob_flags |= PHP_GLOB_ALTDIRFUNC; +#else + char *result = VCWD_GETCWD(cwd, MAXPATHLEN); if (!result) { cwd[0] = '\0'; } @@ -448,13 +488,13 @@ PHP_FUNCTION(glob) snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern); pattern = work_pattern; +#endif } #endif - memset(&globbuf, 0, sizeof(globbuf)); globbuf.gl_offs = 0; - if (0 != (ret = php_glob(pattern, flags & PHP_GLOB_FLAGMASK, NULL, &globbuf))) { + if (0 != (ret = php_glob(pattern, passed_glob_flags, NULL, &globbuf))) { #ifdef PHP_GLOB_NOMATCH if (PHP_GLOB_NOMATCH == ret) { /* Some glob implementation simply return no data if no matches diff --git a/ext/standard/tests/file/gh13204.phpt b/ext/standard/tests/file/gh13204.phpt new file mode 100644 index 0000000000000..1af66539195a4 --- /dev/null +++ b/ext/standard/tests/file/gh13204.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-13204 (glob() fails if square bracket is in current directory) +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + string(11) "./empty.txt" +} diff --git a/ext/standard/tests/file/gh13204[brackets]/empty.txt b/ext/standard/tests/file/gh13204[brackets]/empty.txt new file mode 100644 index 0000000000000..e69de29bb2d1d