ce426f
commit 91ce40854d0b7f865cf5024ef95a8026b76096f3
ce426f
Author: Florian Weimer <fweimer@redhat.com>
ce426f
Date:   Fri Aug 16 09:38:52 2013 +0200
ce426f
ce426f
    CVE-2013-4237, BZ #14699: Buffer overflow in readdir_r
ce426f
    
ce426f
    	* sysdeps/posix/dirstream.h (struct __dirstream): Add errcode
ce426f
    	member.
ce426f
    	* sysdeps/posix/opendir.c (__alloc_dir): Initialize errcode
ce426f
    	member.
ce426f
    	* sysdeps/posix/rewinddir.c (rewinddir): Reset errcode member.
ce426f
    	* sysdeps/posix/readdir_r.c (__READDIR_R): Enforce NAME_MAX limit.
ce426f
    	Return delayed error code.  Remove GETDENTS_64BIT_ALIGNED
ce426f
    	conditional.
ce426f
    	* sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c: Do not define
ce426f
    	GETDENTS_64BIT_ALIGNED.
ce426f
    	* sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise.
ce426f
    	* manual/filesys.texi (Reading/Closing Directory): Document
ce426f
    	ENAMETOOLONG return value of readdir_r.  Recommend readdir more
ce426f
    	strongly.
ce426f
    	* manual/conf.texi (Limits for Files): Add portability note to
ce426f
    	NAME_MAX, PATH_MAX.
ce426f
    	(Pathconf): Add portability note for _PC_NAME_MAX, _PC_PATH_MAX.
ce426f
ce426f
diff --git glibc-2.17-c758a686/manual/conf.texi glibc-2.17-c758a686/manual/conf.texi
ce426f
index 7eb8b36..c720063 100644
ce426f
--- glibc-2.17-c758a686/manual/conf.texi
ce426f
+++ glibc-2.17-c758a686/manual/conf.texi
ce426f
@@ -1149,6 +1149,9 @@ typed ahead as input.  @xref{I/O Queues}.
ce426f
 @deftypevr Macro int NAME_MAX
ce426f
 The uniform system limit (if any) for the length of a file name component, not
ce426f
 including the terminating null character.
ce426f
+
ce426f
+@strong{Portability Note:} On some systems, @theglibc{} defines
ce426f
+@code{NAME_MAX}, but does not actually enforce this limit.
ce426f
 @end deftypevr
ce426f
 
ce426f
 @comment limits.h
ce426f
@@ -1157,6 +1160,9 @@ including the terminating null character.
ce426f
 The uniform system limit (if any) for the length of an entire file name (that
ce426f
 is, the argument given to system calls such as @code{open}), including the
ce426f
 terminating null character.
ce426f
+
ce426f
+@strong{Portability Note:} @Theglibc{} does not enforce this limit
ce426f
+even if @code{PATH_MAX} is defined.
ce426f
 @end deftypevr
ce426f
 
ce426f
 @cindex limits, pipe buffer size
ce426f
@@ -1476,6 +1482,9 @@ Inquire about the value of @code{POSIX_REC_MIN_XFER_SIZE}.
ce426f
 Inquire about the value of @code{POSIX_REC_XFER_ALIGN}.
ce426f
 @end table
ce426f
 
ce426f
+@strong{Portability Note:} On some systems, @theglibc{} does not
ce426f
+enforce @code{_PC_NAME_MAX} or @code{_PC_PATH_MAX} limits.
ce426f
+
ce426f
 @node Utility Limits
ce426f
 @section Utility Program Capacity Limits
ce426f
 
ce426f
diff --git glibc-2.17-c758a686/manual/filesys.texi glibc-2.17-c758a686/manual/filesys.texi
ce426f
index 1df9cf2..814c210 100644
ce426f
--- glibc-2.17-c758a686/manual/filesys.texi
ce426f
+++ glibc-2.17-c758a686/manual/filesys.texi
ce426f
@@ -444,9 +444,9 @@ symbols are declared in the header file @file{dirent.h}.
ce426f
 @comment POSIX.1
ce426f
 @deftypefun {struct dirent *} readdir (DIR *@var{dirstream})
ce426f
 This function reads the next entry from the directory.  It normally
ce426f
-returns a pointer to a structure containing information about the file.
ce426f
-This structure is statically allocated and can be rewritten by a
ce426f
-subsequent call.
ce426f
+returns a pointer to a structure containing information about the
ce426f
+file.  This structure is associated with the @var{dirstream} handle
ce426f
+and can be rewritten by a subsequent call.
ce426f
 
ce426f
 @strong{Portability Note:} On some systems @code{readdir} may not
ce426f
 return entries for @file{.} and @file{..}, even though these are always
ce426f
@@ -461,19 +461,61 @@ conditions are defined for this function:
ce426f
 The @var{dirstream} argument is not valid.
ce426f
 @end table
ce426f
 
ce426f
-@code{readdir} is not thread safe.  Multiple threads using
ce426f
-@code{readdir} on the same @var{dirstream} may overwrite the return
ce426f
-value.  Use @code{readdir_r} when this is critical.
ce426f
+To distinguish between an end-of-directory condition or an error, you
ce426f
+must set @code{errno} to zero before calling @code{readdir}.  To avoid
ce426f
+entering an infinite loop, you should stop reading from the directory
ce426f
+after the first error.
ce426f
+
ce426f
+In POSIX.1-2008, @code{readdir} is not thread-safe.  In @theglibc{}
ce426f
+implementation, it is safe to call @code{readdir} concurrently on
ce426f
+different @var{dirstream}s, but multiple threads accessing the same
ce426f
+@var{dirstream} result in undefined behavior.  @code{readdir_r} is a
ce426f
+fully thread-safe alternative, but suffers from poor portability (see
ce426f
+below).  It is recommended that you use @code{readdir}, with external
ce426f
+locking if multiple threads access the same @var{dirstream}.
ce426f
 @end deftypefun
ce426f
 
ce426f
 @comment dirent.h
ce426f
 @comment GNU
ce426f
 @deftypefun int readdir_r (DIR *@var{dirstream}, struct dirent *@var{entry}, struct dirent **@var{result})
ce426f
-This function is the reentrant version of @code{readdir}.  Like
ce426f
-@code{readdir} it returns the next entry from the directory.  But to
ce426f
-prevent conflicts between simultaneously running threads the result is
ce426f
-not stored in statically allocated memory.  Instead the argument
ce426f
-@var{entry} points to a place to store the result.
ce426f
+This function is a version of @code{readdir} which performs internal
ce426f
+locking.  Like @code{readdir} it returns the next entry from the
ce426f
+directory.  To prevent conflicts between simultaneously running
ce426f
+threads the result is stored inside the @var{entry} object.
ce426f
+
ce426f
+@strong{Portability Note:} It is recommended to use @code{readdir}
ce426f
+instead of @code{readdir_r} for the following reasons:
ce426f
+
ce426f
+@itemize @bullet
ce426f
+@item
ce426f
+On systems which do not define @code{NAME_MAX}, it may not be possible
ce426f
+to use @code{readdir_r} safely because the caller does not specify the
ce426f
+length of the buffer for the directory entry.
ce426f
+
ce426f
+@item
ce426f
+On some systems, @code{readdir_r} cannot read directory entries with
ce426f
+very long names.  If such a name is encountered, @theglibc{}
ce426f
+implementation of @code{readdir_r} returns with an error code of
ce426f
+@code{ENAMETOOLONG} after the final directory entry has been read.  On
ce426f
+other systems, @code{readdir_r} may return successfully, but the
ce426f
+@code{d_name} member may not be NUL-terminated or may be truncated.
ce426f
+
ce426f
+@item
ce426f
+POSIX-1.2008 does not guarantee that @code{readdir} is thread-safe,
ce426f
+even when access to the same @var{dirstream} is serialized.  But in
ce426f
+current implementations (including @theglibc{}), it is safe to call
ce426f
+@code{readdir} concurrently on different @var{dirstream}s, so there is
ce426f
+no need to use @code{readdir_r} in most multi-threaded programs.  In
ce426f
+the rare case that multiple threads need to read from the same
ce426f
+@var{dirstream}, it is still better to use @code{readdir} and external
ce426f
+synchronization.
ce426f
+
ce426f
+@item
ce426f
+It is expected that future versions of POSIX will obsolete
ce426f
+@code{readdir_r} and mandate the level of thread safety for
ce426f
+@code{readdir} which is provided by @theglibc{} and other
ce426f
+implementations today.
ce426f
+@end itemize
ce426f
 
ce426f
 Normally @code{readdir_r} returns zero and sets @code{*@var{result}}
ce426f
 to @var{entry}.  If there are no more entries in the directory or an
ce426f
@@ -481,15 +523,6 @@ error is detected, @code{readdir_r} sets @code{*@var{result}} to a
ce426f
 null pointer and returns a nonzero error code, also stored in
ce426f
 @code{errno}, as described for @code{readdir}.
ce426f
 
ce426f
-@strong{Portability Note:} On some systems @code{readdir_r} may not
ce426f
-return a NUL terminated string for the file name, even when there is no
ce426f
-@code{d_reclen} field in @code{struct dirent} and the file
ce426f
-name is the maximum allowed size.  Modern systems all have the
ce426f
-@code{d_reclen} field, and on old systems multi-threading is not
ce426f
-critical.  In any case there is no such problem with the @code{readdir}
ce426f
-function, so that even on systems without the @code{d_reclen} member one
ce426f
-could use multiple threads by using external locking.
ce426f
-
ce426f
 It is also important to look at the definition of the @code{struct
ce426f
 dirent} type.  Simply passing a pointer to an object of this type for
ce426f
 the second parameter of @code{readdir_r} might not be enough.  Some
ce426f
diff --git glibc-2.17-c758a686/sysdeps/posix/dirstream.h glibc-2.17-c758a686/sysdeps/posix/dirstream.h
ce426f
index a7a074d..8e8570d 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/posix/dirstream.h
ce426f
+++ glibc-2.17-c758a686/sysdeps/posix/dirstream.h
ce426f
@@ -39,6 +39,8 @@ struct __dirstream
ce426f
 
ce426f
     off_t filepos;		/* Position of next entry to read.  */
ce426f
 
ce426f
+    int errcode;		/* Delayed error code.  */
ce426f
+
ce426f
     /* Directory block.  */
ce426f
     char data[0] __attribute__ ((aligned (__alignof__ (void*))));
ce426f
   };
ce426f
diff --git glibc-2.17-c758a686/sysdeps/posix/opendir.c glibc-2.17-c758a686/sysdeps/posix/opendir.c
ce426f
index ddfc3a7..fc05b0f 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/posix/opendir.c
ce426f
+++ glibc-2.17-c758a686/sysdeps/posix/opendir.c
ce426f
@@ -231,6 +231,7 @@ __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp)
ce426f
   dirp->size = 0;
ce426f
   dirp->offset = 0;
ce426f
   dirp->filepos = 0;
ce426f
+  dirp->errcode = 0;
ce426f
 
ce426f
   return dirp;
ce426f
 }
ce426f
diff --git glibc-2.17-c758a686/sysdeps/posix/readdir_r.c glibc-2.17-c758a686/sysdeps/posix/readdir_r.c
ce426f
index b5a8e2e..8ed5c3f 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/posix/readdir_r.c
ce426f
+++ glibc-2.17-c758a686/sysdeps/posix/readdir_r.c
ce426f
@@ -40,6 +40,7 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
ce426f
   DIRENT_TYPE *dp;
ce426f
   size_t reclen;
ce426f
   const int saved_errno = errno;
ce426f
+  int ret;
ce426f
 
ce426f
   __libc_lock_lock (dirp->lock);
ce426f
 
ce426f
@@ -70,10 +71,10 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
ce426f
 		  bytes = 0;
ce426f
 		  __set_errno (saved_errno);
ce426f
 		}
ce426f
+	      if (bytes < 0)
ce426f
+		dirp->errcode = errno;
ce426f
 
ce426f
 	      dp = NULL;
ce426f
-	      /* Reclen != 0 signals that an error occurred.  */
ce426f
-	      reclen = bytes != 0;
ce426f
 	      break;
ce426f
 	    }
ce426f
 	  dirp->size = (size_t) bytes;
ce426f
@@ -106,29 +107,46 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
ce426f
       dirp->filepos += reclen;
ce426f
 #endif
ce426f
 
ce426f
-      /* Skip deleted files.  */
ce426f
+#ifdef NAME_MAX
ce426f
+      if (reclen > offsetof (DIRENT_TYPE, d_name) + NAME_MAX + 1)
ce426f
+	{
ce426f
+	  /* The record is very long.  It could still fit into the
ce426f
+	     caller-supplied buffer if we can skip padding at the
ce426f
+	     end.  */
ce426f
+	  size_t namelen = _D_EXACT_NAMLEN (dp);
ce426f
+	  if (namelen <= NAME_MAX)
ce426f
+	    reclen = offsetof (DIRENT_TYPE, d_name) + namelen + 1;
ce426f
+	  else
ce426f
+	    {
ce426f
+	      /* The name is too long.  Ignore this file.  */
ce426f
+	      dirp->errcode = ENAMETOOLONG;
ce426f
+	      dp->d_ino = 0;
ce426f
+	      continue;
ce426f
+	    }
ce426f
+	}
ce426f
+#endif
ce426f
+
ce426f
+      /* Skip deleted and ignored files.  */
ce426f
     }
ce426f
   while (dp->d_ino == 0);
ce426f
 
ce426f
   if (dp != NULL)
ce426f
     {
ce426f
-#ifdef GETDENTS_64BIT_ALIGNED
ce426f
-      /* The d_reclen value might include padding which is not part of
ce426f
-	 the DIRENT_TYPE data structure.  */
ce426f
-      reclen = MIN (reclen,
ce426f
-		    offsetof (DIRENT_TYPE, d_name) + sizeof (dp->d_name));
ce426f
-#endif
ce426f
       *result = memcpy (entry, dp, reclen);
ce426f
-#ifdef GETDENTS_64BIT_ALIGNED
ce426f
+#ifdef _DIRENT_HAVE_D_RECLEN
ce426f
       entry->d_reclen = reclen;
ce426f
 #endif
ce426f
+      ret = 0;
ce426f
     }
ce426f
   else
ce426f
-    *result = NULL;
ce426f
+    {
ce426f
+      *result = NULL;
ce426f
+      ret = dirp->errcode;
ce426f
+    }
ce426f
 
ce426f
   __libc_lock_unlock (dirp->lock);
ce426f
 
ce426f
-  return dp != NULL ? 0 : reclen ? errno : 0;
ce426f
+  return ret;
ce426f
 }
ce426f
 
ce426f
 #ifdef __READDIR_R_ALIAS
ce426f
diff --git glibc-2.17-c758a686/sysdeps/posix/rewinddir.c glibc-2.17-c758a686/sysdeps/posix/rewinddir.c
ce426f
index 2935a8e..d4991ad 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/posix/rewinddir.c
ce426f
+++ glibc-2.17-c758a686/sysdeps/posix/rewinddir.c
ce426f
@@ -33,6 +33,7 @@ rewinddir (dirp)
ce426f
   dirp->filepos = 0;
ce426f
   dirp->offset = 0;
ce426f
   dirp->size = 0;
ce426f
+  dirp->errcode = 0;
ce426f
 #ifndef NOT_IN_libc
ce426f
   __libc_lock_unlock (dirp->lock);
ce426f
 #endif
ce426f
diff --git glibc-2.17-c758a686/sysdeps/unix/sysv/linux/i386/readdir64_r.c glibc-2.17-c758a686/sysdeps/unix/sysv/linux/i386/readdir64_r.c
ce426f
index 8ebbcfd..a7d114e 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/unix/sysv/linux/i386/readdir64_r.c
ce426f
+++ glibc-2.17-c758a686/sysdeps/unix/sysv/linux/i386/readdir64_r.c
ce426f
@@ -18,7 +18,6 @@
ce426f
 #define __READDIR_R __readdir64_r
ce426f
 #define __GETDENTS __getdents64
ce426f
 #define DIRENT_TYPE struct dirent64
ce426f
-#define GETDENTS_64BIT_ALIGNED 1
ce426f
 
ce426f
 #include <sysdeps/posix/readdir_r.c>
ce426f
 
ce426f
diff --git glibc-2.17-c758a686/sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c glibc-2.17-c758a686/sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c
ce426f
index 5ed8e95..290f2c8 100644
ce426f
--- glibc-2.17-c758a686/sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c
ce426f
+++ glibc-2.17-c758a686/sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c
ce426f
@@ -1,5 +1,4 @@
ce426f
 #define readdir64_r __no_readdir64_r_decl
ce426f
-#define GETDENTS_64BIT_ALIGNED 1
ce426f
 #include <sysdeps/posix/readdir_r.c>
ce426f
 #undef readdir64_r
ce426f
 weak_alias (__readdir_r, readdir64_r)