From patchwork Fri Sep 22 19:11:52 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 114088 Delivered-To: patch@linaro.org Received: by 10.140.106.117 with SMTP id d108csp3642949qgf; Fri, 22 Sep 2017 12:12:13 -0700 (PDT) X-Received: by 10.84.176.65 with SMTP id u59mr151554plb.278.1506107533161; Fri, 22 Sep 2017 12:12:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1506107533; cv=none; d=google.com; s=arc-20160816; b=lDJHWk5Q8JoyGiJpknwz1fsLdQX0Ed1+idwE+2Jj8zWsb2zAj88vAjzP3/52J9SLlv ECiULi0aL40eY83c/AQmFQVlCCkUTh6VuI/Qpiep4xD6vUQhh/WVSa2ZIufl1/jFRsz1 eXKSiiaxuS4GJMRKu5zaRp0niDeGCgQfK1jeCqp5XFvbIr6D5jWGviS3iJvOax0WG/Tg O7ZkuUtQNqRN5bwWTCY40oXEhBS78fIJTA+avebX7Nxl5Tnl5+tFRp6YCxPxnbuFVlGa Z7PtYfz9uEIF9mdqVaHQtFplz/PKB2ZzsRHKpOrGvPq6nt5ETO/qUFl5hHq5MfL+6CXI wLeg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=message-id:date:subject:cc:to:from:delivered-to:sender:list-help :list-post:list-archive:list-subscribe:list-unsubscribe:list-id :precedence:mailing-list:dkim-signature:domainkey-signature :arc-authentication-results; bh=07wqBe6QtJ+hmgy84HohiSfPm7Y0iVAcTLyulgpmNhs=; b=Fub977GuJzktklFDQa8J0jgvG5UxJ4VAx5NzS+fp9C3YzLhsKUgEX7BVxkt8cVJI38 whvH1XS61sGIEbVbIJzMR2PecwnZzh6eCaSFZ5rGKDoYa+tbM4wFVEGSEwWGoHwi7BdA CqFJtj9cV+4UDtzA30HPdD9ZJSt5qGls8ZvCPzXiH1+P+HY1phaH7EPqi+Yqvz2zyIMX +mqoCYccUMLfUltO7YMWU049qwYInZkt5DGgFk7ecsAEHkfzy5Whx7QuIyHOo1c7o4HT 5cY5xlzBtZsNh65EbZ+rH6AZoWsFkFmThhWWc51Y1id8zhjEWGOcP/K1laqTWgosJM6n e7Sw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@sourceware.org header.s=default header.b=V0/trWSi; spf=pass (google.com: domain of libc-alpha-return-84867-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=libc-alpha-return-84867-patch=linaro.org@sourceware.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id o12si274090pgr.430.2017.09.22.12.12.12 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 22 Sep 2017 12:12:13 -0700 (PDT) Received-SPF: pass (google.com: domain of libc-alpha-return-84867-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@sourceware.org header.s=default header.b=V0/trWSi; spf=pass (google.com: domain of libc-alpha-return-84867-patch=linaro.org@sourceware.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=libc-alpha-return-84867-patch=linaro.org@sourceware.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; q=dns; s= default; b=koQR88FTpkhmsNzNdvibQMrDquFReeDf8N+qH2oCYQdNlEFIKx/qR EN/w9G632xiR/Xtzs+wRiUAVVhkGPpNs274C1+aUG2xSFbL2JdGpM5ZL/wF6fFDy KnW86ID/eIpgpRYcqnhVsoc9rsp0Ppko3QlFB51u8dX8n3zqE+dIXw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; s=default; bh=C17qwFFrG5EHvVZzPXItPoYi93I=; b=V0/trWSiSuYmkamKrpQV+zwjNDHR rK0anTuPm13zDd6yPjhAE2nmiws/dCaORv4o9USPZo0mXBgAd8hf/nlMvXcDSb6u FPadTrRqEtiWPpk6Ar1lcD1X3Y9yJngJSJnJ/qq/CGF+b7d8jGTCRbQH+H9NGpkG K5O8Vo96taIS/2o= Received: (qmail 93456 invoked by alias); 22 Sep 2017 19:12:04 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 93446 invoked by uid 89); 22 Sep 2017 19:12:04 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-27.1 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=certify, 1315 X-HELO: mail-qt0-f178.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=07wqBe6QtJ+hmgy84HohiSfPm7Y0iVAcTLyulgpmNhs=; b=oykt53MaZpX+uug0U7R+ilZz94BabKXVnomy+fXx0Awi4ilCdCfQqoayqZAjij39M4 gVktKMQfTVWf4Q5sCGKKPr6Ydp6TwkNXmejFQ6IivpRr8+URd8daTkpjR9PEhrBt7h7N vSbNxLPIeN+Gf91aE6E9AEBlJdtTLjmQ0PDpeOozvi2sQN3u7JwbMvvc1801pGE9WsqM kjTx4DCZVX9UZZxkwExtYRPagEd2wTORYqq5WL+f0J0qxCzPfuMaTwFdAoxGRy6AtOwG LRTNZ6Ax9iFuzGC3hlnWtt5zfkMlrHc7/fG8OHDxlTeIhxYU9LpifWA5YgjTMbpiWYmp gUew== X-Gm-Message-State: AHPjjUiR8zuK0LMwuAMseN46V963G6I016Wfn+VWTEbVrvVMwgtN4veC HAWtu4q3l0ZIfOh9fnxedHWyYUkP2Zc= X-Google-Smtp-Source: AOwi7QCSSkeUE30sB14qgWRxdhrK7hGyCHlvvHMBN++C4JhUkUG1TBgRf3EyYpIiE2S4Mu/iLKI1CQ== X-Received: by 10.200.53.129 with SMTP id k1mr258757qtb.243.1506107519611; Fri, 22 Sep 2017 12:11:59 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Paul Eggert Subject: [PATCH v2] posix: Fix glob with GLOB_NOCHECK returning modified patterns (BZ#10246) Date: Fri, 22 Sep 2017 16:11:52 -0300 Message-Id: <1506107512-5013-1-git-send-email-adhemerval.zanella@linaro.org> This is a follow up of my previous fix of BZ#10246 [1] which addresses the issues raised by Paul Eggert. The issue is for glob calls which internally enabled GLOB_ONLYDIR (essentinally pattern ending with '/') required for dangling symlinks to actually check if the directory is accessible. An extra testcase is added on tst-glob_symlinks. --- According to POSIX glob with GLOB_NOCHECK should return a list consisting of only of the input pattern in case of no match. However GLIBC does not honor in case of '//' or '//' will be handle in same way. This patch fix it by using a empty directory name for the latter (since prefix_array already adds a slash as default for each entry). Checked on x86_64-linux-gnu and on a build using build-many-glibcs.py for all major architectures. * posix/glob (glob_lstat, glob_opendir, glob_readdir, glob_closedir): New function: wrapper for the lstat, opendir, readdir, and closedir respectively. (convert_dirent, convert_dirent64): Remove function. (glob, glob_in_dir): Handle pattern that do not match and start with '/' correctly. * posix/globtest.sh: New tests for NOCHECK. * posix/tst-glob_symlinks.c: Add tests for dangling directory. [1] https://sourceware.org/ml/libc-alpha/2017-09/msg00315.html --- ChangeLog | 11 ++++ posix/glob.c | 124 +++++++++++++++++++++++++--------------------- posix/globtest.sh | 37 ++++++++++++++ posix/tst-glob_symlinks.c | 6 +++ 4 files changed, 121 insertions(+), 57 deletions(-) -- 2.7.4 diff --git a/posix/glob.c b/posix/glob.c index c699177..391aa9c 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -128,36 +128,58 @@ readdir_result_type (struct readdir_result d) # define GL_READDIR(pglob, stream) ((pglob)->gl_readdir (stream)) #endif -/* Extract name and type from directory entry. No copy of the name is - made. If SOURCE is NULL, result name is NULL. Keep in sync with - convert_dirent64 below. */ -static struct readdir_result -convert_dirent (const struct dirent *source) + +union glob_stat { - if (source == NULL) - { - struct readdir_result result = { NULL, }; - return result; - } - struct readdir_result result = READDIR_RESULT_INITIALIZER (source); - return result; + struct stat st; + struct_stat64 st64; +}; + +static int +glob_lstat (glob_t *pglob, int flags, const char *fname, + union glob_stat *ust) +{ + return (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_lstat) (fname, &ust->st) + : __lstat64 (fname, &ust->st64)); +} + +static void * +glob_opendir (glob_t *pglob, int flags, const char *directory) +{ + return (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_opendir) (directory) + : opendir (directory)); } -#ifndef COMPILE_GLOB64 -/* Like convert_dirent, but works on struct dirent64 instead. Keep in - sync with convert_dirent above. */ static struct readdir_result -convert_dirent64 (const struct dirent64 *source) +glob_readdir (glob_t *pglob, int flags, void *stream) { - if (source == NULL) + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) { - struct readdir_result result = { NULL, }; - return result; + const struct dirent *src = GL_READDIR (pglob, stream); + if (src == NULL) + return (struct readdir_result) { NULL, }; + return (struct readdir_result) READDIR_RESULT_INITIALIZER (src); } - struct readdir_result result = READDIR_RESULT_INITIALIZER (source); - return result; -} +#ifdef COMPILE_GLOB64 + const struct dirent *source = __readdir (stream); +#else + const struct dirent64 *source = __readdir64 (stream); #endif + if (source == NULL) + return (struct readdir_result) { NULL, }; + return (struct readdir_result) READDIR_RESULT_INITIALIZER (source); +} + +static void +glob_closedir (glob_t *pglob, int flags, void *stream) +{ + if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)) + (*pglob->gl_closedir) (stream); + else + closedir (stream); +} #ifndef _LIBC /* The results of opendir() in this file are not used with dirfd and fchdir, @@ -271,6 +293,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), size_t oldcount; int meta; int dirname_modified; + /* Indicate if the directory should be prepended on return values. */ + bool dirname_prefix = true; int malloc_dirname = 0; glob_t dirs; int retval = 0; @@ -494,6 +518,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirname = (char *) "/"; dirlen = 1; ++filename; + /* prefix_array adds a separator for each result and DIRNAME is + already '/'. So we indicate later that we should not prepend + anything for this specific case. */ + dirname_prefix = false; } else { @@ -1085,7 +1113,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (dirlen > 0) { /* Stick the directory on the front of each name. */ - if (prefix_array (dirname, + if (prefix_array (dirname_prefix ? dirname : "", &pglob->gl_pathv[old_pathc + pglob->gl_offs], pglob->gl_pathc - old_pathc)) { @@ -1166,11 +1194,6 @@ prefix_array (const char *dirname, char **array, size_t n) size_t dirlen = strlen (dirname); char dirsep_char = '/'; - if (dirlen == 1 && dirname[0] == '/') - /* DIRNAME is just "/", so normal prepending would get us "//foo". - We want "/foo" instead, so don't prepend any chars from DIRNAME. */ - dirlen = 0; - #if defined __MSDOS__ || defined WINDOWS32 if (dirlen > 1) { @@ -1250,11 +1273,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, } else if (meta == GLOBPAT_NONE) { - union - { - struct stat st; - struct_stat64 st64; - } ust; + union glob_stat ust; size_t patlen = strlen (pattern); size_t fullsize; bool alloca_fullname @@ -1273,10 +1292,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), "/", 1), pattern, patlen + 1); - if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? (*pglob->gl_lstat) (fullname, &ust.st) - : __lstat64 (fullname, &ust.st64)) - == 0) + if (glob_lstat (pglob, flags, fullname, &ust) == 0 || errno == EOVERFLOW) /* We found this file to be existing. Now tell the rest of the function to copy this name into the result. */ @@ -1287,9 +1303,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, } else { - stream = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? (*pglob->gl_opendir) (directory) - : opendir (directory)); + stream = glob_opendir (pglob, flags, directory); if (stream == NULL) { if (errno != ENOTDIR @@ -1305,19 +1319,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, while (1) { - struct readdir_result d; - { - if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) - d = convert_dirent (GL_READDIR (pglob, stream)); - else - { -#ifdef COMPILE_GLOB64 - d = convert_dirent (__readdir (stream)); -#else - d = convert_dirent64 (__readdir64 (stream)); -#endif - } - } + struct readdir_result d = glob_readdir (pglob, flags, stream); if (d.name == NULL) break; @@ -1332,6 +1334,17 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (fnmatch (pattern, d.name, fnm_flags) == 0) { + /* We need to certify that the link is a directory. */ + if (flags & GLOB_ONLYDIR && readdir_result_type (d) == DT_LNK) + { + void *ss = glob_opendir (pglob, flags, d.name); + if (ss == NULL) + { + glob_closedir (pglob, flags, ss); + continue; + } + } + if (cur == names->count) { struct globnames *newnames; @@ -1452,10 +1465,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (stream != NULL) { save = errno; - if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)) - (*pglob->gl_closedir) (stream); - else - closedir (stream); + glob_closedir (pglob, flags, stream); __set_errno (save); } diff --git a/posix/globtest.sh b/posix/globtest.sh index 73f7ae3..92a8e37 100755 --- a/posix/globtest.sh +++ b/posix/globtest.sh @@ -242,6 +242,43 @@ if test $failed -ne 0; then result=1 fi +# Test NOCHECK for specific cases where the pattern used starts +# with '/' (BZ#10246). +failed=0 +${test_program_prefix} \ +${common_objpfx}posix/globtest -c "$testdir" "/%" | +sort > $testout +cat <<"EOF" | $CMP - $testout >> $logfile || failed=1 +`/%' +EOF +if test $failed -ne 0; then + echo "No check test failed" >> $logfile + result=1 +fi + +${test_program_prefix} \ +${common_objpfx}posix/globtest -c "$testdir" "//%" | +sort > $testout +cat <<"EOF" | $CMP - $testout >> $logfile || failed=1 +`//%' +EOF +if test $failed -ne 0; then + echo "No check test failed" >> $logfile + result=1 +fi + +${test_program_prefix} \ +${common_objpfx}posix/globtest -c "$testdir" "///%" | +sort > $testout +cat <<"EOF" | $CMP - $testout >> $logfile || failed=1 +`///%' +EOF +if test $failed -ne 0; then + echo "No check test failed" >> $logfile + result=1 +fi + + # Test NOMAGIC without magic characters failed=0 ${test_program_prefix} \ diff --git a/posix/tst-glob_symlinks.c b/posix/tst-glob_symlinks.c index 5c4b4ec..c953204 100644 --- a/posix/tst-glob_symlinks.c +++ b/posix/tst-glob_symlinks.c @@ -131,5 +131,11 @@ do_test (void) TEST_VERIFY_EXIT (strcmp (gl.gl_pathv[0], dangling_link) == 0); globfree (&gl); + /* glob should not try to match link the dangling directories. */ + snprintf (buf, sizeof buf, "/tmp/dangling-symlink-dir-tst-glob*/"); + fprintf (stderr, "%s\n", buf); + TEST_VERIFY_EXIT (glob (buf, 0, NULL, &gl) == GLOB_NOMATCH); + globfree (&gl); + return 0; }