fix(syntax): disable checker in non-project elisp files

CVE-2024-53920 describes an arbitrary code execution vulnerability
during macro expansion, which occurs during byte-compilation or when
evaluating macro calls in uncompiled elisp files.

Flycheck and flymake use byte-compilation to lint elisp files, exposing
users to this vulnerability. This commit attempts to protect users from
this by disabling both in elisp files that aren't part of a
project (because, presumably, untrusted elisp won't live in a project).
What a "project" is depends on your projectile settings, but generally
means a file that lives in a version controlled directory and/or a
directory containing a recognizable project root marker (like a
packages.json or Cargo.toml file).

This heuristic won't catch cases of untrusted elisp living within
legitimate projects, or the case where the user's $HOME is a project and
*all* their elisp files live under it, but there are already too many
ways to shoot yourself in the foot with Emacs to begin with, and
disabling fly(check|make) altogether stands a higher chance of making
people blindly re-enable them to "work around" the fact it's not
"working as expected", bringing them back to square one.

Anyhow, long story short, don't open elisp files you don't trust in
Emacs, mkay?

Ref: CVE-2024-53920
This commit is contained in:
Henrik Lissner
2024-12-03 09:20:36 -05:00
parent ec645b8381
commit 4418c80c95
2 changed files with 73 additions and 50 deletions

View File

@ -23,6 +23,20 @@
;; Display errors a little quicker (default is 0.9s)
(setq flycheck-display-errors-delay 0.25)
;; HACK: Protect against eager expansion of `setf'. The gv setter won't be
;; available until after `flycheck' loads, but macro expand occurs when this
;; file is loaded.
(eval '(setf (flycheck-checker-get 'emacs-lisp 'predicate)
(lambda ()
(and
;; Do not check buffers that ask not to be byte-compiled.
(not (bound-and-true-p no-byte-compile))
;; Disable the emacs-lisp checker in non-project (likely
;; untrusted) buffers to mitigate potential code execution
;; vulnerability during macro expansion. See CVE-2024-53920.
(doom-project-p))))
t)
;; Don't commandeer input focus if the error message pops up (happens when
;; tooltips and childframes are disabled).
(set-popup-rules!
@ -111,7 +125,15 @@
:when (modulep! +flymake)
:hook ((prog-mode text-mode) . flymake-mode)
:config
(setq flymake-fringe-indicator-position 'right-fringe))
(setq flymake-fringe-indicator-position 'right-fringe)
;; HACK: Disable the emacs-lisp checker in non-project (likely untrusted)
;; buffers to mitigate potential code execution vulnerability during macro
;; expansion. See CVE-2024-53920.
(defadvice! +syntax--only-check-elisp-buffers-in-projects-a (fn &rest args)
"Prevent the elisp checker in non-project buffers (for CVE-2024-53920)."
:before-while #'elisp-flymake-byte-compile
(doom-project-p)))
(use-package! flymake-popon

View File

@ -306,55 +306,56 @@ are set by `+emacs-lisp-linter-warnings'
This backend does not need to be added directly
as `+emacs-lisp-non-package-mode' will enable it and disable the other checkers."
;; if a process already exists. kill it.
(when (and +emacs-lisp-reduced-flymake-byte-compile--process
(process-live-p +emacs-lisp-reduced-flymake-byte-compile--process))
(kill-process +emacs-lisp-reduced-flymake-byte-compile--process))
(let ((source (current-buffer))
(tmp-file (make-temp-file "+emacs-lisp-byte-compile-src"))
(out-buf (generate-new-buffer "+emacs-lisp-byte-compile-out")))
;; write the content to a temp file
(save-restriction
(widen)
(write-region nil nil tmp-file nil 'nomessage))
;; make the process
(setq +emacs-lisp-reduced-flymake-byte-compile--process
(make-process
:name "+emacs-reduced-flymake"
:noquery t
:connection-type 'pipe
:buffer out-buf
:command `(,(expand-file-name invocation-name invocation-directory)
"-Q"
"--batch"
,@(mapcan (lambda (p) (list "-L" p)) elisp-flymake-byte-compile-load-path)
;; this is what silences the byte compiler
"--eval" ,(prin1-to-string `(setq doom-modules ',doom-modules
doom-disabled-packages ',doom-disabled-packages
byte-compile-warnings ',+emacs-lisp-linter-warnings))
"-f" "elisp-flymake--batch-compile-for-flymake"
,tmp-file)
:stderr "*stderr of +elisp-flymake-byte-compile-out*"
:sentinel
;; deal with the process when it exits
(lambda (proc _event)
(when (memq (process-status proc) '(exit signal))
(unwind-protect
(cond
;; if the buffer is dead or the process is not the same, log the process as old.
((or (not (buffer-live-p source))
(not (with-current-buffer source (eq proc +emacs-lisp-reduced-flymake-byte-compile--process))))
(flymake-log :warning "byte compile process %s is old" proc))
;; if the process exited without problem process the buffer
((zerop (process-exit-status proc))
(elisp-flymake--byte-compile-done report-fn source out-buf))
;; otherwise something else horrid has gone wrong and we panic
(t (funcall report-fn :panic
:explanation
(format "byte compile process %s died" proc))))
;; cleanup
(ignore-errors (delete-file tmp-file))
(kill-buffer out-buf))))))))
(when (doom-project-p)
;; if a process already exists. kill it.
(when (and +emacs-lisp-reduced-flymake-byte-compile--process
(process-live-p +emacs-lisp-reduced-flymake-byte-compile--process))
(kill-process +emacs-lisp-reduced-flymake-byte-compile--process))
(let ((source (current-buffer))
(tmp-file (make-temp-file "+emacs-lisp-byte-compile-src"))
(out-buf (generate-new-buffer "+emacs-lisp-byte-compile-out")))
;; write the content to a temp file
(save-restriction
(widen)
(write-region nil nil tmp-file nil 'nomessage))
;; make the process
(setq +emacs-lisp-reduced-flymake-byte-compile--process
(make-process
:name "+emacs-reduced-flymake"
:noquery t
:connection-type 'pipe
:buffer out-buf
:command `(,(expand-file-name invocation-name invocation-directory)
"-Q"
"--batch"
,@(mapcan (lambda (p) (list "-L" p)) elisp-flymake-byte-compile-load-path)
;; this is what silences the byte compiler
"--eval" ,(prin1-to-string `(setq doom-modules ',doom-modules
doom-disabled-packages ',doom-disabled-packages
byte-compile-warnings ',+emacs-lisp-linter-warnings))
"-f" "elisp-flymake--batch-compile-for-flymake"
,tmp-file)
:stderr "*stderr of +elisp-flymake-byte-compile-out*"
:sentinel
;; deal with the process when it exits
(lambda (proc _event)
(when (memq (process-status proc) '(exit signal))
(unwind-protect
(cond
;; if the buffer is dead or the process is not the same, log the process as old.
((or (not (buffer-live-p source))
(not (with-current-buffer source (eq proc +emacs-lisp-reduced-flymake-byte-compile--process))))
(flymake-log :warning "byte compile process %s is old" proc))
;; if the process exited without problem process the buffer
((zerop (process-exit-status proc))
(elisp-flymake--byte-compile-done report-fn source out-buf))
;; otherwise something else horrid has gone wrong and we panic
(t (funcall report-fn :panic
:explanation
(format "byte compile process %s died" proc))))
;; cleanup
(ignore-errors (delete-file tmp-file))
(kill-buffer out-buf)))))))))
(define-minor-mode +emacs-lisp--flymake-non-package-mode
""