From patchwork Fri Apr 29 10:08:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 11951 Received: (qmail 88307 invoked by alias); 29 Apr 2016 10:08:33 -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 88295 invoked by uid 89); 29 Apr 2016 10:08:32 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=2.3 required=5.0 tests=BAYES_50, KAM_LAZY_DOMAIN_SECURITY, KAM_STOCKGEN, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=no version=3.3.2 spammy=0755, 2016-04-01, 20160401, 0111 X-HELO: mx1.redhat.com Subject: Re: [PATCH] glob: Avoid copying the d_name field of struct dirent [BZ #19779] To: Roland McGrath References: <56E339A7.7060704@redhat.com> <20160311222757.DB90C2C3C24@topped-with-meat.com> <56FBBA94.1040605@redhat.com> <20160330232737.2A3F32C3C35@topped-with-meat.com> <56FD5B69.1010002@redhat.com> <20160401215549.E32FC2C3BCD@topped-with-meat.com> Cc: GNU C Library From: Florian Weimer Message-ID: <57233290.8090900@redhat.com> Date: Fri, 29 Apr 2016 12:08:16 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.7.1 MIME-Version: 1.0 In-Reply-To: <20160401215549.E32FC2C3BCD@topped-with-meat.com> On 04/01/2016 11:55 PM, Roland McGrath wrote: >> I can remove the use of d_namlen as a separate patch, before the copy >> avoidance. I did not realize it has implications on application >> behavior at the time, for which I apologize. But now, I still think >> it's a desirable improvement. > > That sounds great. Now that the d_namlen change is in, here is a rebased version of the CVE-2016-1234 fix. This version keeps the d_ino-guided skipping. Tested on x86_64 and i386. Thanks, Florian 2016-04-01 Florian Weimer [BZ #19779] CVE-2016-1234 Avoid copying names of directory entries. * posix/glob.c (DIRENT_MUST_BE, DIRENT_MIGHT_BE_SYMLINK) (DIRENT_MIGHT_BE_DIR, CONVERT_D_INO, CONVERT_D_TYPE) (CONVERT_DIRENT_DIRENT64, REAL_DIR_ENTRY): Remove macros. (struct readdir_result): New type. (D_TYPE_TO_RESULT, D_INO_TO_RESULT, READDIR_RESULT_INITIALIZER) (GL_READDIR): New macros. (readdir_result_might_be_symlink, readdir_result_might_be_dir) (convert_dirent, convert_dirent64): New functions. (glob_in_dir): Use struct readdir_result. Call convert_dirent or convert_dirent64. Adjust references to the readdir result. * sysdeps/unix/sysv/linux/i386/glob64.c: (convert_dirent, GL_READDIR): Redefine for second file inclusion. * posix/bug-glob2.c (LONG_NAME): Define. (filesystem): Add LONG_NAME. (my_DIR): Increase the size of room_for_dirent. diff --git a/posix/bug-glob2.c b/posix/bug-glob2.c index 0fdc5d0..5873e08 100644 --- a/posix/bug-glob2.c +++ b/posix/bug-glob2.c @@ -40,6 +40,17 @@ # define PRINTF(fmt, args...) #endif +#define LONG_NAME \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" static struct { @@ -58,6 +69,7 @@ static struct { ".", 3, DT_DIR, 0755 }, { "..", 3, DT_DIR, 0755 }, { "a", 3, DT_REG, 0644 }, + { LONG_NAME, 3, DT_REG, 0644 }, { "unreadable", 2, DT_DIR, 0111 }, { ".", 3, DT_DIR, 0111 }, { "..", 3, DT_DIR, 0755 }, @@ -75,7 +87,7 @@ typedef struct int level; int idx; struct dirent d; - char room_for_dirent[NAME_MAX]; + char room_for_dirent[sizeof (LONG_NAME)]; } my_DIR; diff --git a/posix/glob.c b/posix/glob.c index 9ae76ac..f687023 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* Outcomment the following line for production quality code. */ @@ -73,69 +74,8 @@ # endif /* HAVE_VMSDIR_H */ #endif - -/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available - if the `d_type' member for `struct dirent' is available. - HAVE_STRUCT_DIRENT_D_TYPE plays the same role in GNULIB. */ -#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE -/* True if the directory entry D must be of type T. */ -# define DIRENT_MUST_BE(d, t) ((d)->d_type == (t)) - -/* True if the directory entry D might be a symbolic link. */ -# define DIRENT_MIGHT_BE_SYMLINK(d) \ - ((d)->d_type == DT_UNKNOWN || (d)->d_type == DT_LNK) - -/* True if the directory entry D might be a directory. */ -# define DIRENT_MIGHT_BE_DIR(d) \ - ((d)->d_type == DT_DIR || DIRENT_MIGHT_BE_SYMLINK (d)) - -#else /* !HAVE_D_TYPE */ -# define DIRENT_MUST_BE(d, t) false -# define DIRENT_MIGHT_BE_SYMLINK(d) true -# define DIRENT_MIGHT_BE_DIR(d) true -#endif /* HAVE_D_TYPE */ - -/* If the system has the `struct dirent64' type we use it internally. */ -#if defined _LIBC && !defined COMPILE_GLOB64 - -# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ -# define CONVERT_D_INO(d64, d32) -# else -# define CONVERT_D_INO(d64, d32) \ - (d64)->d_ino = (d32)->d_ino; -# endif - -# ifdef _DIRENT_HAVE_D_TYPE -# define CONVERT_D_TYPE(d64, d32) \ - (d64)->d_type = (d32)->d_type; -# else -# define CONVERT_D_TYPE(d64, d32) -# endif - -# define CONVERT_DIRENT_DIRENT64(d64, d32) \ - strcpy ((d64)->d_name, (d32)->d_name); \ - CONVERT_D_INO (d64, d32) \ - CONVERT_D_TYPE (d64, d32) -#endif - - -#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ -/* Posix does not require that the d_ino field be present, and some - systems do not provide it. */ -# define REAL_DIR_ENTRY(dp) 1 -#else -# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) -#endif /* POSIX */ - #include #include - -/* NAME_MAX is usually defined in or . */ -#include -#ifndef NAME_MAX -# define NAME_MAX (sizeof (((struct dirent *) 0)->d_name)) -#endif - #include #ifdef _LIBC @@ -180,8 +120,109 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; +/* A representation of a directory entry which does not depend on the + layout of struct dirent, or the size of ino_t. */ +struct readdir_result +{ + const char *name; +# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE + unsigned char type; +# endif + bool skip_entry; +}; + +# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE +/* Initializer based on the d_type member of struct dirent. */ +# define D_TYPE_TO_RESULT(source) (source)->d_type, + +/* True if the directory entry D might be a symbolic link. */ +static bool +readdir_result_might_be_symlink (struct readdir_result d) +{ + return d.type == DT_UNKNOWN || d.type == DT_LNK; +} + +/* True if the directory entry D might be a directory. */ +static bool +readdir_result_might_be_dir (struct readdir_result d) +{ + return d.type == DT_DIR || readdir_result_might_be_symlink (d); +} +# else /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ +# define D_TYPE_TO_RESULT(source) + +/* If we do not have type information, symbolic links and directories + are always a possibility. */ + +static bool +readdir_result_might_be_symlink (struct readdir_result d) +{ + return true; +} + +static bool +readdir_result_might_be_dir (struct readdir_result d) +{ + return true; +} + +# endif /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ + +# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ +/* Initializer for skip_entry. POSIX does not require that the d_ino + field be present, and some systems do not provide it. */ +# define D_INO_TO_RESULT(source) false, +# else +# define D_INO_TO_RESULT(source) (source)->d_ino == 0, +# endif + +/* Construct an initializer for a struct readdir_result object from a + struct dirent *. No copy of the name is made. */ +#define READDIR_RESULT_INITIALIZER(source) \ + { \ + source->d_name, \ + D_TYPE_TO_RESULT (source) \ + D_INO_TO_RESULT (source) \ + } + #endif /* !defined _LIBC || !defined GLOB_ONLY_P */ +/* Call gl_readdir on STREAM. This macro can be overridden to reduce + type safety if an old interface version needs to be supported. */ +#ifndef GL_READDIR +# 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. */ +static struct readdir_result +convert_dirent (const struct dirent *source) +{ + if (source == NULL) + { + struct readdir_result result = {NULL}; + return result; + } + struct readdir_result result = READDIR_RESULT_INITIALIZER (source); + return result; +} + +#ifndef COMPILE_GLOB64 +/* Like convert_dirent, but works on struct dirent64 instead. */ +static struct readdir_result +convert_dirent64 (const struct dirent64 *source) +{ + if (source == NULL) + { + struct readdir_result result = {NULL}; + return result; + } + struct readdir_result result = READDIR_RESULT_INITIALIZER (source); + return result; +} +#endif + + #ifndef attribute_hidden # define attribute_hidden #endif @@ -1538,55 +1579,36 @@ glob_in_dir (const char *pattern, const char *directory, int flags, while (1) { - const char *name; -#if defined _LIBC && !defined COMPILE_GLOB64 - struct dirent64 *d; - union - { - struct dirent64 d64; - char room [offsetof (struct dirent64, d_name[0]) - + NAME_MAX + 1]; - } - d64buf; - - if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)) - { - struct dirent *d32 = (*pglob->gl_readdir) (stream); - if (d32 != NULL) - { - CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); - d = &d64buf.d64; - } - else - d = NULL; - } - else - d = __readdir64 (stream); + 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 - struct dirent *d = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? ((struct dirent *) - (*pglob->gl_readdir) (stream)) - : __readdir (stream)); + d = convert_dirent64 (__readdir64 (stream)); #endif - if (d == NULL) + } + } + if (d.name == NULL) break; - if (! REAL_DIR_ENTRY (d)) + if (d.skip_entry) continue; /* If we shall match only directories use the information provided by the dirent call if possible. */ - if ((flags & GLOB_ONLYDIR) && !DIRENT_MIGHT_BE_DIR (d)) + if ((flags & GLOB_ONLYDIR) && !readdir_result_might_be_dir (d)) continue; - name = d->d_name; - - if (fnmatch (pattern, name, fnm_flags) == 0) + if (fnmatch (pattern, d.name, fnm_flags) == 0) { /* If the file we found is a symlink we have to make sure the target file exists. */ - if (!DIRENT_MIGHT_BE_SYMLINK (d) - || link_exists_p (dfd, directory, dirlen, name, pglob, - flags)) + if (!readdir_result_might_be_symlink (d) + || link_exists_p (dfd, directory, dirlen, d.name, + pglob, flags)) { if (cur == names->count) { @@ -1606,7 +1628,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, names = newnames; cur = 0; } - names->name[cur] = strdup (d->d_name); + names->name[cur] = strdup (d.name); if (names->name[cur] == NULL) goto memory_error; ++cur; diff --git a/sysdeps/unix/sysv/linux/i386/glob64.c b/sysdeps/unix/sysv/linux/i386/glob64.c index b4fcd1a..802c957 100644 --- a/sysdeps/unix/sysv/linux/i386/glob64.c +++ b/sysdeps/unix/sysv/linux/i386/glob64.c @@ -1,3 +1,21 @@ +/* Two glob variants with 64-bit support, for dirent64 and __olddirent64. + Copyright (C) 1998-2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + #include #include #include @@ -38,11 +56,15 @@ int __old_glob64 (const char *__pattern, int __flags, #undef dirent #define dirent __old_dirent64 +#undef GL_READDIR +# define GL_READDIR(pglob, stream) \ + ((struct __old_dirent64 *) (pglob)->gl_readdir (stream)) #undef __readdir #define __readdir(dirp) __old_readdir64 (dirp) #undef glob #define glob(pattern, flags, errfunc, pglob) \ __old_glob64 (pattern, flags, errfunc, pglob) +#define convert_dirent __old_convert_dirent #define glob_in_dir __old_glob_in_dir #define GLOB_ATTRIBUTE attribute_compat_text_section