emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] emacs-26 934f08f 1/2: Fix unlikely overflows with wd lengt


From: Paul Eggert
Subject: [Emacs-diffs] emacs-26 934f08f 1/2: Fix unlikely overflows with wd length
Date: Mon, 9 Oct 2017 13:49:01 -0400 (EDT)

branch: emacs-26
commit 934f08f3de7929e54e9da3271a34bb49a8854f2c
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    Fix unlikely overflows with wd length
    
    * src/sysdep.c (get_current_dir_name_or_unreachable):
    Avoid integer overflow if working directory name is absurdly long.
    When allocating memory for getcwd, do not exceed MAXPATHLEN.
---
 src/sysdep.c | 57 +++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/src/sysdep.c b/src/sysdep.c
index c348492..6d24b7f 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -228,6 +228,22 @@ init_standard_fds (void)
 static char *
 get_current_dir_name_or_unreachable (void)
 {
+  /* Use malloc, not xmalloc, since this function can be called before
+     the xmalloc exception machinery is available.  */
+
+  char *pwd;
+
+  /* The maximum size of a directory name, including the terminating null.
+     Leave room so that the caller can append a trailing slash.  */
+  ptrdiff_t dirsize_max = min (PTRDIFF_MAX, SIZE_MAX) - 1;
+
+  /* The maximum size of a buffer for a file name, including the
+     terminating null.  This is bounded by MAXPATHLEN, if available.  */
+  ptrdiff_t bufsize_max = dirsize_max;
+#ifdef MAXPATHLEN
+  bufsize_max = min (bufsize_max, MAXPATHLEN);
+#endif
+
 # if HAVE_GET_CURRENT_DIR_NAME && !BROKEN_GET_CURRENT_DIR_NAME
 #  ifdef HYBRID_MALLOC
   bool use_libc = bss_sbrk_did_unexec;
@@ -238,56 +254,61 @@ get_current_dir_name_or_unreachable (void)
     {
       /* For an unreachable directory, this returns a string that starts
         with "(unreachable)"; see Bug#27871.  */
-      return get_current_dir_name ();
+      pwd = get_current_dir_name ();
+      if (pwd)
+       {
+         if (strlen (pwd) < dirsize_max)
+           return pwd;
+         free (pwd);
+         errno = ERANGE;
+       }
+      return NULL;
     }
 # endif
 
-  char *buf;
-  char *pwd = getenv ("PWD");
+  size_t pwdlen;
   struct stat dotstat, pwdstat;
+  pwd = getenv ("PWD");
+
   /* If PWD is accurate, use it instead of calling getcwd.  PWD is
      sometimes a nicer name, and using it may avoid a fatal error if a
      parent directory is searchable but not readable.  */
   if (pwd
       && (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1])))
+      && (pwdlen = strlen (pwd)) < bufsize_max
       && stat (pwd, &pwdstat) == 0
       && stat (".", &dotstat) == 0
       && dotstat.st_ino == pwdstat.st_ino
-      && dotstat.st_dev == pwdstat.st_dev
-#ifdef MAXPATHLEN
-      && strlen (pwd) < MAXPATHLEN
-#endif
-      )
+      && dotstat.st_dev == pwdstat.st_dev)
     {
-      buf = malloc (strlen (pwd) + 1);
+      char *buf = malloc (pwdlen + 1);
       if (!buf)
         return NULL;
-      strcpy (buf, pwd);
+      return memcpy (buf, pwd, pwdlen + 1);
     }
   else
     {
-      size_t buf_size = 1024;
-      buf = malloc (buf_size);
+      ptrdiff_t buf_size = min (bufsize_max, 1024);
+      char *buf = malloc (buf_size);
       if (!buf)
         return NULL;
       for (;;)
         {
           if (getcwd (buf, buf_size) == buf)
-            break;
-          if (errno != ERANGE)
+           return buf;
+         int getcwd_errno = errno;
+         if (getcwd_errno != ERANGE || buf_size == bufsize_max)
             {
-              int tmp_errno = errno;
               free (buf);
-              errno = tmp_errno;
+             errno = getcwd_errno;
               return NULL;
             }
-          buf_size *= 2;
+         buf_size = buf_size <= bufsize_max / 2 ? 2 * buf_size : bufsize_max;
           buf = realloc (buf, buf_size);
           if (!buf)
             return NULL;
         }
     }
-  return buf;
 }
 
 /* Return the current working directory.  The result should be freed



reply via email to

[Prev in Thread] Current Thread [Next in Thread]