aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2016-02-09 22:04:57 +0100
committerLukas Larsson <[email protected]>2016-04-15 15:07:09 +0200
commitd15ddcb9d407462133ec5d84c45353bd5a928167 (patch)
treebcece6e6839b363976ddc9cb00b64641ed8eaa7c
parent6cb6b59cd4cd5bd4383053e12ae8ab192711c827 (diff)
downloadotp-d15ddcb9d407462133ec5d84c45353bd5a928167.tar.gz
otp-d15ddcb9d407462133ec5d84c45353bd5a928167.tar.bz2
otp-d15ddcb9d407462133ec5d84c45353bd5a928167.zip
erts: Fix FPE bug in erl_nif
erts_block/unblock_fpe should only be called at entry to/exit from native user code.
-rw-r--r--erts/emulator/beam/beam_emu.c2
-rw-r--r--erts/emulator/beam/erl_nif.c51
-rw-r--r--erts/emulator/beam/global.h2
3 files changed, 34 insertions, 21 deletions
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index c356863b60..7cb259cb8a 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -3460,7 +3460,7 @@ do { \
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
NifF* fp = vbf = (NifF*) I[1];
struct enif_environment_t env;
- erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
live_hf_end = c_p->mbuf;
nif_bif_result = (*fp)(&env, bif_nif_arity, reg);
if (env.exception_thrown)
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 59a2b20ff2..7ab5421c91 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -120,7 +120,8 @@ static ERTS_INLINE void ensure_heap(ErlNifEnv* env, unsigned may_need)
}
#endif
-void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
+void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
+ Process* tracee)
{
env->mod_nif = mod_nif;
env->proc = p;
@@ -130,7 +131,7 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
env->fpe_was_unmasked = erts_block_fpe();
env->tmp_obj_list = NULL;
env->exception_thrown = 0;
- env->tracee = NULL;
+ env->tracee = tracee;
}
/* Temporary object header, auto-deallocated when NIF returns
@@ -232,7 +233,8 @@ struct enif_msg_environment_t
static ERTS_INLINE void
setup_nif_env(struct enif_msg_environment_t* msg_env,
- struct erl_module_nif* mod)
+ struct erl_module_nif* mod,
+ Process* tracee)
{
Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */
@@ -241,7 +243,6 @@ setup_nif_env(struct enif_msg_environment_t* msg_env,
msg_env->env.heap_frag = NULL;
msg_env->env.mod_nif = mod;
msg_env->env.tmp_obj_list = NULL;
- msg_env->env.fpe_was_unmasked = erts_block_fpe();
msg_env->env.proc = &msg_env->phony_proc;
msg_env->env.exception_thrown = 0;
memset(&msg_env->phony_proc, 0, sizeof(Process));
@@ -255,13 +256,14 @@ setup_nif_env(struct enif_msg_environment_t* msg_env,
msg_env->phony_proc.space_verified = 0;
msg_env->phony_proc.space_verified_from = NULL;
#endif
+ msg_env->env.tracee = tracee;
}
ErlNifEnv* enif_alloc_env(void)
{
struct enif_msg_environment_t* msg_env =
erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t));
- setup_nif_env(msg_env, NULL);
+ setup_nif_env(msg_env, NULL, NULL);
return &msg_env->env;
}
void enif_free_env(ErlNifEnv* env)
@@ -270,6 +272,20 @@ void enif_free_env(ErlNifEnv* env)
erts_free(ERTS_ALC_T_NIF, env);
}
+static ERTS_INLINE void pre_nif_noproc(struct enif_msg_environment_t* msg_env,
+ struct erl_module_nif* mod,
+ Process* tracee)
+{
+ setup_nif_env(msg_env, mod, tracee);
+ msg_env->env.fpe_was_unmasked = erts_block_fpe();
+}
+
+static ERTS_INLINE void post_nif_noproc(struct enif_msg_environment_t* msg_env)
+{
+ erts_unblock_fpe(msg_env->env.fpe_was_unmasked);
+ enif_clear_env(&msg_env->env);
+}
+
static ERTS_INLINE void clear_offheap(ErlOffHeap* oh)
{
oh->first = NULL;
@@ -294,7 +310,6 @@ void enif_clear_env(ErlNifEnv* env)
menv->env.hp = menv->env.hp_end = HEAP_TOP(p);
ASSERT(!is_offheap(&MSO(p)));
- erts_unblock_fpe(env->fpe_was_unmasked);
free_tmp_objs(env);
}
@@ -465,7 +480,7 @@ enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
else {
/* This clause is taken when the nif is called in the context
of a traced process. We do not know which locks we have
- so we have to do a try lock and if that fails we en queue
+ so we have to do a try lock and if that fails we enqueue
the message in a special trace message output queue of the
tracee */
ErlTraceMessageQueue *msgq;
@@ -1648,9 +1663,9 @@ static void close_lib(struct erl_module_nif* lib)
if (lib->entry != NULL && lib->entry->unload != NULL) {
struct enif_msg_environment_t msg_env;
- setup_nif_env(&msg_env, lib);
+ pre_nif_noproc(&msg_env, lib, NULL);
lib->entry->unload(&msg_env.env, lib->priv_data);
- enif_clear_env(&msg_env.env);
+ post_nif_noproc(&msg_env);
}
if (!erts_is_static_nif(lib->handle))
erts_sys_ddll_close(lib->handle);
@@ -1797,9 +1812,9 @@ static void nif_resource_dtor(Binary* bin)
if (type->dtor != NULL) {
struct enif_msg_environment_t msg_env;
- setup_nif_env(&msg_env, type->owner);
+ pre_nif_noproc(&msg_env, type->owner, NULL);
type->dtor(&msg_env.env, resource->data);
- enif_clear_env(&msg_env.env);
+ post_nif_noproc(&msg_env);
}
if (erts_refc_dectest(&type->refc, 0) == 0) {
ASSERT(type->next == NULL);
@@ -2959,7 +2974,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
old_func = next_func(mod->curr.nif->entry, &old_incr, old_func);
}
- erts_pre_nif(&env, BIF_P, lib);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
@@ -2980,7 +2995,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->upgrade(&env, &lib->priv_data, &mod->old.nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
@@ -2991,7 +3006,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
commit_opened_resource_types(lib);
}
else if (entry->load != NULL) { /********* Initial load ***********/
- erts_pre_nif(&env, BIF_P, lib);
+ erts_pre_nif(&env, BIF_P, lib, NULL);
veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
@@ -3152,8 +3167,7 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
if (p) {
struct enif_environment_t env;
ASSERT(is_internal_pid(p->common.id));
- erts_pre_nif(&env, p, mod);
- env.tracee = tracee;
+ erts_pre_nif(&env, p, mod, tracee);
nif_result = (*fun->fptr)(&env, argc, argv);
if (env.exception_thrown)
nif_result = THE_NON_VALUE;
@@ -3162,12 +3176,11 @@ Eterm erts_nif_call_function(Process *p, Process *tracee,
/* Nif call was done without a process context,
so we create a phony one. */
struct enif_msg_environment_t msg_env;
- setup_nif_env(&msg_env, mod);
- msg_env.env.tracee = tracee;
+ pre_nif_noproc(&msg_env, mod, tracee);
nif_result = (*fun->fptr)(&msg_env.env, argc, argv);
if (msg_env.env.exception_thrown)
nif_result = THE_NON_VALUE;
- enif_clear_env(&msg_env.env);
+ post_nif_noproc(&msg_env);
}
return nif_result;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 5c2e8bbd09..a7bc990deb 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -59,7 +59,7 @@ struct enif_environment_t /* ErlNifEnv */
Process *tracee;
};
extern void erts_pre_nif(struct enif_environment_t*, Process*,
- struct erl_module_nif*);
+ struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(int to, void* to_arg);