mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
CVE-2013-4237, BZ #14699: Buffer overflow in readdir_r
* sysdeps/posix/dirstream.h (struct __dirstream): Add errcode member. * sysdeps/posix/opendir.c (__alloc_dir): Initialize errcode member. * sysdeps/posix/rewinddir.c (rewinddir): Reset errcode member. * sysdeps/posix/readdir_r.c (__READDIR_R): Enforce NAME_MAX limit. Return delayed error code. Remove GETDENTS_64BIT_ALIGNED conditional. * sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c: Do not define GETDENTS_64BIT_ALIGNED. * sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise. * manual/filesys.texi (Reading/Closing Directory): Document ENAMETOOLONG return value of readdir_r. Recommend readdir more strongly. * manual/conf.texi (Limits for Files): Add portability note to NAME_MAX, PATH_MAX. (Pathconf): Add portability note for _PC_NAME_MAX, _PC_PATH_MAX.
This commit is contained in:
@ -40,6 +40,7 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
|
||||
DIRENT_TYPE *dp;
|
||||
size_t reclen;
|
||||
const int saved_errno = errno;
|
||||
int ret;
|
||||
|
||||
__libc_lock_lock (dirp->lock);
|
||||
|
||||
@ -70,10 +71,10 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
|
||||
bytes = 0;
|
||||
__set_errno (saved_errno);
|
||||
}
|
||||
if (bytes < 0)
|
||||
dirp->errcode = errno;
|
||||
|
||||
dp = NULL;
|
||||
/* Reclen != 0 signals that an error occurred. */
|
||||
reclen = bytes != 0;
|
||||
break;
|
||||
}
|
||||
dirp->size = (size_t) bytes;
|
||||
@ -106,29 +107,46 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
|
||||
dirp->filepos += reclen;
|
||||
#endif
|
||||
|
||||
/* Skip deleted files. */
|
||||
#ifdef NAME_MAX
|
||||
if (reclen > offsetof (DIRENT_TYPE, d_name) + NAME_MAX + 1)
|
||||
{
|
||||
/* The record is very long. It could still fit into the
|
||||
caller-supplied buffer if we can skip padding at the
|
||||
end. */
|
||||
size_t namelen = _D_EXACT_NAMLEN (dp);
|
||||
if (namelen <= NAME_MAX)
|
||||
reclen = offsetof (DIRENT_TYPE, d_name) + namelen + 1;
|
||||
else
|
||||
{
|
||||
/* The name is too long. Ignore this file. */
|
||||
dirp->errcode = ENAMETOOLONG;
|
||||
dp->d_ino = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Skip deleted and ignored files. */
|
||||
}
|
||||
while (dp->d_ino == 0);
|
||||
|
||||
if (dp != NULL)
|
||||
{
|
||||
#ifdef GETDENTS_64BIT_ALIGNED
|
||||
/* The d_reclen value might include padding which is not part of
|
||||
the DIRENT_TYPE data structure. */
|
||||
reclen = MIN (reclen,
|
||||
offsetof (DIRENT_TYPE, d_name) + sizeof (dp->d_name));
|
||||
#endif
|
||||
*result = memcpy (entry, dp, reclen);
|
||||
#ifdef GETDENTS_64BIT_ALIGNED
|
||||
#ifdef _DIRENT_HAVE_D_RECLEN
|
||||
entry->d_reclen = reclen;
|
||||
#endif
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
*result = NULL;
|
||||
{
|
||||
*result = NULL;
|
||||
ret = dirp->errcode;
|
||||
}
|
||||
|
||||
__libc_lock_unlock (dirp->lock);
|
||||
|
||||
return dp != NULL ? 0 : reclen ? errno : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __READDIR_R_ALIAS
|
||||
|
Reference in New Issue
Block a user