Boost logo

Boost :

Subject: Re: [boost] Boost.Build check to see if cxxflags flag is supported?
From: Alexander Grund (alexander.grund_at_[hidden])
Date: 2018-11-09 08:08:42


Am 09.11.18 um 06:50 schrieb degski via Boost:
> I would like to
> request for someone involved in that discussion to write a tl;dr of that
> discussion, as it seems to touch upon a number of issues that are poorly
> understood [also by long-time cpp-war-heroes] in general. /r/cpp might also
> be a good place for this and [comments] could shed more light on this.

As the original author of the PR(s) I'll do this. Note that everything
below is already explained in detail in the description of the PRs and
initial issue:

- https://github.com/boostorg/serialization/pull/111,
https://github.com/boostorg/serialization/pull/128,
https://github.com/boostorg/serialization/pull/129,
https://github.com/boostorg/serialization/pull/131
https://github.com/boostorg/serialization/issues/105

There is also my question on SO:
https://stackoverflow.com/questions/50064617/order-of-destruction-for-static-function-members-in-shared-libraries

To summarize:

There are 2 issues:

1. Wrong destruction order in shared libraries
2. Bug in Boost.Serialization (`singleton::is_destroyed`)

on 1.: As per the C++ standard the destructors are called in reverse
order of how the constructors finished. This is even true for static
initialization (pre-main) and destruction (post-main) of global static
instances which we have here.
This means if type `Foo` accesses a type `Registry` in its constructor
it is guaranteed that `Registry` will be constructed before `Foo` and
destroyed after.
Boost.Serialization makes use of this fact and lets `Foo` register
itself in `Registry` in the ctor of `Foo` and unregister in the
destructor of `Foo`.
Now there is the case of shared libraries (see also
https://github.com/boostorg/serialization/pull/131#discussion_r231498983):
Assume 2 shared libraries build against static Boost, so each of them
has separate instances of `Registry`, which is also ok.
Now lib1 uses a type `Foo` which gets registered in lib1-`Registry` and
lib2 uses a type `Bar` which gets registered in lib2-`Registry`. During
unload/destruction `Foo` is destroyed first as before and
lib1-`Registry` afterwards. But because shared library behaviour is
implementation defined we now run into the following situation on Linux
(not on OSX or Windows): With the destruction of lib1-`Registry` the
runtime also destroys lib2-`Registry`, probably because they are of the
same type. One can only guess what happens behind the scenes, but maybe
a name-map is used or so.
The problem: Now lib2-`Registry` is destroyed before `Bar` and when that
accesses the `Registry` you get a use-after-free situation and a corruption.

This can be handled (and was pre Boost 1.65) by a flag `is_destroyed`
which gets set once the singleton destructor is called and checked in
the destructors accessing another singleton. (Basically:
`if(!Registry::is_destroyed()) Registry::unregister(this)`)

but here issue 2 (introduced in 1.65):
The singleton template can be used directly by **not** inheriting from
it. In this case an instance of `T` is returned directly:
https://github.com/boostorg/serialization/blob/boost-1.68.0/include/boost/serialization/singleton.hpp#L122
BUT: The `is_destroyed` flag is set in the destructor of the singleton
template:
https://github.com/boostorg/serialization/blob/boost-1.68.0/include/boost/serialization/singleton.hpp#L157
As `T` is not inherited from `singleton<T>` this destructor
`~singleton<T>` is never called (because it is never constructed either)
and hence the flag is never set. See
https://github.com/boostorg/serialization/pull/131#discussion_r232088304
This is different when `T` is inherited from `singleton<T>` in which
case the construction of `T` will lead to the construction of
`singleton<T>` and also destruction which is why **in this case**
`is_destroyed` works correctly.
Due to this the issue 1. does not get caught and leads to the crash.

The question on this thread was now on how to include the "crash test"
into BJam:
- We need to build 2 shared libraries
- Link them into static boost

As Boost is build statically BJam does not add `-fPIC` to its compile
flags but as it is linked into shared libraries later this fails as
addresses are fixed already. Although BJam has the information (link
boost library into a shared library) it does not "backpropagate" this
into the build of boost. See
https://github.com/boostorg/serialization/pull/131#discussion_r231964634,
https://github.com/boostorg/serialization/pull/131#issuecomment-437236135

Thanks to Peter Dimovs help we got Robert convinced to include my fix
into the develop branch of Boost.Serialization:
https://github.com/boostorg/serialization/commit/f297d80d6ef55ac66525320d0477c17806aa57cd.
I now hope the tests get included to so it doesn't break in the future.




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk