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