diff --git a/ext/standard/link.c b/ext/standard/link.c index df2831b87846..948cf618c02e 100644 --- a/ext/standard/link.c +++ b/ext/standard/link.c @@ -131,11 +131,21 @@ PHP_FUNCTION(symlink) char dirname[MAXPATHLEN]; size_t len; + zend_stat_t v_lstat = {0}; + zend_stat_t v_stat = {0}; + ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_PATH(topath, topath_len) Z_PARAM_PATH(frompath, frompath_len) ZEND_PARSE_PARAMETERS_END(); + // dangling link should be treated as existing file + ret = VCWD_LSTAT(frompath, &v_lstat); + if (!ret && S_ISLNK(v_lstat.st_mode) && VCWD_STAT(frompath, &v_stat)) { + php_error_docref(NULL, E_WARNING, "Dangling symlink at %s", frompath); + RETURN_FALSE; + } + if (!expand_filepath(frompath, source_p)) { php_error_docref(NULL, E_WARNING, "No such file or directory"); RETURN_FALSE; diff --git a/ext/standard/tests/gh21992t.phpt b/ext/standard/tests/gh21992t.phpt new file mode 100644 index 000000000000..60af0d2bcb1d --- /dev/null +++ b/ext/standard/tests/gh21992t.phpt @@ -0,0 +1,76 @@ +--TEST-- +GH-21992: symlink() must not resolve the final dangling link component +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +bool(true) +string(%d) "%s%egh21992%etarget_dir%ereal.txt" + +Warning: symlink(): File exists in %s on line %d +bool(false) +bool(true) +string(%d) "%s%egh21992%etarget_dir%ereal.txt" +bool(false) +bool(true) +string(%d) "%s%egh21992%etarget_dir%eghost.txt" + +Warning: symlink(): Dangling symlink at %s on line %d +bool(false) +bool(true) +string(%d) "%s%egh21992%etarget_dir%eghost.txt" +bool(false)