crioux's picture By

Type safety is a feature of numerous modern programming languages. C++ is not strict about type safety, and as a result, vulnerabilities may appear in programs in unexpected ways. Here's an example I recently discovered.

Consider this structure:

typedef struct _NOTIFYICONDATAA {
    DWORD cbSize;
    HWND hWnd;
    UINT uID;
    UINT uFlags;
    UINT uCallbackMessage;
    HICON hIcon;

#if (_WIN32_IE < 0x0500)
    CHAR szTip[64];
    CHAR szTip[128];

#if (_WIN32_IE >= 0x0500)
    DWORD dwState;
    DWORD dwStateMask;
    CHAR szInfo[256];
    union {
        UINT uTimeout;
        UINT uVersion;
    CHAR szInfoTitle[64];
    DWORD dwInfoFlags;

#if (_WIN32_IE >= 0x600)
    GUID guidItem;

Note all the _WIN32_IE preprocessor macros. Problem scenario is this: In one file, you create NOTIFYICONDATAA structure, fill in cbSize with sizeof(NOTIFYICONDATAA), and pass it to another routine in another translation unit/source file. In one source file, you have _WIN32_IE undefined, so it defaults to the latest version for your Platform SDK. In the other source file you #define _WIN32_IE to 0x0400 for backward compatibility purposes. Note that this creates a discrepancy in the structural layout, but if you only use the IE4 features of that structure, then you might think you're okay.

This is all too common. We've analyzed a lot of binaries here over at Veracode and found that it is relatively common to have two versions of the same structure compiled with different #define settings and they end up with different lengths, easily leading to overflow conditions that are non-obvious to the casual analysis. In fact, if you only look at one source file at a time, you'll always miss this case, because you need to look interprocedurally between the translation units to note that the types are different between the point of allocation and the point of use.

This underscores a fundamental issue of concern with C++ -- that a type defined in one translation unit is considered equivalent to another type of the same name in another translation unit as long as their names are the same, regardless of their layout. The linker cares when comparing decorated/manged names, but nothing else does. This is usually mitigated by declaring your classes/structures in a header file that is uniformly #included everywhere. However, in the scenario outlined above, it is relatively easy to have things go wrong without hearing a peep from your compiler.

The security ramifications of this issue are clear, but we haven't really been looking for them. I propose that for future correspondence, we refer to this issue as a 'non-uniform layout' bug. Privacy concerns prevent me from posting the examples that I have of this problem, but if anyone out there has good real-life examples of this issue they'd like to publish, I'd love to see them.

Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.




contact menu