diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2018-04-24 15:41:21 +0200 |
---|---|---|
committer | Milian Wolff <mail@milianw.de> | 2018-05-07 21:38:19 +0200 |
commit | 3d473e183da99d9965112e45ab43f4d06c01ccab (patch) | |
tree | 9ede21541f9fb1b2feb99e450f8403cda77f4633 | |
parent | 3f689c9ee1562c44506f563c381036be88fb82de (diff) | |
download | libunwind-3d473e183da99d9965112e45ab43f4d06c01ccab.tar.gz |
Fix race conditions in validate_mem via atomics
When atomics are not available, the address cache is disabled.
Fixes data race found by TSAN:
WARNING: ThreadSanitizer: data race (pid=17824)
Read of size 8 at 0x7f4dac484140 by thread T3:
#0 validate_mem ../../src/x86_64/Ginit.c:201 (libunwind.so.
8+0x00000001f836)
#1 access_mem ../../src/x86_64/Ginit.c:242 (libunwind.so.8+0x00000001fe06)
#2 dwarf_get ../../include/tdep-x86_64/libunwind_i.h:168 (libunwind.so.
8+0x0000000221cf)
#3 _ULx86_64_access_reg ../../src/x86_64/Gregs.c:130 (libunwind.so.
8+0x000000022e99)
#4 _ULx86_64_get_reg ../../src/mi/Gget_reg.c:40 (libunwind.so.
8+0x00000001e5bc)
#5 apply_reg_state ../../src/dwarf/Gparser.c:796 (libunwind.so.
8+0x000000038209)
#6 _ULx86_64_dwarf_step ../../src/dwarf/Gparser.c:961 (libunwind.so.
8+0x000000039dbc)
#7 _ULx86_64_step ../../src/x86_64/Gstep.c:71 (libunwind.so.
8+0x00000002481f)
#8 trace_init_addr ../../src/x86_64/Gtrace.c:248 (libunwind.so.
8+0x000000026f0e)
#9 trace_lookup ../../src/x86_64/Gtrace.c:330 (libunwind.so.
8+0x000000027429)
#10 _ULx86_64_tdep_trace ../../src/x86_64/Gtrace.c:447 (libunwind.so.
8+0x00000002789a)
#11 unw_backtrace ../../src/mi/backtrace.c:69 (libunwind.so.
8+0x00000001cb07)
Previous write of size 8 at 0x7f4dac484140 by thread T2:
#0 validate_mem ../../src/x86_64/Ginit.c:220 (libunwind.so.
8+0x00000001fc54)
#1 access_mem ../../src/x86_64/Ginit.c:242 (libunwind.so.8+0x00000001fe06)
#2 dwarf_get ../../include/tdep-x86_64/libunwind_i.h:168 (libunwind.so.
8+0x0000000221cf)
#3 _ULx86_64_access_reg ../../src/x86_64/Gregs.c:130 (libunwind.so.
8+0x000000022e99)
#4 _ULx86_64_get_reg ../../src/mi/Gget_reg.c:40 (libunwind.so.
8+0x00000001e5bc)
#5 apply_reg_state ../../src/dwarf/Gparser.c:796 (libunwind.so.
8+0x000000038209)
#6 _ULx86_64_dwarf_step ../../src/dwarf/Gparser.c:961 (libunwind.so.
8+0x000000039dbc)
#7 _ULx86_64_step ../../src/x86_64/Gstep.c:71 (libunwind.so.
8+0x00000002481f)
#8 trace_init_addr ../../src/x86_64/Gtrace.c:248 (libunwind.so.
8+0x000000026f0e)
#9 trace_lookup ../../src/x86_64/Gtrace.c:330 (libunwind.so.
8+0x000000027429)
#10 _ULx86_64_tdep_trace ../../src/x86_64/Gtrace.c:447 (libunwind.so.
8+0x00000002789a)
#11 unw_backtrace ../../src/mi/backtrace.c:69 (libunwind.so.
8+0x00000001cb07)
Location is global 'last_good_addr' of size 32 at 0x7f4dac484140
(libunwind.so.8+0x000000273140)
-rw-r--r-- | src/x86_64/Ginit.c | 71 |
1 files changed, 50 insertions, 21 deletions
diff --git a/src/x86_64/Ginit.c b/src/x86_64/Ginit.c index 76e183e0..21d8c496 100644 --- a/src/x86_64/Ginit.c +++ b/src/x86_64/Ginit.c @@ -174,14 +174,56 @@ tdep_init_mem_validate (void) } /* Cache of already validated addresses */ +#if HAVE_ATOMIC_OPS_H #define NLGA 4 -static unw_word_t last_good_addr[NLGA]; -static int lga_victim; +static AO_T last_good_addr[NLGA]; +static AO_T lga_victim; static int -validate_mem (unw_word_t addr) +is_cached_valid_mem(unw_word_t addr) +{ + int i; + for (i = 0; i < NLGA; i++) + { + if (addr == AO_load(&last_good_addr[i])) + return 1; + } + return 0; +} + +static void +cache_valid_mem(unw_word_t addr) { int i, victim; + victim = AO_load(&lga_victim); + for (i = 0; i < NLGA; i++) { + if (AO_compare_and_swap(&last_good_addr[victim], 0, addr)) { + return; + } + victim = (victim + 1) % NLGA; + } + + /* All slots full. Evict the victim. */ + AO_store(&last_good_addr[victim], addr); + victim = (victim + 1) % NLGA; + AO_store(&lga_victim, victim); +} +#else +static int +is_cached_valid_mem(unw_word_t addr UNUSED) +{ + return 0; +} + +static void +cache_valid_mem(unw_word_t addr UNUSED) +{ +} +#endif + +static int +validate_mem (unw_word_t addr) +{ size_t len; if (PAGE_START(addr + sizeof (unw_word_t) - 1) == PAGE_START(addr)) @@ -194,28 +236,13 @@ validate_mem (unw_word_t addr) if (addr == 0) return -1; - for (i = 0; i < NLGA; i++) - { - if (last_good_addr[i] && (addr == last_good_addr[i])) - return 0; - } + if (is_cached_valid_mem(addr)) + return 0; if (mem_validate_func ((void *) addr, len) == -1) return -1; - victim = lga_victim; - for (i = 0; i < NLGA; i++) { - if (!last_good_addr[victim]) { - last_good_addr[victim++] = addr; - return 0; - } - victim = (victim + 1) % NLGA; - } - - /* All slots full. Evict the victim. */ - last_good_addr[victim] = addr; - victim = (victim + 1) % NLGA; - lga_victim = victim; + cache_valid_mem(addr); return 0; } @@ -330,8 +357,10 @@ x86_64_local_addr_space_init (void) local_addr_space.acc.get_proc_name = get_static_proc_name; unw_flush_cache (&local_addr_space, 0, 0); +#if NLGA > 0 memset (last_good_addr, 0, sizeof (unw_word_t) * NLGA); lga_victim = 0; +#endif } #endif /* !UNW_REMOTE_ONLY */ |