diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2022-03-16 06:54:03 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2022-04-24 17:07:20 +0300 |
commit | 35d17ba4317433820494d422d19a7a8db8d92a01 (patch) | |
tree | 5daa3c2fed6c328a1b629fa218659b24c6c45ca4 | |
parent | 47bb700fc4125bf0898bfbfbf024a9712f33d3c3 (diff) | |
download | bdwgc-35d17ba4317433820494d422d19a7a8db8d92a01.tar.gz |
Fix SIGSEGV caused by dropped stack access from child process in gctest
(a cherry-pick of commit 296320cd3 from 'release-7_6')
Issue #256 (bdwgc).
Previously, a local variable was passed in gctest to
GC_move_disappearing_link as a link to clear, this led to a memory
access violation during garbage collection in the child process
if the fork occurred between GC_move_disappearing_link(..., &new_link)
and GC_unregister_disappearing_link(&new_link).
This commit changes the mentioned local variable (new_link) use - now
it contains a pointer to a heap-allocated link rather then containing
just a link.
* tests/test.c [!GC_NO_FINALIZATION] (mktree): Change type of new_link
from void* to void**; initialize it to GC_NEW(void*) and ensure it is
non-null; increment collectable_count, replace new_link with
*new_link; add comment new_link object is reclaimed correctly
even if the thread is dropped in the child process between
GC_move_disappearing_link(new_link) and
GC_unregister_disappearing_link.
-rw-r--r-- | tests/test.c | 27 |
1 files changed, 17 insertions, 10 deletions
diff --git a/tests/test.c b/tests/test.c index 7dc0a754..a6085028 100644 --- a/tests/test.c +++ b/tests/test.c @@ -841,9 +841,11 @@ tn * mktree(int n) if (counter++ % 119 == 0) { # ifndef GC_NO_FINALIZATION int my_index; - void *new_link; -# endif + void **new_link = GC_NEW(void *); + CHECK_OUT_OF_MEMORY(new_link); + collectable_count++; +# endif { FINALIZER_LOCK(); /* Losing a count here causes erroneous report of failure. */ @@ -873,18 +875,23 @@ tn * mktree(int n) GC_printf("GC_move_disappearing_link(link,link) failed\n"); FAIL; } - new_link = (void *)live_indicators[my_index]; + *new_link = (void *)live_indicators[my_index]; if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), - &new_link) != GC_SUCCESS) { + new_link) != GC_SUCCESS) { GC_printf("GC_move_disappearing_link(new_link) failed\n"); FAIL; } - if (GC_unregister_disappearing_link(&new_link) == 0) { + /* Note: if other thread is performing fork at this moment, */ + /* then the stack of the current thread is dropped (together */ + /* with new_link variable) in the child process, and */ + /* GC_dl_hashtbl entry with the link equal to new_link will be */ + /* removed when a collection occurs (as expected). */ + if (GC_unregister_disappearing_link(new_link) == 0) { GC_printf("GC_unregister_disappearing_link failed\n"); FAIL; } if (GC_move_disappearing_link((void **)(&(live_indicators[my_index])), - &new_link) != GC_NOT_FOUND) { + new_link) != GC_NOT_FOUND) { GC_printf("GC_move_disappearing_link(new_link) failed 2\n"); FAIL; } @@ -903,18 +910,18 @@ tn * mktree(int n) GC_printf("GC_move_long_link(link,link) failed\n"); FAIL; } - new_link = live_long_refs[my_index]; + *new_link = live_long_refs[my_index]; if (GC_move_long_link(&live_long_refs[my_index], - &new_link) != GC_SUCCESS) { + new_link) != GC_SUCCESS) { GC_printf("GC_move_long_link(new_link) failed\n"); FAIL; } - if (GC_unregister_long_link(&new_link) == 0) { + if (GC_unregister_long_link(new_link) == 0) { GC_printf("GC_unregister_long_link failed\n"); FAIL; } if (GC_move_long_link(&live_long_refs[my_index], - &new_link) != GC_NOT_FOUND) { + new_link) != GC_NOT_FOUND) { GC_printf("GC_move_long_link(new_link) failed 2\n"); FAIL; } |