diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2016-01-26 14:58:44 +0100 |
---|---|---|
committer | Markus Metzger <markus.t.metzger@intel.com> | 2016-02-12 09:49:48 +0100 |
commit | 33b4777ca1b7b456af8201b98eda27d1b272cbab (patch) | |
tree | ed42f9fc456aba8951c1abcb4ecf050b434caaab /gdb/frame.c | |
parent | a038fa3e14a477d4d72a26c2e139fa47d2774be2 (diff) | |
download | gdb-33b4777ca1b7b456af8201b98eda27d1b272cbab.zip gdb-33b4777ca1b7b456af8201b98eda27d1b272cbab.tar.gz gdb-33b4777ca1b7b456af8201b98eda27d1b272cbab.tar.bz2 |
btrace, frame: fix crash in get_frame_type
In skip_artificial_frames we repeatedly call get_prev_frame_always until we get
a non-inline and non-tailcall frame assuming that there must be such a frame
eventually.
For record targets, however, we may have a frame chain that consists only of
artificial frames. This leads to a crash in get_frame_type when dereferencing a
NULL frame pointer.
Change skip_artificial_frames and skip_tailcall_frames to return NULL in such a
case and modify each caller to cope with a NULL return.
In frame_unwind_caller_pc and frame_unwind_caller_arch, we simply assert that
the returned value is not NULL. Their caller was supposed to check
frame_unwind_caller_id before calling those functions.
In other cases, we thrown an error.
In infcmd further move the skip_tailcall_frames call to the forward-stepping
case since we don't need a frame for reverse execution and we don't want to fail
because of that. Reverse-finish does make sense for a tailcall frame.
gdb/
* frame.h (skip_tailcall_frames): Update comment.
* frame.c (skip_artificial_frames, skip_tailcall_frames): Return NULL
if only artificial frames are found. Update comment.
(frame_unwind_caller_id): Handle NULL return.
(frame_unwind_caller_pc, frame_unwind_caller_arch): Assert that
skip_artificial_frames does not return NULL.
(frame_pop): Add an error if only tailcall frames are found.
* infcmd.c (finish_command): Move skip_tailcall_frames call into forward-
execution case. Add an error if only tailcall frames are found.
testsuite/
* gdb.btrace/tailcall-only.exp: New.
* gdb.btrace/tailcall-only.c: New.
* gdb.btrace/x86_64-tailcall-only.S: New.
* gdb.btrace/i686-tailcall-only.S: New.
Diffstat (limited to 'gdb/frame.c')
-rw-r--r-- | gdb/frame.c | 48 |
1 files changed, 40 insertions, 8 deletions
diff --git a/gdb/frame.c b/gdb/frame.c index b7832c7..d621dd7 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -420,7 +420,8 @@ fprint_frame (struct ui_file *file, struct frame_info *fi) /* Given FRAME, return the enclosing frame as found in real frames read-in from inferior memory. Skip any previous frames which were made up by GDB. - Return the original frame if no immediate previous frames exist. */ + Return FRAME if FRAME is a non-artificial frame. + Return NULL if FRAME is the start of an artificial-only chain. */ static struct frame_info * skip_artificial_frames (struct frame_info *frame) @@ -428,12 +429,17 @@ skip_artificial_frames (struct frame_info *frame) /* Note we use get_prev_frame_always, and not get_prev_frame. The latter will truncate the frame chain, leading to this function unintentionally returning a null_frame_id (e.g., when the user - sets a backtrace limit). This is safe, because as these frames - are made up by GDB, there must be a real frame in the chain - below. */ + sets a backtrace limit). + + Note that for record targets we may get a frame chain that consists + of artificial frames only. */ while (get_frame_type (frame) == INLINE_FRAME || get_frame_type (frame) == TAILCALL_FRAME) - frame = get_prev_frame_always (frame); + { + frame = get_prev_frame_always (frame); + if (frame == NULL) + break; + } return frame; } @@ -444,7 +450,13 @@ struct frame_info * skip_tailcall_frames (struct frame_info *frame) { while (get_frame_type (frame) == TAILCALL_FRAME) - frame = get_prev_frame (frame); + { + /* Note that for record targets we may get a frame chain that consists of + tailcall frames only. */ + frame = get_prev_frame (frame); + if (frame == NULL) + break; + } return frame; } @@ -507,6 +519,9 @@ frame_unwind_caller_id (struct frame_info *next_frame) requests the frame ID of "main()"s caller. */ next_frame = skip_artificial_frames (next_frame); + if (next_frame == NULL) + return null_frame_id; + this_frame = get_prev_frame_always (next_frame); if (this_frame) return get_frame_id (skip_artificial_frames (this_frame)); @@ -880,7 +895,14 @@ frame_unwind_pc (struct frame_info *this_frame) CORE_ADDR frame_unwind_caller_pc (struct frame_info *this_frame) { - return frame_unwind_pc (skip_artificial_frames (this_frame)); + this_frame = skip_artificial_frames (this_frame); + + /* We must have a non-artificial frame. The caller is supposed to check + the result of frame_unwind_caller_id (), which returns NULL_FRAME_ID + in this case. */ + gdb_assert (this_frame != NULL); + + return frame_unwind_pc (this_frame); } int @@ -985,6 +1007,9 @@ frame_pop (struct frame_info *this_frame) entering THISFRAME. */ prev_frame = skip_tailcall_frames (prev_frame); + if (prev_frame == NULL) + error (_("Cannot find the caller frame.")); + /* Make a copy of all the register values unwound from this frame. Save them in a scratch buffer so that there isn't a race between trying to extract the old values from the current regcache while @@ -2571,7 +2596,14 @@ frame_unwind_arch (struct frame_info *next_frame) struct gdbarch * frame_unwind_caller_arch (struct frame_info *next_frame) { - return frame_unwind_arch (skip_artificial_frames (next_frame)); + next_frame = skip_artificial_frames (next_frame); + + /* We must have a non-artificial frame. The caller is supposed to check + the result of frame_unwind_caller_id (), which returns NULL_FRAME_ID + in this case. */ + gdb_assert (next_frame != NULL); + + return frame_unwind_arch (next_frame); } /* Gets the language of FRAME. */ |