none
Stack/heap corruption with C++ STL collection and WinUnit

    Question

  • Here's the situation. We've got a set of C++ production DLLs. Some of the classes in there use some STL collections (e.g. list<int>). We wrote a set of unit tests for those using WinUnit.

    Now, when we were running DevStudio 2005, everything was working fine. But, when we switched to DevStudio 2008, we started to see stack & heap corruption errors.

    Some investigation revealed the problem was with the STL collections (lists, maps, etc.). For example, we'd instantiate a class from our production code that contained a list<int> definition. If you examine the collection in the debugger, the thing that jumps out at you is that the size of the collection is very large (millions). It should be zero. When the object goes out of scope, we get the stack corruption error.

    Any thoughts? This only started occurring after moving to DevStudio 2008. Moving back to 2005 isn't an option, which leaves the only viable option to just forget about the unit tests.

    Thanks!
    Thursday, May 08, 2008 8:03 PM

Answers

  • in both the production DLL and the unit test DLL put the following in the stdafx.h very close to the top of the file

     

    #define _HAS_ITERATOR_DEBUGGING 0

    #define _SECURE_SCL 0

     

    recompile, and the sizes should be the same in both DLLs.  If not let me know. 

     

    You can read more of STL's vcblog post comments that is referenced above.

    Monday, May 12, 2008 9:14 PM

All replies

  • If you think there is a problem with std::list<int>, please put together a simple code sample that demonstrates the stack corruption and post it here.

     

    Thursday, May 08, 2008 8:32 PM
  • can you tell me if these are across boundaries (e.g. the class definition in a DLL and being called from an EXE?)

     

    Thursday, May 08, 2008 8:38 PM
  • Well, it's pretty simple really. Something like this.

    The DLL has something like this:

    class MyClass {
    list<int> temp;
    }

    The WinUnit executable does something like this.

    void Test1 () {
    StartTest ();
    }

    void StartTest () {
    MyClass X;
    }

    When MyClass is instantiated, and you look at temp in the debugger, it's got a size of several million. When we exit StartTest (i.e. X goes out of scope), you get a stack corruption error.

    I don't think there's a problem with list<int> per se, but there's something screwy going on between the DLL and the WinUnit test code that wasn't happening under DevStudio 2008.

    Thanks for any help you can provide...


    Friday, May 09, 2008 12:05 PM
  • Yes (which give me hope, since it sounds like you might be onto something).

    If you're not familiar with WinUnit, it's an executable that you point at a DLL. It cracks open the DLL looking for methods that fit a naming convention (you use WinUnit macros to define your test methods). Our test methods then call over into our production DLL code to perform their tests.

    So, there's three parts here:
    1) The WinUnit executable.
    2) Our test DLL, which contains test methods to exercize the production DLL.
    3) Our production DLL.

     
    The production DLL code in question (much simplified) looks something like this:

    class MyClass {
    list<int> temp;
    }

    The test DLL code looks something like this:

    void Test1 () {
    StartTest ();
    }

    void StartTest () {
    MyClass X;
    }

    When MyClass is instantiated, and you look at temp in the debugger, it's got a size of several million. When we exit StartTest (i.e. X goes out of scope), you get a stack corruption error.

    If you create a list<int> variable in the test DLL, it works just fine.

    This was all working fine under DevStudio 2005. The problems only occurred when we moved to DevStudio 2008.

    Thanks...



    Friday, May 09, 2008 12:17 PM
  • Did you ensure that all components (DLL, WinUnit, production DLL) are compiled under the same compiler? This is required when you define compiler-dependent constructs such as STL containers and share them across compilation boundaries.

    Friday, May 09, 2008 3:48 PM
  • Yeah, they were all built with the same compiler (the production DLL, the test DLL, and the WinUnit executable itself).
    Friday, May 09, 2008 7:48 PM
  • Do you have _HAS_ITERATOR_DEBUGGING turned on or off in either the EXE or the DLL?  In VS 2008 the value of _HAS_ITERATOR_DEBUGGING MUST match in all compilation units.  If they are different then you get different sizes of your classes that contain STL class members.  This would cause a problem for obvious reasons (your class members would no longer be aligned in the places the EXE expects)

     

    In VC 2005 this wasn't true.  Now with VC 2008 the size changes (actually gets smaller) when _HAS_ITERATOR_DEBUGGING is true. 

     

    I filed a bug during the VC 2008 beta test but it was rejected as by design.  I'll post the contents of the response I got in the bug item if you're interested.

     

    proof: use the following program in a debug build, the first time with _HAS_ITERATOR_DEBUGGING set to 0 then set to 1. The first time you run, size will be 28, the second time it will be 24. In VC 2005 it was 24 in both cases.
    #define _HAS_ITERATOR_DEBUGGING 0
    #include <list>
    #include <stdio.h>
    #include <tchar.h>
    int _tmain(int argc, _TCHAR* argv[])
    {
    std::list<char *> test;
    int size = sizeof(test);
    return 0;
    }

     

    P.S. this issue might happen with _SECURE_SCL as well.

    Monday, May 12, 2008 4:47 PM
  • Unfortunately, this doesn't seem to be it. Neither of those symbols are being defined in any of the components.

     

    However, the symptoms we're seeing seem to fit the situation you define (size differences).

     

    Any thoughts on anything else that might cause the same thing?

     

    Thanks...

    Monday, May 12, 2008 6:15 PM
  • I think there is a clear lesson to be learned here, and that is to avoid using STL collections across compilation units. There are just too many pitfalls involved here, and we haven't found which one is involved in this particular case.

     

     

    Monday, May 12, 2008 7:39 PM
  • can you do the same test as mine? i.e. try to see what the size is in the DLL, then check in the EXE as well.  There may be other things changing the size.  Are both either "debug" or "release" builds of the DLLs? you cannot mix and match even with static linking.   Try both being release or both being debug.  

     

    Mixing debug and release versions may also explain it because the values for the symbols I mentioned change depending on whether you're debug or release.  If you do have to mix and match then you MUST define them to be 0 explicitly in both, because not having any value defined at all is going to give you different values for your debug and release.

     

    Any way here is the text that STL gave me.

     

    We significantly changed the representations of the Standard containers in VC9 to fix a conformance bug; see my explanation at http://blogs.msdn.com/vcblog/archive/2007/08/10/the-future-of-the-c-language.aspx . As a side effect, this removed unnecessary data members from the Standard containers and iterators in many configurations, making them smaller and faster.

    It is not supported with VC8 (nor with VC9, nor will it ever be supported) to break the rules that I described in the comment to my VCBlog post at http://blogs.msdn.com/vcblog/archive/2007/08/10/the-future-of-the-c-language.aspx#4617984 . You're dealing with Rule #3, passing STL objects between binaries (EXEs/DLLs). VC9 happens to make things break earlier because the containers and iterators have sizes that change in some configurations where they didn't previously change. This is a blessing, not a curse - passing STL objects between binaries with different settings of release/debug, _SECURE_SCL on/off, and _HAS_ITERATOR_DEBUGGING on/off is likely to produce incomprehensible runtime crashes and asserts and other badness even with VC8, but not quite as immediately as in VC9.

    (If you look very closely at the implementation of the Standard Library, you will notice that std :: string alone can be passed between binaries that are both release or both debug, but with different _SECURE_SCL and _HAS_ITERATOR_DEBUGGING settings - this is due to how the CRT DLL is implemented. You should not rely on this, and it may change in VC10.)

    There is absolutely no way to get around Rules #1 and #2 - complete consistency within binaries is necessary. If you have the unenviable situation of multiple third-party DLLs with different _SECURE_SCL/_HAS_ITERATOR_DEBUGGING settings, you could introduce additional DLLs as "adapters" that would be built with the same settings as the third-party DLLs that they talk to, and expose non-STL interfaces to your main application. It would probably be easier to get properly built versions from your third-party vendors.

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

    Thanks!
    Stephan T. Lavavej
    Visual C++ Libraries Developer

    Monday, May 12, 2008 7:41 PM
  • Now we're getting somewhere. For the production DLL, the size of a list<int> was 28. For the unit tests DLL, it was 24.

     

    Any suggestions on options to look at that might be different? I'd already investigated this several times looking for any build differences, but either didn't find anything, or my experimentation tests didn't yield anything.


    Thanks! (and thanks for the other info)...

     

    Monday, May 12, 2008 8:51 PM
  • in both the production DLL and the unit test DLL put the following in the stdafx.h very close to the top of the file

     

    #define _HAS_ITERATOR_DEBUGGING 0

    #define _SECURE_SCL 0

     

    recompile, and the sizes should be the same in both DLLs.  If not let me know. 

     

    You can read more of STL's vcblog post comments that is referenced above.

    Monday, May 12, 2008 9:14 PM
  • That did it. After forcing those symbols to zero, the list<int> sizes are the same, and everything's working just fine.

     

    Not sure why the production DLL didn't also have them on by default (in debug), but at this point I'm not sure I care.

     

    Thanks for all the help!

     

    Tuesday, May 13, 2008 12:30 PM