A place where programmers can discuss various programming topics and experiences.



Know Your C++: Using Exceptions

As most of you already know, exceptions are errors or anomalies that occur during program execution. They are used as part of a programming philosophy that states, "You will not ignore this error scenario!" Rather than getting into why one would use exceptions, I am going to discuss a bit about their details and proper usage. The guts of C++ are always more interesting (right?).

How They Work

Lets begin with our standard throw invocation. In C++, the first thing performed is a copy of the exception object thrown is created. This copy is then "returned" from the function initiating the throw expression (the "throwing" function's scope is exited immediately and he is popped off the call stack). This process is called stack unwinding and will continue until an appropriate handler is found that can catch the object type thrown. As part of stack unwinding, each object that is popped off the stack goes out of scope, and therefore its destructor is called (this is good). This ensures that objects are cleaned up as they go out of scope (assuming the popped object's destructor does not throw an exception as well - more about this in a bit).

If an appropriate exception handler is not found, the terminate() function is automatically invoked. By default, this calls the Standard C library function abort() which kills your application. As a side note, if an object's destructor throws an exception (nobody does that right?) terminate() is called as well. Therefore, the general process goes as follows:

  1. Function foo() throws an exception.
  2. That function and all objects local to it are destructed and popped of the call stack along with the foo() function itself.
  3. Then an appropriate handler is searched for all the way up the call stack until one (or none) is found.
  4. If this happens, terminate() is called which calls abort() and it sends your application packing.
Using Exceptions

Now that we see the general idea of how they work, we need to make sure we account for various pitfalls in their use. Because the proper use of exceptions is critical to an application's viability, it is imperative that we know these extra details. Therefore, I have provided a few tips on how to use exceptions below.

Catch Exceptions by Reference

In order to guarantee that all parts of your object are handled by a catch block, make sure that you catch your exceptions by reference. There are good reasons to do this. One, is that you avoid the normal overhead of an extra copy when the object is passed-by-value. Second, you ensure that any modifications provided to the exception object itself are preserved if the object needs to be re-thrown. Finally, you avoid the slicing of derived exception objects when base-class handlers catch them. Here is a quick sample of catching an exception by reference:

try
{
    ...
}
catch (MyException & e)
{
    ...
}
Never Let Exceptions Leave an Object's Destructor

We all hate memory leaks right? Well in order to prevent memory leaks with exceptions, you have to first make sure that your destructors do not throw them. For example, I have a class below, the MyLeakyClass class that is defined like this:

class MyLeakyClass
{
public:
    MyLeakyClass();
    ~MyLeakyClass();
    ...
private:
    wchar_t * szSimpleString1;
    wchar_t * szSimpleString2;
};

MyLeakyClass::MyLeakyClass()
{
    szSimpleString1 = new wchar_t[32];
    szSimpleString2 = new wchar_t[32];
}

MyLeakyClass::~MyLeakyClass()
{
    delete [] szSimpleString1;
    delete [] szSimpleString2;
}

What happens if the first delete call above generates an exception? We know that execution stops immediately at the exception generating code and a matching exception handler is searched for by the caller (e.g. you called delete on an instance of MyLeakyClass). The first thing to notice is that the szSimpleString2 character array object is lost. He never is deleted and therefore you have a leak of the 32 bytes. Nice job, but it gets worse. What if the calling code was not your invocation of the delete operator? If it was say, a part of the stack unwinding by another exception being generated, your destructor's exception (the second exception in this example) will result in a call to the terminate() function as we mentioned earlier. Moreover, we know this means your application will most likely die. Therefore, best-case scenario here, we leak if we have dynamically allocated objects on the heap. Worst case is we leak and we die. Not a good combination. There is really only one solution to this problem if you do not want your application to die. You need a try/catch all which does nothing. This is the only way to guarantee that exceptions are not thrown from the handler and therefore no exceptions leave the destructor. It is not pretty and some of you will disagree with it, but here is a simple example of MyLeakyClass destructor rewritten:

MyLeakyClass::~MyLeakyClass()
{
    try
    {
        delete [] szSimpleString1;
    }
    catch(...) {}

    try
    {
        delete [] szSimpleString2;
    }
    catch(...) {}
}
Catch exceptions in order of object inheritance

When catching exceptions you have to understand that a handler will match the object type it is catches with the type of object being thrown. Like all objects in C++, a catch block which specifies a base class will match with a derived class object because of the is-a relationship. Therefore, you must order your catch blocks by the object hierarchy of the exception objects themselves. For example, if you have three exception object types:

class Base {};
class Derived : public Base {};
class DerivedAgain : public Derived {};

you need to provide your catch blocks in the following order:

try
{
    ...
}
catch (DerivedAgain & e)
{
    ...
}
catch (Derived & e)
{
    ...
}
catch (Base & e)
{
    ...
}

Starting with the most derived object and progressing up to the base-class, you are guaranteed to catch each and every type of the above three objects where appropriate. If you change that order, bad things can (and will) happen. For example, if you swapped the order of the Base and Derived handlers, your Derived objects would never be caught (by the Derived handler) because they would always be caught by the Base handler. This is because a Derived object is-a Base object.

Avoid exception specifications

Exception specifications are a mechanism that allows the programmer to declare the types (if any) of exceptions, a function will throw. In my opinion, they biggest benefit of using specifications is that it documents for a client/user what you throw. This is rather nice, but the adverse affect of using specifications greatly outweighs that benefit. The problem is when your function throws an exception that is not one of the specified types. Here is an example.

// function declaration...
unsigned long FooBar() throw(int);

// function definition...
unsigned long FooBar()
{
    EvilExceptionThrower()    // Might throw anything
}

What happens if EvilExceptionThrower() throws an exception of type std::runtime_error? FooBar() is defined to only throw exceptions of type int and therefore we enter the unknown. Because our function isn't supposed to throw std::runtime_error exceptions, the default behavior is to call terminate(). Oh yea, we know what happens next. You just killed your application, congratulations. Now, if you still like the specifications and want to use them, you have one solution. Provide a catch all handler in your function that makes sure only specified exceptions are thrown (you could also pray that nothing called in your function throws non-specified exceptions). Depending on your design, this could be good or bad. In general, catch all handlers are not elegant in the general sense. If you want your app to die, do not add the catch all. If your application should try to continue on, add 'em. In my opinion, it is preferable to add a comment in your function declaration, saying what type(s) of exceptions you throw. This way you do not limit your function and inadvertently kill your application.

In general, exceptions are a effective and useful programming philosophy that I have used a number of times. Like most things in C++, if used correctly and in the right circumstances, they perform their job well. Until next time...

- Gilemonster

Labels:

posted by Gilemonster @ 10:00 PM,

3 Comments:

At 9:05 AM, Anonymous TigerEagle said...

Are there any security related programming considerations with exceptions? i.e. are there things you can do to prevent an exception or handler from being used in a buffer overrun type attack?

 
At 10:10 AM, Blogger Gilemonster said...

There are a couple of obvious paths that I think you are pushing at:

1. Security breaches which generate exceptions.
2. Security breaches within exceptions.

As for the former, this is probably beyond the scope of the article. I'm sure there are a number of scenarios which could cause routines to throw exceptions if secure code isn't a primary coding concern. For this article, the important one is having security breaches in the exception object itself or within the catch block that handles the exception. Here are my suggestions. First of all, make sure the exception is caught by reference so you are guaranteed that your reference object isn't NULL (references are guaranteed to not be NULL). Second, if you define your own exception object, make sure that you define your object with all its boundary conditions in mind (integer overflows, buffer overflows, etc). And second, try to limit any type of memory allocation in your catch block. If you can't, make sure the handler code verifies the size of objects, buffers, etc. before actually using them.

For example, let's say you have a function that throws a char*. In your catch block you need to verify the validity (size, non-null, etc) of that object before it is used. Not much else you can do here. Other than that I can't think of any specific considerations. Good question though. I'll do some research and see if I can find anything specific. If so, I'll post it.

 
At 5:40 PM, Blogger Gilemonster said...

I did some looking and found an interesting (and simple) security flaw with exception handling in general (rather obvious now that I've looked it over). The vulnerability is that the exception handler frame lives on the stack. If a buffer overrun is encountered, you can possibly corrupt the handler frame. This allows the attacker to have the exception throwing function pass control to arbitrary code he supplied (in our corrupted EH frame). I didn't know this, but the Code Red Virus in 2001 was actually a buffer overrun that corrupted the exception handler frame.

To alleviate this, the Visual C++ linker now has a "Safe Exception Handler" option which you can enable. Here are the links explaining the exploit and the linker option in more detail.

- Safe Exception Handler Linker Option -
http://msdn2.microsoft.com/en-us/library/9a89h429.aspx

- Compiler Security Checks In Depth -
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vctchCompilerSecurityChecksInDepth.asp

 

Post a Comment

Links to this post:

Create a Link

<< Home