Skip to content

Commit ed6a158

Browse files
committed
Merge pull request #1604 from pguyot/w13/fix-erlnif-monitor-possible-crash
Fix possible crash with enif_demonitor_process Resource may no longer exist when enif_demonitor_process is called if the monitor was already destroyed as the resource was deallocated. Fix this by storing resource_type in ErlNifMonitor. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents d416d90 + 7b4c82f commit ed6a158

File tree

3 files changed

+30
-18
lines changed

3 files changed

+30
-18
lines changed

src/libAtomVM/erl_nif.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ typedef struct ResourceType ErlNifResourceType;
5656
/**
5757
* @brief Opaque monitor type
5858
*/
59-
typedef uint64_t ErlNifMonitor;
59+
typedef struct
60+
{
61+
struct ResourceType *resource_type;
62+
uint64_t ref_ticks;
63+
} ErlNifMonitor;
6064

6165
/**
6266
* @brief Selectable event.

src/libAtomVM/resources.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ int enif_monitor_process(ErlNifEnv *env, void *obj, const ErlNifPid *target_pid,
354354
globalcontext_get_process_unlock(env->global, target);
355355

356356
if (mon) {
357-
*mon = ref_ticks;
357+
mon->ref_ticks = ref_ticks;
358+
mon->resource_type = resource_type;
358359
}
359360

360361
return 0;
@@ -380,7 +381,10 @@ void resource_type_fire_monitor(struct ResourceType *resource_type, ErlNifEnv *e
380381
synclist_unlock(&resource_type->monitors);
381382

382383
if (refc) {
383-
resource_type->down(env, resource, &process_id, &ref_ticks);
384+
ErlNifMonitor monitor;
385+
monitor.ref_ticks = ref_ticks;
386+
monitor.resource_type = resource_type;
387+
resource_type->down(env, resource, &process_id, &monitor);
384388
refc_binary_decrement_refcount(refc, env->global);
385389
}
386390
}
@@ -404,17 +408,21 @@ void resource_type_demonitor(struct ResourceType *resource_type, uint64_t ref_ti
404408
int enif_demonitor_process(ErlNifEnv *env, void *obj, const ErlNifMonitor *mon)
405409
{
406410
GlobalContext *global = env->global;
407-
struct RefcBinary *resource = refc_binary_from_data(obj);
408-
struct ResourceType *resource_type = resource->resource_type;
409-
if (resource_type == NULL || resource_type->down == NULL) {
411+
struct ResourceType *resource_type = mon->resource_type;
412+
if (resource_type->down == NULL) {
410413
return -1;
411414
}
412415

413416
struct ListHead *monitors = synclist_wrlock(&resource_type->monitors);
414417
struct ListHead *item;
415418
LIST_FOR_EACH (item, monitors) {
416419
struct ResourceMonitor *monitor = GET_LIST_ENTRY(item, struct ResourceMonitor, resource_list_head);
417-
if (monitor->ref_ticks == *mon) {
420+
if (monitor->ref_ticks == mon->ref_ticks) {
421+
struct RefcBinary *resource = refc_binary_from_data(obj);
422+
if (resource->resource_type != mon->resource_type) {
423+
return -1;
424+
}
425+
418426
Context *target = globalcontext_get_process_lock(global, monitor->process_id);
419427
if (target) {
420428
mailbox_send_ref_signal(target, DemonitorSignal, monitor->ref_ticks);
@@ -457,8 +465,8 @@ void destroy_resource_monitors(struct RefcBinary *resource, GlobalContext *globa
457465

458466
int enif_compare_monitors(const ErlNifMonitor *monitor1, const ErlNifMonitor *monitor2)
459467
{
460-
uint64_t ref_ticks1 = *monitor1;
461-
uint64_t ref_ticks2 = *monitor2;
468+
uint64_t ref_ticks1 = monitor1->ref_ticks;
469+
uint64_t ref_ticks2 = monitor2->ref_ticks;
462470
if (ref_ticks1 < ref_ticks2) {
463471
return -1;
464472
}

tests/test-enif.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030

3131
static uint32_t cb_read_resource = 0;
3232
static int32_t down_pid = 0;
33-
static ErlNifMonitor down_mon = 0;
33+
static ErlNifMonitor down_mon = { NULL, 0 };
3434

3535
static uint32_t cb_read_resource_two = 0;
3636
static int32_t down_pid_two = 0;
37-
static ErlNifMonitor down_mon_two = 0;
37+
static ErlNifMonitor down_mon_two = { NULL, 0 };
3838

3939
static int32_t lockable_pid = 0;
4040

@@ -233,7 +233,7 @@ void test_resource_monitor()
233233
// Monitor called on destroy
234234
cb_read_resource = 0;
235235
down_pid = 0;
236-
down_mon = 0;
236+
down_mon.ref_ticks = 0;
237237
ctx = context_new(glb);
238238
pid = ctx->process_id;
239239
monitor_result = enif_monitor_process(&env, ptr, &pid, &mon);
@@ -248,7 +248,7 @@ void test_resource_monitor()
248248
// Monitor not called if demonitored
249249
cb_read_resource = 0;
250250
down_pid = 0;
251-
down_mon = 0;
251+
down_mon.ref_ticks = 0;
252252
ctx = context_new(glb);
253253
pid = ctx->process_id;
254254
monitor_result = enif_monitor_process(&env, ptr, &pid, &mon);
@@ -265,7 +265,7 @@ void test_resource_monitor()
265265
// Monitor not called if resource is deallocated
266266
cb_read_resource = 0;
267267
down_pid = 0;
268-
down_mon = 0;
268+
down_mon.ref_ticks = 0;
269269
ctx = context_new(glb);
270270
pid = ctx->process_id;
271271
monitor_result = enif_monitor_process(&env, ptr, &pid, &mon);
@@ -317,7 +317,7 @@ void test_resource_monitor_handler_can_lock()
317317
// Monitor called on destroy
318318
cb_read_resource = 0;
319319
down_pid = 0;
320-
down_mon = 0;
320+
down_mon.ref_ticks = 0;
321321
ctx = context_new(glb);
322322
another_ctx = context_new(glb);
323323
lockable_pid = another_ctx->process_id;
@@ -381,8 +381,8 @@ void test_resource_monitor_two_resources_two_processes()
381381
cb_read_resource = 0;
382382
down_pid = 0;
383383
down_pid_two = 0;
384-
down_mon = 0;
385-
down_mon_two = 0;
384+
down_mon.ref_ticks = 0;
385+
down_mon_two.ref_ticks = 0;
386386
ctx_1 = context_new(glb);
387387
ctx_2 = context_new(glb);
388388
pid_1 = ctx_1->process_id;
@@ -411,7 +411,7 @@ void test_resource_monitor_two_resources_two_processes()
411411
cb_read_resource = 0;
412412
cb_read_resource_two = 0;
413413
down_pid = 0;
414-
down_mon = 0;
414+
down_mon.ref_ticks = 0;
415415

416416
// Process #2 terminates, mon_3 is fired.
417417
scheduler_terminate(ctx_2);

0 commit comments

Comments
 (0)