Bug 10728

Summary: gdb 7.0 branch: infinite loop evaluating pointer difference w/o complete debug info
Product: gdb Reporter: scott snyder <snyder>
Component: expAssignee: Chris Moller <cmoller>
Status: RESOLVED FIXED    
Severity: normal CC: gdb-prs, gerardo.ganis, tromey
Priority: P2    
Version: 6.8   
Target Milestone: 6.8   
Host: x86_64-unknown-linux-gnu Target: x86_64-unknown-linux-gnu
Build: x86_64-unknown-linux-gnu Last reconfirmed:

Description scott snyder 2009-10-03 03:16:41 UTC
hi -

Running gdb from the head of the gdb_7_0-branch, i found that trying
to evaluate the difference between two pointers for which complete
debugging information is not available causes gdb to go into an
infinite loop.

Here's an example of how to reproduce this problem.
This was done using gcc 4.1.2 on a Scientific Linux 5.3 system.

Here are the sources:

-- x.h -------------------------------------
struct Y;
struct X
{
  Y* y1;
  Y* y2;
};

X* y();
-- x.cc ------------------------------------
#include "x.h"

int main()
{
  X* x = y();
  return 0;
}
-- y.cc ------------------------------------
#include "x.h"
struct Y{};

X* y()
{
  static X xx;
  static Y yy;
  xx.y1 = &yy;
  xx.y2 = xx.y1+1;
  return &xx;
}
--------------------------------------------

Build like this:

$ g++ -c y.cc
$ g++ -g -o x x.cc y.o

Then run under the debugger:


$ .../gdb ./x
...
GNU gdb (GDB) 6.8.92.20091003-cvs
...
This GDB was configured as "x86_64-unknown-linux-gnu".
...
(gdb) break main
Breakpoint 1 at 0x400530: file x.cc, line 5.
(gdb) run
Starting program: /direct/usatlas+u/snyder/x 

Breakpoint 1, main () at x.cc:5
5         X* x = y();
(gdb) n
6         return 0;
(gdb) p *x
$1 = {y1 = 0x600990, y2 = 0x600991}
(gdb) p x->y2-x->y1

At which point gdb goes into an infinite loop.

Attaching to the looping gdb gives this stack trace:

0x0000000000510fc4 in value_ptrdiff (arg1=0x7ba1840, arg2=0x7ba1a70)
    at valarith.c:125
125       return (value_as_long (arg1) - value_as_long (arg2)) / sz;
(gdb) bt
#0  0x0000000000510fc4 in value_ptrdiff (arg1=0x7ba1840, arg2=0x7ba1a70)
    at valarith.c:125
#1  0x000000000050793a in evaluate_subexp_standard (expect_type=0x0, 
    exp=0x7ba13a0, pos=0x7fff779a07fc, noside=EVAL_NORMAL) at eval.c:1802
#2  0x00000000005caa0f in evaluate_subexp_c (expect_type=0x0, exp=0x7ba13a0, 
    pos=0x7fff779a07fc, noside=EVAL_NORMAL) at c-lang.c:1038
#3  0x0000000000502cc7 in evaluate_subexp (expect_type=0x0, exp=0x7ba13a0, 
    pos=0x7fff779a07fc, noside=EVAL_NORMAL) at eval.c:73
#4  0x0000000000502e82 in evaluate_expression (exp=0x7ba13a0) at eval.c:163
#5  0x0000000000517fd9 in print_command_1 (exp=0x7a35102 "x->y2-x->y1", 
    inspect=0, voidprint=1) at ./printcmd.c:898
#6  0x0000000000518158 in print_command (exp=0x7a35102 "x->y2-x->y1", 
    from_tty=1) at ./printcmd.c:946
#7  0x00000000004aef74 in do_cfunc (c=0x7a6ff20, args=0x7a35102 "x->y2-x->y1", 
    from_tty=1) at ./cli/cli-decode.c:67
#8  0x00000000004b1e71 in cmd_func (cmd=0x7a6ff20, 
    args=0x7a35102 "x->y2-x->y1", from_tty=1) at ./cli/cli-decode.c:1738
...

In the expression here, sz is 0:

(gdb) p sz
$1 = 0

Trying to continue gives:

(gdb) c
Continuing.

Program received signal SIGFPE, Arithmetic exception.
0x0000000000510fc4 in value_ptrdiff (arg1=0x7ba1840, arg2=0x7ba1a70)
    at valarith.c:125
125       return (value_as_long (arg1) - value_as_long (arg2)) / sz;


So we're endlessly trying to divide by zero.

I made this patch to protect against this.

2009-10-02  scott s snyder  <snyder@bnl.gov>

	* valarith.c (value_ptrdiff): sz can be zero if the target type of
	the pointer is not complete.  Protect against division by zero,
	which will cause an infinite loop.

Index: valarith.c
===================================================================
RCS file: /cvs/src/src/gdb/valarith.c,v
retrieving revision 1.76
diff -u -p -r1.76 valarith.c
--- valarith.c	2 Jul 2009 17:25:59 -0000	1.76
+++ valarith.c	3 Oct 2009 02:59:13 -0000
@@ -122,6 +122,10 @@ First argument of `-' is a pointer and s
 an integer nor a pointer of the same type."));
 
   sz = TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (type1)));
+  if (sz == 0) {
+    error (_("Cannot perform pointer math on incomplete types, "
+             "try casting to a known type, or void *."));
+  }
   return (value_as_long (arg1) - value_as_long (arg2)) / sz;
 }
Comment 1 Sourceware Commits 2010-02-08 18:28:06 UTC
Subject: Bug 10728

CVSROOT:	/cvs/src
Module name:	src
Changes by:	cmoller@sourceware.org	2010-02-08 18:27:53

Modified files:
	gdb            : ChangeLog valarith.c 
	gdb/testsuite  : ChangeLog 
	gdb/testsuite/gdb.cp: Makefile.in 
Added files:
	gdb/testsuite/gdb.cp: pr10728-x.cc pr10728-x.h pr10728-y.cc 
	                      pr10728.exp 

Log message:
	PR gdb/10728
	* valarith.c (value_ptrdiff): Added a test for a zero type length,
	warn if found, and assume length = 1.
	* gdb.cp/pr10728-x.h: New file.
	* gdb.cp/pr10728-x.cc: New file.
	* gdb.cp/pr10728-y.cc: New file.
	* gdb.cp/pr10728.exp: New file.
	* gdb.cp/Makefile.in (EXECUTABLES): Add pr10728

Patches:
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/ChangeLog.diff?cvsroot=src&r1=1.11334&r2=1.11335
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/valarith.c.diff?cvsroot=src&r1=1.79&r2=1.80
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/ChangeLog.diff?cvsroot=src&r1=1.2125&r2=1.2126
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/gdb.cp/pr10728-x.cc.diff?cvsroot=src&r1=NONE&r2=1.1
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/gdb.cp/pr10728-x.h.diff?cvsroot=src&r1=NONE&r2=1.1
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/gdb.cp/pr10728-y.cc.diff?cvsroot=src&r1=NONE&r2=1.1
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/gdb.cp/pr10728.exp.diff?cvsroot=src&r1=NONE&r2=1.1
http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/gdb/testsuite/gdb.cp/Makefile.in.diff?cvsroot=src&r1=1.8&r2=1.9

Comment 2 Chris Moller 2010-02-08 18:53:03 UTC
Fixed by the patch shown below.
Comment 3 gerardo.ganis 2010-11-03 09:32:16 UTC
Hello,

I have a very similar problem with kubuntu 10.04, gcc 4.4.3, gdb 7.1-ubuntu .
GDB goes into infinite loops when just trying to print a pointer if the debug information is incomplete.
Should I open a new, separate bug report?

Gerardo Ganis
Comment 4 Tom Tromey 2010-11-15 17:10:13 UTC
Yes, please make a new bug report with all the details.
Thanks.