Re: [Boost-bugs] [Boost C++ Libraries] #4842: "pure virtual method called; terminate called without an active exception" on shutdown

Subject: Re: [Boost-bugs] [Boost C++ Libraries] #4842: "pure virtual method called; terminate called without an active exception" on shutdown
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2011-03-19 20:27:48


#4842: "pure virtual method called; terminate called without an active exception"
on shutdown
-------------------------------+--------------------------------------------
  Reporter: eric_niebler | Owner: ramey
      Type: Bugs | Status: reopened
 Milestone: To Be Determined | Component: serialization
   Version: Boost 1.44.0 | Severity: Regression
Resolution: | Keywords: serialization singleton
-------------------------------+--------------------------------------------

Comment (by Aaron Barany <akb825@…>):

 Though I can't enable that trap right now, I can say without a doubt that
 it would be hit in my case. I have encountered other issues due to
 serializers being instantiated in multiple libraries, which I provided a
 patch for in ticket #5341. Unfortunately, in my use case it would be
 nearly impossible to avoid serializing across shared library boundaries,
 and I'm sure that's the case for a number of other users as well.

 After looking at void_cast.cpp for a while and thinking about different
 situations where there are multiple instances of void_casters, I think
 that I have found a situation that would cause a crash. This situation can
 also cause a crash if everything is statically linked.

 First, I will point out an issue with the current deregistration method.
 The first void caster that is constructed for a particular type relation
 will be inserted into the set, and any duplicates will be ignored.
 However, since it's doing a find and remove in recursive_unregister, the
 frist void caster to be destructed will remove whichever void caster was
 registered for that type relation, since it's comparing the type infos.
 For example, if you have void casters A and B, which both represent the
 same base and derived types, A will be registered on construction, while B
 will be ignored. On destruction, B will be destructed first and remove A.
 With the previous behavior, it was comparing pointers, so B wouldn't find
 itself in the set, and A wouldn't be unregistered until A was destructed.

 I will now lay out a particular case where this unregistration behavior
 can cause a crash. Lets say you have a class hierarchy of A, B, C, and D,
 where A is the base class and D is the most derived class. If void casters
 are registered for A->B, B->C, and C->D, shortcuts will be made along the
 way. Depending on the order of initialization, such as if C->D is
 registered before B->C, those shortcuts can then then recursively create
 shortcuts of their own. In this example, let's say the shortcut B->D
 recursively creates the shortcut A->D, so the parent of A->D is the
 shortcut for B->D.

 Now we have our hierarchy of void casters set up. However, let's say
 somewhere else in code the void caster B->D is registered directly, and is
 constructed after all the void casters and resulting shortcuts above. That
 void caster will see the B->D shortcut registered already, and won't
 insert it into the set. When the program shuts down, that explicit B->D
 void caster will be destructed first, since it was constructed last. When
 that void caster is destructed, recursive_unregister will do a find for
 the B->D type relation, and end up removing the shortcut registered
 earlier. That shortcut is merely removed from the set, and is never
 actually deleted, so it is leaked. As a result of that, the A->D shortcut
 created by the B->D shortcut is never removed from the set, since its
 parent's destructor is never called and is never recursively unregistered.
 At some point, the type infos for A and D are destroyed, but that A->D
 shortcut that references them is still left in the set. If there are still
 void casters registered at that point, the next time a find is performed,
 it will crash with the pure virtual function call.

 Since order of construction at startup isn't guaranteed, this specific
 ordering can occur regardless of if shared libraries are used or not. This
 will generally occur if you are serializing polymorphic type hierarchies
 by base pointer, as that serialization will register an explicit void
 caster that can skip over multiple classes in the hierarchy and conflict
 with an existing shortcut. I think there has been a correlation with the
 use of shared libraries and experiencing this bug since projects with
 large and complex type hierarchies are more likely to use shared
 libraries.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/4842#comment:23>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:06 UTC