edit:
I've figured it out with the help of commenters. To answer the question posed in my title: No, it is not stack corruption, its gdb reporting the wrong values. The program actually behaves as expected and has the right this
pointer.
The actual buggy behaviour which prompted me to post this question is probably completely unrelated to the issue I describe here.
First a warning. I believe this is a memory corruption issue, and I would normally not expect an answer except for "check your code thoroughly", but I've seen this behaviour pop up repeatedly and was hoping some of you had insight on the problem and how I can find its source.
I'm currently implementing an interval static analysis which tracks the possible range of variables in a C program. The copy constructor for my base interval class looks like this:
itvt::itvt(const itvt& i)
: _i(i.type == INTBV ? new intbv_intervalt(i.i()) : NULL),
_f(i.type == FLOAT ? new float_intervalt(i.f()) : NULL),
type(i.type), other_bottom(i.other_bottom)
{ }
Now, I found a memory corruption bug and managed to trace it to the following snippet of code:
itvt itvt::get_split(bool le) const
{
itvt result(*this);
[...]
}
Using gdb, I find that the call to the constructor does not seem to construct the "result" object:
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) n
519 if(is_singleton() || is_bot())
(gdb) print result
$3 = {
_i = {
_M_ptr = 0x7fff5fbfe100
},
_f = {
_M_ptr = 0x7fff5fbfed60
},
type = 1606410016,
other_bottom = 255
}
(gdb) print *this
$4 = {
_i = {
_M_ptr = 0x1020833a0
},
_f = {
_M_ptr = 0x0
},
type = itvt::INTBV,
other_bottom = false
}
Looking deeper, I find that inside the copy constructor, the "this" pointer points to the wrong object:
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) print &result
$5 = (itvt *) 0x7fff5fbfdee0
(gdb) s
itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
500 type(i.type), other_bottom(i.other_bottom)
(gdb) print this
$6 = (itvt * const) 0x7fff5fbfdf80
Since "result" is allocated on the stack at address 0x7fff5fbfdee0, I would expect the "this" pointer inside the copy constructor to point to the same address. Instead it points to 0x7fff5fbfdf80.
It looks like the copy constructor is initialising something, but not the "result" object on the stack on which it is called. In fact, I can access the memory location that the constructor initialised perfectly well:
Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517 itvt result(*this);
(gdb) s
itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
500 type(i.type), other_bottom(i.other_bottom)
(gdb) finish
Run till exit from #0 itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
itvt::get_split (this=0x1016af560, le=false) at itv.cpp:519
519 if(is_singleton() || is_bot())
(gdb) print *((const itvt*) (0x7fff5fbfdf80))
$7 = {
_i = {
_M_ptr = 0x1016b6d10
},
_f = {
_M_ptr = 0x0
},
type = itvt::INTBV,
other_bottom = false
}
My first question: Can the fact that the "this" pointer points to the wrong object be explained as normal behaviour? It seems like some weird memory corruption issue, but maybe I'm missing something.
I'm compiling with g++ and "-O0 -ggdb" flags and did a fresh recompile of everything btw. Here's my g++ version:
leo@scythe ai$ g++ --version
i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
My second question: If it is memory corruption, do you have any advice on how I can track down the source. I usually follow such issues to their root cause using gdb, but I don't know where to look now.
This is not the first time I encounter this specific behaviour. I've seen it happen when looking into other people's bugs. I've never actually directly managed to address it directly, it just stopped happening or at least being a visible issue after some other code change. This leads me to believe that maybe its just an odd artefact of looking at the stack using gdb.
I thankful for any advice or insight you can offer.
edit: Here are is the relevant snippet of the itvt class:
class itvt
{
protected:
typedef std::auto_ptr<intbv_intervalt> iptrt;
typedef std::auto_ptr<float_intervalt> fptrt;
iptrt _i;
fptrt _f;
public:
typedef enum {INTBV, FLOAT, OTHER} itv_typet;
itv_typet type;
bool other_bottom;
//copy constr
itvt(const itvt& i);
inline intbv_intervalt& i() { return *_i; }
inline float_intervalt& f() { return *_f; }
inline const intbv_intervalt& i() const { return *_i; }
inline const float_intervalt& f() const { return *_f; }
itvt get_split(bool le) const;
[...]
};
Second question : use valgrind, it’s really what you need here. It tracks down every allocation / free, and tells you if you try and use freed memory, as well as lots of other stuff. It’ll show you a memory corruption.
I've figured it out with the help of commenters: It looks like gdb is not telling the truth, (possibly due to an old gdb version). The program actually behaves as expected.
The actual buggy behaviour which prompted me to look into the issue was completely unrelated to the object initialisation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With