From e8a60d8eb9ff1cd8d226ece4706ab3e3418619d4 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 6 Dec 2024 23:12:48 +0100 Subject: [PATCH 1/4] Fix GH-17067: glob:// wrapper doesn't cater to CWD for ZTS builds `glob(3)` doesn't know the virtual CWD of PHP, so we need to pass an absolute path for ZTS builds. In lack of a reusable routine, we copy the code from `glob()` and adapt as needed. --- ext/standard/tests/streams/gh17067.phpt | 22 ++++++++++++++++++++++ main/streams/glob_wrapper.c | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 ext/standard/tests/streams/gh17067.phpt diff --git a/ext/standard/tests/streams/gh17067.phpt b/ext/standard/tests/streams/gh17067.phpt new file mode 100644 index 0000000000000..f3ea6a682a5ef --- /dev/null +++ b/ext/standard/tests/streams/gh17067.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-17067 (glob:// wrapper doesn't cater to CWD for ZTS builds) +--FILE-- + +--CLEAN-- + +--EXPECT-- +array(1) { + [0]=> + string(3) "foo" +} diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 66f2e8e0dfc70..f5be13cc322cc 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -226,6 +226,28 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha } } +#ifdef ZTS + char cwd[MAXPATHLEN]; + char work_pattern[MAXPATHLEN]; + char *result; + size_t cwd_skip = 0; + if (!IS_ABSOLUTE_PATH(path, strlen(path))) { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + if (!result) { + cwd[0] = '\0'; + } +#ifdef PHP_WIN32 + if (IS_SLASH(*path)) { + cwd[2] = '\0'; + } +#endif + cwd_skip = strlen(cwd)+1; + + snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); + path = work_pattern; + } +#endif + pglob = ecalloc(1, sizeof(*pglob)); if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { From 8f7cd29aae998a3262e5438cec18a5485fb2298e Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 7 Dec 2024 20:31:09 +0100 Subject: [PATCH 2/4] Actually use cwd_skip We should strip the CWD whenever we call `php_glob_stream_path_split()`, but that would require to store the value in `glob_s_t` what we cannot do for stable PHP versions since it would be an ABI break. Thus we traverse `gl_pathv`, and strip the prepended CWD there. --- main/streams/glob_wrapper.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index f5be13cc322cc..70e7322c1a722 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -236,21 +236,22 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha if (!result) { cwd[0] = '\0'; } -#ifdef PHP_WIN32 +# ifdef PHP_WIN32 if (IS_SLASH(*path)) { cwd[2] = '\0'; } -#endif +# endif cwd_skip = strlen(cwd)+1; snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); - path = work_pattern; } +#else + char *work_pattern = path; #endif pglob = ecalloc(1, sizeof(*pglob)); - if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { + if (0 != (ret = glob(work_pattern, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { #ifdef GLOB_NOMATCH if (GLOB_NOMATCH != ret) #endif @@ -260,6 +261,19 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha } } +#ifdef ZTS + /* strip prepended CWD */ + for (i = 0; i < pglob->glob.gl_pathc; i++) { + char *p = pglob->glob.gl_pathv[i]; + char *q = p + cwd_skip; + char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1; + while (q <= e) { + *p++ = *q++; + } + *p = '\0'; + } +#endif + /* if open_basedir in use, check and filter restricted paths */ if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) { pglob->open_basedir_used = true; From 2008df79b97db3bf253527929b19c451478b7131 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sat, 7 Dec 2024 22:37:29 +0100 Subject: [PATCH 3/4] fix --- main/streams/glob_wrapper.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 70e7322c1a722..75c2c21a5e888 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -225,7 +225,7 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha *opened_path = zend_string_init(path, strlen(path), 0); } } - + const char *pattern = path; #ifdef ZTS char cwd[MAXPATHLEN]; char work_pattern[MAXPATHLEN]; @@ -244,14 +244,13 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha cwd_skip = strlen(cwd)+1; snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); + pattern = work_pattern; } -#else - char *work_pattern = path; #endif pglob = ecalloc(1, sizeof(*pglob)); - if (0 != (ret = glob(work_pattern, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { + if (0 != (ret = glob(pattern, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { #ifdef GLOB_NOMATCH if (GLOB_NOMATCH != ret) #endif From d60b3b23b8e1ad9dbeb91d078343c5227d5113d6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 8 Dec 2024 18:25:41 +0100 Subject: [PATCH 4/4] Only strip CWD if we actually prepended it --- main/streams/glob_wrapper.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 75c2c21a5e888..8772850f784f3 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -261,15 +261,17 @@ static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const cha } #ifdef ZTS - /* strip prepended CWD */ - for (i = 0; i < pglob->glob.gl_pathc; i++) { - char *p = pglob->glob.gl_pathv[i]; - char *q = p + cwd_skip; - char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1; - while (q <= e) { - *p++ = *q++; + if (cwd_skip > 0) { + /* strip prepended CWD */ + for (i = 0; i < pglob->glob.gl_pathc; i++) { + char *p = pglob->glob.gl_pathv[i]; + char *q = p + cwd_skip; + char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1; + while (q <= e) { + *p++ = *q++; + } + *p = '\0'; } - *p = '\0'; } #endif