From owner-svn-src-head@freebsd.org Wed Jul 20 07:30:45 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 736AFB9C7CB; Wed, 20 Jul 2016 07:30:45 +0000 (UTC) (envelope-from ache@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 483DE147B; Wed, 20 Jul 2016 07:30:45 +0000 (UTC) (envelope-from ache@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u6K7UiPc096328; Wed, 20 Jul 2016 07:30:44 GMT (envelope-from ache@FreeBSD.org) Received: (from ache@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u6K7Ui3s096327; Wed, 20 Jul 2016 07:30:44 GMT (envelope-from ache@FreeBSD.org) Message-Id: <201607200730.u6K7Ui3s096327@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ache set sender to ache@FreeBSD.org using -f From: "Andrey A. Chernov" Date: Wed, 20 Jul 2016 07:30:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r303074 - head/lib/libc/gen X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 20 Jul 2016 07:30:45 -0000 Author: ache Date: Wed Jul 20 07:30:44 2016 New Revision: 303074 URL: https://svnweb.freebsd.org/changeset/base/303074 Log: 1) Per POSIX (and glibc) GLOB_NOCHECK should return original pattern, unmodified, if no matches found. But our original code strips all '\' returning it. Rewrite the code to allow to reconstruct exact the original pattern with backslashes for this case. 2) Prevent to use truncated pattern if MAXPATHLEN exceeded, return GLOB_NOMATCH instead. 3) Fix few end loop conditions filling Char arrays with mbrtowc(), MB_CUR_MAX is unneeded in two places and condition is less by one in other place. 4) Prevent to use truncated filenames match if MAXPATHLEN exceeded, skip such directory entries. 5) Don't end *pathend with L'/' in glob3() if limit is reached, this change will be not visible since error is returned. 6) If error happens in (*readdirfunc)(), do the same GLOB_ABORTED processing as for g_opendir() as POSIX requires. Modified: head/lib/libc/gen/glob.c Modified: head/lib/libc/gen/glob.c ============================================================================== --- head/lib/libc/gen/glob.c Wed Jul 20 06:29:26 2016 (r303073) +++ head/lib/libc/gen/glob.c Wed Jul 20 07:30:44 2016 (r303074) @@ -152,6 +152,7 @@ typedef char Char; #define CHAR(c) ((Char)((c)&M_CHAR)) #define META(c) ((Char)((c)|M_QUOTE)) +#define UNPROT(c) ((c) & ~M_PROTECT) #define M_ALL META(L'*') #define M_END META(L']') #define M_NOT META(L'!') @@ -159,10 +160,11 @@ typedef char Char; #define M_RNG META(L'-') #define M_SET META(L'[') #define ismeta(c) (((c)&M_QUOTE) != 0) +#define isprot(c) (((c)&M_PROTECT) != 0) static int compare(const void *, const void *); -static int g_Ctoc(const Char *, char *, size_t); +static int g_Ctoc(const Char *, char *, size_t, int); static int g_lstat(Char *, struct stat *, glob_t *); static DIR *g_opendir(Char *, glob_t *); static const Char *g_strchr(const Char *, wchar_t); @@ -176,7 +178,7 @@ static int glob2(Char *, Char *, Char * struct glob_limit *); static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, struct glob_limit *); -static int globextend(const Char *, glob_t *, struct glob_limit *); +static int globextend(const Char *, glob_t *, struct glob_limit *, int); static const Char * globtilde(const Char *, Char *, size_t, glob_t *); static int globexp1(const Char *, glob_t *, struct glob_limit *); @@ -197,6 +199,7 @@ glob(const char * __restrict pattern, in mbstate_t mbs; wchar_t wc; size_t clen; + int too_long; patnext = pattern; if (!(flags & GLOB_APPEND)) { @@ -216,24 +219,27 @@ glob(const char * __restrict pattern, in bufnext = patbuf; bufend = bufnext + MAXPATHLEN - 1; + too_long = 1; if (flags & GLOB_NOESCAPE) { memset(&mbs, 0, sizeof(mbs)); - while (bufend - bufnext >= MB_CUR_MAX) { + while (bufnext <= bufend) { clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (GLOB_NOMATCH); - else if (clen == 0) + else if (clen == 0) { + too_long = 0; break; + } *bufnext++ = wc; patnext += clen; } } else { /* Protect the quoted characters. */ memset(&mbs, 0, sizeof(mbs)); - while (bufend - bufnext >= MB_CUR_MAX) { + while (bufnext <= bufend) { if (*patnext == '\\') { if (*++patnext == '\0') { - *bufnext++ = QUOTE | M_PROTECT; + *bufnext++ = QUOTE; continue; } prot = M_PROTECT; @@ -242,13 +248,16 @@ glob(const char * __restrict pattern, in clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (GLOB_NOMATCH); - else if (clen == 0) + else if (clen == 0) { + too_long = 0; break; - *bufnext++ = wc | ((wc != DOT && wc != SEP) ? - prot : 0); + } + *bufnext++ = wc | prot; patnext += clen; } } + if (too_long) + return (GLOB_NOMATCH); *bufnext = EOS; if (flags & GLOB_BRACE) @@ -447,7 +456,7 @@ globtilde(const Char *pattern, Char *pat /* * Expand a ~user */ - if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf))) + if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf), 0)) return (NULL); if ((pwd = getpwnam((char *)wbuf)) == NULL) return (pattern); @@ -479,8 +488,8 @@ globtilde(const Char *pattern, Char *pat return (NULL); dc = wbuf; - for (b = patbuf; b < eb && *dc != EOS; b++, dc++) - *b = *dc | ((*dc != DOT && *dc != SEP) ? M_PROTECT : 0); + for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT) + continue; if (*dc != EOS) return (NULL); @@ -589,7 +598,7 @@ glob0(const Char *pattern, glob_t *pglob if (((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR)))) - return (globextend(pattern, pglob, limit)); + return (globextend(pattern, pglob, limit, 1)); else return (GLOB_NOMATCH); } @@ -645,10 +654,11 @@ glob2(Char *pathbuf, Char *pathend, Char errno = 0; return (GLOB_NOSPACE); } - if (((pglob->gl_flags & GLOB_MARK) && - pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) - || (S_ISLNK(sb.st_mode) && - (g_stat(pathbuf, &sb, pglob) == 0) && + if ((pglob->gl_flags & GLOB_MARK) && + UNPROT(pathend[-1]) != SEP && + (S_ISDIR(sb.st_mode) || + (S_ISLNK(sb.st_mode) && + g_stat(pathbuf, &sb, pglob) == 0 && S_ISDIR(sb.st_mode)))) { if (pathend + 1 > pathend_last) { errno = 0; @@ -658,13 +668,13 @@ glob2(Char *pathbuf, Char *pathend, Char *pathend = EOS; } ++pglob->gl_matchc; - return (globextend(pathbuf, pglob, limit)); + return (globextend(pathbuf, pglob, limit, 0)); } /* Find end of next segment, copy tentatively to pathend. */ q = pathend; p = pattern; - while (*p != EOS && *p != SEP) { + while (*p != EOS && UNPROT(*p) != SEP) { if (ismeta(*p)) anymeta = 1; if (q + 1 > pathend_last) { @@ -677,7 +687,7 @@ glob2(Char *pathbuf, Char *pathend, Char if (!anymeta) { /* No expansion, do next segment. */ pathend = q; pattern = p; - while (*pattern == SEP) { + while (UNPROT(*pattern) == SEP) { if (pathend + 1 > pathend_last) { errno = 0; return (GLOB_NOSPACE); @@ -698,28 +708,29 @@ glob3(Char *pathbuf, Char *pathend, Char { struct dirent *dp; DIR *dirp; - int err; + int err, too_long, saverrno; char buf[MAXPATHLEN + MB_LEN_MAX - 1]; struct dirent *(*readdirfunc)(DIR *); - errno = 0; - if (pathend > pathend_last) + if (pathend > pathend_last) { + errno = 0; return (GLOB_NOSPACE); + } *pathend = EOS; + if (pglob->gl_errfunc != NULL && + g_Ctoc(pathbuf, buf, sizeof(buf), 0)) { + errno = 0; + return (GLOB_NOSPACE); + } + errno = 0; if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { if (errno == ENOENT || errno == ENOTDIR) return (0); - if (pglob->gl_errfunc != NULL) { - if (g_Ctoc(pathbuf, buf, sizeof(buf))) { - errno = 0; - return (GLOB_NOSPACE); - } - if (pglob->gl_errfunc(buf, errno)) - return (GLOB_ABORTED); - } - if (pglob->gl_flags & GLOB_ERR) + if ((pglob->gl_errfunc != NULL && + pglob->gl_errfunc(buf, errno)) || + (pglob->gl_flags & GLOB_ERR)) return (GLOB_ABORTED); return (0); } @@ -732,6 +743,7 @@ glob3(Char *pathbuf, Char *pathend, Char else readdirfunc = readdir; + errno = 0; /* Search directory for matching names. */ while ((dp = (*readdirfunc)(dirp)) != NULL) { char *sc; @@ -743,23 +755,18 @@ glob3(Char *pathbuf, Char *pathend, Char if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { errno = 0; - if (pathend + 1 > pathend_last) - err = GLOB_NOSPACE; - else { - *pathend++ = SEP; - *pathend = EOS; - err = GLOB_NOSPACE; - } + err = GLOB_NOSPACE; break; } /* Initial DOT must be matched literally. */ - if (dp->d_name[0] == '.' && *pattern != DOT) + if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT) continue; memset(&mbs, 0, sizeof(mbs)); dc = pathend; sc = dp->d_name; - while (dc < pathend_last) { + too_long = 1; + while (dc <= pathend_last) { clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) { /* XXX See initial comment #2. */ @@ -767,11 +774,13 @@ glob3(Char *pathbuf, Char *pathend, Char clen = 1; memset(&mbs, 0, sizeof(mbs)); } - if ((*dc++ = wc) == EOS) + if ((*dc++ = wc) == EOS) { + too_long = 0; break; + } sc += clen; } - if (!match(pathend, pattern, restpattern)) { + if (too_long || !match(pathend, pattern, restpattern)) { *pathend = EOS; continue; } @@ -779,13 +788,24 @@ glob3(Char *pathbuf, Char *pathend, Char pglob, limit); if (err) break; + errno = 0; } + saverrno = errno; if (pglob->gl_flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir)(dirp); else closedir(dirp); - return (err); + errno = saverrno; + + if (err) + return (err); + + if (dp == NULL && errno != 0 && ((pglob->gl_errfunc != NULL && + pglob->gl_errfunc(buf, errno)) || (pglob->gl_flags & GLOB_ERR))) + return (GLOB_ABORTED); + + return (0); } @@ -804,7 +824,8 @@ glob3(Char *pathbuf, Char *pathend, Char * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(const Char *path, glob_t *pglob, struct glob_limit *limit) +globextend(const Char *path, glob_t *pglob, struct glob_limit *limit, + int prot) { char **pathv; size_t i, newsize, len; @@ -831,9 +852,11 @@ globextend(const Char *path, glob_t *pgl } pglob->gl_pathv = pathv; - for (p = path; *p++;) + for (p = path; *p++ != EOS;) continue; len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ + if (prot) + len += (size_t)(p - path) - 1; limit->l_string_cnt += len; if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_string_cnt >= GLOB_LIMIT_STRING) { @@ -841,7 +864,7 @@ globextend(const Char *path, glob_t *pgl return (GLOB_NOSPACE); } if ((copy = malloc(len)) != NULL) { - if (g_Ctoc(path, copy, len)) { + if (g_Ctoc(path, copy, len, prot)) { free(copy); errno = 0; return (GLOB_NOSPACE); @@ -935,7 +958,7 @@ g_opendir(Char *str, glob_t *pglob) if (*str == EOS) strcpy(buf, "."); else { - if (g_Ctoc(str, buf, sizeof(buf))) { + if (g_Ctoc(str, buf, sizeof(buf), 0)) { errno = ENAMETOOLONG; return (NULL); } @@ -952,7 +975,7 @@ g_lstat(Char *fn, struct stat *sb, glob_ { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; - if (g_Ctoc(fn, buf, sizeof(buf))) { + if (g_Ctoc(fn, buf, sizeof(buf), 0)) { errno = ENAMETOOLONG; return (-1); } @@ -966,7 +989,7 @@ g_stat(Char *fn, struct stat *sb, glob_t { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; - if (g_Ctoc(fn, buf, sizeof(buf))) { + if (g_Ctoc(fn, buf, sizeof(buf), 0)) { errno = ENAMETOOLONG; return (-1); } @@ -987,23 +1010,31 @@ g_strchr(const Char *str, wchar_t ch) } static int -g_Ctoc(const Char *str, char *buf, size_t len) +g_Ctoc(const Char *str, char *buf, size_t len, int prot) { mbstate_t mbs; size_t clen; + Char Ch; + Ch = *str; memset(&mbs, 0, sizeof(mbs)); while (len >= MB_CUR_MAX) { - clen = wcrtomb(buf, CHAR(*str), &mbs); + if (prot && isprot(Ch)) { + Ch = UNPROT(Ch); + *buf++ = '\\'; + len--; + continue; + } + clen = wcrtomb(buf, CHAR(Ch), &mbs); if (clen == (size_t)-1) { /* XXX See initial comment #2. */ - *buf = (char)CHAR(*str); + *buf = (char)CHAR(Ch); clen = 1; memset(&mbs, 0, sizeof(mbs)); } - if (CHAR(*str) == EOS) + if (CHAR(Ch) == EOS) return (0); - str++; + Ch = *++str; buf += clen; len -= clen; }