Commit c365c355 authored by Paul Eggert's avatar Paul Eggert
Browse files

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;'.
parent bb35f42f
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
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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 */
}
......
......@@ -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. */
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment