On Thu, Apr 2, 2009 at 7:09 PM, cpu <c...@chromium.org> wrote:

>
>
>
> On Apr 2, 3:53 pm, Nicolas Sylvain <nsylv...@chromium.org> wrote:
> > Another simple(r) example :
> http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx
> >
> > <http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx>But, as
> > discussed in bug 8544, we've see many purecall crashes that happens and
> we
> > don't
> > think it's related to virtual functions. The only thing I can think of is
> > that the vtable is corrupted. (overwritten or freed)
> >
> > Does it not make sense?
>
> I don't think you can overwrite a vtables because they should be in
> the code section of the executable (the pages marked as read-execute),
> they are known at compile time and it would not make sense to
> construct them on the fly.
>
> But if you know of a case then that would be very interesting.


yes they should be protected with read/execute and besides, you'd have to
overwrite entries in the vtable with a pointer to __purecall for that to
happen


>
>
>
> >
> > Nicolas
> >
> >
> >
> > On Thu, Apr 2, 2009 at 1:54 PM, cpu <c...@chromium.org> wrote:
> >
> > > After reading some speculation in bugs such as
> > >http://code.google.com/p/chromium/issues/detail?id=8544I felt
> > > compelled to dispel some myths and misunderstandings about the origin
> > > and meaning of the mythical _purecall_ exception. My hope is that then
> > > you can spot the problems in our source code and fix them. Sorry for
> > > the long post.
> >
> > > So first of all, what do you see when you get this error? if you are
> > > in a debug build and you are not eating the exceptions via some custom
> > > handler you see this dialog:
> >
> > > ---------------------------
> > > Debug Error!
> > > R6025
> > > - pure virtual function call
> > > (Press Retry to debug the application)
> > > ---------------------------
> > > Abort   Retry   Ignore
> > > ---------------------------
> >
> > > For chrome/chromium we install a special handler, which forces a crash
> > > dump in which case you'll see in in the debugger analysis something
> > > like this:
> >
> > >  [chrome_dll_main.cc:100] - `anonymous namespace'::PureCall()
> > >  [purevirt.c:47] - _purecall
> >
> > > Before going into too much detail, let me show you a small program
> > > that causes this exception:
> >
> > > =================================
> > > class Base {
> > >  public:
> > >  virtual ~Base() {
> > >    ThreeFn();
> > >  }
> >
> > >  virtual void OneFn() = 0;
> > >  virtual void TwoFn() = 0;
> >
> > >  void ThreeFn() {
> > >    OneFn();
> > >    TwoFn();
> > >  }
> > > };
> >
> > > class Concrete : public Base {
> > >  public:
> > >  Concrete() : state_(0) {
> > >  }
> >
> > >  virtual void OneFn() {
> > >    state_ += 1;
> > >  }
> > >  virtual void TwoFn() {
> > >    state_ += 2;
> > >  }
> > >  private:
> > >  int state_;
> > > };
> >
> > > int _tmain(int argc, _TCHAR* argv[]) {
> >
> > >  Concrete* obj = new  Concrete();
> > >  obj->OneFn();
> > >  obj->TwoFn();
> > >  obj->ThreeFn();
> >
> > >  delete obj;
> >
> > >  return 0;
> > > }
> > > =================================
> >
> > > Can you spot the problem? do you know at which line it crashes, do you
> > > know why? if so I have wasted your time, apologies. If you are unsure
> > > then read on.
> >
> > > This program crashes when trying to call OneFn() with a purecall
> > > exception on debug build. On release build it exits with no error, but
> > > your mileage might vary depending on what optimizations are active.
> >
> > > The call stack for the crash is:
> >
> > >        msvcr80d.dll!__purecall()  + 0x25                        <------
> > > shows the
> > > dialog (debug only)
> > >        app.exe!Base::ThreeFn()  Line 16 + 0xfc       <-----  error here
> > >        app.exe!Base::~Base()  Line 10  C++
> > >        app.exe!Concrete::~Concrete()  + 0x2b
> > >        app.exe!Concrete::`scalar deleting destructor'()  + 0x2b  <-----
> > > delete obj
> >
> > > So as you have guessed it has to do with calling virtual functions
> > > from a destructor.
> >
> > > What happens is that during construction an object evolves from the
> > > earliest base class to the actual type and during destruction the
> > > object devolves (is that a word?) from the actual object to the
> > > earliest base class; when we reach ~Base() body the object is no
> > > longer of type Concrete but of type Base and thus the call Base::OneFn
> > > () is an error because that class does not in fact have any
> > > implementation.
> >
> > > What the compiler does is create two vtables, the vtable of Concrete
> > > looks like this:
> >
> > > vtable 1:
> > > [ 0 ] -> Concrete::OneFn()
> > > [ 1 ] -> Concrete::TwoFn()
> >
> > > vtable 2:
> > > [ 0 ]-> msvcr80d.dll!__purecall()
> > > [ 1 ]-> msvcr80d.dll!__purecall()
> >
> > > The dtor of Concrete is the default dtor which does nothing except
> > > calling Base::~Base(), but the dtor of base does:
> >
> > > this->vtbl_ptr = vtable2
> > > call ThreeFn()
> >
> > > Now, why doesn't the release build crash?
> >
> > > That's because the compiler does not bother with generating the second
> > > vtable, after all is not going to be used and thus also eliminates the
> > > related lines such as this->vtbl_ptr = vtable2. Therefore the object
> > > reaches the base dtor with the vtbl_ptr pointing to vtable1 which
> > > makes the call ThreeFn() just work.
> >
> > > But that was just luck. If you ever modify the base class, such as
> > > introducing a new virtual function that is not pure, like this:
> >
> > > class Base {
> > >  public:
> > >  virtual ~Base() {
> > >    ThreeFn();
> > >  }
> >
> > >  virtual void OneFn() = 0;
> > >  virtual void TwoFn() = 0;
> >
> > >  virtual void FourFn() {          <--- new function, not pure virtual
> > >    wprintf(L"aw snap");
> > >  }
> >
> > >  void ThreeFn() {
> > >    OneFn();
> > >    TwoFn();
> > >  }
> > > };
> >
> > > // Same program below.
> > > // .......
> > > // ========================
> >
> > > Then you are forcing the compiler to generate vtable 2, which looks:
> >
> > > vtable 2:
> > > [ 0 ]-> msvcr80d.dll!__purecall()
> > > [ 1 ]-> msvcr80d.dll!__purecall()
> > > [ 2 [-> Base::FourFn()
> >
> > > And now the purecall crash magically happens (on the same spot) on
> > > release builds, which is quite surprising since the trigger was the
> > > introduction of FourFn() which has _nothing_ to do with the crash or
> > > the problem and is many commits after the introduction of the problem.
> >
> > > So the moral of the story? beware of virtual calls on dtors and ctors.
> > > Note that in practice this is quite tricky because of layers of
> > > indirection / complexity of the code base.
> >
> > > ... so and what about the manbearpig ? Ah, yes no longer a myth:
> >
> > >http://www.thinkgene.com/scientists-successfully-create-human-bear-pi.
> ..
> >
> > > -cpu
> >
>

--~--~---------~--~----~------------~-------~--~----~
Chromium Developers mailing list: chromium-dev@googlegroups.com 
View archives, change email options, or unsubscribe: 
    http://groups.google.com/group/chromium-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to