Commit 84b0a73685d for php.net
commit 84b0a73685d1218abe21764fdabb54d1a0b42b79
Author: Nora Dossche <7771979+ndossche@users.noreply.github.com>
Date: Tue Mar 3 22:29:07 2026 +0100
Fix GH-13204: glob() fails if square bracket is in current directory (#19647)
The problem is not limited to square brackets, but to every meta
character. The solution is to override the glob functions for handling
paths with the VCWD ones in PHP. If that is not available, use the old
but limited workaround.
diff --git a/NEWS b/NEWS
index cf36b1bad7c..75332b89a1e 100644
--- a/NEWS
+++ b/NEWS
@@ -123,6 +123,8 @@ PHP NEWS
defaulted to 0. (Jorg Sowa)
. Fixed bug GH-21058 (error_log() crashes with message_type 3 and
null destination). (David Carlier)
+ . Fixed bug GH-13204 (glob() fails if square bracket is in current directory).
+ (ndossche)
- Streams:
. Added so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt stream
diff --git a/ext/standard/dir.c b/ext/standard/dir.c
index 7c1f8efe688..730ef615490 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,28 +454,45 @@ 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';
}
-#ifdef PHP_WIN32
+# ifdef PHP_WIN32
if (IS_SLASH(*pattern)) {
cwd[2] = '\0';
}
-#endif
+# endif
cwd_skip = strlen(cwd)+1;
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 00000000000..1af66539195
--- /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--
+<?php
+chdir(__DIR__ . '/gh13204[brackets]/');
+var_dump(glob('./*'));
+?>
+--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 00000000000..e69de29bb2d