Question

I've got a program that is to become part of an already existing, larger product which is built using C++ Builder 2010.

The smaller program does not (yet) depend on C++ Builder. It works fine in MS Visual Studio, but with C++ Builder it produces strange access violations.

Please let me explain this.

Depending on the code and on compiler settings, access violations happen or do not happen. The access violations are reproducible: When the program is built then the access violation does never occur or it does always occur at the same place. If the program is rebuilt with the same settings, it will show the same behavior. (I'm really glad about that).

The access violation happens at places where the delete operator is called. This can happen (depending on compiler settings and exact code) inside certain destructors, including destructors of own classes and inside the destructor of std::string.

The following things make the access violation less likely:

  • Build with "Debug" settings (instead of "Release").
  • No compiler optimizations.
  • Compiler switch "Slow exception epilogues".
  • Static RTL instead of dynamic.
  • Derive exceptions from std::exception instead of Borland's Exception class.
  • Use less "complicated" expressions (e.g. use "string s = "..." + "..."; throw SomeException(s);" instead of "throw SomeException(string("...") + "...");")
  • Use try... __finally with manual cleanup instead of automatic variables with destructors.
  • Use a small console application instead a VCL windows application.

The program makes use of several C++ features, including exceptions, STL, move constructors etc. and it of course uses the heap.

I already tried some tools, none of them reported problems:

  • Borland's CodeGuard.
  • Microsoft Application Verifyer.
  • pageheap/gflags.
  • As already mentioned, there is absolutely no problem when building with MS Visual Studio.

Use of precompiled headers and incremental linking (which both seem to me are prone to errors) are disabled.

Neither the C++ Builder compiler ("enable all warnings") nor the one of Visual Studio (/W4) produces a warning that might be related to this issue.

I do not have access to another version of C++ Builder.

As the program will become part of a larger product, it is not an option to switch to a different compiler, and it is not an option to tune the compiler settings until the access violation does no longer happen. (I fear if this really should a compiler bug, the bug might show up again.)

Putting this together, I'm guessing this might result from heap corruption that is related to some compiler bug. However, I was not able to find a bug on qc.embarcadero.com. I'm guessing further this is related to cleanup code that is executed upon stack rewinding when an exception has been thrown. But, well, maybe it's only a stupid code bug.

Currently, I do not have any idea how to proceed. Any help appreciated. Thank you in advance!

Was it helpful?

Solution

tl;dr I believe the bug is that code is generated to delete the std::string from both branches of the ternary operator during stack unwinding, however only one of them was actually created of course.


Here is a simpler MCVE, which shows the problem via outputs in XE5:

#include <vcl.h>
#include <tchar.h>
#include <stdio.h>
using namespace std;

struct S
{
    S() { printf("Create: %p\n", this); }
    S(S const &) { printf("Copy: %p\n", this); }
    void operator=(S const &) { printf("Assign: %p\n", this); }
    ~S() { printf("Destroy: %p\n", this); }

    char const *c_str() { return "xx"; }
};

S rX() { return S(); }
int foo() { return 2; }

#pragma argsused
int _tmain(int argc, _TCHAR* argv[])
{
   try
   {
      throw Exception( (foo() ? rX() : rX()).c_str() );
   }
   catch (const Exception& e)
   {
   }

   getchar();
   return 0;
}

This version shows the problem via output strings on the console. Check the edit history for this post to see a version that uses std::string and causes the segfault instead.

My output is:

 Create: 0018FF38
Destroy: 0018FF2C
Destroy: 0018FF38

In the original code, the segfault comes from the bogus Destroy ending up calling delete on the bogus value it obtains by trying to retrieve the internal data pointer for a std::string which was actually never created at that location.

My conjecture is that the code generation for stack unwinding is bugged and tries to delete the temporary string from both branches of the ternary operator. The presence of the temporary UnicodeString does have something to do with it; as the bug did not occur in any variations where I tried to avoid that temporary.

In the debugger you can see the call stack and it is during global stack unwinding that this happens.

OTHER TIPS

Phew, that was so simple that it took me some time:

#include <vcl.h>
#include <tchar.h>
#include <string>
using namespace std;

struct B
{
   B(const char* c) { }
   string X() const { return "xx"; }
   int Length() const { return 2; }
};

struct C
{
   void ViolateAccess(const B& r)
   {
      try
      {
         throw Exception(string("aoei").c_str());
      }
      catch (const Exception&) { }

      throw Exception(((string) "a" + (r.Length() < 10 ? r.X() : r.X() + "...") + "b").c_str());
   }
};

#pragma argsused
int _tmain(int argc, _TCHAR* argv[])
{
   try
   {
      C c;
      c.ViolateAccess("11");
   }
   catch (const Exception& e) { }
   return 0;
}

(Preemptive comment: No, this code does not make any sense.)

Create a new console application and make sure to use the VCL. It might depend on the project settings whether there will be an access violation or not; my debug builds always crashed, release builds didn't.

Crashes with C++ Builder 2010 and XE3 trial.

Thus, bug in compiler or in VCL or in STL or whatever.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top