double free when both shared library and executable linked to boost libraries
I've run into a run-time error (double free) with my software which uses a plug-in architecture. The main executable (a.out) uses dlopen to load a shared library (mylib.so). Both a.out and mylib.so utilize the boost libraries (version 1.54) and are therefore linked separately to them. Depending on whether I use static or dynamic linking to the boost libraries I encounter the double free error as confirmed by address sanitizer using gcc 4.9.2 on linux x86_64. Here is a summary of the results: a.out link mylib.so link double free? static dynamic YES static static NO dynamic dynamic NO dynamic static NO So you can see, only when the main executable used static linking to boost and the shared library used dynamic linking to boost is when I saw the double free error at run time. My question is, why is the error only triggered under that condition and what is the recommended link configuration when using boost libraries for both a shared library and an executable. Ideally, the shared library plug-in should be free to use any boost version with either static or dynamic linking, and it should be independent from (i.e. not conflict with) the main executable's boost version and link type. What compiler/link settings would be necessary to achieve this, if it is even possible at all? -- Eric Roller
On 21/12/2014 07:45, Eric Roller wrote:
I've run into a run-time error (double free) with my software which uses a plug-in architecture. The main executable (a.out) uses dlopen to load a shared library (mylib.so). Both a.out and mylib.so utilize the boost libraries (version 1.54) and are therefore linked separately to them. Depending on whether I use static or dynamic linking to the boost libraries I encounter the double free error as confirmed by address sanitizer using gcc 4.9.2 on linux x86_64. Here is a summary of the results:
a.out link mylib.so link double free? static dynamic YES static static NO dynamic dynamic NO dynamic static NO
So you can see, only when the main executable used static linking to boost and the shared library used dynamic linking to boost is when I saw the double free error at run time. My question is, why is the error only triggered under that condition and what is the recommended link configuration when using boost libraries for both a shared library and an executable.
Ideally, the shared library plug-in should be free to use any boost version with either static or dynamic linking, and it should be independent from (i.e. not conflict with) the main executable's boost version and link type. What compiler/link settings would be necessary to achieve this, if it is even possible at all?
Provided that Boost objects appear *nowhere* in the ABI of your library (safest is to ensure that none of your public header files ever #include or forward-reference anything from Boost), you should be safe static-linking on both sides, or even mixing things up like above. If Boost objects do appear in the ABI of your library, then you have to be more careful. The safest option is to dynamic link on both sides. (You may sometimes be able to get away with static linking on both sides depending on which libraries you're using, particularly when they're header-only, but it's still not a good idea.)
So you can see, only when the main executable used static linking to boost and the shared library used dynamic linking to boost is when I saw the double free error at run time. My question is, why is the error only triggered under that condition and what is the recommended link configuration when using boost libraries for both a shared library and an executable.
Ideally, the shared library plug-in should be free to use any boost version with either static or dynamic linking, and it should be independent from (i.e. not conflict with) the main executable's boost version and link type. What compiler/link settings would be necessary to achieve this, if it is even possible at all?
Provided that Boost objects appear *nowhere* in the ABI of your library (safest is to ensure that none of your public header files ever #include or forward-reference anything from Boost), you should be safe static-linking on both sides, or even mixing things up like above.
If Boost objects do appear in the ABI of your library, then you have to be more careful. The safest option is to dynamic link on both sides. (You may sometimes be able to get away with static linking on both sides depending on which libraries you're using, particularly when they're header-only, but it's still not a good idea.)
I meant to reply to this and forgot :-( You don't say which Boost lib is causing the issue, but my guess it goes something like this: * You're using Boost.Regex (or a similar lib which frees resources on exit). * When the pluggin is loaded, the link-loader resolves dependencies by using the static version of the lib already present in the executable rather than by loading the shared lib. * When the pluggin is unloaded, it prematurely calls the libraries cleanup routines. * When the application exits, it calls the same routines again. As Gavin says, there's only one safe option and that's either all-dynamic or all static linking, mixing the two is a recipe for problems like this - not just with Boost, but with any third party library. I guess the problem is that you don't have control over the pluggin? There may be one other option - assuming this is Linux/GCC you could, perhaps even *should* make all third party library symbols private by compiling with -fvisibility=hidden. In fact if one or more Boost libraries are not doing this anyway for the static lib builds, please file a bug report! HTH, John.
On Tue, Jan 6, 2015 at 12:55 AM, John Maddock
So you can see, only when the main executable used static linking to boost and the shared library used dynamic linking to boost is when I saw the double free error at run time. My question is, why is the error only triggered under that condition and what is the recommended link configuration when using boost libraries for both a shared library and an executable.
Ideally, the shared library plug-in should be free to use any boost version with either static or dynamic linking, and it should be independent from (i.e. not conflict with) the main executable's boost version and link type. What compiler/link settings would be necessary to achieve this, if it is even possible at all?
Provided that Boost objects appear *nowhere* in the ABI of your library (safest is to ensure that none of your public header files ever #include or forward-reference anything from Boost), you should be safe static-linking on both sides, or even mixing things up like above.
If Boost objects do appear in the ABI of your library, then you have to be more careful. The safest option is to dynamic link on both sides. (You may sometimes be able to get away with static linking on both sides depending on which libraries you're using, particularly when they're header-only, but it's still not a good idea.)
I meant to reply to this and forgot :-(
You don't say which Boost lib is causing the issue, but my guess it goes something like this:
* You're using Boost.Regex (or a similar lib which frees resources on exit). * When the pluggin is loaded, the link-loader resolves dependencies by using the static version of the lib already present in the executable rather than by loading the shared lib. * When the pluggin is unloaded, it prematurely calls the libraries cleanup routines. * When the application exits, it calls the same routines again.
As Gavin says, there's only one safe option and that's either all-dynamic or all static linking, mixing the two is a recipe for problems like this - not just with Boost, but with any third party library. I guess the problem is that you don't have control over the pluggin?
There may be one other option - assuming this is Linux/GCC you could, perhaps even *should* make all third party library symbols private by compiling with -fvisibility=hidden. In fact if one or more Boost libraries are not doing this anyway for the static lib builds, please file a bug report!
Thanks for your responses, they are very helpful. Unfortunately I was not able to pinpoint the specific boost library that caused the double free using address sanitizer. As you mentioned the double free occurs during program exit routine and all I see is that some std::string object is being freed. In this case I do have access to both the main executable and the plugins so I can choose how they are compiled to avoid this error. It would still be good to figure out how I can compile the main executable to be robust to how the shared library plugins are compiled. I will try compiling boost with hidden visibility to see if it resolves this issue. Is this the correct way to achieve that?: ./b2 link=static threading=multi cxxflags='-fPIC -fvisibility=hidden -fvisibility-inlines-hidden' install Aren't there some boost libraries that force symbols to have default visibility? If so, setting -fvisibility=hidden won't change that. -Eric
Unfortunately I was not able to pinpoint the specific boost library that caused the double free using address sanitizer. As you mentioned the double free occurs during program exit routine and all I see is that some std::string object is being freed.
In this case I do have access to both the main executable and the plugins so I can choose how they are compiled to avoid this error. It would still be good to figure out how I can compile the main executable to be robust to how the shared library plugins are compiled. I will try compiling boost with hidden visibility to see if it resolves this issue. Is this the correct way to achieve that?:
./b2 link=static threading=multi cxxflags='-fPIC -fvisibility=hidden -fvisibility-inlines-hidden' install
You don't want -fPIC in there for a static link do you?
Aren't there some boost libraries that force symbols to have default visibility? If so, setting -fvisibility=hidden won't change that.
Not sure about that - a quick grep suggests that where they do they set visibility to "default" which I think means "same as command line" ? John.
participants (3)
-
Eric Roller
-
Gavin Lambert
-
John Maddock