This is the mail archive of the gdb-prs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug breakpoints/14901] New: Breakpoints can end up in the middle ofan instruction, causing corruption


http://sourceware.org/bugzilla/show_bug.cgi?id=14901

             Bug #: 14901
           Summary: Breakpoints can end up in the middle of an
                    instruction, causing corruption
           Product: gdb
           Version: 7.5
            Status: NEW
          Severity: normal
          Priority: P2
         Component: breakpoints
        AssignedTo: unassigned@sourceware.org
        ReportedBy: gdb@dima.secretsauce.net
    Classification: Unclassified


I'm using gdb 7.5.1, on a more-or-less vanilla amd64 box. I'm seeing that it is
possible to

1. set a breakpoint somewhere
2. load a new version of the executable being debugged
3. observe the breakpoint 0xCC byte end up in the middle of an instruction,
   causing havoc

The 0xCC byte corrupts the instruction it ends up in. Furthermore, unless one
knows that a breakpoint is involved, the observed behavior is extremely
mysterious. The output of "disass /r" doesn't display the 0xCC, so the user
thinks they're stepping through one instruction, while gdb is executing a
completely different one.

To reproduce, I have the following source I named tst.c:

==============================
#include <stdio.h>
#include <stdlib.h>

static void f(void)
{
#ifdef PAD
  char c = 3;
#endif
  int  a = 5;

  printf("0x%x\n", a);
}

int main(void)
{
  f();
  return 0;
}
==============================

I compile this with and without defining PAD:

gcc-4.7 -DPAD -g -o tst_pad tst.c; gcc-4.7 -g -o tst tst.c

I then load "tst_pad", set the breakpoint on the "a=5" line, then load "tst".
The old breakpoint stays at the same exact address, corrupting the a=5
statement.

This can be shown by running "gdb -x gdb.cmd" with gdb.cmd as

==============================
file /tmp/tst_pad
b main
r
s
n
b
info b
c
file /tmp/tst
r
info b
disass /r f
c
q
==============================

The output I see from this is

==============================
GNU gdb (GDB) 7.5.1
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Breakpoint 1 at 0x400539: file tst.c, line 16.

Breakpoint 1, main () at tst.c:16
16      f();
f () at tst.c:7
7      char c = 3;
9      int  a = 5;
Breakpoint 2 at 0x400518: file tst.c, line 9.
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400539 in main at tst.c:16
    breakpoint already hit 1 time
2       breakpoint     keep y   0x0000000000400518 in f at tst.c:9
0x5
[Inferior 1 (process 17031) exited normally]

Breakpoint 1, main () at tst.c:16
16      f();
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400535 in main at tst.c:16
    breakpoint already hit 1 time
2       breakpoint     keep y   0x0000000000400518 in f at tst.c:9
Dump of assembler code for function f:
   0x000000000040050c <+0>:    55    push   rbp
   0x000000000040050d <+1>:    48 89 e5    mov    rbp,rsp
   0x0000000000400510 <+4>:    48 83 ec 10    sub    rsp,0x10
   0x0000000000400514 <+8>:    c7 45 fc 05 00 00 00    mov    DWORD PTR
[rbp-0x4],0x5
   0x000000000040051b <+15>:    8b 45 fc    mov    eax,DWORD PTR [rbp-0x4]
   0x000000000040051e <+18>:    89 c6    mov    esi,eax
   0x0000000000400520 <+20>:    bf fc 05 40 00    mov    edi,0x4005fc
   0x0000000000400525 <+25>:    b8 00 00 00 00    mov    eax,0x0
   0x000000000040052a <+30>:    e8 b1 fe ff ff    call   0x4003e0 <printf@plt>
   0x000000000040052f <+35>:    c9    leave  
   0x0000000000400530 <+36>:    c3    ret    
End of assembler dump.
0xcc05
[Inferior 1 (process 17039) exited normally]
==============================

Note that the breakpoint is at 0x400518 in BOTH executables, and the "a"
variable ends up being 0xCC05 instead of 5. This is extremely perplexing to the
unsuspecting user, since the output of "disass /r" doesn't show the 0xCC. So
the
user steps through the instruction "mov DWORD PTR [rbp-0x4],0x5", but observes
something completely different happening!

gdb appears to have some safeguards against this. In the above script the
breakpoint was set with a simple "b" command when gdb was stopped at the
particular location. If it was set with "b 9" instead (breaking on line 9,
which
should be the same) then when loading the new executable, the breakpoint is
moved to 0x400514, which is at the start of an instruction, and produces more
predictable behavior.

I think it would be great if the breakpoint reinterpretation was either fixed,
or documented more clearly to indicate when the breakpoint should be moved and
when not. "help b" doesn't make clear that "b" and "b 9" produce very different
behaviors. Also, it would be great if "disass /r" had some sort of indication
that a particular instruction has been overwritten by a breakpoint; this would
make the observed failure much less baffling.

Thanks

-- 
Configure bugmail: http://sourceware.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are on the CC list for the bug.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]