]> git.eshelyaron.com Git - emacs.git/commitdiff
Don't let very long directory names overrun the stack.
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 23 Aug 2013 17:57:07 +0000 (10:57 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 23 Aug 2013 17:57:07 +0000 (10:57 -0700)
Fix some related minor problems involving "//", vfork.
* callproc.c (encode_current_directory): New function.
(call_process): Don't append "/"; not needed.
* fileio.c (file_name_as_directory_slop): New constant.
(file_name_as_directory): Allow SRC to be longer than SRCLEN;
this can save the caller having to alloca.
(Ffile_name_as_directory, Fdirectory_file_name, Fexpand_file_name):
Use SAFE_ALLOCA, not alloca.
(directory_file_name, Fexpand_file_name): Leave leading "//"
alone, since it can be special even on POSIX platforms.
* callproc.c (call_process):
* process.c (Fformat_network_address):
* sysdep.c (sys_subshell):
Use encode_current_directory rather than rolling our own.
(create_process): No need to encode directory; caller does that now.
* process.h (encode_current_directory): New decl.
* sysdep.c (sys_subshell): Work even if vfork trashes saved_handlers.
Rework to avoid 'goto xyzzy;'.

src/ChangeLog
src/callproc.c
src/fileio.c
src/process.c
src/process.h
src/sysdep.c

index bee477c745a45d596c51bdb19dab557638b5126e..73fdb0221ce1f69dc26524d3baf740c9fe6bcc35 100644 (file)
@@ -1,3 +1,25 @@
+2013-08-23  Paul Eggert  <eggert@cs.ucla.edu>
+
+       Don't let very long directory names overrun the stack.
+       Fix some related minor problems involving "//", vfork.
+       * callproc.c (encode_current_directory): New function.
+       (call_process): Don't append "/"; not needed.
+       * fileio.c (file_name_as_directory_slop): New constant.
+       (file_name_as_directory): Allow SRC to be longer than SRCLEN;
+       this can save the caller having to alloca.
+       (Ffile_name_as_directory, Fdirectory_file_name, Fexpand_file_name):
+       Use SAFE_ALLOCA, not alloca.
+       (directory_file_name, Fexpand_file_name): Leave leading "//"
+       alone, since it can be special even on POSIX platforms.
+       * callproc.c (call_process):
+       * process.c (Fformat_network_address):
+       * sysdep.c (sys_subshell):
+       Use encode_current_directory rather than rolling our own.
+       (create_process): No need to encode directory; caller does that now.
+       * process.h (encode_current_directory): New decl.
+       * sysdep.c (sys_subshell): Work even if vfork trashes saved_handlers.
+       Rework to avoid 'goto xyzzy;'.
+
 2013-08-23  Eli Zaretskii  <eliz@gnu.org>
 
        * xdisp.c (handle_face_prop): If the default face was remapped use
index fca4216b5f72e7426a86de22709113933d54d7eb..d4b4a26ec3ae3f16bc1b0113be2e0a4c74caa952 100644 (file)
@@ -123,6 +123,37 @@ unblock_child_signal (void)
   pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
 }
 
+/* Return the current buffer's working directory, or the home
+   directory if it's unreachable, as a string suitable for a system call.
+   Signal an error if the result would not be an accessible directory.  */
+
+Lisp_Object
+encode_current_directory (void)
+{
+  Lisp_Object dir;
+  struct gcpro gcpro1;
+
+  dir = BVAR (current_buffer, directory);
+  GCPRO1 (dir);
+
+  dir = Funhandled_file_name_directory (dir);
+
+  /* If the file name handler says that dir is unreachable, use
+     a sensible default. */
+  if (NILP (dir))
+    dir = build_string ("~");
+
+  dir = expand_and_dir_to_file (dir, Qnil);
+
+  if (STRING_MULTIBYTE (dir))
+    dir = ENCODE_FILE (dir);
+  if (! file_accessible_directory_p (SSDATA (dir)))
+    report_file_error ("Setting current directory",
+                      BVAR (current_buffer, directory));
+
+  RETURN_UNGCPRO (dir);
+}
+
 /* If P is reapable, record it as a deleted process and kill it.
    Do this in a critical section.  Unless PID is wedged it will be
    reaped on receipt of the first SIGCHLD after the critical section.  */
@@ -408,24 +439,10 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd,
   {
     struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
 
-    current_dir = BVAR (current_buffer, directory);
+    current_dir = encode_current_directory ();
 
     GCPRO4 (buffer, current_dir, error_file, output_file);
 
-    current_dir = Funhandled_file_name_directory (current_dir);
-    if (NILP (current_dir))
-      /* If the file name handler says that current_dir is unreachable, use
-        a sensible default. */
-      current_dir = build_string ("~/");
-    current_dir = expand_and_dir_to_file (current_dir, Qnil);
-    current_dir = Ffile_name_as_directory (current_dir);
-
-    if (NILP (Ffile_accessible_directory_p (current_dir)))
-      report_file_error ("Setting current directory",
-                        BVAR (current_buffer, directory));
-
-    if (STRING_MULTIBYTE (current_dir))
-      current_dir = ENCODE_FILE (current_dir);
     if (STRINGP (error_file) && STRING_MULTIBYTE (error_file))
       error_file = ENCODE_FILE (error_file);
     if (STRINGP (output_file) && STRING_MULTIBYTE (output_file))
@@ -1176,23 +1193,21 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp,
      static variables as if the superior had done alloca and will be
      cleaned up in the usual way. */
   {
-    register char *temp;
-    size_t i; /* size_t, because ptrdiff_t might overflow here!  */
+    char *temp;
+    ptrdiff_t i;
 
     i = SBYTES (current_dir);
 #ifdef MSDOS
     /* MSDOS must have all environment variables malloc'ed, because
        low-level libc functions that launch subsidiary processes rely
        on that.  */
-    pwd_var = xmalloc (i + 6);
+    pwd_var = xmalloc (i + 5);
 #else
-    pwd_var = alloca (i + 6);
+    pwd_var = alloca (i + 5);
 #endif
     temp = pwd_var + 4;
     memcpy (pwd_var, "PWD=", 4);
-    memcpy (temp, SDATA (current_dir), i);
-    if (!IS_DIRECTORY_SEP (temp[i - 1])) temp[i++] = DIRECTORY_SEP;
-    temp[i] = 0;
+    strcpy (temp, SSDATA (current_dir));
 
 #ifndef DOS_NT
     /* We can't signal an Elisp error here; we're in a vfork.  Since
index 08caf1022667d37a6fce32d76a9ec93ad43d9268..7cad8d29da2dc0ad60e4f23a0335c79ab299b288 100644 (file)
@@ -504,6 +504,10 @@ get a current directory to run processes in.  */)
   return Ffile_name_directory (filename);
 }
 
+/* Maximum number of bytes that DST will be longer than SRC
+   in file_name_as_directory.  This occurs when SRCLEN == 0.  */
+enum { file_name_as_directory_slop = 2 };
+
 /* Convert from file name SRC of length SRCLEN to directory name in
    DST.  MULTIBYTE non-zero means the file name in SRC is a multibyte
    string.  On UNIX, just make sure there is a terminating /.  Return
@@ -521,14 +525,10 @@ file_name_as_directory (char *dst, const char *src, ptrdiff_t srclen,
       return 2;
     }
 
-  strcpy (dst, src);
-
+  memcpy (dst, src, srclen);
   if (!IS_DIRECTORY_SEP (dst[srclen - 1]))
-    {
-      dst[srclen] = DIRECTORY_SEP;
-      dst[srclen + 1] = '\0';
-      srclen++;
-    }
+    dst[srclen++] = DIRECTORY_SEP;
+  dst[srclen] = 0;
 #ifdef DOS_NT
   dostounix_filename (dst, multibyte);
 #endif
@@ -547,7 +547,8 @@ For a Unix-syntax file name, just appends a slash.  */)
 {
   char *buf;
   ptrdiff_t length;
-  Lisp_Object handler;
+  Lisp_Object handler, val;
+  USE_SAFE_ALLOCA;
 
   CHECK_STRING (file);
   if (NILP (file))
@@ -569,10 +570,12 @@ For a Unix-syntax file name, just appends a slash.  */)
   if (!NILP (Vw32_downcase_file_names))
     file = Fdowncase (file);
 #endif
-  buf = alloca (SBYTES (file) + 10);
+  buf = SAFE_ALLOCA (SBYTES (file) + file_name_as_directory_slop + 1);
   length = file_name_as_directory (buf, SSDATA (file), SBYTES (file),
                                   STRING_MULTIBYTE (file));
-  return make_specified_string (buf, -1, length, STRING_MULTIBYTE (file));
+  val = make_specified_string (buf, -1, length, STRING_MULTIBYTE (file));
+  SAFE_FREE ();
+  return val;
 }
 \f
 /* Convert from directory name SRC of length SRCLEN to file name in
@@ -584,18 +587,17 @@ static ptrdiff_t
 directory_file_name (char *dst, char *src, ptrdiff_t srclen, bool multibyte)
 {
   /* Process as Unix format: just remove any final slash.
-     But leave "/" unchanged; do not change it to "".  */
-  strcpy (dst, src);
-  if (srclen > 1
-      && IS_DIRECTORY_SEP (dst[srclen - 1])
+     But leave "/" and "//" unchanged.  */
+  while (srclen > 1
 #ifdef DOS_NT
-      && !IS_ANY_SEP (dst[srclen - 2])
+        && !IS_ANY_SEP (src[srclen - 2])
 #endif
-      )
-    {
-      dst[srclen - 1] = 0;
-      srclen--;
-    }
+        && IS_DIRECTORY_SEP (src[srclen - 1])
+        && ! (srclen == 2 && IS_DIRECTORY_SEP (src[0])))
+    srclen--;
+
+  memcpy (dst, src, srclen);
+  dst[srclen] = 0;
 #ifdef DOS_NT
   dostounix_filename (dst, multibyte);
 #endif
@@ -613,7 +615,8 @@ In Unix-syntax, this function just removes the final slash.  */)
 {
   char *buf;
   ptrdiff_t length;
-  Lisp_Object handler;
+  Lisp_Object handler, val;
+  USE_SAFE_ALLOCA;
 
   CHECK_STRING (directory);
 
@@ -636,10 +639,12 @@ In Unix-syntax, this function just removes the final slash.  */)
   if (!NILP (Vw32_downcase_file_names))
     directory = Fdowncase (directory);
 #endif
-  buf = alloca (SBYTES (directory) + 20);
+  buf = SAFE_ALLOCA (SBYTES (directory) + 1);
   length = directory_file_name (buf, SSDATA (directory), SBYTES (directory),
                                STRING_MULTIBYTE (directory));
-  return make_specified_string (buf, -1, length, STRING_MULTIBYTE (directory));
+  val = make_specified_string (buf, -1, length, STRING_MULTIBYTE (directory));
+  SAFE_FREE ();
+  return val;
 }
 
 static const char make_temp_name_tbl[64] =
@@ -837,6 +842,7 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
   Lisp_Object handler, result, handled_name;
   bool multibyte;
   Lisp_Object hdir;
+  USE_SAFE_ALLOCA;
 
   CHECK_STRING (name);
 
@@ -1011,11 +1017,11 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
                  || (p[2] == '.' && (IS_DIRECTORY_SEP (p[3])
                                      || p[3] == 0))))
            lose = 1;
-         /* We want to replace multiple `/' in a row with a single
-            slash.  */
-         else if (p > nm
-                  && IS_DIRECTORY_SEP (p[0])
-                  && IS_DIRECTORY_SEP (p[1]))
+         /* Replace multiple slashes with a single one, except
+            leave leading "//" alone.  */
+         else if (IS_DIRECTORY_SEP (p[0])
+                  && IS_DIRECTORY_SEP (p[1])
+                  && (p != nm || IS_DIRECTORY_SEP (p[2])))
            lose = 1;
          p++;
        }
@@ -1098,10 +1104,11 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
       else                     /* ~user/filename */
        {
          char *o, *p;
-         for (p = nm; *p && (!IS_DIRECTORY_SEP (*p)); p++);
-         o = alloca (p - nm + 1);
+         for (p = nm; *p && !IS_DIRECTORY_SEP (*p); p++)
+           continue;
+         o = SAFE_ALLOCA (p - nm + 1);
          memcpy (o, nm, p - nm);
-         o [p - nm] = 0;
+         o[p - nm] = 0;
 
          block_input ();
          pw = getpwnam (o + 1);
@@ -1217,7 +1224,8 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
          if (!IS_DIRECTORY_SEP (nm[0]))
            {
              ptrdiff_t newlen = strlen (newdir);
-             char *tmp = alloca (newlen + strlen (nm) + 2);
+             char *tmp = alloca (newlen + file_name_as_directory_slop
+                                 + strlen (nm) + 1);
              file_name_as_directory (tmp, newdir, newlen, multibyte);
              strcat (tmp, nm);
              nm = tmp;
@@ -1271,31 +1279,18 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
 
   if (newdir)
     {
-      /* Get rid of any slash at the end of newdir, unless newdir is
-        just / or // (an incomplete UNC name).  */
+      /* Ignore any slash at the end of newdir, unless newdir is
+        just "/" or "//".  */
       length = strlen (newdir);
-      tlen = length + 1;
-      if (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1])
-#ifdef WINDOWSNT
-         && !(length == 2 && IS_DIRECTORY_SEP (newdir[0]))
-#endif
-         )
-       {
-         char *temp = alloca (length);
-         memcpy (temp, newdir, length - 1);
-         temp[length - 1] = 0;
-         length--;
-         newdir = temp;
-       }
+      while (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1])
+            && ! (length == 2 && IS_DIRECTORY_SEP (newdir[0])))
+       length--;
     }
   else
-    {
-      length = 0;
-      tlen = 0;
-    }
+    length = 0;
 
   /* Now concatenate the directory and name to new space in the stack frame.  */
-  tlen += strlen (nm) + 1;
+  tlen = length + file_name_as_directory_slop + strlen (nm) + 1;
 #ifdef DOS_NT
   /* Reserve space for drive specifier and escape prefix, since either
      or both may need to be inserted.  (The Microsoft x86 compiler
@@ -1303,7 +1298,7 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
   target = alloca (tlen + 4);
   target += 4;
 #else  /* not DOS_NT */
-  target = alloca (tlen);
+  target = SAFE_ALLOCA (tlen);
 #endif /* not DOS_NT */
   *target = 0;
 
@@ -1320,7 +1315,10 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
          if (!(drive && nm[0] && IS_DIRECTORY_SEP (newdir[0])
                && newdir[1] == '\0'))
 #endif
-           strcpy (target, newdir);
+           {
+             memcpy (target, newdir, length);
+             target[length] = 0;
+           }
        }
       else
        file_name_as_directory (target, newdir, length, multibyte);
@@ -1380,8 +1378,9 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
              ++o;
            p += 3;
          }
-       else if (p > target && IS_DIRECTORY_SEP (p[1]))
-         /* Collapse multiple `/' in a row.  */
+       else if (IS_DIRECTORY_SEP (p[1])
+                && (p != target || IS_DIRECTORY_SEP (p[2])))
+         /* Collapse multiple "/", except leave leading "//" alone.  */
          p++;
        else
          {
@@ -1429,11 +1428,12 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
     {
       handled_name = call3 (handler, Qexpand_file_name,
                            result, default_directory);
-      if (STRINGP (handled_name))
-       return handled_name;
-      error ("Invalid handler in `file-name-handler-alist'");
+      if (STRINGP (handled_name))
+       error ("Invalid handler in `file-name-handler-alist'");
+      result = handled_name;
     }
 
+  SAFE_FREE ();
   return result;
 }
 
index 75cb590fc57ab7beff098ab60369615447eb39ba..ea1129ffbb886dba97c36103044e80dbdd525eb8 100644 (file)
@@ -1408,22 +1408,9 @@ usage: (start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)  */)
      function.  The argument list is protected by the caller, so all
      we really have to worry about is buffer.  */
   {
-    struct gcpro gcpro1, gcpro2;
-
-    current_dir = BVAR (current_buffer, directory);
-
-    GCPRO2 (buffer, current_dir);
-
-    current_dir = Funhandled_file_name_directory (current_dir);
-    if (NILP (current_dir))
-      /* If the file name handler says that current_dir is unreachable, use
-        a sensible default. */
-      current_dir = build_string ("~/");
-    current_dir = expand_and_dir_to_file (current_dir, Qnil);
-    if (NILP (Ffile_accessible_directory_p (current_dir)))
-      report_file_error ("Setting current directory",
-                        BVAR (current_buffer, directory));
-
+    struct gcpro gcpro1;
+    GCPRO1 (buffer);
+    current_dir = encode_current_directory ();
     UNGCPRO;
   }
 
@@ -1670,7 +1657,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   bool pty_flag = 0;
   char pty_name[PTY_NAME_SIZE];
   Lisp_Object lisp_pty_name = Qnil;
-  Lisp_Object encoded_current_dir;
 
   inchannel = outchannel = -1;
 
@@ -1735,15 +1721,13 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   /* This may signal an error. */
   setup_process_coding_systems (process);
 
-  encoded_current_dir = ENCODE_FILE (current_dir);
-
   block_input ();
   block_child_signal ();
 
 #ifndef WINDOWSNT
   /* vfork, and prevent local vars from being clobbered by the vfork.  */
   {
-    Lisp_Object volatile encoded_current_dir_volatile = encoded_current_dir;
+    Lisp_Object volatile current_dir_volatile = current_dir;
     Lisp_Object volatile lisp_pty_name_volatile = lisp_pty_name;
     char **volatile new_argv_volatile = new_argv;
     int volatile forkin_volatile = forkin;
@@ -1752,7 +1736,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
 
     pid = vfork ();
 
-    encoded_current_dir = encoded_current_dir_volatile;
+    current_dir = current_dir_volatile;
     lisp_pty_name = lisp_pty_name_volatile;
     new_argv = new_argv_volatile;
     forkin = forkin_volatile;
@@ -1864,11 +1848,9 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
       if (pty_flag)
        child_setup_tty (xforkout);
 #ifdef WINDOWSNT
-      pid = child_setup (xforkin, xforkout, xforkout,
-                        new_argv, 1, encoded_current_dir);
+      pid = child_setup (xforkin, xforkout, xforkout, new_argv, 1, current_dir);
 #else  /* not WINDOWSNT */
-      child_setup (xforkin, xforkout, xforkout,
-                  new_argv, 1, encoded_current_dir);
+      child_setup (xforkin, xforkout, xforkout, new_argv, 1, current_dir);
 #endif /* not WINDOWSNT */
     }
 
index b312f7f2425ee0048ca3ba4b8a019057bbb75949..6aff95686a24c7b4ce42dad010f85e1825320a1b 100644 (file)
@@ -218,6 +218,7 @@ enum
 
 extern void block_child_signal (void);
 extern void unblock_child_signal (void);
+extern Lisp_Object encode_current_directory (void);
 extern void record_kill_process (struct Lisp_Process *, Lisp_Object);
 
 /* Defined in sysdep.c.  */
index 78e3d908cfea4d418fa15ad79ded7ad4b5aa660b..c6d5f9942ab56430631bfeff26821110f519e744 100644 (file)
@@ -471,10 +471,20 @@ sys_subshell (void)
   pid_t pid;
   int status;
   struct save_signal saved_handlers[5];
-  Lisp_Object dir;
-  unsigned char *volatile str_volatile = 0;
-  unsigned char *str;
-  int len;
+  char *str = SSDATA (encode_current_directory ());
+
+#ifdef DOS_NT
+  pid = 0;
+#else
+  {
+    char *volatile str_volatile = str;
+    pid = vfork ();
+    str = str_volatile;
+  }
+#endif
+
+  if (pid < 0)
+    error ("Can't spawn subshell");
 
   saved_handlers[0].code = SIGINT;
   saved_handlers[1].code = SIGQUIT;
@@ -486,31 +496,8 @@ sys_subshell (void)
   saved_handlers[3].code = 0;
 #endif
 
-  /* Mentioning current_buffer->buffer would mean including buffer.h,
-     which somehow wedges the hp compiler.  So instead...  */
-
-  dir = intern ("default-directory");
-  if (NILP (Fboundp (dir)))
-    goto xyzzy;
-  dir = Fsymbol_value (dir);
-  if (!STRINGP (dir))
-    goto xyzzy;
-
-  dir = expand_and_dir_to_file (Funhandled_file_name_directory (dir), Qnil);
-  str_volatile = str = alloca (SCHARS (dir) + 2);
-  len = SCHARS (dir);
-  memcpy (str, SDATA (dir), len);
-  if (str[len - 1] != '/') str[len++] = '/';
-  str[len] = 0;
- xyzzy:
-
 #ifdef DOS_NT
-  pid = 0;
   save_signal_handlers (saved_handlers);
-#else
-  pid = vfork ();
-  if (pid == -1)
-    error ("Can't spawn subshell");
 #endif
 
   if (pid == 0)
@@ -528,11 +515,10 @@ sys_subshell (void)
        sh = "sh";
 
       /* Use our buffer's default directory for the subshell.  */
-      str = str_volatile;
-      if (str && chdir ((char *) str) != 0)
+      if (chdir (str) != 0)
        {
 #ifndef DOS_NT
-         emacs_perror ((char *) str);
+         emacs_perror (str);
          _exit (EXIT_CANCELED);
 #endif
        }
@@ -546,8 +532,6 @@ sys_subshell (void)
        if (epwd)
          {
            strcpy (old_pwd, epwd);
-           if (str[len - 1] == '/')
-             str[len - 1] = '\0';
            setenv ("PWD", str, 1);
          }
        st = system (sh);