@@ -400,13 +400,34 @@ PHP_FUNCTION(getcwd)
400400/* }}} */
401401
402402/* {{{ Find pathnames matching a pattern */
403+ #if defined(ZTS ) && defined(PHP_GLOB_ALTDIRFUNC )
404+ static void * php_glob_opendir_wrapper (const char * path )
405+ {
406+ return VCWD_OPENDIR (path );
407+ }
408+
409+ static void php_glob_closedir_wrapper (void * dir )
410+ {
411+ (void ) closedir (dir );
412+ }
413+
414+ static int php_glob_lstat_wrapper (const char * buf , zend_stat_t * sb )
415+ {
416+ return VCWD_LSTAT (buf , sb );
417+ }
418+
419+ static int php_glob_stat_wrapper (const char * buf , zend_stat_t * sb )
420+ {
421+ return VCWD_STAT (buf , sb );
422+ }
423+ #endif
424+
403425PHP_FUNCTION (glob )
404426{
405427 size_t cwd_skip = 0 ;
406- #ifdef ZTS
428+ #if defined( ZTS ) && !defined( PHP_GLOB_ALTDIRFUNC )
407429 char cwd [MAXPATHLEN ];
408430 char work_pattern [MAXPATHLEN ];
409- char * result ;
410431#endif
411432 char * pattern = NULL ;
412433 size_t pattern_len ;
@@ -433,28 +454,45 @@ PHP_FUNCTION(glob)
433454 RETURN_FALSE ;
434455 }
435456
457+ memset (& globbuf , 0 , sizeof (globbuf ));
458+
459+ int passed_glob_flags = flags & PHP_GLOB_FLAGMASK ;
460+
436461#ifdef ZTS
437462 if (!IS_ABSOLUTE_PATH (pattern , pattern_len )) {
438- result = VCWD_GETCWD (cwd , MAXPATHLEN );
463+ /* System glob uses the current work directory which is not thread safe.
464+ * The first fix is to override the functions used to open/read/... paths
465+ * with the VCWD ones used in PHP.
466+ * If that functionality is unavailable for whatever reason, fall back
467+ * to prepending the current working directory to the passed path.
468+ * However, that comes with limitations regarding meta characters
469+ * that is not solvable in general (GH-13204). */
470+ # ifdef PHP_GLOB_ALTDIRFUNC
471+ globbuf .gl_opendir = php_glob_opendir_wrapper ;
472+ globbuf .gl_readdir = (struct dirent * (* )(void * )) readdir ;
473+ globbuf .gl_closedir = php_glob_closedir_wrapper ;
474+ globbuf .gl_lstat = php_glob_lstat_wrapper ;
475+ globbuf .gl_stat = php_glob_stat_wrapper ;
476+ passed_glob_flags |= PHP_GLOB_ALTDIRFUNC ;
477+ # else
478+ char * result = VCWD_GETCWD (cwd , MAXPATHLEN );
439479 if (!result ) {
440480 cwd [0 ] = '\0' ;
441481 }
442- #ifdef PHP_WIN32
482+ # ifdef PHP_WIN32
443483 if (IS_SLASH (* pattern )) {
444484 cwd [2 ] = '\0' ;
445485 }
446- #endif
486+ # endif
447487 cwd_skip = strlen (cwd )+ 1 ;
448488
449489 snprintf (work_pattern , MAXPATHLEN , "%s%c%s" , cwd , DEFAULT_SLASH , pattern );
450490 pattern = work_pattern ;
491+ # endif
451492 }
452493#endif
453494
454-
455- memset (& globbuf , 0 , sizeof (globbuf ));
456- globbuf .gl_offs = 0 ;
457- if (0 != (ret = php_glob (pattern , flags & PHP_GLOB_FLAGMASK , NULL , & globbuf ))) {
495+ if (0 != (ret = php_glob (pattern , passed_glob_flags , NULL , & globbuf ))) {
458496#ifdef PHP_GLOB_NOMATCH
459497 if (PHP_GLOB_NOMATCH == ret ) {
460498 /* Some glob implementation simply return no data if no matches
0 commit comments