diff options
| author | Eli Zaretskii | 2017-09-16 12:45:24 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2017-09-16 12:45:24 +0300 |
| commit | a103dbe36022cd2454eaeed96def1c777c049762 (patch) | |
| tree | a3455f3e0ef50b9bbc1085c3199b4434851ebf35 | |
| parent | 6d6dc246f93486fc8370399b6e1af8a17f371e4f (diff) | |
| download | emacs-a103dbe36022cd2454eaeed96def1c777c049762.tar.gz emacs-a103dbe36022cd2454eaeed96def1c777c049762.zip | |
Disable execution of unsafe Lisp by Enriched Text mode
* src/xdisp.c (handle_display_spec): If the display property is
wrapped in 'disable-eval' form, disable Lisp evaluation while
processing this property.
(handle_single_display_spec): Accept new argument ENABLE_EVAL_P.
If that argument is false, don't evaluate Lisp while processing
display properties.
* lisp/textmodes/enriched.el
(enriched-allow-eval-in-display-props): New defcustom.
(enriched-decode-display-prop): If
enriched-allow-eval-in-display-props is nil, wrap the display
property with 'disable-eval' to disable Lisp evaluation when the
display property is processed for display. (Bug#28350)
* lisp/gnus/mm-view.el (mm-inline-text): Re-enable processing of
enriched text.
* doc/lispref/display.texi (Display Property): Document the
'disable-eval' wrapping of 'display' properties.
* doc/emacs/text.texi (Enriched Properties): Document
'enriched-allow-eval-in-display-props'.
* etc/NEWS: Describe the security issues with Enriched Text mode
and their solution.
| -rw-r--r-- | doc/emacs/text.texi | 17 | ||||
| -rw-r--r-- | doc/lispref/display.texi | 11 | ||||
| -rw-r--r-- | etc/NEWS | 22 | ||||
| -rw-r--r-- | lisp/gnus/mm-view.el | 10 | ||||
| -rw-r--r-- | lisp/textmodes/enriched.el | 23 | ||||
| -rw-r--r-- | src/xdisp.c | 37 |
6 files changed, 101 insertions, 19 deletions
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 3b54aa82631..496b43ce1e3 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi | |||
| @@ -2398,6 +2398,23 @@ these special properties from the text in the region. | |||
| 2398 | 2398 | ||
| 2399 | The @code{invisible} and @code{intangible} properties are not saved. | 2399 | The @code{invisible} and @code{intangible} properties are not saved. |
| 2400 | 2400 | ||
| 2401 | @vindex enriched-allow-eval-in-display-props | ||
| 2402 | @cindex security, when displaying enriched text | ||
| 2403 | Enriched mode also supports saving and restoring @code{display} | ||
| 2404 | properties (@pxref{Display Property,,,elisp, the Emacs Lisp Reference | ||
| 2405 | Manual}), which affect how text is displayed on the screen, and also | ||
| 2406 | allow displaying images and strings that come from sources other than | ||
| 2407 | buffer text. The @code{display} properties also support execution of | ||
| 2408 | arbitrary Lisp forms as part of processing the property for display, | ||
| 2409 | thus providing a means to dynamically tailor the display to some | ||
| 2410 | conditions that can only be known at display time. Since execution of | ||
| 2411 | arbitrary Lisp opens Emacs to potential attacks, especially when the | ||
| 2412 | source of enriched text is outside of Emacs or even outside of your | ||
| 2413 | system (e.g., if it was received in an email message), such execution | ||
| 2414 | is by default disabled in Enriched mode. You can enable it by | ||
| 2415 | customizing the variable @code{enriched-allow-eval-in-display-props} | ||
| 2416 | to a non-@code{nil} value. | ||
| 2417 | |||
| 2401 | @node Text Based Tables | 2418 | @node Text Based Tables |
| 2402 | @section Editing Text-based Tables | 2419 | @section Editing Text-based Tables |
| 2403 | @cindex table mode | 2420 | @cindex table mode |
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 1dbc0bbb5bf..3dae984f339 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi | |||
| @@ -4486,6 +4486,17 @@ for the @code{display} property, only one of the values takes effect, | |||
| 4486 | following the rules of @code{get-char-property}. @xref{Examining | 4486 | following the rules of @code{get-char-property}. @xref{Examining |
| 4487 | Properties}. | 4487 | Properties}. |
| 4488 | 4488 | ||
| 4489 | @cindex display property, unsafe evaluation | ||
| 4490 | @cindex security, and display specifications | ||
| 4491 | Some of the display specifications allow inclusion of Lisp forms, | ||
| 4492 | which are evaluated at display time. This could be unsafe in certain | ||
| 4493 | situations, e.g., when the display specification was generated by some | ||
| 4494 | external program/agent. Wrapping a display specification in a list | ||
| 4495 | that begins with the special symbol @code{disable-eval}, as in | ||
| 4496 | @w{@code{('disable-eval @var{spec})}}, will disable evaluation of any | ||
| 4497 | Lisp in @var{spec}, while still supporting all the other display | ||
| 4498 | property features. | ||
| 4499 | |||
| 4489 | The rest of this section describes several kinds of | 4500 | The rest of this section describes several kinds of |
| 4490 | display specifications and what they mean. | 4501 | display specifications and what they mean. |
| 4491 | 4502 | ||
| @@ -117,6 +117,28 @@ The effect is similar to that of "toolBar" resource on the tool bar. | |||
| 117 | 117 | ||
| 118 | * Changes in Emacs 26.1 | 118 | * Changes in Emacs 26.1 |
| 119 | 119 | ||
| 120 | ** Security vulnerability related to Enriched Text mode is removed. | ||
| 121 | |||
| 122 | +++ | ||
| 123 | *** Enriched Text mode does not evaluate Lisp in 'display' properties. | ||
| 124 | This feature allows saving 'display' properties as part of text. | ||
| 125 | Emacs 'display' properties support evaluation of arbitrary Lisp forms | ||
| 126 | as part of processing the property for display, so displaying Enriched | ||
| 127 | Text could be vulnerable to executing arbitrary malicious Lisp code | ||
| 128 | included in the text (e.g., sent as part of an email message). | ||
| 129 | Therefore, execution of arbitrary Lisp forms in 'display' properties | ||
| 130 | decoded by Enriched Text mode is now disabled by default. Customize | ||
| 131 | the new option 'enriched-allow-eval-in-display-props' to a non-nil | ||
| 132 | value to allow Lisp evaluation in decoded 'display' properties. | ||
| 133 | |||
| 134 | This vulnerability was introduced in Emacs 21.1. To work around that | ||
| 135 | in Emacs versions before 25.3, append the following to your ~/.emacs | ||
| 136 | init file: | ||
| 137 | |||
| 138 | (eval-after-load "enriched" | ||
| 139 | '(defun enriched-decode-display-prop (start end &optional param) | ||
| 140 | (list start end))) | ||
| 141 | |||
| 120 | +++ | 142 | +++ |
| 121 | ** Functions in 'write-contents-functions' can fully short-circuit the | 143 | ** Functions in 'write-contents-functions' can fully short-circuit the |
| 122 | 'save-buffer' process. Previously, saving a buffer that was not | 144 | 'save-buffer' process. Previously, saving a buffer that was not |
diff --git a/lisp/gnus/mm-view.el b/lisp/gnus/mm-view.el index 86e217131ac..d7a41b84930 100644 --- a/lisp/gnus/mm-view.el +++ b/lisp/gnus/mm-view.el | |||
| @@ -362,12 +362,10 @@ | |||
| 362 | (goto-char (point-max)))) | 362 | (goto-char (point-max)))) |
| 363 | (save-restriction | 363 | (save-restriction |
| 364 | (narrow-to-region b (point)) | 364 | (narrow-to-region b (point)) |
| 365 | ;; Disabled in Emacs 25.3 to avoid execution of arbitrary Lisp | 365 | (when (member type '("enriched" "richtext")) |
| 366 | ;; forms in display properties supported by enriched.el. | 366 | (set-text-properties (point-min) (point-max) nil) |
| 367 | ;; (when (member type '("enriched" "richtext")) | 367 | (ignore-errors |
| 368 | ;; (set-text-properties (point-min) (point-max) nil) | 368 | (enriched-decode (point-min) (point-max)))) |
| 369 | ;; (ignore-errors | ||
| 370 | ;; (enriched-decode (point-min) (point-max)))) | ||
| 371 | (mm-handle-set-undisplayer | 369 | (mm-handle-set-undisplayer |
| 372 | handle | 370 | handle |
| 373 | `(lambda () | 371 | `(lambda () |
diff --git a/lisp/textmodes/enriched.el b/lisp/textmodes/enriched.el index d90c207575b..be5cd6b7310 100644 --- a/lisp/textmodes/enriched.el +++ b/lisp/textmodes/enriched.el | |||
| @@ -147,6 +147,22 @@ them and their old values to `enriched-old-bindings'." | |||
| 147 | :type 'hook | 147 | :type 'hook |
| 148 | :group 'enriched) | 148 | :group 'enriched) |
| 149 | 149 | ||
| 150 | (defcustom enriched-allow-eval-in-display-props nil | ||
| 151 | "If non-nil allow to evaluate arbitrary forms in display properties. | ||
| 152 | |||
| 153 | Enriched mode recognizes display properties of text stored using | ||
| 154 | an extension command to the text/enriched format, \"x-display\". | ||
| 155 | These properties must not, by default, include evaluation of | ||
| 156 | Lisp forms, otherwise they are not applied. Customize this option | ||
| 157 | to t to turn off this safety feature, and allow Enriched mode to | ||
| 158 | apply display properties which evaluate arbitrary Lisp forms. | ||
| 159 | Note, however, that applying unsafe display properties could | ||
| 160 | execute malicious Lisp code, if that code came from an external source." | ||
| 161 | :risky t | ||
| 162 | :type 'boolean | ||
| 163 | :version "26.1" | ||
| 164 | :group 'enriched) | ||
| 165 | |||
| 150 | (defvar enriched-old-bindings nil | 166 | (defvar enriched-old-bindings nil |
| 151 | "Store old variable values that we change when entering mode. | 167 | "Store old variable values that we change when entering mode. |
| 152 | The value is a list of \(VAR VALUE VAR VALUE...).") | 168 | The value is a list of \(VAR VALUE VAR VALUE...).") |
| @@ -503,9 +519,8 @@ the range of text to assign text property SYMBOL with value VALUE." | |||
| 503 | (error nil))))) | 519 | (error nil))))) |
| 504 | (unless prop | 520 | (unless prop |
| 505 | (message "Warning: invalid <x-display> parameter %s" param)) | 521 | (message "Warning: invalid <x-display> parameter %s" param)) |
| 506 | ;; Disabled in Emacs 25.3 to avoid execution of arbitrary Lisp | 522 | (if enriched-allow-eval-in-display-props |
| 507 | ;; forms in display properties stored within enriched text. | 523 | (list start end 'display prop) |
| 508 | ;; (list start end 'display prop))) | 524 | (list start end 'display (list 'disable-eval prop))))) |
| 509 | (list start end))) | ||
| 510 | 525 | ||
| 511 | ;;; enriched.el ends here | 526 | ;;; enriched.el ends here |
diff --git a/src/xdisp.c b/src/xdisp.c index 8ca9037a00d..dc5dbb05762 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -876,9 +876,9 @@ static int face_before_or_after_it_pos (struct it *, bool); | |||
| 876 | static ptrdiff_t next_overlay_change (ptrdiff_t); | 876 | static ptrdiff_t next_overlay_change (ptrdiff_t); |
| 877 | static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object, | 877 | static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object, |
| 878 | Lisp_Object, struct text_pos *, ptrdiff_t, bool); | 878 | Lisp_Object, struct text_pos *, ptrdiff_t, bool); |
| 879 | static int handle_single_display_spec (struct it *, Lisp_Object, | 879 | static int handle_single_display_spec (struct it *, Lisp_Object, Lisp_Object, |
| 880 | Lisp_Object, Lisp_Object, | 880 | Lisp_Object, struct text_pos *, |
| 881 | struct text_pos *, ptrdiff_t, int, bool); | 881 | ptrdiff_t, int, bool, bool); |
| 882 | static int underlying_face_id (struct it *); | 882 | static int underlying_face_id (struct it *); |
| 883 | 883 | ||
| 884 | #define face_before_it_pos(IT) face_before_or_after_it_pos (IT, true) | 884 | #define face_before_it_pos(IT) face_before_or_after_it_pos (IT, true) |
| @@ -4748,6 +4748,14 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4748 | ptrdiff_t bufpos, bool frame_window_p) | 4748 | ptrdiff_t bufpos, bool frame_window_p) |
| 4749 | { | 4749 | { |
| 4750 | int replacing = 0; | 4750 | int replacing = 0; |
| 4751 | bool enable_eval = true; | ||
| 4752 | |||
| 4753 | /* Support (disable-eval PROP) which is used by enriched.el. */ | ||
| 4754 | if (CONSP (spec) && EQ (XCAR (spec), Qdisable_eval)) | ||
| 4755 | { | ||
| 4756 | enable_eval = false; | ||
| 4757 | spec = XCAR (XCDR (spec)); | ||
| 4758 | } | ||
| 4751 | 4759 | ||
| 4752 | if (CONSP (spec) | 4760 | if (CONSP (spec) |
| 4753 | /* Simple specifications. */ | 4761 | /* Simple specifications. */ |
| @@ -4771,7 +4779,8 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4771 | { | 4779 | { |
| 4772 | int rv = handle_single_display_spec (it, XCAR (spec), object, | 4780 | int rv = handle_single_display_spec (it, XCAR (spec), object, |
| 4773 | overlay, position, bufpos, | 4781 | overlay, position, bufpos, |
| 4774 | replacing, frame_window_p); | 4782 | replacing, frame_window_p, |
| 4783 | enable_eval); | ||
| 4775 | if (rv != 0) | 4784 | if (rv != 0) |
| 4776 | { | 4785 | { |
| 4777 | replacing = rv; | 4786 | replacing = rv; |
| @@ -4789,7 +4798,8 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4789 | { | 4798 | { |
| 4790 | int rv = handle_single_display_spec (it, AREF (spec, i), object, | 4799 | int rv = handle_single_display_spec (it, AREF (spec, i), object, |
| 4791 | overlay, position, bufpos, | 4800 | overlay, position, bufpos, |
| 4792 | replacing, frame_window_p); | 4801 | replacing, frame_window_p, |
| 4802 | enable_eval); | ||
| 4793 | if (rv != 0) | 4803 | if (rv != 0) |
| 4794 | { | 4804 | { |
| 4795 | replacing = rv; | 4805 | replacing = rv; |
| @@ -4802,7 +4812,8 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4802 | } | 4812 | } |
| 4803 | else | 4813 | else |
| 4804 | replacing = handle_single_display_spec (it, spec, object, overlay, position, | 4814 | replacing = handle_single_display_spec (it, spec, object, overlay, position, |
| 4805 | bufpos, 0, frame_window_p); | 4815 | bufpos, 0, frame_window_p, |
| 4816 | enable_eval); | ||
| 4806 | return replacing; | 4817 | return replacing; |
| 4807 | } | 4818 | } |
| 4808 | 4819 | ||
| @@ -4847,6 +4858,8 @@ display_prop_end (struct it *it, Lisp_Object object, struct text_pos start_pos) | |||
| 4847 | don't set up IT. In that case, FRAME_WINDOW_P means SPEC | 4858 | don't set up IT. In that case, FRAME_WINDOW_P means SPEC |
| 4848 | is intended to be displayed in a window on a GUI frame. | 4859 | is intended to be displayed in a window on a GUI frame. |
| 4849 | 4860 | ||
| 4861 | Enable evaluation of Lisp forms only if ENABLE_EVAL_P is true. | ||
| 4862 | |||
| 4850 | Value is non-zero if something was found which replaces the display | 4863 | Value is non-zero if something was found which replaces the display |
| 4851 | of buffer or string text. */ | 4864 | of buffer or string text. */ |
| 4852 | 4865 | ||
| @@ -4854,7 +4867,7 @@ static int | |||
| 4854 | handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | 4867 | handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, |
| 4855 | Lisp_Object overlay, struct text_pos *position, | 4868 | Lisp_Object overlay, struct text_pos *position, |
| 4856 | ptrdiff_t bufpos, int display_replaced, | 4869 | ptrdiff_t bufpos, int display_replaced, |
| 4857 | bool frame_window_p) | 4870 | bool frame_window_p, bool enable_eval_p) |
| 4858 | { | 4871 | { |
| 4859 | Lisp_Object form; | 4872 | Lisp_Object form; |
| 4860 | Lisp_Object location, value; | 4873 | Lisp_Object location, value; |
| @@ -4872,6 +4885,8 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4872 | spec = XCDR (spec); | 4885 | spec = XCDR (spec); |
| 4873 | } | 4886 | } |
| 4874 | 4887 | ||
| 4888 | if (!NILP (form) && !EQ (form, Qt) && !enable_eval_p) | ||
| 4889 | form = Qnil; | ||
| 4875 | if (!NILP (form) && !EQ (form, Qt)) | 4890 | if (!NILP (form) && !EQ (form, Qt)) |
| 4876 | { | 4891 | { |
| 4877 | ptrdiff_t count = SPECPDL_INDEX (); | 4892 | ptrdiff_t count = SPECPDL_INDEX (); |
| @@ -4920,7 +4935,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4920 | steps = - steps; | 4935 | steps = - steps; |
| 4921 | it->face_id = smaller_face (it->f, it->face_id, steps); | 4936 | it->face_id = smaller_face (it->f, it->face_id, steps); |
| 4922 | } | 4937 | } |
| 4923 | else if (FUNCTIONP (it->font_height)) | 4938 | else if (FUNCTIONP (it->font_height) && enable_eval_p) |
| 4924 | { | 4939 | { |
| 4925 | /* Call function with current height as argument. | 4940 | /* Call function with current height as argument. |
| 4926 | Value is the new height. */ | 4941 | Value is the new height. */ |
| @@ -4941,7 +4956,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, | |||
| 4941 | new_height = (XFLOATINT (it->font_height) | 4956 | new_height = (XFLOATINT (it->font_height) |
| 4942 | * XINT (f->lface[LFACE_HEIGHT_INDEX])); | 4957 | * XINT (f->lface[LFACE_HEIGHT_INDEX])); |
| 4943 | } | 4958 | } |
| 4944 | else | 4959 | else if (enable_eval_p) |
| 4945 | { | 4960 | { |
| 4946 | /* Evaluate IT->font_height with `height' bound to the | 4961 | /* Evaluate IT->font_height with `height' bound to the |
| 4947 | current specified height to get the new height. */ | 4962 | current specified height to get the new height. */ |
| @@ -32204,6 +32219,10 @@ They are still logged to the *Messages* buffer. */); | |||
| 32204 | DEFSYM (Qfontified, "fontified"); | 32219 | DEFSYM (Qfontified, "fontified"); |
| 32205 | DEFSYM (Qfontification_functions, "fontification-functions"); | 32220 | DEFSYM (Qfontification_functions, "fontification-functions"); |
| 32206 | 32221 | ||
| 32222 | /* Name of the symbol which disables Lisp evaluation in 'display' | ||
| 32223 | properties. This is used by enriched.el. */ | ||
| 32224 | DEFSYM (Qdisable_eval, "disable-eval"); | ||
| 32225 | |||
| 32207 | /* Name of the face used to highlight trailing whitespace. */ | 32226 | /* Name of the face used to highlight trailing whitespace. */ |
| 32208 | DEFSYM (Qtrailing_whitespace, "trailing-whitespace"); | 32227 | DEFSYM (Qtrailing_whitespace, "trailing-whitespace"); |
| 32209 | 32228 | ||