From: Paul Eggert Date: Fri, 23 Aug 2013 17:57:07 +0000 (-0700) Subject: Don't let very long directory names overrun the stack. X-Git-Tag: emacs-24.3.90~173^2^2~42^2~45^2~387^2~1686^2~138 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=c365c3558065fca4c11c3f46605d1045763485ab;p=emacs.git 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;'. --- diff --git a/src/ChangeLog b/src/ChangeLog index bee477c745a..73fdb0221ce 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,25 @@ +2013-08-23 Paul Eggert + + 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 * xdisp.c (handle_face_prop): If the default face was remapped use diff --git a/src/callproc.c b/src/callproc.c index fca4216b5f7..d4b4a26ec3a 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -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 diff --git a/src/fileio.c b/src/fileio.c index 08caf102266..7cad8d29da2 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -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; } /* 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; } diff --git a/src/process.c b/src/process.c index 75cb590fc57..ea1129ffbb8 100644 --- a/src/process.c +++ b/src/process.c @@ -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 */ } diff --git a/src/process.h b/src/process.h index b312f7f2425..6aff95686a2 100644 --- a/src/process.h +++ b/src/process.h @@ -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. */ diff --git a/src/sysdep.c b/src/sysdep.c index 78e3d908cfe..c6d5f9942ab 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -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);