aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorPhilipp Stephani2017-07-04 22:50:46 +0200
committerPhilipp Stephani2017-07-08 15:25:01 +0200
commitb7dab24b7953f7a31b806f83e15043c94aaa7745 (patch)
treefc31284ef0fac82accddc573069652b1c8adccf0 /test
parenta87d767c7acc43b3679d87d2d225b1edeb69326c (diff)
downloademacs-b7dab24b7953f7a31b806f83e15043c94aaa7745.tar.gz
emacs-b7dab24b7953f7a31b806f83e15043c94aaa7745.zip
Module assertions: check for garbage collections
It's technically possible to write a user pointer finalizer that calls into Emacs module functions. This would be disastrous because it would allow arbitrary Lisp code to run during garbage collection. Therefore extend the module assertions to check for this case. * src/emacs-module.c (module_assert_thread): Also check whether a garbage collection is in progress. * test/data/emacs-module/mod-test.c (invalid_finalizer) (Fmod_test_invalid_finalizer): New test module functions. (emacs_module_init): Register new test function. * test/src/emacs-module-tests.el (module--test-assertion) (module--with-temp-directory): New helper macros. (module--test-assertions--load-non-live-object): Rename existing unit test, use helper macros. (module--test-assertions--call-emacs-from-gc): New unit test.
Diffstat (limited to 'test')
-rw-r--r--test/data/emacs-module/mod-test.c23
-rw-r--r--test/src/emacs-module-tests.el87
2 files changed, 81 insertions, 29 deletions
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c
index eee9466c5d6..42e1c2bd4ae 100644
--- a/test/data/emacs-module/mod-test.c
+++ b/test/data/emacs-module/mod-test.c
@@ -235,6 +235,27 @@ Fmod_test_invalid_load (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
235 return invalid_stored_value; 235 return invalid_stored_value;
236} 236}
237 237
238/* An invalid finalizer: Finalizers are run during garbage collection,
239 where Lisp code can’t be executed. -module-assertions tests for
240 this case. */
241
242static emacs_env *current_env;
243
244static void
245invalid_finalizer (void *ptr)
246{
247 current_env->intern (current_env, "nil");
248}
249
250static emacs_value
251Fmod_test_invalid_finalizer (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
252 void *data)
253{
254 current_env = env;
255 env->make_user_ptr (env, invalid_finalizer, NULL);
256 return env->funcall (env, env->intern (env, "garbage-collect"), 0, NULL);
257}
258
238 259
239/* Lisp utilities for easier readability (simple wrappers). */ 260/* Lisp utilities for easier readability (simple wrappers). */
240 261
@@ -300,6 +321,8 @@ emacs_module_init (struct emacs_runtime *ert)
300 DEFUN ("mod-test-vector-eq", Fmod_test_vector_eq, 2, 2, NULL, NULL); 321 DEFUN ("mod-test-vector-eq", Fmod_test_vector_eq, 2, 2, NULL, NULL);
301 DEFUN ("mod-test-invalid-store", Fmod_test_invalid_store, 0, 0, NULL, NULL); 322 DEFUN ("mod-test-invalid-store", Fmod_test_invalid_store, 0, 0, NULL, NULL);
302 DEFUN ("mod-test-invalid-load", Fmod_test_invalid_load, 0, 0, NULL, NULL); 323 DEFUN ("mod-test-invalid-load", Fmod_test_invalid_load, 0, 0, NULL, NULL);
324 DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0,
325 NULL, NULL);
303 326
304#undef DEFUN 327#undef DEFUN
305 328
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index a4994b6223b..988a7a178c6 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -182,37 +182,66 @@ changes."
182 (should (equal (help-function-arglist #'mod-test-sum) 182 (should (equal (help-function-arglist #'mod-test-sum)
183 '(arg1 arg2)))) 183 '(arg1 arg2))))
184 184
185(ert-deftest module--test-assertions () 185(defmacro module--with-temp-directory (name &rest body)
186 "Check that -module-assertions work." 186 "Bind NAME to the name of a temporary directory and evaluate BODY.
187NAME must be a symbol. Delete the temporary directory after BODY
188exits normally or non-locally. NAME will be bound to the
189directory name (not the directory file name) of the temporary
190directory."
191 (declare (indent 1))
192 (cl-check-type name symbol)
193 `(let ((,name (file-name-as-directory
194 (make-temp-file "emacs-module-test" :directory))))
195 (unwind-protect
196 (progn ,@body)
197 (delete-directory ,name :recursive))))
198
199(defmacro module--test-assertion (pattern &rest body)
200 "Test that PATTERN matches the assertion triggered by BODY.
201Run Emacs as a subprocess, load the test module `mod-test-file',
202and evaluate BODY. Verify that Emacs aborts and prints a module
203assertion message that matches PATTERN. PATTERN is evaluated and
204must evaluate to a regular expression string."
205 (declare (indent 1))
206 ;; To contain any core dumps.
207 `(module--with-temp-directory tempdir
208 (with-temp-buffer
209 (let* ((default-directory tempdir)
210 (status (call-process mod-test-emacs nil t nil
211 "-batch" "-Q" "-module-assertions" "-eval"
212 ,(prin1-to-string
213 `(progn
214 (require 'mod-test ,mod-test-file)
215 ,@body)))))
216 (should (stringp status))
217 ;; eg "Aborted" or "Abort trap: 6"
218 (should (string-prefix-p "Abort" status))
219 (search-backward "Emacs module assertion: ")
220 (goto-char (match-end 0))
221 (should (string-match-p ,pattern
222 (buffer-substring-no-properties
223 (point) (point-max))))))))
224
225(ert-deftest module--test-assertions--load-non-live-object ()
226 "Check that -module-assertions verify that non-live objects
227aren’t accessed."
187 (skip-unless (file-executable-p mod-test-emacs)) 228 (skip-unless (file-executable-p mod-test-emacs))
188 ;; This doesn’t yet cause undefined behavior. 229 ;; This doesn’t yet cause undefined behavior.
189 (should (eq (mod-test-invalid-store) 123)) 230 (should (eq (mod-test-invalid-store) 123))
190 ;; To contain any core dumps. 231 (module--test-assertion (rx "Emacs value not found in "
191 (let ((tempdir (make-temp-file "emacs-module-test" t))) 232 (+ digit) " values of "
192 (unwind-protect 233 (+ digit) " environments\n" eos)
193 (with-temp-buffer 234 ;; Storing and reloading a local value causes undefined behavior,
194 (should (string-match-p 235 ;; which should be detected by the module assertions.
195 "Abort" ; eg "Aborted" or "Abort trap: 6" 236 (mod-test-invalid-store)
196 (let ((default-directory tempdir)) 237 (mod-test-invalid-load)))
197 (call-process mod-test-emacs nil t nil 238
198 "-batch" "-Q" "-module-assertions" "-eval" 239(ert-deftest module--test-assertions--call-emacs-from-gc ()
199 (prin1-to-string 240 "Check that -module-assertions prevents calling Emacs functions
200 `(progn 241during garbage collection."
201 (require 'mod-test ,mod-test-file) 242 (skip-unless (file-executable-p mod-test-emacs))
202 ;; Storing and reloading a local 243 (module--test-assertion
203 ;; value causes undefined behavior, 244 (rx "Module function called during garbage collection\n" eos)
204 ;; which should be detected by the 245 (mod-test-invalid-finalizer)))
205 ;; module assertions.
206 (mod-test-invalid-store)
207 (mod-test-invalid-load)))))))
208 (search-backward "Emacs module assertion:")
209 (should (string-match-p (rx bos "Emacs module assertion: "
210 "Emacs value not found in "
211 (+ digit) " values of "
212 (+ digit) " environments" eos)
213 (buffer-substring-no-properties
214 (line-beginning-position)
215 (line-end-position)))))
216 (delete-directory tempdir t))))
217 246
218;;; emacs-module-tests.el ends here 247;;; emacs-module-tests.el ends here