mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2025-01-10 19:23:41 +01:00
Fix a race condition in dwarf unwinding.
Original code was accessing rs_cache memory without holding a lock in some cases. If there was sufficient cache pressure, entry being accessed may be overwritten by another thread, resulting in a data race. We now make a thread local copy of the data, before releasing the lock. If we end up supporting UNW_CACHE_PER_THREAD properly in the future, this memcpy should be unnecessary.
This commit is contained in:
parent
890a630d76
commit
9626d66019
3 changed files with 1512 additions and 4 deletions
|
@ -784,7 +784,7 @@ HIDDEN int
|
||||||
dwarf_find_save_locs (struct dwarf_cursor *c)
|
dwarf_find_save_locs (struct dwarf_cursor *c)
|
||||||
{
|
{
|
||||||
dwarf_state_record_t sr;
|
dwarf_state_record_t sr;
|
||||||
dwarf_reg_state_t *rs;
|
dwarf_reg_state_t *rs, rs_copy;
|
||||||
struct dwarf_rs_cache *cache;
|
struct dwarf_rs_cache *cache;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
intrmask_t saved_mask;
|
intrmask_t saved_mask;
|
||||||
|
@ -796,7 +796,7 @@ dwarf_find_save_locs (struct dwarf_cursor *c)
|
||||||
rs = rs_lookup(cache, c);
|
rs = rs_lookup(cache, c);
|
||||||
|
|
||||||
if (rs)
|
if (rs)
|
||||||
c->ret_addr_column = rs->ret_addr_column;
|
c->ret_addr_column = rs->ret_addr_column;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((ret = fetch_proc_info (c, c->ip, 1)) < 0 ||
|
if ((ret = fetch_proc_info (c, c->ip, 1)) < 0 ||
|
||||||
|
@ -816,8 +816,9 @@ dwarf_find_save_locs (struct dwarf_cursor *c)
|
||||||
put_unwind_info (c, &c->pi);
|
put_unwind_info (c, &c->pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy (&rs_copy, rs, sizeof (rs_copy));
|
||||||
put_rs_cache (c->as, cache, &saved_mask);
|
put_rs_cache (c->as, cache, &saved_mask);
|
||||||
if ((ret = apply_reg_state (c, rs)) < 0)
|
if ((ret = apply_reg_state (c, &rs_copy)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -41,7 +41,7 @@ endif #ARCH_IA64
|
||||||
Gtest-dyn1 Ltest-dyn1 \
|
Gtest-dyn1 Ltest-dyn1 \
|
||||||
test-async-sig test-flush-cache test-init-remote \
|
test-async-sig test-flush-cache test-init-remote \
|
||||||
test-mem test-setjmp test-ptrace \
|
test-mem test-setjmp test-ptrace \
|
||||||
Ltest-nomalloc
|
Ltest-nomalloc rs-race
|
||||||
noinst_PROGRAMS_cdep = forker mapper test-ptrace-misc test-varargs \
|
noinst_PROGRAMS_cdep = forker mapper test-ptrace-misc test-varargs \
|
||||||
Gperf-simple Lperf-simple
|
Gperf-simple Lperf-simple
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ test_ptrace_LDADD = ../src/libunwind-ptrace.a $(LIBUNWIND)
|
||||||
Ltest_concurrent_LDADD = $(LIBUNWIND) -lpthread
|
Ltest_concurrent_LDADD = $(LIBUNWIND) -lpthread
|
||||||
Gtest_concurrent_LDADD = $(LIBUNWIND) -lpthread
|
Gtest_concurrent_LDADD = $(LIBUNWIND) -lpthread
|
||||||
test_async_sig_LDADD = $(LIBUNWIND) -lpthread
|
test_async_sig_LDADD = $(LIBUNWIND) -lpthread
|
||||||
|
rs_race_LDADD = $(LIBUNWIND) -lpthread
|
||||||
|
|
||||||
LDADD += -ldl
|
LDADD += -ldl
|
||||||
Ltest_nomalloc_SOURCES = Ltest-nomalloc.c
|
Ltest_nomalloc_SOURCES = Ltest-nomalloc.c
|
||||||
|
|
1506
tests/rs-race.c
Normal file
1506
tests/rs-race.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue