summaryrefslogtreecommitdiff
path: root/src/emacs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emacs.c')
-rw-r--r--src/emacs.c400
1 files changed, 341 insertions, 59 deletions
diff --git a/src/emacs.c b/src/emacs.c
index fd08667f3fd..866e43fda94 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -37,6 +37,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <fcntl.h>
#include <sys/socket.h>
#include <mbstring.h>
+#include <filename.h> /* for IS_ABSOLUTE_FILE_NAME */
#include "w32.h"
#include "w32heap.h"
#endif
@@ -61,6 +62,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
# include <sys/socket.h>
#endif
+#if defined HAVE_LINUX_SECCOMP_H && defined HAVE_LINUX_FILTER_H \
+ && HAVE_DECL_SECCOMP_SET_MODE_FILTER \
+ && HAVE_DECL_SECCOMP_FILTER_FLAG_TSYNC
+# define SECCOMP_USABLE 1
+#else
+# define SECCOMP_USABLE 0
+#endif
+
+#if SECCOMP_USABLE
+# include <linux/seccomp.h>
+# include <linux/filter.h>
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@@ -241,6 +257,11 @@ Initialization options:\n\
--dump-file FILE read dumped state from FILE\n\
",
#endif
+#if SECCOMP_USABLE
+ "\
+--sandbox=FILE read Seccomp BPF filter from FILE\n\
+"
+#endif
"\
--no-build-details do not add build details such as time stamps\n\
--no-desktop do not load a saved desktop\n\
@@ -418,9 +439,9 @@ terminate_due_to_signal (int sig, int backtrace_limit)
/* This shouldn't be executed, but it prevents a warning. */
exit (1);
}
+
/* Code for dealing with Lisp access to the Unix command line. */
-
static void
init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
{
@@ -462,8 +483,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
if (NILP (Vinvocation_directory))
{
Lisp_Object found;
- int yes = openp (Vexec_path, Vinvocation_name,
- Vexec_suffixes, &found, make_fixnum (X_OK), false);
+ int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
+ &found, make_fixnum (X_OK), false, false);
if (yes == 1)
{
/* Add /: to the front of the name
@@ -649,7 +670,9 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr,
}
arglen = (valptr != NULL && (p = strchr (arg, '=')) != NULL
? p - arg : strlen (arg));
- if (lstr == 0 || arglen < minlen || strncmp (arg, lstr, arglen) != 0)
+ if (!lstr)
+ return 0;
+ if (arglen < minlen || strncmp (arg, lstr, arglen) != 0)
return 0;
else if (valptr == NULL)
{
@@ -718,15 +741,29 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t *candidate_size)
implementation of malloc, since the caller calls our free. */
#ifdef WINDOWSNT
char *prog_fname = w32_my_exename ();
+ if (prog_fname)
+ *candidate_size = strlen (prog_fname) + 1;
return prog_fname ? xstrdup (prog_fname) : NULL;
#else /* !WINDOWSNT */
char *candidate = NULL;
/* If the executable name contains a slash, we have some kind of
- path already, so just copy it. */
+ path already, so just resolve symlinks and return the result. */
eassert (argv0);
if (strchr (argv0, DIRECTORY_SEP))
- return xstrdup (argv0);
+ {
+ char *real_name = realpath (argv0, NULL);
+
+ if (real_name)
+ {
+ *candidate_size = strlen (real_name) + 1;
+ return real_name;
+ }
+
+ char *val = xstrdup (argv0);
+ *candidate_size = strlen (val) + 1;
+ return val;
+ }
ptrdiff_t argv0_length = strlen (argv0);
const char *path = getenv ("PATH");
@@ -763,7 +800,22 @@ load_pdump_find_executable (char const *argv0, ptrdiff_t *candidate_size)
struct stat st;
if (file_access_p (candidate, X_OK)
&& stat (candidate, &st) == 0 && S_ISREG (st.st_mode))
- return candidate;
+ {
+ /* People put on PATH a symlink to the real Emacs
+ executable, with all the auxiliary files where the real
+ executable lives. Support that. */
+ if (lstat (candidate, &st) == 0 && S_ISLNK (st.st_mode))
+ {
+ char *real_name = realpath (candidate, NULL);
+
+ if (real_name)
+ {
+ *candidate_size = strlen (real_name) + 1;
+ return real_name;
+ }
+ }
+ return candidate;
+ }
*candidate = '\0';
}
while (*path++ != '\0');
@@ -777,6 +829,7 @@ load_pdump (int argc, char **argv)
{
const char *const suffix = ".pdmp";
int result;
+ char *emacs_executable = argv[0];
const char *strip_suffix =
#if defined DOS_NT || defined CYGWIN
".exe"
@@ -784,6 +837,13 @@ load_pdump (int argc, char **argv)
NULL
#endif
;
+ const char *argv0_base =
+#ifdef NS_SELF_CONTAINED
+ "Emacs"
+#else
+ "emacs"
+#endif
+ ;
/* TODO: maybe more thoroughly scrub process environment in order to
make this use case (loading a dump file in an unexeced emacs)
@@ -806,9 +866,19 @@ load_pdump (int argc, char **argv)
skip_args++;
}
+ /* Where's our executable? */
+ ptrdiff_t bufsize, exec_bufsize;
+ emacs_executable = load_pdump_find_executable (argv[0], &bufsize);
+ exec_bufsize = bufsize;
+
+ /* If we couldn't find our executable, go straight to looking for
+ the dump in the hardcoded location. */
+ if (!(emacs_executable && *emacs_executable))
+ goto hardcoded;
+
if (dump_file)
{
- result = pdumper_load (dump_file);
+ result = pdumper_load (dump_file, emacs_executable);
if (result != PDUMPER_LOAD_SUCCESS)
fatal ("could not load dump file \"%s\": %s",
@@ -822,65 +892,46 @@ load_pdump (int argc, char **argv)
so we can't use decode_env_path. We're working in whatever
encoding the system natively uses for filesystem access, so
there's no need for character set conversion. */
- ptrdiff_t bufsize;
- dump_file = load_pdump_find_executable (argv[0], &bufsize);
-
- /* If we couldn't find our executable, go straight to looking for
- the dump in the hardcoded location. */
- if (dump_file && *dump_file)
+ ptrdiff_t exenamelen = strlen (emacs_executable);
+ if (strip_suffix)
{
-#ifdef WINDOWSNT
- /* w32_my_exename resolves symlinks internally, so no need to
- call realpath. */
-#else
- char *real_exename = realpath (dump_file, NULL);
- if (!real_exename)
- fatal ("could not resolve realpath of \"%s\": %s",
- dump_file, strerror (errno));
- xfree (dump_file);
- dump_file = real_exename;
-#endif
- ptrdiff_t exenamelen = strlen (dump_file);
-#ifndef WINDOWSNT
- bufsize = exenamelen + 1;
-#endif
- if (strip_suffix)
- {
- ptrdiff_t strip_suffix_length = strlen (strip_suffix);
- ptrdiff_t prefix_length = exenamelen - strip_suffix_length;
- if (0 <= prefix_length
- && !memcmp (&dump_file[prefix_length], strip_suffix,
- strip_suffix_length))
- exenamelen = prefix_length;
- }
- ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
- if (bufsize < needed)
- dump_file = xpalloc (dump_file, &bufsize, needed - bufsize, -1, 1);
- strcpy (dump_file + exenamelen, suffix);
- result = pdumper_load (dump_file);
- if (result == PDUMPER_LOAD_SUCCESS)
- goto out;
-
- if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
- fatal ("could not load dump file \"%s\": %s",
- dump_file, dump_error_to_string (result));
+ ptrdiff_t strip_suffix_length = strlen (strip_suffix);
+ ptrdiff_t prefix_length = exenamelen - strip_suffix_length;
+ if (0 <= prefix_length
+ && !memcmp (&emacs_executable[prefix_length], strip_suffix,
+ strip_suffix_length))
+ exenamelen = prefix_length;
}
+ ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
+ dump_file = xpalloc (NULL, &bufsize, needed - bufsize, -1, 1);
+ memcpy (dump_file, emacs_executable, exenamelen);
+ strcpy (dump_file + exenamelen, suffix);
+ result = pdumper_load (dump_file, emacs_executable);
+ if (result == PDUMPER_LOAD_SUCCESS)
+ goto out;
+
+ if (result != PDUMPER_LOAD_FILE_NOT_FOUND)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+
+ hardcoded:
#ifdef WINDOWSNT
/* On MS-Windows, PATH_EXEC normally starts with a literal
"%emacs_dir%", so it will never work without some tweaking. */
path_exec = w32_relocate (path_exec);
+#elif defined (HAVE_NS)
+ path_exec = ns_relocate (path_exec);
#endif
/* Look for "emacs.pdmp" in PATH_EXEC. We hardcode "emacs" in
"emacs.pdmp" so that the Emacs binary still works if the user
copies and renames it. */
- const char *argv0_base = "emacs";
- ptrdiff_t needed = (strlen (path_exec)
- + 1
- + strlen (argv0_base)
- + strlen (suffix)
- + 1);
+ needed = (strlen (path_exec)
+ + 1
+ + strlen (argv0_base)
+ + strlen (suffix)
+ + 1);
if (bufsize < needed)
{
xfree (dump_file);
@@ -888,7 +939,23 @@ load_pdump (int argc, char **argv)
}
sprintf (dump_file, "%s%c%s%s",
path_exec, DIRECTORY_SEP, argv0_base, suffix);
- result = pdumper_load (dump_file);
+#if !defined (NS_SELF_CONTAINED)
+ /* Assume the Emacs binary lives in a sibling directory as set up by
+ the default installation configuration. */
+ const char *go_up = "../../../../bin/";
+ needed += (strip_suffix ? strlen (strip_suffix) : 0)
+ - strlen (suffix) + strlen (go_up);
+ if (exec_bufsize < needed)
+ {
+ xfree (emacs_executable);
+ emacs_executable = xpalloc (NULL, &exec_bufsize, needed - exec_bufsize,
+ -1, 1);
+ }
+ sprintf (emacs_executable, "%s%c%s%s%s",
+ path_exec, DIRECTORY_SEP, go_up, argv0_base,
+ strip_suffix ? strip_suffix : "");
+#endif
+ result = pdumper_load (dump_file, emacs_executable);
if (result == PDUMPER_LOAD_FILE_NOT_FOUND)
{
@@ -923,7 +990,7 @@ load_pdump (int argc, char **argv)
#endif
sprintf (dump_file, "%s%c%s%s",
path_exec, DIRECTORY_SEP, argv0_base, suffix);
- result = pdumper_load (dump_file);
+ result = pdumper_load (dump_file, emacs_executable);
}
if (result != PDUMPER_LOAD_SUCCESS)
@@ -935,9 +1002,185 @@ load_pdump (int argc, char **argv)
out:
xfree (dump_file);
+ xfree (emacs_executable);
}
#endif /* HAVE_PDUMPER */
+#if SECCOMP_USABLE
+
+/* Wrapper function for the `seccomp' system call on GNU/Linux. This
+ system call usually doesn't have a wrapper function. See the
+ manual page of `seccomp' for the signature. */
+
+static int
+emacs_seccomp (unsigned int operation, unsigned int flags, void *args)
+{
+#ifdef SYS_seccomp
+ return syscall (SYS_seccomp, operation, flags, args);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+/* Read SIZE bytes into BUFFER. Return the number of bytes read, or
+ -1 if reading failed altogether. */
+
+static ptrdiff_t
+read_full (int fd, void *buffer, ptrdiff_t size)
+{
+ eassert (0 <= fd);
+ eassert (buffer != NULL);
+ eassert (0 <= size);
+ enum
+ {
+ /* See MAX_RW_COUNT in sysdep.c. */
+#ifdef MAX_RW_COUNT
+ max_size = MAX_RW_COUNT
+#else
+ max_size = INT_MAX >> 18 << 18
+#endif
+ };
+ if (PTRDIFF_MAX < size || max_size < size)
+ {
+ errno = EFBIG;
+ return -1;
+ }
+ char *ptr = buffer;
+ ptrdiff_t read = 0;
+ while (size != 0)
+ {
+ ptrdiff_t n = emacs_read (fd, ptr, size);
+ if (n < 0)
+ return -1;
+ if (n == 0)
+ break; /* Avoid infinite loop on encountering EOF. */
+ eassert (n <= size);
+ size -= n;
+ ptr += n;
+ read += n;
+ }
+ return read;
+}
+
+/* Attempt to load Secure Computing filters from FILE. Return false
+ if that doesn't work for some reason. */
+
+static bool
+load_seccomp (const char *file)
+{
+ bool success = false;
+ void *buffer = NULL;
+ int fd
+ = emacs_open_noquit (file, O_RDONLY | O_CLOEXEC | O_BINARY, 0);
+ if (fd < 0)
+ {
+ emacs_perror ("open");
+ goto out;
+ }
+ struct stat stat;
+ if (fstat (fd, &stat) != 0)
+ {
+ emacs_perror ("fstat");
+ goto out;
+ }
+ if (! S_ISREG (stat.st_mode))
+ {
+ fprintf (stderr, "seccomp file %s is not regular\n", file);
+ goto out;
+ }
+ struct sock_fprog program;
+ if (stat.st_size <= 0 || SIZE_MAX <= stat.st_size
+ || PTRDIFF_MAX <= stat.st_size
+ || stat.st_size % sizeof *program.filter != 0)
+ {
+ fprintf (stderr, "seccomp filter %s has invalid size %ld\n",
+ file, (long) stat.st_size);
+ goto out;
+ }
+ size_t size = stat.st_size;
+ size_t count = size / sizeof *program.filter;
+ eassert (0 < count && count < SIZE_MAX);
+ if (USHRT_MAX < count)
+ {
+ fprintf (stderr, "seccomp filter %s is too big\n", file);
+ goto out;
+ }
+ /* Try reading one more byte to detect file size changes. */
+ buffer = malloc (size + 1);
+ if (buffer == NULL)
+ {
+ emacs_perror ("malloc");
+ goto out;
+ }
+ ptrdiff_t read = read_full (fd, buffer, size + 1);
+ if (read < 0)
+ {
+ emacs_perror ("read");
+ goto out;
+ }
+ eassert (read <= SIZE_MAX);
+ if (read != size)
+ {
+ fprintf (stderr,
+ "seccomp filter %s changed size while reading\n",
+ file);
+ goto out;
+ }
+ if (emacs_close (fd) != 0)
+ emacs_perror ("close"); /* not a fatal error */
+ fd = -1;
+ program.len = count;
+ program.filter = buffer;
+
+ /* See man page of `seccomp' why this is necessary. Note that we
+ intentionally don't check the return value: a parent process
+ might have made this call before, in which case it would fail;
+ or, if enabling privilege-restricting mode fails, the `seccomp'
+ syscall will fail anyway. */
+ prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ /* Install the filter. Make sure that potential other threads can't
+ escape it. */
+ if (emacs_seccomp (SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, &program)
+ != 0)
+ {
+ emacs_perror ("seccomp");
+ goto out;
+ }
+ success = true;
+
+ out:
+ if (0 <= fd)
+ emacs_close (fd);
+ free (buffer);
+ return success;
+}
+
+/* Load Secure Computing filter from file specified with the --seccomp
+ option. Exit if that fails. */
+
+static void
+maybe_load_seccomp (int argc, char **argv)
+{
+ int skip_args = 0;
+ char *file = NULL;
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-seccomp", "--seccomp", 9, &file,
+ &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL, &skip_args))
+ break;
+ ++skip_args;
+ }
+ if (file == NULL)
+ return;
+ if (! load_seccomp (file))
+ fatal ("cannot enable seccomp filter from %s", file);
+}
+
+#endif /* SECCOMP_USABLE */
+
int
main (int argc, char **argv)
{
@@ -945,6 +1188,13 @@ main (int argc, char **argv)
for pointers. */
void *stack_bottom_variable;
+ /* First, check whether we should apply a seccomp filter. This
+ should come at the very beginning to allow the filter to protect
+ the initialization phase. */
+#if SECCOMP_USABLE
+ maybe_load_seccomp (argc, argv);
+#endif
+
bool no_loadup = false;
char *junk = 0;
char *dname_arg = 0;
@@ -1607,6 +1857,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_json ();
#endif
+ if (!initialized)
+ syms_of_comp ();
+
no_loadup
= argmatch (argv, argc, "-nl", "--no-loadup", 6, NULL, &skip_args);
@@ -1778,7 +2031,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
/* Init buffer storage and default directory of main buffer. */
init_buffer ();
- init_callproc_1 (); /* Must precede init_cmdargs and init_sys_modes. */
+ /* Must precede init_cmdargs and init_sys_modes. */
+ init_callproc_1 ();
/* Must precede init_lread. */
init_cmdargs (argc, argv, skip_args, original_pwd);
@@ -1958,6 +2212,11 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
keys_of_keyboard ();
+
+#ifdef HAVE_NATIVE_COMP
+ /* Must be after the last defsubr has run. */
+ hash_native_abi ();
+#endif
}
else
{
@@ -2047,6 +2306,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
Vdump_mode = build_string (dump_mode);
/* Enter editor command loop. This never returns. */
+ set_initial_minibuffer_mode ();
Frecursive_edit ();
eassume (false);
}
@@ -2133,12 +2393,15 @@ static const struct standard_args standard_args[] =
{ "-color", "--color", 5, 0},
{ "-no-splash", "--no-splash", 3, 0 },
{ "-no-desktop", "--no-desktop", 3, 0 },
- /* The following two must be just above the file-name args, to get
+ /* The following three must be just above the file-name args, to get
them out of our way, but without mixing them with file names. */
{ "-temacs", "--temacs", 1, 1 },
#ifdef HAVE_PDUMPER
{ "-dump-file", "--dump-file", 1, 1 },
#endif
+#if SECCOMP_USABLE
+ { "-seccomp", "--seccomp", 1, 1 },
+#endif
#ifdef HAVE_NS
{ "-NSAutoLaunch", 0, 5, 1 },
{ "-NXAutoLaunch", 0, 5, 1 },
@@ -2393,6 +2656,10 @@ all of which are called before Emacs is actually killed. */
unlink (SSDATA (listfile));
}
+#ifdef HAVE_NATIVE_COMP
+ eln_load_path_final_clean_up ();
+#endif
+
if (FIXNUMP (arg))
exit_code = (XFIXNUM (arg) < 0
? XFIXNUM (arg) | INT_MIN
@@ -2705,7 +2972,11 @@ decode_env_path (const char *evarname, const char *defalt, bool empty)
path = 0;
if (!path)
{
+#ifdef NS_SELF_CONTAINED
+ path = ns_relocate (defalt);
+#else
path = defalt;
+#endif
#ifdef WINDOWSNT
defaulted = 1;
#endif
@@ -3043,7 +3314,18 @@ because they do not depend on external libraries and are always available.
Also note that this is not a generic facility for accessing external
libraries; only those already known by Emacs will be loaded. */);
+#ifdef WINDOWSNT
+ /* FIXME: We may need to load libgccjit when dumping before
+ term/w32-win.el defines `dynamic-library-alist`. This will fail
+ if that variable is empty, so add libgccjit-0.dll to it. */
+ if (will_dump_p ())
+ Vdynamic_library_alist = list1 (list2 (Qgccjit,
+ build_string ("libgccjit-0.dll")));
+ else
+ Vdynamic_library_alist = Qnil;
+#else
Vdynamic_library_alist = Qnil;
+#endif
Fput (intern_c_string ("dynamic-library-alist"), Qrisky_local_variable, Qt);
#ifdef WINDOWSNT