Unhomed std::locale::facet and std::locale::_Locimp destructors cause crashes - by CornedBee

Status : 

  Fixed<br /><br />
		This item has been fixed in the current or upcoming version of this product.<br /><br />
		A more detailed explanation for the resolution of this particular item may have been provided in the comments section.

Sign in
to vote
ID 650567 Comments
Status Closed Workarounds
Type Bug Repros 5
Opened 3/10/2011 2:16:18 AM
Access Restriction Public


The destructors and vtables of std::locale::facet and std::locale::_Locimp are not homed. This can cause issues when dynamically loading and unloading DLLs, in ways that are very hard for most users to understand. Specifically, vtables and destructors are duplicated in every module they are used in, and which one is actually used in a given object depends on where the constructor is used. When DLLs are unloaded, this can cause a vtable and destructor that are still referenced to vanish.

Here's a simple example where this could cause crashes:

=== main module ===
#include <fstream>
#include <Windows.h>
typedef void (*Callback)(int);
typedef void (*DllFunc)(Callback);

std::ofstream *g_log;

void log(const char* msg)
  if (!g_log) g_log = new std::ofstream("log.txt");
  (*g_log) << msg << std::endl;

void helper(int)
  log("helper called");

int main() {
  HINSTANCE lib = LoadLibrary("thelibrary.dll");
  DllFunc dllFunc = (DllFunc)GetProcAddress(lib, "DllFunc");


  // Cleanup
  delete g_log; // CRASH!

=== dll implementation ===
#include <locale>

typedef void (*Callback)(int);

void DllFunc(Callback cb)
  // enforce C for numeric facet of global locale
  std::locale oldGlob;
    std::locale(std::locale(), "C", std::locale::numeric));




The above code isn't exactly perfect: DllFunc() may well document that it changes the global locale for its duration, but that means that helper() shouldn't create a stream because that will be influenced. But how many programmers actually know that streams capture the current global locale? In my team of 7, I was the only one.

In the example, the following happens:
First, when DllFunc() creates its own locale, a new _Locimp object is allocated on the heap. Because _Locimp is not homed, this _Locimp object points to a vtable that resides in thelibrary.dll. The locale has a refcount of 1.
The DLL then changes the global locale.
The callback function helper() is called.
helper() calls log(), which creates the log output stream. In its constructor, the ofstream object grabs the current global locale and stores a reference (increasing the refcount to 2).
DllFunc() then resets the global locale (dropping the refcount of its own locale to 1) and returns.
main() unloads thelibrary.dll.
Then, main() deletes the log stream. The ofstream destructor, in turn, releases its captured locale object. The refcount drops to 0 and the object gets freed. But the vtable sits in the already unloaded thelibrary.dll! As a consequence, the program crashes deep in the implementation details of the C++RT.

Fundamentally the issue is that locale's internal refcounting can keep objects alive after the library that created them has been unloaded. Because the internal classes aren't homed, these objects reference code from the unloaded library.
Sign in to post a comment.
Posted by Microsoft on 1/18/2012 at 8:27 PM

Thanks for reporting this bug. We've fixed it, and the fix will be available in VC11.

If you have any further questions, feel free to E-mail me at stl@microsoft.com .

Stephan T. Lavavej
Visual C++ Libraries Developer
Posted by Prophetable on 9/13/2011 at 3:48 AM
I am seeing this issue as well, Is there any news on a workaround or (hopefully) a fix?
Posted by Microsoft on 3/11/2011 at 12:07 AM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.
Posted by Microsoft on 3/10/2011 at 3:12 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)