Every programmer worthy of the name knows that it’s better to avoid bugs than to fix them. Whole libraries are filled with suggestions about how to facilitate this, but the particular angle that interests me the most is how we can use computers themselves to help us write better code. A lot of bugs are the result of violating some kind of rule, such as the rule that says not to dereference a pointer that might be null, and computers are really good at dealing with rules. From this observation came the field of static program analysis, which checks code before it’s even compiled to make sure that it conforms to rules the programmer has defined. Engler et al referred to this as Meta-Level Compilation (the “meta” refers to “metadata” about the state of variables or of the program itself) and showed that it could be applied to real-life code without tedious manual code annotation, before they went off to start Coverity. Cqual is another tool based on a similar idea of type annotations. These annotations allow you to distinguish the “tainted” character buffer that contains user data from the “safe” character buffer that contains an SQL command (note that they might be exactly the same bits in either kind of buffer) and flag inappropriate use of one where the other is expected.

I used Coverity’s product at Revivio, and found it extremely useful. Such tools are not without their drawbacks, however. Most significant is the fact that they run separately from the compiler you actually use to generate code, often using their own separate parser to do their stuff. This costs time, makes build processes more complex, and potentially creates problems with incompatibility between their parser and yours. What I would often prefer is some tool or technique that allows me to use my existing compiler, with the help of some macros, to catch a (necessarily more limited) set of rules violations without all that overhead. The other night I thought of one such technique, to solve what I often use as a “test case” for my thinking about all forms of automated bug-finding. Much of my work has to do with concurrency, and one of the rules I’d often like to apply is one that says a certain structure member shouldn’t be accessed without holding a certain lock on that structure. To do this, one must track three types of “events” that might occur within a program. For simplicity, let’s assume the structure type is X and is always allocated dynamically. The three events are:

  • Obtaining/assigning an X pointer.
  • Locking/unlocking an X.
  • Dereferencing an X pointer.

The last one is the trickiest. In C++ you can do some interesting things by overloading the arrow operator for X, but what about C? You can require that all access to X’s members be through macros or functions that check lock state, but that has several drawbacks of its own – it makes the code much uglier, adherence to the coding standard must be checked by humans, and there might be a performance penalty. Read on for a technique that I think achieves much of the same value while avoiding the drawback.

#define DECL_UNSAFE(t)                          \
typedef struct _unsafe_##t * unsafe_##t##_ptr;  \
inline void                                     \
lock_##t (unsafe_##t##_ptr unsafe, t ** safe)   \
{                                               \
    *safe = (t *)unsafe;                        \
    normal_lock_function(*safe);                \
}                                               \
inline void                                     \
unlock_##t (t ** safe)                          \
{                                               \
    normal_unlock_function(*safe);              \
    *safe = NULL;                               \
}

This takes advantage of two C features, both of which I normally consider warts. The first is token-pasting within macros. The second is the fact that you can have a pointer to a struct that you know nothing about, and that might actually not exist (as is the case here). Thus, the pointer is kind of type-safe but otherwise useless. What the above does is allow you to declare a “parallel” pointer type, which is just like a pointer to some base type but which represents that base pointer type in its unsafe state. To see what this means, consider an example:

DECL_UNSAFE(my_struct)

void
use (unsafe_my_struct_ptr in)
{
    my_struct * out;

    // Error: no such thing.
    //in->foo = 6;

    lock_my_struct(in,&out);
    // Locked access is OK.
    printf("out->foo = %d\n",out->foo);
    unlock_my_struct(&out);

    // Unlocked access will cause null-pointer exception.
    printf("out->foo = %d\n",out->foo);
}

In a way, this is exactly what we want: the rule-following access is allowed and the rule-breaking access is denied, without serious uglification of the code. Sure, you have to keep two pointers, but the conversion between them is mostly explicit and it’s far cleaner than having to make every access through a macro. Any compiler of the last ten years will also realize that the two pointer variables have non-overlapping lifetimes, so there shouldn’t be a performance penalty. Note that the NULL assignment in the synthesized unlock function ensures that the “safe” pointer can’t be used after an unlock, so this works even for branching/looping control paths quite naturally. With a little imagination, the same basic approach can also protect against double locks/unlocks, unlocks without locks (locking issues are a big part of my programming life), the tainted-buffer example given earlier, and a bunch of other similar kinds of rule violations.