mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 20:40:05 +00:00
CVE-2016-1234: glob: Do not copy d_name field of struct dirent [BZ #19779]
Instead, we store the data we need from the return value of readdir in an object of the new type struct readdir_result. This type is independent of the layout of struct dirent.
This commit is contained in:
parent
2faba597ec
commit
5171f3079f
21
ChangeLog
21
ChangeLog
@ -1,3 +1,24 @@
|
|||||||
|
2016-05-04 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
|
[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.
|
||||||
|
|
||||||
2016-05-03 Joseph Myers <joseph@codesourcery.com>
|
2016-05-03 Joseph Myers <joseph@codesourcery.com>
|
||||||
|
|
||||||
[BZ #20041]
|
[BZ #20041]
|
||||||
|
4
NEWS
4
NEWS
@ -44,6 +44,10 @@ Security related changes:
|
|||||||
resulting in a stack overflow. getaddrinfo now uses a heap allocation
|
resulting in a stack overflow. getaddrinfo now uses a heap allocation
|
||||||
instead. Reported by Michael Petlan. (CVE-2016-3706)
|
instead. Reported by Michael Petlan. (CVE-2016-3706)
|
||||||
|
|
||||||
|
* The glob function suffered from a stack-based buffer overflow when it was
|
||||||
|
called with the GLOB_ALTDIRFUNC flag and encountered a long file name.
|
||||||
|
Reported by Alexander Cherepanov. (CVE-2016-1234)
|
||||||
|
|
||||||
The following bugs are resolved with this release:
|
The following bugs are resolved with this release:
|
||||||
|
|
||||||
[The release manager will add the list generated by
|
[The release manager will add the list generated by
|
||||||
|
@ -40,6 +40,17 @@
|
|||||||
# define PRINTF(fmt, args...)
|
# define PRINTF(fmt, args...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define LONG_NAME \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
|
||||||
|
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
static struct
|
static struct
|
||||||
{
|
{
|
||||||
@ -58,6 +69,7 @@ static struct
|
|||||||
{ ".", 3, DT_DIR, 0755 },
|
{ ".", 3, DT_DIR, 0755 },
|
||||||
{ "..", 3, DT_DIR, 0755 },
|
{ "..", 3, DT_DIR, 0755 },
|
||||||
{ "a", 3, DT_REG, 0644 },
|
{ "a", 3, DT_REG, 0644 },
|
||||||
|
{ LONG_NAME, 3, DT_REG, 0644 },
|
||||||
{ "unreadable", 2, DT_DIR, 0111 },
|
{ "unreadable", 2, DT_DIR, 0111 },
|
||||||
{ ".", 3, DT_DIR, 0111 },
|
{ ".", 3, DT_DIR, 0111 },
|
||||||
{ "..", 3, DT_DIR, 0755 },
|
{ "..", 3, DT_DIR, 0755 },
|
||||||
@ -75,7 +87,7 @@ typedef struct
|
|||||||
int level;
|
int level;
|
||||||
int idx;
|
int idx;
|
||||||
struct dirent d;
|
struct dirent d;
|
||||||
char room_for_dirent[NAME_MAX];
|
char room_for_dirent[sizeof (LONG_NAME)];
|
||||||
} my_DIR;
|
} my_DIR;
|
||||||
|
|
||||||
|
|
||||||
|
223
posix/glob.c
223
posix/glob.c
@ -24,7 +24,9 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* Outcomment the following line for production quality code. */
|
/* Outcomment the following line for production quality code. */
|
||||||
/* #define NDEBUG 1 */
|
/* #define NDEBUG 1 */
|
||||||
@ -73,69 +75,8 @@
|
|||||||
# endif /* HAVE_VMSDIR_H */
|
# endif /* HAVE_VMSDIR_H */
|
||||||
#endif
|
#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 <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* NAME_MAX is usually defined in <dirent.h> or <limits.h>. */
|
|
||||||
#include <limits.h>
|
|
||||||
#ifndef NAME_MAX
|
|
||||||
# define NAME_MAX (sizeof (((struct dirent *) 0)->d_name))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
|
|
||||||
#ifdef _LIBC
|
#ifdef _LIBC
|
||||||
@ -180,8 +121,111 @@
|
|||||||
|
|
||||||
static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
|
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
|
||||||
|
uint8_t 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 */
|
#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. Keep in sync with
|
||||||
|
convert_dirent64 below. */
|
||||||
|
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. Keep in
|
||||||
|
sync with convert_dirent above. */
|
||||||
|
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
|
#ifndef attribute_hidden
|
||||||
# define attribute_hidden
|
# define attribute_hidden
|
||||||
#endif
|
#endif
|
||||||
@ -1538,55 +1582,36 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
|
|||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
const char *name;
|
struct readdir_result d;
|
||||||
#if defined _LIBC && !defined COMPILE_GLOB64
|
{
|
||||||
struct dirent64 *d;
|
if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
|
||||||
union
|
d = convert_dirent (GL_READDIR (pglob, stream));
|
||||||
{
|
else
|
||||||
struct dirent64 d64;
|
{
|
||||||
char room [offsetof (struct dirent64, d_name[0])
|
#ifdef COMPILE_GLOB64
|
||||||
+ NAME_MAX + 1];
|
d = convert_dirent (__readdir (stream));
|
||||||
}
|
|
||||||
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);
|
|
||||||
#else
|
#else
|
||||||
struct dirent *d = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
|
d = convert_dirent64 (__readdir64 (stream));
|
||||||
? ((struct dirent *)
|
|
||||||
(*pglob->gl_readdir) (stream))
|
|
||||||
: __readdir (stream));
|
|
||||||
#endif
|
#endif
|
||||||
if (d == NULL)
|
}
|
||||||
|
}
|
||||||
|
if (d.name == NULL)
|
||||||
break;
|
break;
|
||||||
if (! REAL_DIR_ENTRY (d))
|
if (d.skip_entry)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* If we shall match only directories use the information
|
/* If we shall match only directories use the information
|
||||||
provided by the dirent call if possible. */
|
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;
|
continue;
|
||||||
|
|
||||||
name = d->d_name;
|
if (fnmatch (pattern, d.name, fnm_flags) == 0)
|
||||||
|
|
||||||
if (fnmatch (pattern, name, fnm_flags) == 0)
|
|
||||||
{
|
{
|
||||||
/* If the file we found is a symlink we have to
|
/* If the file we found is a symlink we have to
|
||||||
make sure the target file exists. */
|
make sure the target file exists. */
|
||||||
if (!DIRENT_MIGHT_BE_SYMLINK (d)
|
if (!readdir_result_might_be_symlink (d)
|
||||||
|| link_exists_p (dfd, directory, dirlen, name, pglob,
|
|| link_exists_p (dfd, directory, dirlen, d.name,
|
||||||
flags))
|
pglob, flags))
|
||||||
{
|
{
|
||||||
if (cur == names->count)
|
if (cur == names->count)
|
||||||
{
|
{
|
||||||
@ -1606,7 +1631,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
|
|||||||
names = newnames;
|
names = newnames;
|
||||||
cur = 0;
|
cur = 0;
|
||||||
}
|
}
|
||||||
names->name[cur] = strdup (d->d_name);
|
names->name[cur] = strdup (d.name);
|
||||||
if (names->name[cur] == NULL)
|
if (names->name[cur] == NULL)
|
||||||
goto memory_error;
|
goto memory_error;
|
||||||
++cur;
|
++cur;
|
||||||
|
@ -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
|
||||||
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -38,11 +56,15 @@ int __old_glob64 (const char *__pattern, int __flags,
|
|||||||
|
|
||||||
#undef dirent
|
#undef dirent
|
||||||
#define dirent __old_dirent64
|
#define dirent __old_dirent64
|
||||||
|
#undef GL_READDIR
|
||||||
|
# define GL_READDIR(pglob, stream) \
|
||||||
|
((struct __old_dirent64 *) (pglob)->gl_readdir (stream))
|
||||||
#undef __readdir
|
#undef __readdir
|
||||||
#define __readdir(dirp) __old_readdir64 (dirp)
|
#define __readdir(dirp) __old_readdir64 (dirp)
|
||||||
#undef glob
|
#undef glob
|
||||||
#define glob(pattern, flags, errfunc, pglob) \
|
#define glob(pattern, flags, errfunc, pglob) \
|
||||||
__old_glob64 (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_in_dir __old_glob_in_dir
|
||||||
#define GLOB_ATTRIBUTE attribute_compat_text_section
|
#define GLOB_ATTRIBUTE attribute_compat_text_section
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user