-2

Defining a variable in a class causes a random crash during the execution of the application.

The crash does not appear in debug mode, it only happens in release build.

It also happens in various point of execution. I am outputing logs every now and then during execution, and they will differ from time to time.

The class in question is the middle one in the inheritance chain:

class Base
{
public:
    virtual ~BaseClass() { }

    // Quite a few virtual methods declared here.
};

class Middle : public Base
{
public:
    virtual ~Middle() { }

protected:
    Middle(const std::string& name)
        : _name(name)
        , _db(Context::DbInstance())
    {
    }

    /**
     * Commenting out any of the following crashes or does not.
     */
    // CareTaker* _careTaker;   // 4 bytes, crashes.
    // void* dummy;             // 4 bytes, crashes.
    // int dummy;               // 4 bytes, crashes.
    // short dummy;             // 2 bytes, crashes.
    // class Dummy {};          // 1 bytes, does not crash.
    //                          // 0 bytes, member removed, does not crash.
    std::string _name;
    // Crash also happens/does not if a variable described above put here.
    Database& _db;
    // But not if it is here. Variable of any size is OK here.
};

class Derived : public Middle
{
public:
    Derived() : Middle("Foo") { }
    virtual ~Derived() { }

    // Some virtual methods from Base overriden here.
};

In a nutshell, if a variable of size 2 or more comes before Database& _db definition, the crashes will happen. If it comes afterwards, they will not.

How would I go about to try to solve the crash without having access to a debugger in this scenario?

EDIT:

The class is used in the initializer method run after a DLL is loaded. I cannot give more details than this, unfortunately.

int DllInitializer()
{
    // Complex code.

    DbPlugger::instance().addPlug(new Derived());

    // Complex code.
}
Nikola Malešević
  • 1,738
  • 6
  • 21
  • 44
  • 3
    What does Context:DbInstance() return? If the answer is a temp var, that would cause a crash. The code you have provided are just declarations and class, where does this get invoked from? – Mansoor Jul 03 '19 at 12:12
  • 1
    Can you add `Context::DbInstance()` and `main` which produces the crash? – mch Jul 03 '19 at 12:12
  • When crash happens find window "call stack". It will show trace how code reached point of crash. Click each position in this window and analyze the code each item on this list refers to. Currently you didn't provide enough information to solve your issue. – Marek R Jul 03 '19 at 12:13
  • The temporary variable is the likely reason for the class, but besides that, you are most definitely required to have "access to a debugger" in order to develop in C++. Full stop. If you do not have a debugger, or don't know how to use one, you will not be able to accomplish much of anything, with C++. – Sam Varshavchik Jul 03 '19 at 12:15
  • 3
    Just a minor point: `class Dummy {}; // 1 bytes, does not crash.` You aren't adding a new data member here, you're just defining a class. That doesn't take up any space. – Kevin Jul 03 '19 at 12:32
  • @SamVarshavchik As soon as I attach the debugger, the crash does not happen, meaning I cannot use the debugger. – Nikola Malešević Jul 03 '19 at 12:40
  • @Mansoor Context::DbInstance() does not return temp var, that object is valid throughout runtime. It is a singleton class. – Nikola Malešević Jul 03 '19 at 12:41
  • Please do not mark for closure if I have failed to explain the problem enough, but let me know what kind of information would be useful instead. – Nikola Malešević Jul 03 '19 at 12:43
  • @Kevin Not according to this: https://stackoverflow.com/questions/2362097/why-is-the-size-of-an-empty-class-in-c-not-zero – Nikola Malešević Jul 03 '19 at 12:45
  • 1
    @NikolaMalešević Just because the class has a size, doesn't mean that it's existence uses any storage. It is usually the instances of the class that use storage. A nested class never increases the size of the outer class. – eerorika Jul 03 '19 at 12:51
  • @eerorika Point taken. Thanks to both you and Kevin. – Nikola Malešević Jul 03 '19 at 12:59
  • 1
    The symptoms you describe suggest (to me at least) that the dll and the client code might not be seeing/using the same class definition for `Middle`. Are you sure both client and dll are rebuilt correctly after the changes are made and that you are loading the correct dll? – G.M. Jul 03 '19 at 13:45
  • @G.M. Yes, I am sure about that everything is built correctly. The issue happens after a full clean rebuild of the whole software package. – Nikola Malešević Jul 03 '19 at 13:50

2 Answers2

1

You haven't provided a mcve , so this is based on some speculation, but I assume that at some point you make a copy either implicitly or explicitly.

All of the three crash causing members are trivially constructible. Since you don't initialise them in the constructor, they are left with an indeterminate value (assuming non static storage).

When you copy such object, the values of the members are read. Behaviour of reading an indeterminate value (of these types) is undefined. When behaviour is undefined, the program may crash.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Without posting the entire codebase, I just cannot reproduce it - I have tried. You may be right about copying, however, the behaviour is exactly the same even if I initialize the value of one of those variables in the constructor. – Nikola Malešević Jul 03 '19 at 12:50
  • @NikolaMalešević Well, at least we've solved one bug. Unfortunate that it wasn't the one that caused the crash. – eerorika Jul 03 '19 at 12:55
  • That was not the bug, actually, the value of `_careTaker` (the original pointer in the code) was initialized from the get-go. The reason I have removed that info is to try to have a minimum class description that would cause the crash - only defining a variable at a specific place without ever accessing it is causing the crash. – Nikola Malešević Jul 03 '19 at 12:58
0

The issue was that there were two separate sets of Derived.h/Derived.cpp files. One of them was outdated and left lingering around, forever forgotten.

The set I have been working on was included in the C++ project itself, but source file which was including the actual header file was using the old path.

This resulted in discrepancy between h and cpp files, resulting in heap corruption due to different memory signatures of header file included in the project and header file actually included by one of the cpp files in the project.

Quite a lot of debugging and headaches solved by one-line #include path change.

Nikola Malešević
  • 1,738
  • 6
  • 21
  • 44