Bug 18074 - crash using "info frame"
Summary: crash using "info frame"
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: gdb (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-03-02 20:25 UTC by Tom Tromey
Modified: 2017-03-17 19:05 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tom Tromey 2015-03-02 20:25:41 UTC
The firefox jit compiler makes stack frames that aren't
unwindable by gdb.

I was trying to unwind one by hand and happened to use "info frame"
with an address argument.  gdb crashed.

I'm using a git master gdb from today on x86-64 Fedora 20.


(gdb) info frame 0x7fffffffdac0
Stack frame at 0x7fffffffdac0:
 rip = 0x0; saved rip = 0x7ffff00517cc
 Outermost frame: previous frame identical to this frame (corrupt stack?)
 Arglist at 0x7fffffffda78, args: 
 Locals at 0x7fffffffda78, Previous frame's sp is 0x7fffffffda88
../../binutils-gdb/gdb/value.c:3818: internal-error: value_fetch_lazy: Assertion `frame != NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) 

Here's the stack trace:

#0  0x00007f6a0e8d3700 in __poll_nocancel ()
    at ../sysdeps/unix/syscall-template.S:81
#1  0x00000000005c35ef in gdb_wait_for_event (block=block@entry=1)
    at ../../binutils-gdb/gdb/event-loop.c:696
#2  0x00000000005c3c22 in gdb_do_one_event ()
    at ../../binutils-gdb/gdb/event-loop.c:309
#3  0x0000000000688cd7 in gdb_readline_wrapper (
    prompt=prompt@entry=0x3184740 "../../binutils-gdb/gdb/value.c:3818: internal-error: value_fetch_lazy: Assertion `frame != NULL' failed.\nA problem internal to GDB has been detected,\nfurther debugging may prove unreliable.\nQuit this "...) at ../../binutils-gdb/gdb/top.c:842
#4  0x000000000068ca66 in defaulted_query (ctlstr=<optimized out>, 
    defchar=defchar@entry=0 '\000', args=args@entry=0x7fff2f3d1988)
    at ../../binutils-gdb/gdb/utils.c:1279
#5  0x000000000068ccce in query (ctlstr=<optimized out>)
    at ../../binutils-gdb/gdb/utils.c:1375
#6  0x000000000068cf6a in internal_vproblem (
    problem=problem@entry=0xa12a00 <internal_error_problem>, 
    file=<optimized out>, line=3818, fmt=<optimized out>, 
    ap=ap@entry=0x7fff2f3d1ab8) at ../../binutils-gdb/gdb/utils.c:746
#7  0x000000000068d0b9 in internal_verror (file=<optimized out>, 
    line=<optimized out>, fmt=<optimized out>, ap=ap@entry=0x7fff2f3d1ab8)
    at ../../binutils-gdb/gdb/utils.c:811
#8  0x00000000006be452 in internal_error (
    file=file@entry=0x811690 "../../binutils-gdb/gdb/value.c", 
    line=line@entry=3818, fmt=<optimized out>)
    at ../../binutils-gdb/gdb/common/errors.c:55
#9  0x000000000056838c in value_fetch_lazy (val=val@entry=0x498ed70)
    at ../../binutils-gdb/gdb/value.c:3818
#10 0x0000000000568b28 in value_optimized_out (value=value@entry=0x498ed70)
    at ../../binutils-gdb/gdb/value.c:1351
#11 0x0000000000692213 in frame_register_unwind (frame=frame@entry=0xe82300, 
    regnum=regnum@entry=0, optimizedp=optimizedp@entry=0x7fff2f3d1cf4, 
    unavailablep=unavailablep@entry=0x7fff2f3d1cf8, 
    lvalp=lvalp@entry=0x7fff2f3d1cf0, addrp=addrp@entry=0x7fff2f3d1d08, 
    realnump=realnump@entry=0x7fff2f3d1cfc, bufferp=bufferp@entry=0x0)
    at ../../binutils-gdb/gdb/frame.c:1020
#12 0x00000000005b6043 in frame_info (addr_exp=<optimized out>, 
    from_tty=<optimized out>) at ../../binutils-gdb/gdb/stack.c:1676
#13 0x00000000006889ed in execute_command (p=<optimized out>, 
    p@entry=0xdcb1a0 "info frame 0x7fffffffdac0", from_tty=1)
    at ../../binutils-gdb/gdb/top.c:476
#14 0x00000000005c4a01 in command_handler (
    command=0xdcb1a0 "info frame 0x7fffffffdac0")
    at ../../binutils-gdb/gdb/event-top.c:494
#15 0x00000000005c4f5c in command_line_handler (rl=<optimized out>)
    at ../../binutils-gdb/gdb/event-top.c:692
#16 0x00000000006d9710 in rl_callback_read_char ()
    at ../../binutils-gdb/readline/callback.c:220
#17 0x00000000005c4a69 in rl_callback_read_char_wrapper (
    client_data=<optimized out>) at ../../binutils-gdb/gdb/event-top.c:171
#18 0x00000000005c4ab3 in stdin_event_handler (error=<optimized out>, 
    client_data=0x0) at ../../binutils-gdb/gdb/event-top.c:432
#19 0x00000000005c39f9 in gdb_wait_for_event (block=block@entry=0)
    at ../../binutils-gdb/gdb/event-loop.c:772
#20 0x00000000005c3bf0 in gdb_do_one_event ()
    at ../../binutils-gdb/gdb/event-loop.c:284
#21 0x00000000005c3ca7 in start_event_loop ()
    at ../../binutils-gdb/gdb/event-loop.c:334
#22 0x00000000005bda53 in captured_command_loop (data=data@entry=0x0)
    at ../../binutils-gdb/gdb/main.c:321
#23 0x00000000005bac95 in catch_errors (
    func=func@entry=0x5bda40 <captured_command_loop>, 
    func_args=func_args@entry=0x0, errstring=errstring@entry=0x7967a0 "", 
    mask=mask@entry=RETURN_MASK_ALL) at ../../binutils-gdb/gdb/exceptions.c:235
#24 0x00000000005be9ae in captured_main (data=data@entry=0x7fff2f3d2110)
    at ../../binutils-gdb/gdb/main.c:1148
#25 0x00000000005bac95 in catch_errors (
    func=func@entry=0x5bdf60 <captured_main>, 
    func_args=func_args@entry=0x7fff2f3d2110, 
    errstring=errstring@entry=0x7967a0 "", mask=mask@entry=RETURN_MASK_ALL)
    at ../../binutils-gdb/gdb/exceptions.c:235
#26 0x00000000005bee5b in gdb_main (args=args@entry=0x7fff2f3d2110)
    at ../../binutils-gdb/gdb/main.c:1156
#27 0x0000000000463835 in main (argc=<optimized out>, argv=<optimized out>)
    at ../../binutils-gdb/gdb/gdb.c:32
Comment 1 Pedro Alves 2015-03-02 22:58:39 UTC
Man, we _still_ haven't fixed this...  :-/

"frame ADDR" / "info frame ADDR" are very broken at several levels, IMO.  Even though in your case, you explicitly wanted a frame at an address, the fact that the user can typo a frame number and GDB ends up creating a new frame on the spot is quite misleading.  It should be an explicit switch for "create frame if you can't find it in the frame frame", IMO:

  https://sourceware.org/ml/gdb/2014-11/msg00028.html

In addition, I think I'd expect "bt" after "frame ADDR" to attempt to backtracing starting at that created frame.

The crash in this case is a different bogosity: parse_frame_specification at the tail end creates the new frame, but "current_frame" is not set to point at it.  So, here:

3808          while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
3809            {
3810              struct frame_id frame_id = VALUE_FRAME_ID (new_val);
3811
3812              frame = frame_find_by_id (frame_id);
3813              regnum = VALUE_REGNUM (new_val);

This looks up that frame that was created for ADDR in the frame chain, starting at current_frame, and of course that never finds that hacked up frame...

Maybe parse_frame_specification should override current_frame.  But it isn't that simple: we also need to handle the cases where gdb switches thread/frame behind the user's back temporarily, and then restores them (do_restore_current_thread_cleanup / restore_selected_frame use), in which case we'd need to restore that cooked up frame.
Comment 2 Tom Tromey 2015-03-03 15:03:59 UTC
(In reply to Pedro Alves from comment #1)

> "frame ADDR" / "info frame ADDR" are very broken at several levels, IMO. 

Yeah.  Also the docs could use a little love :-)

> Even though in your case, you explicitly wanted a frame at an address, the
> fact that the user can typo a frame number and GDB ends up creating a new
> frame on the spot is quite misleading.  It should be an explicit switch for
> "create frame if you can't find it in the frame frame", IMO:

Good idea.

> In addition, I think I'd expect "bt" after "frame ADDR" to attempt to
> backtracing starting at that created frame.
[...]
> Maybe parse_frame_specification should override current_frame.  But it isn't
> that simple: we also need to handle the cases where gdb switches
> thread/frame behind the user's back temporarily, and then restores them
> (do_restore_current_thread_cleanup / restore_selected_frame use), in which
> case we'd need to restore that cooked up frame.

It didn't even occur to me that "frame ADDR" might affect gdb's state.
I was viewing it as purely a "playing around" command, where I might
try something and see if the results look sensible.

I would not mind a separate command to change gdb's state.
Like "bt assuming ADDR" or "frame remember ADDR" or, well, whatever
spelling seems good.
Comment 3 An-jie Yang 2015-03-18 20:55:20 UTC
(In reply to Pedro Alves from comment #1)
> Man, we _still_ haven't fixed this...  :-/
> 
> "frame ADDR" / "info frame ADDR" are very broken at several levels, IMO. 
> Even though in your case, you explicitly wanted a frame at an address, the
> fact that the user can typo a frame number and GDB ends up creating a new
> frame on the spot is quite misleading.  It should be an explicit switch for
> "create frame if you can't find it in the frame frame", IMO:
> 
>   https://sourceware.org/ml/gdb/2014-11/msg00028.html
> 
> In addition, I think I'd expect "bt" after "frame ADDR" to attempt to
> backtracing starting at that created frame.
> 
> The crash in this case is a different bogosity: parse_frame_specification at
> the tail end creates the new frame, but "current_frame" is not set to point
> at it.  So, here:
> 
> 3808          while (VALUE_LVAL (new_val) == lval_register && value_lazy
> (new_val))
> 3809            {
> 3810              struct frame_id frame_id = VALUE_FRAME_ID (new_val);
> 3811
> 3812              frame = frame_find_by_id (frame_id);
> 3813              regnum = VALUE_REGNUM (new_val);
> 
> This looks up that frame that was created for ADDR in the frame chain,
> starting at current_frame, and of course that never finds that hacked up
> frame...
> 
> Maybe parse_frame_specification should override current_frame.  But it isn't
> that simple: we also need to handle the cases where gdb switches
> thread/frame behind the user's back temporarily, and then restores them
> (do_restore_current_thread_cleanup / restore_selected_frame use), in which
> case we'd need to restore that cooked up frame.

I have the same problem and use a git master gdb on x86-32 ubuntu 14.04.

When I use "info frame" with a number witch not a number of frame in "bt", gdb crashed.

(gdb) info frame 2
Stack frame at 0x0:
 eip = 0x80484e1 in _start; saved eip = 0x80484e1
 Outermost frame: outermost
 caller of frame at 0xbffff500
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp in esp
(gdb) info frame 1
Stack frame at 0xbffff500:
 eip = 0xb7e2aa83 in __libc_start_main; saved eip = 0x80484e1
 called by frame at 0x0, caller of frame at 0xbffff490
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp is 0xbffff500
 Saved registers:
  ebx at 0xbffff4ec, ebp at 0xbffff4f8, esi at 0xbffff4f0, edi at 0xbffff4f4,
  eip at 0xbffff4fc
(gdb) info frame 4
value.c:3821: internal-error: value_fetch_lazy: Assertion `frame != NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.

This is a bug, please report it.  For instructions, see:
<http://www.gnu.org/software/gdb/bugs/>.

Aborted (core dumped)

I think the user who type the "info frame" should see that "The stack frame not found" rather then crash.

So I patch parse_frame_specification such that it wouldn't create a new frame when the frame number is not found. 

diff --git a/gdb/stack.c b/gdb/stack.c
index 76a2360..5da2be8 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1396,9 +1396,9 @@ parse_frame_specification_1 (const char *frame_exp, const
   /* We couldn't identify the frame as an existing frame, but
      perhaps we can create one with a single argument.  */
   if (numargs == 1)
-    return create_new_frame (addrs[0], 0);
+    error (_("The stack frame not found"));
   else if (numargs == 2)
-    return create_new_frame (addrs[0], addrs[1]);
+    error (_("The stack frame not found"));
   else
     error (_("Too many args in frame specification"));
 }

After I patch it, I tried to type "info frame" with wrong number and correct number,it work normally and does not crash.

But I don't know would there have any effects ?
Comment 4 Tom Tromey 2015-03-19 13:18:42 UTC
(In reply to An-jie Yang from comment #3)

> After I patch it, I tried to type "info frame" with wrong number and correct
> number,it work normally and does not crash.
> 
> But I don't know would there have any effects ?

Yeah, "frame" and "info frame" are defined to let you pass in
an address and then see what would happen if there were a frame there.
This is sometimes handy for debugging when the stack is partially
trashed or when there is some other problem unwinding.

It would be good, IMO, to require a separate syntax for this
(see comment #1); but removing it entirely would be a loss.
Comment 5 Linas Vepstas 2016-09-24 00:03:57 UTC
Me too, I hit this bug.

Also bug id=20385 seems to be a duplicate of this.