Avoiding C++ vulnerabilities

Just returned from Blackhat – it always seems that the presentations I most want to see happen at the same time as I'm scheduled to talk. Neel Mehta, John McDonald and Mark Dowd were talking about finding exploitable C++ specific flaws, and I was only able to watch the first 30 minutes. Something came up there that is addressed by a technique documented by Scott Meyers in "Effective C++". Here's the problem – if ownership of some resource can be ambiguous, all sorts of bugs (security and otherwise) can happen. This happens because if I declare a class like so:

class Foo
{
public:
int j;
};

There are actually 4 operators created by default:

  • Constructor - void Foo()
  • Destructor - void ~Foo()
  • Copy constructor - void Foo( const Foo& )
  • Assignment operator - const Foo& operator=( const Foo& )

The last two are what gets us in trouble, especially considering that most programmers don't know these get created. Where this gets us in trouble from a security standpoint is when I go and make a class like so:

class DumbPointer
{
public:
void DumbPointer() : m_ptr( 0 ){}
void ~DumbPointer() { if( m_ptr ) free( m_ptr ); }
// other stuff left out
private:
void* m_ptr;
};

What happens here is that when I assign this to another DumbPointer object, no one is really sure when the pointer gets freed. This leads to a lot of badness, and can happen in odd places, like passing an instance of the class by value, which will invoke the copy constructor. Unless you're willing to put in the work needed to transfer ownership, or do ref counting, the best thing to do is to explicitly tell everyone not to do this like so:

private:
void DumbPointer( const DumbPointer& ); // don't implement this
const DumbPointer& operator=( const DumbPointer& ); // or this one, either

What now happens is that if someone does inadvertently invoke one of these operators, they won't compile, and if you invoke one of them internal to your class, it won't link. The resulting compiler errors are less than intuitive, but it's better than undefined behavior.

BTW, this is items 5 and 6 in "Effective C++".