diff options
author | Pedro Alves <pedro@palves.net> | 2020-07-23 16:29:28 +0100 |
---|---|---|
committer | Pedro Alves <pedro@palves.net> | 2020-07-23 16:29:28 +0100 |
commit | e7bc9db8f447e056f4faa11702230239b4075c2c (patch) | |
tree | 0ee125f1e067bcff441fb6f289ff20fcd02cb24d /gdb/frame-unwind.c | |
parent | 90fcc46681979a7759d16e738a98c9e666e6e88f (diff) | |
download | gdb-e7bc9db8f447e056f4faa11702230239b4075c2c.zip gdb-e7bc9db8f447e056f4faa11702230239b4075c2c.tar.gz gdb-e7bc9db8f447e056f4faa11702230239b4075c2c.tar.bz2 |
Don't touch frame_info objects if frame cache was reinitialized
This fixes yet another bug exposed by ASAN + multi-target.exp
Running an Asan-enabled GDB against gdb.multi/multi-target.exp exposed
yet another latent GDB bug. See here for the full log:
https://sourceware.org/pipermail/gdb-patches/2020-July/170761.html
As Simon described, the problem is:
- We create a new frame_info object in restore_selected_frame (by
calling find_relative_frame)
- The frame is allocated on the frame_cache_obstack
- In frame_unwind_try_unwinder, we try to find an unwinder for that
frame
- While trying unwinders, memory read fails because the remote target
closes, because of "monitor exit"
- That calls reinit_frame_cache (as shown above), which resets
frame_cache_obstack
- When handling the exception in frame_unwind_try_unwinder, we try to
set some things on the frame_info object (like *this_cache, which
in fact tries to write into frame_info::prologue_cache), but the
frame_info object is no more, it went away with the obstack.
Fix this by maintaining a frame cache generation counter. Then in
exception handling code paths, don't touch frame objects if the
generation is not the same as it was on entry.
This commit generalizes the gdb.server/server-kill.exp testcase and
reuses it to test the scenario in question. The new tests fail
without the GDB fix.
gdb/ChangeLog:
* frame-unwind.c (frame_unwind_try_unwinder): On exception, don't
touch THIS_CACHE/THIS_FRAME if the frame cache was cleared
meanwhile.
* frame.c (frame_cache_generation, get_frame_cache_generation):
New.
(reinit_frame_cache): Increment FRAME_CACHE_GENERATION.
(get_prev_frame_if_no_cycle): On exception, don't touch
PREV_FRAME/THIS_FRAME if the frame cache was cleared meanwhile.
* frame.h (get_frame_cache_generation): Declare.
gdb/testsuite/ChangeLog:
* gdb.server/server-kill.exp (prepare): New, factored out from the
top level.
(kill_server): New.
(test_tstatus, test_unwind_nosyms, test_unwind_syms): New.
(top level) : Call test_tstatus, test_unwind_nosyms, test_unwind_syms.
Diffstat (limited to 'gdb/frame-unwind.c')
-rw-r--r-- | gdb/frame-unwind.c | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c index 3334c47..064f6eb 100644 --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -121,6 +121,8 @@ frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache, { int res = 0; + unsigned int entry_generation = get_frame_cache_generation (); + frame_prepare_for_sniffer (this_frame, unwinder); try @@ -130,9 +132,14 @@ frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache, catch (const gdb_exception &ex) { /* Catch all exceptions, caused by either interrupt or error. - Reset *THIS_CACHE. */ - *this_cache = NULL; - frame_cleanup_after_sniffer (this_frame); + Reset *THIS_CACHE, unless something reinitialized the frame + cache meanwhile, in which case THIS_FRAME/THIS_CACHE are now + dangling. */ + if (get_frame_cache_generation () == entry_generation) + { + *this_cache = NULL; + frame_cleanup_after_sniffer (this_frame); + } if (ex.error == NOT_AVAILABLE_ERROR) { |