hehe, understood! :)

On Sat, Apr 4, 2009 at 12:43 PM, Nicolas Sylvain <nsylv...@chromium.org>wrote:

>
>
> On Fri, Apr 3, 2009 at 7:19 PM, Tommi <to...@chromium.org> wrote:
>
>> Yes, that's one way of running into purecall.  but, just in case my email
>> is being misunderstood, now with italics! :)
>>
>>   "purecall is not called *when* an exception occurs.  purecall actually
>> *throws the exception - or exits the program"*
>>
>> purecall is called when attempting to call a virtual method for which
>> there is no implementation.  purecall is the default virtual method if you
>> will.
>>
>
> Yes, that's the low level description of purecall, and no one is debating
> that.
>
> But it is also misleading, because, from a high level perspective, when you
> look at my code, you see that the developer actually did implement the
> virtual method explicitly, so, still from a high level perspective, it can
> also happen for a virtual method that does have an implementation if the
> object has been deleted prior to the call.  [All that because when the
> derived object is deleted, one of the thing it does is to revert its vtable
> to the base class vtable. That part is not obvious/known to the high level
> developer]
>
> It's not because you implement all your virtual functions correctly that
> your objects wont purecall. But I'm sure you know that, I just wanted to
> make sure I'm not misunderstood either ;)
>
> Nicolas
>
>
>
>
>> When you call _set_purecall_handler, you're giving _purecall a pointer to
>> your function that purecall will delegate to.  There's not an exception that
>> triggers this.  Calling purecall is just a regular function call.
>>
>> Here's CRT's implementation of __purecall:
>>
>> void __cdecl _purecall() {
>>     _purecall_handler purecall = (_purecall_handler)
>> _decode_pointer(__pPurecall);
>>     if(purecall != NULL) {
>>         purecall();
>>         /*  shouldn't return, but if it does, we drop back to
>>             default behaviour
>>         */
>>     }
>>
>>     _NMSG_WRITE(_RT_PUREVIRT);
>>     /* do not write the abort message */
>>     _set_abort_behavior(0, _WRITE_ABORT_MSG);
>>     abort();
>> }
>>
>> and here's the implementation of _set_purecall_handler:
>>
>> _purecall_handler _set_purecall_handler(_purecall_handler pNew) {
>>     _purecall_handler pOld = NULL;
>>     pOld = (_purecall_handler) _decode_pointer(__pPurecall);
>>     __pPurecall = (_purecall_handler) _encode_pointer(pNew);
>>     return pOld;
>> }
>>
>>
>> On Fri, Apr 3, 2009 at 8:42 PM, Nicolas Sylvain <nsylv...@chromium.org>wrote:
>>
>>> The code below shows that it's possible to throw a purecall exception by
>>> calling a function from a delete object.
>>>
>>> I suspect this is what is happening in our code.
>>>
>>> Nicolas
>>>
>>>
>>> class Derived;
>>> class Base {
>>>  public:
>>>    Base(Derived *derived): m_pDerived(derived) {};
>>>    ~Base() {};  // Needed, dont know why.
>>>    virtual void function(void) = 0;
>>>    void bleh();
>>>    Derived * m_pDerived;
>>> };
>>>
>>> class Derived : public Base {
>>>  public:
>>>    Derived() : Base(this) {};   // C4355
>>>    virtual void function(void) {};
>>> };
>>>
>>> void Base::bleh() {
>>>   m_pDerived -> function();
>>> }
>>>
>>> void purecall(void) {
>>>    __debugbreak();
>>> }
>>>
>>> #include <windows.h>
>>> int _tmain(int argc, _TCHAR* argv[]) {
>>>    _set_purecall_handler(purecall);
>>>    Base* base = NULL;
>>>    {
>>>      Derived myDerived;
>>>      myDerived.function();
>>>      base =  &myDerived;
>>>    }
>>>    base->bleh();
>>>  }
>>>
>>> On Fri, Apr 3, 2009 at 2:17 PM, Tommi <to...@chromium.org> wrote:
>>>
>>>> purecall isn't called when an exception occurs.  purecall actually
>>>> throws the exception - or exits the program (by default the crt throws up a
>>>> dialog and then abort()s).  in addition to cpu's email, raymond chen's
>>>> article is a good (and short) read :)
>>>> http://blogs.msdn.com/oldnewthing/archive/2004/04/28/122037.aspx
>>>>
>>>> On Fri, Apr 3, 2009 at 3:15 PM, Huan Ren <hu...@google.com> wrote:
>>>>
>>>>> Based on what I saw in the bug, it looks like an exception happening
>>>>> during CALL instruction may lead to PureCall().
>>>>>
>>>>> For example, an object obj has been freed and later on someone calls
>>>>> obj->func(). Then the assembly code looks like this:
>>>>>
>>>>> // ecx: pointer to obj which is in memory
>>>>> // [ecx]: supposed to be pointer to vtable, it has invalid value since
>>>>> obj is freed
>>>>> // edx: now has pointer to vtable, which is invalid
>>>>> mov edx,dword ptr [ecx]
>>>>>
>>>>> // deref the vtable and make the call
>>>>> call dword ptr [edx+4]
>>>>>
>>>>> When a (hardware) exception happens during the call instruction, the
>>>>> control will be eventually transfered to the routine handling this
>>>>> type of exception which I *think* is PureCall().
>>>>>
>>>>> Huan
>>>>>
>>>>> On Fri, Apr 3, 2009 at 11:26 AM, Ricardo Vargas <rvar...@chromium.org>
>>>>> wrote:
>>>>> > I certainly don't want to imply that it is the case with this
>>>>> particular
>>>>> > bug, but I have seen crashes when the cause of the problem is using
>>>>> an
>>>>> > object that was previously deleted (and only end up with this
>>>>> exception when
>>>>> > all the planets are properly aligned). I guess that it depends on the
>>>>> actual
>>>>> > class hierarchy of the objects in question, but I'd think that
>>>>> "simple"
>>>>> > examples end up on a lot of crashes right after the cl that exposes
>>>>> the
>>>>> > problem.
>>>>> >
>>>>> > On Fri, Apr 3, 2009 at 12:52 AM, Dean McNamee <de...@chromium.org>
>>>>> wrote:
>>>>> >>
>>>>> >> You could, however, corrupt the vtable pointer (not the vtable).
>>>>>  Say
>>>>> >> somehow 32 was added to it, now the table is misaligned, and you
>>>>> might
>>>>> >> get a purecall, etc.  Not sure that's likely at all though.
>>>>> >>
>>>>> >> Since  the vtable pointer is the first field, it seems ripe for
>>>>> >> problems w/ use after free, etc.  I kinda doubt that's what's
>>>>> >> happening here though.  Anyone who is working on one of these can
>>>>> bug
>>>>> >> me and I'll look at the crash dump.
>>>>> >>
>>>>> >> On Fri, Apr 3, 2009 at 7:24 AM, Tommi <to...@chromium.org> wrote:
>>>>> >> > 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