Skip to content

Commit 508842b

Browse files
committed
Forward port changes from v0.6 release branch
Merge fixe for resources concurrency bug (#1603) and fixes for memory corruptions (#1600 and #1601) from release-0.6 branch.
2 parents 0848033 + d416d90 commit 508842b

File tree

7 files changed

+62
-10
lines changed

7 files changed

+62
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ memory error
9191
- Fixed memory corruption issue with `erlang:make_tuple/2`
9292
- Fix potential use after free with code generated from OTP <= 24
9393
- Fix `is_function/2` guard
94+
- Fixed segfault when calling `lists:reverse/1` (#1600)
95+
- Fixed nif_atomvm_posix_read GC bug
9496

9597
### Changed
9698

src/libAtomVM/context.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,7 @@ void context_destroy(Context *ctx)
180180
struct Monitor *monitor = GET_LIST_ENTRY(item, struct Monitor, monitor_list_head);
181181
void *resource = term_to_term_ptr(monitor->monitor_obj);
182182
struct RefcBinary *refc = refc_binary_from_data(resource);
183-
resource_type_demonitor(refc->resource_type, monitor->ref_ticks);
184-
refc->resource_type->down(&env, resource, &ctx->process_id, &monitor->ref_ticks);
183+
resource_type_fire_monitor(refc->resource_type, &env, resource, ctx->process_id, monitor->ref_ticks);
185184
free(monitor);
186185
}
187186
}

src/libAtomVM/nifs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4935,7 +4935,7 @@ static term nif_lists_reverse(Context *ctx, int argc, term argv[])
49354935
RAISE_ERROR(BADARG_ATOM);
49364936
}
49374937

4938-
if (UNLIKELY(memory_ensure_free_with_roots(ctx, len * CONS_SIZE, 2, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
4938+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, len * CONS_SIZE, argc, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
49394939
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
49404940
}
49414941

src/libAtomVM/posix_nifs.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,14 +362,17 @@ static term nif_atomvm_posix_read(Context *ctx, int argc, term argv[])
362362
VALIDATE_VALUE(count_term, term_is_integer);
363363
int count = term_to_int(count_term);
364364

365+
size_t size = term_binary_data_size_in_terms(count) + BINARY_HEADER_SIZE + TERM_BOXED_SUB_BINARY_SIZE + TUPLE_SIZE(2);
366+
if (UNLIKELY(memory_ensure_free_with_roots(ctx, size, argc, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
367+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
368+
}
369+
365370
void *fd_obj_ptr;
371+
366372
if (UNLIKELY(!enif_get_resource(erl_nif_env_from_context(ctx), argv[0], glb->posix_fd_resource_type, &fd_obj_ptr))) {
367373
RAISE_ERROR(BADARG_ATOM);
368374
}
369375
struct PosixFd *fd_obj = (struct PosixFd *) fd_obj_ptr;
370-
if (UNLIKELY(memory_ensure_free_opt(ctx, term_binary_data_size_in_terms(count) + BINARY_HEADER_SIZE + TERM_BOXED_SUB_BINARY_SIZE + TUPLE_SIZE(2), MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
371-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
372-
}
373376
term bin_term = term_create_uninitialized_binary(count, &ctx->heap, glb);
374377
int res = read(fd_obj->fd, (void *) term_binary_data(bin_term), count);
375378
if (UNLIKELY(res < 0)) {

src/libAtomVM/refc_binary.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ void refc_binary_increment_refcount(struct RefcBinary *refc)
6666
bool refc_binary_decrement_refcount(struct RefcBinary *refc, struct GlobalContext *global)
6767
{
6868
if (--refc->ref_count == 0) {
69+
if (refc->resource_type && refc->resource_type->down) {
70+
// There may be monitors associated with this resource.
71+
destroy_resource_monitors(refc, global);
72+
// After this point, the resource can no longer be found by
73+
// resource_type_fire_monitor
74+
// However, resource_type_fire_monitor may have incremented ref_count
75+
// to call the monitor handler.
76+
// So we check ref_count again. We're not affected by the ABA problem
77+
// here as the resource cannot (should not) be monitoring while it is
78+
// being destroyed, i.e. no resource monitor will be created now
79+
if (UNLIKELY(refc->ref_count != 0)) {
80+
return false;
81+
}
82+
}
6983
synclist_remove(&global->refc_binaries, &refc->head);
7084
refc_binary_destroy(refc, global);
7185
return true;
@@ -78,10 +92,6 @@ void refc_binary_destroy(struct RefcBinary *refc, struct GlobalContext *global)
7892
UNUSED(global);
7993

8094
if (refc->resource_type) {
81-
if (refc->resource_type->down) {
82-
// There may be monitors associated with this resource.
83-
destroy_resource_monitors(refc, global);
84-
}
8595
if (refc->resource_type->dtor) {
8696
ErlNifEnv env;
8797
erl_nif_env_partial_init_from_globalcontext(&env, global);

src/libAtomVM/resources.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,31 @@ int enif_monitor_process(ErlNifEnv *env, void *obj, const ErlNifPid *target_pid,
360360
return 0;
361361
}
362362

363+
void resource_type_fire_monitor(struct ResourceType *resource_type, ErlNifEnv *env, void *resource, int32_t process_id, uint64_t ref_ticks)
364+
{
365+
struct RefcBinary *refc = NULL;
366+
struct ListHead *monitors = synclist_wrlock(&resource_type->monitors);
367+
struct ListHead *item;
368+
LIST_FOR_EACH (item, monitors) {
369+
struct ResourceMonitor *monitor = GET_LIST_ENTRY(item, struct ResourceMonitor, resource_list_head);
370+
if (monitor->ref_ticks == ref_ticks) {
371+
// Resource still exists.
372+
refc = refc_binary_from_data(resource);
373+
refc_binary_increment_refcount(refc);
374+
list_remove(&monitor->resource_list_head);
375+
free(monitor);
376+
break;
377+
}
378+
}
379+
380+
synclist_unlock(&resource_type->monitors);
381+
382+
if (refc) {
383+
resource_type->down(env, resource, &process_id, &ref_ticks);
384+
refc_binary_decrement_refcount(refc, env->global);
385+
}
386+
}
387+
363388
void resource_type_demonitor(struct ResourceType *resource_type, uint64_t ref_ticks)
364389
{
365390
struct ListHead *monitors = synclist_wrlock(&resource_type->monitors);

src/libAtomVM/resources.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,19 @@ void destroy_resource_monitors(struct RefcBinary *resource, GlobalContext *globa
149149
*/
150150
term select_event_make_notification(void *rsrc_obj, uint64_t ref_ticks, bool is_write, Heap *heap);
151151

152+
/**
153+
* @brief Call down handler for a given resource and remove monitor from list.
154+
* @details handler is called while holding lock on the list of monitors and
155+
* if monitor is still in the list of resource monitors, thus ensuring that
156+
* the resource still exists.
157+
* @param resource_type type holding the list of monitors
158+
* @param env environment for calling the down handler
159+
* @param resource resource that monitored the process
160+
* @param process_id id of the process monitored
161+
* @param ref_ticks reference of the monitor
162+
*/
163+
void resource_type_fire_monitor(struct ResourceType *resource_type, ErlNifEnv *env, void *resource, int32_t process_id, uint64_t ref_ticks);
164+
152165
/**
153166
* @brief Remove monitor from list of monitors.
154167
* @param resource_type type holding the list of monitors

0 commit comments

Comments
 (0)