unsafep.el 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. ;;;; unsafep.el -- Determine whether a Lisp form is safe to evaluate
  2. ;; Copyright (C) 2002-2012 Free Software Foundation, Inc.
  3. ;; Author: Jonathan Yavner <jyavner@member.fsf.org>
  4. ;; Maintainer: Jonathan Yavner <jyavner@member.fsf.org>
  5. ;; Keywords: safety lisp utility
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This is a simplistic implementation that does not allow any modification of
  19. ;; buffers or global variables. It does no dataflow analysis, so functions
  20. ;; like `funcall' and `setcar' are completely disallowed. It is designed
  21. ;; for "pure Lisp" formulas, like those in spreadsheets, that don't make any
  22. ;; use of the text editing capabilities of Emacs.
  23. ;; A formula is safe if:
  24. ;; 1. It's an atom.
  25. ;; 2. It's a function call to a safe function and all arguments are safe
  26. ;; formulas.
  27. ;; 3. It's a special form whose arguments are like a function's (and,
  28. ;; catch, if, or, prog1, prog2, progn, while, unwind-protect).
  29. ;; 4. It's a special form or macro that creates safe temporary bindings
  30. ;; (condition-case, dolist, dotimes, lambda, let, let*).
  31. ;; 4. It's one of (cond, quote) that have special parsing.
  32. ;; 5. It's one of (add-to-list, setq, push, pop) and the assignment variable
  33. ;; is safe.
  34. ;; 6. It's one of (apply, mapc, mapcar, mapconcat) and its first arg is a
  35. ;; quoted safe function.
  36. ;;
  37. ;; A function is safe if:
  38. ;; 1. It's a lambda containing safe formulas.
  39. ;; 2. It's a member of list `safe-functions', so the user says it's safe.
  40. ;; 3. It's a symbol with the `side-effect-free' property, defined by the
  41. ;; byte compiler or function author.
  42. ;; 4. It's a symbol with the `safe-function' property, defined here or by
  43. ;; the function author. Value t indicates a function that is safe but
  44. ;; has innocuous side effects. Other values will someday indicate
  45. ;; functions with side effects that are not always safe.
  46. ;; The `side-effect-free' and `safe-function' properties are provided for
  47. ;; built-in functions and for functions and macros defined in subr.el.
  48. ;;
  49. ;; A temporary binding is unsafe if its symbol:
  50. ;; 1. Has the `risky-local-variable' property.
  51. ;; 2. Has a name that ends with -command, font-lock-keywords(-[0-9]+)?,
  52. ;; font-lock-syntactic-keywords, -form, -forms, -frame-alist, -function,
  53. ;; -functions, -history, -hook, -hooks, -map, -map-alist, -mode-alist,
  54. ;; -predicate, or -program.
  55. ;;
  56. ;; An assignment variable is unsafe if:
  57. ;; 1. It would be unsafe as a temporary binding.
  58. ;; 2. It doesn't already have a temporary or buffer-local binding.
  59. ;; There are unsafe forms that `unsafep' cannot detect. Beware of these:
  60. ;; 1. The form's result is a string with a display property containing a
  61. ;; form to be evaluated later, and you insert this result into a
  62. ;; buffer. Always remove display properties before inserting!
  63. ;; 2. The form alters a risky variable that was recently added to Emacs and
  64. ;; is not yet marked with the `risky-local-variable' property.
  65. ;; 3. The form uses undocumented features of built-in functions that have
  66. ;; the `side-effect-free' property. For example, in Emacs-20 if you
  67. ;; passed a circular list to `assoc', Emacs would crash. Historically,
  68. ;; problems of this kind have been few and short-lived.
  69. ;;; Code:
  70. (provide 'unsafep)
  71. (require 'byte-opt) ;Set up the `side-effect-free' properties
  72. (defcustom safe-functions nil
  73. "A list of assumed-safe functions, or t to disable `unsafep'."
  74. :group 'lisp
  75. :type '(choice (const :tag "No" nil) (const :tag "Yes" t) hook))
  76. (defvar unsafep-vars nil
  77. "Dynamically-bound list of variables with lexical bindings at this point
  78. in the parse.")
  79. (put 'unsafep-vars 'risky-local-variable t)
  80. ;;Side-effect-free functions from subr.el
  81. (dolist (x '(assoc-default assoc-ignore-case butlast last match-string
  82. match-string-no-properties member-ignore-case remove remq))
  83. (put x 'side-effect-free t))
  84. ;;Other safe functions
  85. (dolist (x '(;;Special forms
  86. and catch if or prog1 prog2 progn while unwind-protect
  87. ;;Safe subrs that have some side-effects
  88. ding error random signal sleep-for string-match throw
  89. ;;Defsubst functions from subr.el
  90. caar cadr cdar cddr
  91. ;;Macros from subr.el
  92. save-match-data unless when
  93. ;;Functions from subr.el that have side effects
  94. split-string replace-regexp-in-string play-sound-file))
  95. (put x 'safe-function t))
  96. ;;;###autoload
  97. (defun unsafep (form &optional unsafep-vars)
  98. "Return nil if evaluating FORM couldn't possibly do any harm.
  99. Otherwise result is a reason why FORM is unsafe.
  100. UNSAFEP-VARS is a list of symbols with local bindings."
  101. (catch 'unsafep
  102. (if (or (eq safe-functions t) ;User turned off safety-checking
  103. (atom form)) ;Atoms are never unsafe
  104. (throw 'unsafep nil))
  105. (let* ((fun (car form))
  106. (reason (unsafep-function fun))
  107. arg)
  108. (cond
  109. ((not reason)
  110. ;;It's a normal function - unsafe if any arg is
  111. (unsafep-progn (cdr form)))
  112. ((eq fun 'quote)
  113. ;;Never unsafe
  114. nil)
  115. ((memq fun '(apply mapc mapcar mapconcat))
  116. ;;Unsafe if 1st arg isn't a quoted lambda
  117. (setq arg (cadr form))
  118. (cond
  119. ((memq (car-safe arg) '(quote function))
  120. (setq reason (unsafep-function (cadr arg))))
  121. ((eq (car-safe arg) 'lambda)
  122. ;;Self-quoting lambda
  123. (setq reason (unsafep arg unsafep-vars)))
  124. (t
  125. (setq reason `(unquoted ,arg))))
  126. (or reason (unsafep-progn (cddr form))))
  127. ((eq fun 'lambda)
  128. ;;First arg is temporary bindings
  129. (mapc #'(lambda (x)
  130. (or (memq x '(&optional &rest))
  131. (let ((y (unsafep-variable x t)))
  132. (if y (throw 'unsafep y))
  133. (push x unsafep-vars))))
  134. (cadr form))
  135. (unsafep-progn (cddr form)))
  136. ((eq fun 'let)
  137. ;;Creates temporary bindings in one step
  138. (setq unsafep-vars (nconc (mapcar #'unsafep-let (cadr form))
  139. unsafep-vars))
  140. (unsafep-progn (cddr form)))
  141. ((eq fun 'let*)
  142. ;;Creates temporary bindings iteratively
  143. (dolist (x (cadr form))
  144. (push (unsafep-let x) unsafep-vars))
  145. (unsafep-progn (cddr form)))
  146. ((eq fun 'setq)
  147. ;;Safe if odd arguments are local-var syms, evens are safe exprs
  148. (setq arg (cdr form))
  149. (while arg
  150. (setq reason (or (unsafep-variable (car arg) nil)
  151. (unsafep (cadr arg) unsafep-vars)))
  152. (if reason (throw 'unsafep reason))
  153. (setq arg (cddr arg))))
  154. ((eq fun 'pop)
  155. ;;safe if arg is local-var sym
  156. (unsafep-variable (cadr form) nil))
  157. ((eq fun 'push)
  158. ;;Safe if 2nd arg is a local-var sym
  159. (or (unsafep (cadr form) unsafep-vars)
  160. (unsafep-variable (nth 2 form) nil)))
  161. ((eq fun 'add-to-list)
  162. ;;Safe if first arg is a quoted local-var sym
  163. (setq arg (cadr form))
  164. (if (not (eq (car-safe arg) 'quote))
  165. `(unquoted ,arg)
  166. (or (unsafep-variable (cadr arg) nil)
  167. (unsafep-progn (cddr form)))))
  168. ((eq fun 'cond)
  169. ;;Special form with unusual syntax - safe if all args are
  170. (dolist (x (cdr form))
  171. (setq reason (unsafep-progn x))
  172. (if reason (throw 'unsafep reason))))
  173. ((memq fun '(dolist dotimes))
  174. ;;Safe if COUNT and RESULT are safe. VAR is bound while checking BODY.
  175. (setq arg (cadr form))
  176. (or (unsafep-progn (cdr arg))
  177. (let ((unsafep-vars (cons (car arg) unsafep-vars)))
  178. (unsafep-progn (cddr form)))))
  179. ((eq fun 'condition-case)
  180. ;;Special form with unusual syntax - safe if all args are
  181. (or (unsafep-variable (cadr form) t)
  182. (unsafep (nth 2 form) unsafep-vars)
  183. (let ((unsafep-vars (cons (cadr form) unsafep-vars)))
  184. ;;var is bound only during handlers
  185. (dolist (x (nthcdr 3 form))
  186. (setq reason (unsafep-progn (cdr x)))
  187. (if reason (throw 'unsafep reason))))))
  188. ((eq fun '\`)
  189. ;; Backquoted form - safe if its expansion is.
  190. (unsafep (cdr (backquote-process (cadr form)))))
  191. (t
  192. ;;First unsafep-function call above wasn't nil, no special case applies
  193. reason)))))
  194. (defun unsafep-function (fun)
  195. "Return nil if FUN is a safe function.
  196. \(Either a safe lambda or a symbol that names a safe function).
  197. Otherwise result is a reason code."
  198. (cond
  199. ((eq (car-safe fun) 'lambda)
  200. (unsafep fun unsafep-vars))
  201. ((not (and (symbolp fun)
  202. (or (get fun 'side-effect-free)
  203. (eq (get fun 'safe-function) t)
  204. (eq safe-functions t)
  205. (memq fun safe-functions))))
  206. `(function ,fun))))
  207. (defun unsafep-progn (list)
  208. "Return nil if all forms in LIST are safe.
  209. Else, return the reason for the first unsafe form."
  210. (catch 'unsafep-progn
  211. (let (reason)
  212. (dolist (x list)
  213. (setq reason (unsafep x unsafep-vars))
  214. (if reason (throw 'unsafep-progn reason))))))
  215. (defun unsafep-let (clause)
  216. "Check the safety of a let binding.
  217. CLAUSE is a let-binding, either SYM or (SYM) or (SYM VAL).
  218. Check VAL and throw a reason to `unsafep' if unsafe.
  219. Return SYM."
  220. (let (reason sym)
  221. (if (atom clause)
  222. (setq sym clause)
  223. (setq sym (car clause)
  224. reason (unsafep (cadr clause) unsafep-vars)))
  225. (setq reason (or (unsafep-variable sym t) reason))
  226. (if reason (throw 'unsafep reason))
  227. sym))
  228. (defun unsafep-variable (sym to-bind)
  229. "Return nil if SYM is safe to set or bind, or a reason why not.
  230. If TO-BIND is nil, check whether SYM is safe to set.
  231. If TO-BIND is t, check whether SYM is safe to bind."
  232. (cond
  233. ((not (symbolp sym))
  234. `(variable ,sym))
  235. ((risky-local-variable-p sym nil)
  236. `(risky-local-variable ,sym))
  237. ((not (or to-bind
  238. (memq sym unsafep-vars)
  239. (local-variable-p sym)))
  240. `(global-variable ,sym))))
  241. ;;; unsafep.el ends here