bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#45198: 28.0.50; Sandbox mode


From: Stefan Monnier
Subject: bug#45198: 28.0.50; Sandbox mode
Date: Sat, 12 Dec 2020 23:25:27 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

>> > You cannot usefully call error from redisplay.
>> Hmm... but this is at the entrance to redisplay, so I though it should
>> still be safe at that point.  If it's a problem we can replace the above
>> with
>>     if (emacs_is_sandboxed)
>>       return;
> Yes, I think this is what we should do in this case.

With the change I just installed into `master`, I can now get
`elisp-flymake-byte-compile` to use sandboxing successfully with the
revised patch below.

Besides the above change, I made the same change in `Fdo_auto_save`
(i.e. `do-auto-save` was made to just silently do nothing instead of
signaling an error since it seemed to be too much trouble to change its
callers to avoid calling it when sandboxed).

I'm still worried that there remain wide open security holes, tho.


        Stefan


diff --git a/etc/NEWS b/etc/NEWS
index 909473f4e7..ee51495ca0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -85,6 +85,16 @@ useful on systems such as FreeBSD which ships only with 
"etc/termcap".
 
 * Changes in Emacs 28.1
 
+** Sandbox mode to run untrusted code.
+The new function 'sandbox-enter' puts Emacs mode into a state in which
+it can (hopefully) run untrusted code safely.  This mode is restricted such
+that Emacs cannot exit the mode, nor can it write to files or launch
+new processes.  It can still read arbitrary files and communicate over
+pre-existing communication links.  This can only be used in batch mode.
+The expected use is to launch a new batch Emacs session, put it
+into sandbox mode, then run the untrusted code, send back the
+result via 'message', and then exit.
+
 ** Minibuffer scrolling is now conservative by default.
 This is controlled by the new variable 'scroll-minibuffer-conservatively'.
 
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index b7e0c45228..df839d20b9 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1836,6 +1836,7 @@ elisp-flymake--batch-compile-for-flymake
             (push (list string position fill level)
                   collected)
             t)))
+    (sandbox-enter)
     (unwind-protect
         (byte-compile-file file)
       (ignore-errors
diff --git a/src/emacs-module.c b/src/emacs-module.c
index b7cd835c83..79ff2b6ce6 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -1091,6 +1091,7 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
        doc: /* Load module FILE.  */)
   (Lisp_Object file)
 {
+  ensure_no_sandbox ();
   dynlib_handle_ptr handle;
   emacs_init_function module_init;
   void *gpl_sym;
@@ -1151,6 +1152,7 @@ DEFUN ("module-load", Fmodule_load, Smodule_load, 1, 1, 0,
 Lisp_Object
 funcall_module (Lisp_Object function, ptrdiff_t nargs, Lisp_Object *arglist)
 {
+  ensure_no_sandbox ();
   const struct Lisp_Module_Function *func = XMODULE_FUNCTION (function);
   eassume (0 <= func->min_arity);
   if (! (func->min_arity <= nargs
diff --git a/src/emacs.c b/src/emacs.c
index 2a32083ba1..0df70ae43e 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -139,6 +139,8 @@ #define MAIN_PROGRAM
 struct gflags gflags;
 bool initialized;
 
+bool emacs_is_sandboxed;
+
 /* If true, Emacs should not attempt to use a window-specific code,
    but instead should use the virtual terminal under which it was started.  */
 bool inhibit_window_system;
@@ -2886,6 +2888,30 @@ DEFUN ("daemon-initialized", Fdaemon_initialized, 
Sdaemon_initialized, 0, 0, 0,
   return Qt;
 }
 
+DEFUN ("sandbox-enter", Fsandbox_enter, Ssandbox_enter, 0, 0, 0,
+  doc: /* Put Emacs in sandboxed mode.
+After calling this function, Emacs cannot have externally visible
+side effects any more.  For example, it will not be able to write to files,
+create new processes, open new displays, or call functionality provided by
+modules, ...
+It can still send data to pre-existing processes, on the other hand.
+There is no mechanism to exit sandboxed mode.  */)
+  (void)
+{
+  if (!noninteractive)
+    /* We could allow a sandbox in interactive sessions, but it seems
+       useless, so we'll assume that it was a pilot-error.  */
+    error ("Can't enter sandbox in interactive session");
+  emacs_is_sandboxed = true;
+  return Qnil;
+}
+
+void ensure_no_sandbox (void)
+{
+  if (emacs_is_sandboxed)
+    error ("Sandboxed");
+}
+
 void
 syms_of_emacs (void)
 {
@@ -2906,6 +2932,7 @@ syms_of_emacs (void)
   defsubr (&Sinvocation_directory);
   defsubr (&Sdaemonp);
   defsubr (&Sdaemon_initialized);
+  defsubr (&Ssandbox_enter);
 
   DEFVAR_LISP ("command-line-args", Vcommand_line_args,
               doc: /* Args passed by shell to Emacs, as a list of strings.
diff --git a/src/fileio.c b/src/fileio.c
index 702c143828..509341351d 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -689,6 +689,7 @@ DEFUN ("make-temp-file-internal", Fmake_temp_file_internal,
   (Lisp_Object prefix, Lisp_Object dir_flag, Lisp_Object suffix,
    Lisp_Object text)
 {
+  ensure_no_sandbox ();
   CHECK_STRING (prefix);
   CHECK_STRING (suffix);
   Lisp_Object encoded_prefix = ENCODE_FILE (prefix);
@@ -2043,6 +2044,7 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6,
    Lisp_Object keep_time, Lisp_Object preserve_uid_gid,
    Lisp_Object preserve_permissions)
 {
+  ensure_no_sandbox ();
   Lisp_Object handler;
   ptrdiff_t count = SPECPDL_INDEX ();
   Lisp_Object encoded_file, encoded_newname;
@@ -2301,6 +2303,7 @@ DEFUN ("make-directory-internal", 
Fmake_directory_internal,
        doc: /* Create a new directory named DIRECTORY.  */)
   (Lisp_Object directory)
 {
+  ensure_no_sandbox ();
   const char *dir;
   Lisp_Object handler;
   Lisp_Object encoded_dir;
@@ -2327,6 +2330,7 @@ DEFUN ("delete-directory-internal", 
Fdelete_directory_internal,
        doc: /* Delete the directory named DIRECTORY.  Does not follow 
symlinks.  */)
   (Lisp_Object directory)
 {
+  ensure_no_sandbox ();
   const char *dir;
   Lisp_Object encoded_dir;
 
@@ -2356,6 +2360,7 @@ DEFUN ("delete-file", Fdelete_file, Sdelete_file, 1, 2,
 With a prefix argument, TRASH is nil.  */)
   (Lisp_Object filename, Lisp_Object trash)
 {
+  ensure_no_sandbox ();
   Lisp_Object handler;
   Lisp_Object encoded_file;
 
@@ -2494,6 +2499,7 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3,
 This is what happens in interactive use with M-x.  */)
   (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists)
 {
+  ensure_no_sandbox ();
   Lisp_Object handler;
   Lisp_Object encoded_file, encoded_newname;
 
@@ -2614,6 +2620,7 @@ DEFUN ("add-name-to-file", Fadd_name_to_file, 
Sadd_name_to_file, 2, 3,
 This is what happens in interactive use with M-x.  */)
   (Lisp_Object file, Lisp_Object newname, Lisp_Object ok_if_already_exists)
 {
+  ensure_no_sandbox ();
   Lisp_Object handler;
   Lisp_Object encoded_file, encoded_newname;
 
@@ -2667,6 +2674,7 @@ DEFUN ("make-symbolic-link", Fmake_symbolic_link, 
Smake_symbolic_link, 2, 3,
 This happens for interactive use with M-x.  */)
   (Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists)
 {
+  ensure_no_sandbox ();
   Lisp_Object handler;
   Lisp_Object encoded_target, encoded_linkname;
 
@@ -3176,6 +3184,7 @@ DEFUN ("set-file-selinux-context", 
Fset_file_selinux_context,
 or if Emacs was not compiled with SELinux support.  */)
   (Lisp_Object filename, Lisp_Object context)
 {
+  ensure_no_sandbox ();
   Lisp_Object absname;
   Lisp_Object handler;
 #if HAVE_LIBSELINUX
@@ -3307,6 +3316,7 @@ DEFUN ("set-file-acl", Fset_file_acl, Sset_file_acl,
 support.  */)
   (Lisp_Object filename, Lisp_Object acl_string)
 {
+  ensure_no_sandbox ();
 #if USE_ACL
   Lisp_Object absname;
   Lisp_Object handler;
@@ -3392,6 +3402,7 @@ DEFUN ("set-file-modes", Fset_file_modes, 
Sset_file_modes, 2, 3,
 symbolic notation, like the `chmod' command from GNU Coreutils.  */)
   (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
 {
+  ensure_no_sandbox ();
   CHECK_FIXNUM (mode);
   int nofollow = symlink_nofollow_flag (flag);
   Lisp_Object absname = Fexpand_file_name (filename,
@@ -3458,6 +3469,7 @@ DEFUN ("set-file-times", Fset_file_times, 
Sset_file_times, 1, 3, 0,
 TIMESTAMP is in the format of `current-time'. */)
   (Lisp_Object filename, Lisp_Object timestamp, Lisp_Object flag)
 {
+  ensure_no_sandbox ();
   int nofollow = symlink_nofollow_flag (flag);
 
   struct timespec ts[2];
@@ -5039,6 +5051,7 @@ DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 7,
   (Lisp_Object start, Lisp_Object end, Lisp_Object filename, Lisp_Object 
append,
    Lisp_Object visit, Lisp_Object lockname, Lisp_Object mustbenew)
 {
+  ensure_no_sandbox ();
   return write_region (start, end, filename, append, visit, lockname, 
mustbenew,
                       -1);
 }
@@ -5837,6 +5850,8 @@ DEFUN ("do-auto-save", Fdo_auto_save, Sdo_auto_save, 0, 
2, "",
 A non-nil CURRENT-ONLY argument means save only current buffer.  */)
   (Lisp_Object no_message, Lisp_Object current_only)
 {
+  if (emacs_is_sandboxed)
+    return Qnil;
   struct buffer *old = current_buffer, *b;
   Lisp_Object tail, buf, hook;
   bool auto_saved = 0;
diff --git a/src/lisp.h b/src/lisp.h
index e83304462f..107a2d9f03 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -603,6 +603,10 @@ #define ENUM_BF(TYPE) enum TYPE
    subsequent starts.  */
 extern bool initialized;
 
+/* Whether we've been neutralized.  */
+extern bool emacs_is_sandboxed;
+extern void ensure_no_sandbox (void);
+
 extern struct gflags
 {
   /* True means this Emacs instance was born to dump.  */
diff --git a/src/process.c b/src/process.c
index 4fe8ac7fc0..68460868c4 100644
--- a/src/process.c
+++ b/src/process.c
@@ -862,6 +862,8 @@ allocate_pty (char pty_name[PTY_NAME_SIZE])
 static struct Lisp_Process *
 allocate_process (void)
 {
+  ensure_no_sandbox ();
+
   return ALLOCATE_ZEROED_PSEUDOVECTOR (struct Lisp_Process, thread,
                                       PVEC_PROCESS);
 }
diff --git a/src/xdisp.c b/src/xdisp.c
index 96dd4fade2..04972c248e 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -15442,6 +15442,11 @@ #define RESUME_POLLING                                 
\
 static void
 redisplay_internal (void)
 {
+  /* Not sure if it's important to avoid redisplay inside a sandbox,
+     but it seems safer to avoid risking introducing security holes via
+     image libraries and such.  */
+  if (emacs_is_sandboxed)
+    return;
   struct window *w = XWINDOW (selected_window);
   struct window *sw;
   struct frame *fr;
diff --git a/src/xterm.c b/src/xterm.c
index 3de0d2e73c..e8a4d2f29d 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -12698,6 +12698,7 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   xcb_connection_t *xcb_conn;
 #endif
 
+  ensure_no_sandbox ();
   block_input ();
 
   if (!x_initialized)






reply via email to

[Prev in Thread] Current Thread [Next in Thread]