From 9b7e83dee592c24d81288087b8722d485d01d617 Mon Sep 17 00:00:00 2001 From: Philipp Stephani Date: Thu, 30 Jan 2025 16:12:49 +0100 Subject: [PATCH] Don't overwrite non-local exit symbol and data (Bug#65796). The previous approach would incorrectly invalidate the returned module values if another non-local exit occurred while dealing with a non-local exit. See Bug#65796. Instead, allocate the values from the usual environment storage, and return statically-allocated objects if that fails. * src/emacs-module.c (struct emacs_env_private): Turn non-local exit symbol and data into normal Lisp objects. (initialize_environment): Initialize them. (mark_module_environment): Prevent them from being garbage-collected. (module_signal_or_throw, module_non_local_exit_signal_1) (module_non_local_exit_throw_1): Adapt uses. (value_to_lisp): No longer scan for them with module assertions enabled. (module_out_of_memory_signal, module_out_of_memory_data): New statically-allocated module values to return in case of allocation failure. (syms_of_module): Initialize them. (module_non_local_exit_get): Allocate module values normally. If that fails, return statically-allocated values. * doc/lispref/internals.texi (Module Nonlocal): Document new behavior. (cherry picked from commit 32da093e524d5e28945557701f7c50d7c4a898cd) --- doc/lispref/internals.texi | 6 +++- src/emacs-module.c | 63 ++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index ee1fcbbbd68..a8b9ea88f7f 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -2076,7 +2076,11 @@ error symbol in @code{*@var{symbol}} and the error data in tag symbol in @code{*@var{symbol}} and the @code{throw} value in @code{*@var{data}}. The function doesn't store anything in memory pointed by these arguments when the return value is -@code{emacs_funcall_exit_return}. +@code{emacs_funcall_exit_return}. If the function fails to allocate +storage for @var{symbol} or @var{data}, it stores a value representing +the symbol @code{module-out-of-memory} in @code{*@var{symbol}}, stores a +value representing @code{nil} in @code{*@var{data}}, and returns +@code{emacs_funcall_exit_signal}. @end deftypefn You should check nonlocal exit conditions where it matters: before you diff --git a/src/emacs-module.c b/src/emacs-module.c index 0a67433ec70..ab6b900df8d 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -167,7 +167,7 @@ struct emacs_env_private /* Dedicated storage for non-local exit symbol and data so that storage is always available for them, even in an out-of-memory situation. */ - struct emacs_value_tag non_local_exit_symbol, non_local_exit_data; + Lisp_Object non_local_exit_symbol, non_local_exit_data; struct emacs_value_storage storage; }; @@ -500,6 +500,9 @@ module_non_local_exit_clear (emacs_env *env) env->private_members->pending_non_local_exit = emacs_funcall_exit_return; } +static struct emacs_value_tag module_out_of_memory_symbol; +static struct emacs_value_tag module_out_of_memory_data; + static enum emacs_funcall_exit module_non_local_exit_get (emacs_env *env, emacs_value *symbol, emacs_value *data) @@ -507,12 +510,23 @@ module_non_local_exit_get (emacs_env *env, module_assert_thread (); module_assert_env (env); struct emacs_env_private *p = env->private_members; - if (p->pending_non_local_exit != emacs_funcall_exit_return) + enum emacs_funcall_exit ret = p->pending_non_local_exit; + if (ret != emacs_funcall_exit_return) { - *symbol = &p->non_local_exit_symbol; - *data = &p->non_local_exit_data; + emacs_value sym + = allocate_emacs_value (env, p->non_local_exit_symbol); + emacs_value dat + = allocate_emacs_value (env, p->non_local_exit_data); + if (sym == NULL || dat == NULL) + { + sym = &module_out_of_memory_symbol; + dat = &module_out_of_memory_data; + ret = emacs_funcall_exit_signal; + } + *symbol = sym; + *data = dat; } - return p->pending_non_local_exit; + return ret; } /* Like for `signal', DATA must be a list. */ @@ -1185,11 +1199,11 @@ module_signal_or_throw (struct emacs_env_private *env) case emacs_funcall_exit_return: return; case emacs_funcall_exit_signal: - xsignal (value_to_lisp (&env->non_local_exit_symbol), - value_to_lisp (&env->non_local_exit_data)); + xsignal (env->non_local_exit_symbol, + env->non_local_exit_data); case emacs_funcall_exit_throw: - Fthrow (value_to_lisp (&env->non_local_exit_symbol), - value_to_lisp (&env->non_local_exit_data)); + Fthrow (env->non_local_exit_symbol, + env->non_local_exit_data); default: eassume (false); } @@ -1389,8 +1403,8 @@ module_non_local_exit_signal_1 (emacs_env *env, Lisp_Object sym, if (p->pending_non_local_exit == emacs_funcall_exit_return) { p->pending_non_local_exit = emacs_funcall_exit_signal; - p->non_local_exit_symbol.v = sym; - p->non_local_exit_data.v = data; + p->non_local_exit_symbol = sym; + p->non_local_exit_data = data; } } @@ -1402,8 +1416,8 @@ module_non_local_exit_throw_1 (emacs_env *env, Lisp_Object tag, if (p->pending_non_local_exit == emacs_funcall_exit_return) { p->pending_non_local_exit = emacs_funcall_exit_throw; - p->non_local_exit_symbol.v = tag; - p->non_local_exit_data.v = value; + p->non_local_exit_symbol = tag; + p->non_local_exit_data = value; } } @@ -1439,13 +1453,6 @@ value_to_lisp (emacs_value v) { const emacs_env *env = pdl->unwind_ptr.arg; struct emacs_env_private *priv = env->private_members; - /* The value might be one of the nonlocal exit values. Note - that we don't check whether a nonlocal exit is currently - pending, because the module might have cleared the flag - in the meantime. */ - if (&priv->non_local_exit_symbol == v - || &priv->non_local_exit_data == v) - goto ok; if (value_storage_contains_p (&priv->storage, v, &num_values)) goto ok; ++num_environments; @@ -1536,6 +1543,8 @@ mark_module_environment (void *ptr) { emacs_env *env = ptr; struct emacs_env_private *priv = env->private_members; + mark_object (priv->non_local_exit_symbol); + mark_object (priv->non_local_exit_data); for (struct emacs_value_frame *frame = &priv->storage.initial; frame != NULL; frame = frame->next) for (int i = 0; i < frame->offset; ++i) @@ -1561,6 +1570,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) } priv->pending_non_local_exit = emacs_funcall_exit_return; + priv->non_local_exit_symbol = Qnil; + priv->non_local_exit_data = Qnil; initialize_storage (&priv->storage); env->size = sizeof *env; env->private_members = priv; @@ -1711,6 +1722,18 @@ syms_of_module (void) Vmodule_refs_hash = make_hash_table (&hashtest_eq, DEFAULT_HASH_SIZE, Weak_None); + DEFSYM (Qmodule_out_of_memory, "module-out-of-memory"); + Fput (Qmodule_out_of_memory, Qerror_conditions, + list2 (Qmodule_out_of_memory, Qerror)); + Fput (Qmodule_out_of_memory, Qerror_message, + build_unibyte_string ("Module out of memory")); + + staticpro (&module_out_of_memory_symbol.v); + module_out_of_memory_symbol.v = Qmodule_out_of_memory; + + staticpro (&module_out_of_memory_data.v); + module_out_of_memory_data.v = Qnil; + DEFSYM (Qmodule_load_failed, "module-load-failed"); Fput (Qmodule_load_failed, Qerror_conditions, list (Qmodule_load_failed, Qerror)); -- 2.39.5