Bottoming Out

When you have a class like

class C {
    C(): hasId(false) {}
    // ...
private:
    // ...
    bool hasId;
    int id; // iff hasId
};

instances of which may under certain circumstances be labeled with an ominous, externally inflicted ID (with legal values anywhere in the INT_MININT_MAX range), Coverity Scan will tell you that you have a medium-sized security hole and you should better initialize that id scalar member variable in the C constructor.

Is that helpful? I doubt it.

What shall I initialize it with? Zero? Why zero, what’s so special about zero? If the invariant for C was that “id contains a valid ID iff hasId is true” before, has that now changed to the more complex “if hasId is true, then id contains a valid ID, otherwise id contains zero?” If I do maintenance work on that class a year from now, do I need to worry to reset id to zero when I reset hasId to false in some place in the code?

A better value to initialize such a member variable with would arguably be bottom. After all, every C++ type is inhabited by that. But, alas, C++ doesn’t give you first-class access to bottom as a value to asign to a variable. The closest approximation is probably to leave the variable uninitialized and allow tools like Valgrind to detect violations of the invariant at runtime. The exact thing that Coverity Scan advises you not to do. Oh my.

So a programming error that would have been identified by Valgrind becomes a programming error that is drowned in a too-complex class invariant? “Defensive programming” deluxe?

Another option would be to combine hasId and id into a single member variable of type boost::optional<int>, but that can become unwieldy if multiple members shall be controlled by one conditional bool. Keep it simple.

A somewhat acceptable approach to make Coverity Scan happy might be to explicitly value-initialize id via an empty set of parentheses,

    C(): hasId(false), id() {}

That would still initialize id to that funny zero, but would at least give the adept reader a clue that it shall have no influence on any invariant, and is merely there to appease some automatic analyzer.

Forgive my rant, it’s Friday.

Advertisements

3 thoughts on “Bottoming Out

  1. Karellen

    “leave the variable uninitialized and allow tools like Valgrind to detect violations of the invariant at runtime.”

    That only works for the times you actually run the program under Valgrind. Other users of your (buggy) program will just use the unpredictable uninitialised value and end up doing something unexpected, possibly damaging their data. If all possible ids really are legal, then boost::optional<int> does sound like a better option, because it actaully encapsulates the very thing you’re trying to express. If you have multiple members which may be optional together, make a private struct in your class to contain them, and use boost::optional<members> to express that.

    When you come to read the code again in six months time to fix a bug/add a feature, future you will thank your past self for writing code that explicitly describes exactly what it is doing. You won’t need a comment saying…

    bool hasMembers; // Covers members .foo, .bar and .baz. Do not access them if false.

    …which, when you later add .quux that is also covered by it, you’ll totally forget to update, causing much hilarity^Wwailing and gnashing of teeth when another maintenance programmer does something else with quux, not realising they need to check hasMembers first…

    One variable telling you if another variable is valid? If at all possible, don’t do that.

    Reply
  2. Knödel Dödel

    optional is clearly the right solution here. And if you want more than one member to be controlled by the same flag, use a tuple or a struct in the optional. It’s not hard and certainly not an excuse to employ unsafe coding practises.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s