]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix unlikely overflows with wd length
authorPaul Eggert <eggert@cs.ucla.edu>
Mon, 9 Oct 2017 17:30:40 +0000 (10:30 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 9 Oct 2017 17:48:55 +0000 (10:48 -0700)
* 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

index c3484920d0cf58df01e2a4a4ee6ac5e4d0ac01ea..6d24b7fa2b10e09d0ff1666e29a525fdc20ae646 100644 (file)
@@ -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