From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.bugs Subject: bug#45198: 28.0.50; Sandbox mode Date: Sat, 12 Dec 2020 23:25:27 -0500 Message-ID: References: <83mtyierfs.fsf@gnu.org> <83ft4ae63m.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="31666"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: bzg@gnu.org, 45198@debbugs.gnu.org, joaotavora@gmail.com To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Dec 13 05:26:12 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1koIxX-00086v-QZ for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 13 Dec 2020 05:26:12 +0100 Original-Received: from localhost ([::1]:38434 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1koIxW-0006rW-NL for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 12 Dec 2020 23:26:10 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:38902) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1koIxQ-0006rP-4Y for bug-gnu-emacs@gnu.org; Sat, 12 Dec 2020 23:26:04 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:35709) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1koIxP-0002ED-Sc for bug-gnu-emacs@gnu.org; Sat, 12 Dec 2020 23:26:03 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1koIxN-0001mR-PJ for bug-gnu-emacs@gnu.org; Sat, 12 Dec 2020 23:26:01 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Stefan Monnier Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 13 Dec 2020 04:26:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 45198 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 45198-submit@debbugs.gnu.org id=B45198.16078335396803 (code B ref 45198); Sun, 13 Dec 2020 04:26:01 +0000 Original-Received: (at 45198) by debbugs.gnu.org; 13 Dec 2020 04:25:39 +0000 Original-Received: from localhost ([127.0.0.1]:47255 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1koIx1-0001ld-1w for submit@debbugs.gnu.org; Sat, 12 Dec 2020 23:25:39 -0500 Original-Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:21198) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1koIwz-0001lQ-GP for 45198@debbugs.gnu.org; Sat, 12 Dec 2020 23:25:38 -0500 Original-Received: from pmg3.iro.umontreal.ca (localhost [127.0.0.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id E7846440F88; Sat, 12 Dec 2020 23:25:31 -0500 (EST) Original-Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1]) by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id 68889440F93; Sat, 12 Dec 2020 23:25:29 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca; s=mail; t=1607833529; bh=TmfW7nqEXzYwEhYLpo0mvNCF/UldCIaea6/c+w0ADAc=; h=From:To:Cc:Subject:References:Date:In-Reply-To:From; b=IZpFeYPCG8z9fvdGpziXv+hsGqHeCFUKSifFz1EQg0MaJ3gGymW5mgYCAk+gEKT0l 2aGrdD/JkQbJcuXj/+G2aGf6HkRV/u1/zTNISZZYUOmugif/WmKRu8hqimTxlqapR/ xM1XR74Hc00mHBNuBB8U4tTNZR7pE0YTKJ/MZctO0X7NkV3znNr6rIyxKGMizkIELf AjV3rk2aDQq4qPn49xJ4Z0B5VrPLLTNrKqfXgaCKiJz1Y+2Tr5EF/cfnuV4tDXuM1I loCGsttFHD7xQKdnxtcNoCDeugGUoeQBKiGNF06f5FB+S7ADAl3WOmrjf258enss11 U3XHKnuqbq7iw== Original-Received: from alfajor (69-165-136-52.dsl.teksavvy.com [69.165.136.52]) by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 28618120201; Sat, 12 Dec 2020 23:25:29 -0500 (EST) In-Reply-To: <83ft4ae63m.fsf@gnu.org> (Eli Zaretskii's message of "Sun, 13 Dec 2020 05:29:33 +0200") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:195930 Archived-At: >> > 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)