#include "emacs-module.h"
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
return XUNTAG (o, Lisp_Vectorlike, struct module_global_reference);
}
+/* Returns whether V is a global reference. Only used to check module
+ assertions. If V is not a global reference, increment *N by the
+ number of global references (for debugging output). */
+
+static bool
+module_global_reference_p (emacs_value v, ptrdiff_t *n)
+{
+ struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
+ /* Note that we can't use `hash_lookup' because V might be a local
+ reference that's identical to some global reference. */
+ for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); ++i)
+ {
+ if (!EQ (HASH_KEY (h, i), Qunbound)
+ && &XMODULE_GLOBAL_REFERENCE (HASH_VALUE (h, i))->value == v)
+ return true;
+ }
+ /* Only used for debugging, so we don't care about overflow, just
+ make sure the operation is defined. */
+ INT_ADD_WRAPV (*n, h->count, n);
+ return false;
+}
+
static emacs_value
module_make_global_ref (emacs_env *env, emacs_value value)
{
++num_environments;
}
/* Also check global values. */
- struct Lisp_Hash_Table *h = XHASH_TABLE (Vmodule_refs_hash);
- if (hash_lookup (h, v->v, NULL) != -1)
+ if (module_global_reference_p (v, &num_values))
goto ok;
- INT_ADD_WRAPV (num_values, h->count, &num_values);
module_abort (("Emacs value not found in %"pD"d values "
"of %"pD"d environments"),
num_values, num_environments);
return invalid_stored_value;
}
+/* The next function works in conjunction with the two previous ones.
+ It stows away a copy of the object created by
+ `Fmod_test_invalid_store' in a global reference. Module assertions
+ should still detect the invalid load of the local reference. */
+
+static emacs_value global_copy_of_invalid_stored_value;
+
+static emacs_value
+Fmod_test_invalid_store_copy (emacs_env *env, ptrdiff_t nargs,
+ emacs_value *args, void *data)
+{
+ emacs_value local = Fmod_test_invalid_store (env, 0, NULL, NULL);
+ return global_copy_of_invalid_stored_value
+ = env->make_global_ref (env, local);
+}
+
/* An invalid finalizer: Finalizers are run during garbage collection,
where Lisp code can't be executed. -module-assertions tests for
this case. */
DEFUN ("mod-test-vector-fill", Fmod_test_vector_fill, 2, 2, NULL, NULL);
DEFUN ("mod-test-vector-eq", Fmod_test_vector_eq, 2, 2, NULL, NULL);
DEFUN ("mod-test-invalid-store", Fmod_test_invalid_store, 0, 0, NULL, NULL);
+ DEFUN ("mod-test-invalid-store-copy", Fmod_test_invalid_store_copy, 0, 0,
+ NULL, NULL);
DEFUN ("mod-test-invalid-load", Fmod_test_invalid_load, 0, 0, NULL, NULL);
DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0,
NULL, NULL);
(mod-test-invalid-store)
(mod-test-invalid-load)))
+(ert-deftest module--test-assertions--load-non-live-object-with-global-copy ()
+ "Check that -module-assertions verify that non-live objects aren't accessed.
+This differs from `module--test-assertions-load-non-live-object'
+in that it stows away a global reference. The module assertions
+should nevertheless detect the invalid load."
+ (skip-unless (or (file-executable-p mod-test-emacs)
+ (and (eq system-type 'windows-nt)
+ (file-executable-p (concat mod-test-emacs ".exe")))))
+ ;; This doesn't yet cause undefined behavior.
+ (should (eq (mod-test-invalid-store-copy) 123))
+ (module--test-assertion (rx "Emacs value not found in "
+ (+ digit) " values of "
+ (+ digit) " environments\n")
+ ;; Storing and reloading a local value causes undefined behavior,
+ ;; which should be detected by the module assertions.
+ (mod-test-invalid-store-copy)
+ (mod-test-invalid-load)))
+
(ert-deftest module--test-assertions--call-emacs-from-gc ()
"Check that -module-assertions prevents calling Emacs functions
during garbage collection."