Crash Investigation – Part 1 – Seg fault in case of accessing reference and exception handling

Introduction

In this blog, I discuss a few concepts and root causes that result in segmentation issue, seg fault or simply put, program crash.

For this particular post, I discuss about bad usage of references and exception handling that cause the program to crash. For example, a segmentation fault (seg fault) can occur during exception handling if the program attempts to access memory that is not valid or has not been allocated. The usage of reference, despite being very useful to update value, could lead to memory fault when an exception occurs if it is not used properly.

Concepts

Reference in C++

I have a more detail dicussion of C++ reference at this page.

C++ reference is usually used to update value immediate as the change in the reference will reflect the the object that it refers to. C++ reference will become "invalid" when the item it refers is deleted. Thus accessing the reference will cause seg fault.

Exception

How C++ implements exception:

  • Exception object is created when throw keyword is used.
    •  The exception object can be of any type, including built-in types, user-defined types, or standard library types.
    • When an exception is thrown, the program creates a copy of the exception object and stores it in memory. The copy is then passed to the appropriate catch block for handling
  • Catch exception: when the exception is catch, the exception object is copied to catch block for handling. Thus if the exeption constructor is not defined, C++ will make copy using default constructor and shallow-copy.
  • Stack unwinding: When an exception is thrown and control passes from a try block to a handler, the C++ run time calls destructors for all automatic objects constructed since the beginning of the try block.
    • The automatic objects are destroyed in reverse order of their construction.
    • If during stack unwinding a destructor throws an exception and that exception is not handled, the terminate() function is called. The following example demonstrates this:

How Segfault may happen in exception handling:

  • By accessing items that are already destroyed in catch block.
  • By throw but fail to catch exception (unhandled exception). I write a more detail explaination in the following post.

Sample programs

Seg Fault caused by target of reference being deleted:

The following program creates an object of MyClass and a reference ref that refers to the object. When the object is deleted, the memory is returned to the heap. The heap can either return the memory to the OS or keep it in its free memory list. When the OS un-maps that part of the memory from the process running the program, a seg fault can occur.

Thus, accessing the reference will result in undefined behavior, possibly seg fault, by the compiler.

#include <iostream>
using namespace std;

class MyClass {
    public:
    MyClass(){};
    void set(int & x) {
        a = x;
    }
    int get() const {
        return a;
    }
    ~MyClass(){ cout<<"delete myclass"<<endl;}
    private:
    int a;
};

int main() {
    MyClass * myclass = new MyClass(); 
    MyClass &ref = *myclass;
    int a = 5;
    ref.set(a);
    cout << ref.get()<<endl;

    delete myclass;
    
    cout << ref.get()<< endl;   // SEG FAULT or undefined behavior when the target of a reference is deleted.
    return 0;
}

Using reference to easily copy and update value of an item is common. However, prudence should be taken so that we don't use the reference when the item it refers to get destroyed.

Seg Fault caused by accessing out-of-scope reference object in exception handling:

This program runs a logic the purposely throw MyException. In addition an object, MyCustomResponseMessage, and a string resMsg in try block is passed to MyException.

Note that passing the MyCustomResponseMessage object by reference or by value would have the same effect and is not the root cause of seg fault. MyCustomResponseMessage object is available in the catch block just as how a parameter is available inside a called function despite that its original object was destroyed due to stack unwinding.

Due to stack unwinding, when the exception is handled in catch block, the string resMsgs, is destroyed. Thus the MyCustomResponseMessage's member reference msg becomes invalid in the catch blog. Thus accessing that value will cause seg fault.

#include <iostream>
#include <string>
using namespace std;

class MyCustomResponseMessage{
    public:
    MyCustomResponseMessage (string & myMsg) : msg(myMsg) { };
    void setMsg(string & customMsg) { msg = customMsg; }
    string & msg;
};

class MyException : public std::exception
{
public:
  MyException( MyCustomResponseMessage & custom) : custom_( custom ) {
    m_ = custom_.msg;
    cout<<"MyException constructor : "<<m_<<endl;
  }

  virtual ~MyException() throw(){};
  const char* what()  throw() {
      cout<<"what() is invoked"<<endl;
      m_ = custom_.msg;                    // SEG FAULT HERE IF MyCustomResponseMessage stores ref msg            
      return m_.c_str();
  }

private:
  std::string m_;
    MyCustomResponseMessage custom_;
};

class MyClass{
    public: 
        MyClass(string & tmp)  : message_ (tmp) {};
        virtual void doBadLogic(int x) {};
        ~ MyClass()  { 
            cout<<"Myclass destructor, message_ is deleted "<<endl; 
        };
    private: string & message_; 
};

class MyChildClass : public MyClass{
    public:
    MyChildClass(string & tmp)  : MyClass(tmp), message_ (tmp) {}
    virtual void doBadLogic(int x) override { 
        if (x== 0) {
            string resMsg = "bad logic, gonna catch MyException";
            MyCustomResponseMessage custom(resMsg);         
            throw MyException(custom); 
        }
    }
    ~MyChildClass(){
        cout<<"MyChildClass destructor, message_ is deleted "<<endl; 
    };
    protected: string & message_; 
};

int main(){
    try{
        string tmp = "tmp";
        MyClass * child = new MyChildClass(tmp);
        child->doBadLogic(0);
        std::cout<<"program has no exception"<<std::endl;
    }
    catch( MyException & ex ){ 
        std::cout<<"1st exception"<<std::endl;      
        std::cout <<ex.what()<<std::endl;         // SEG FAULT HERE IF MyCustomResponseMessage stores ref msg
    } 
    return 0;   
}

Passing custom message to exception is a common practice; the custom message is further managed in data structure such as a class.

However, a combination of using member reference in a class to store value and passing the reference to a soon-to-be-destroyed item to an exception in this case crashes the program.

Reference:

Stack unwinding in catch block.

Leave a Reply

Your email address will not be published. Required fields are marked *