logoalt Hacker News

dataflowtoday at 6:14 PM1 replyview on HN

Here's a trivial example to illustrate:

  class User {
    explicit User(const char* name) {
      if (!look_up_uid(&uid, name)) {
       abort();
     }
    }

    bool is_root() const {
      return uid == 0;
    }

    int uid;
  };
Let's say look_up_uid() forgot to fill in uid for certain special kinds of users. Like maybe you have a dummy 'nobody' user that was introduced specially after the fact and which is not in the database like the rest.

As C++ is right now, uid would contain garbage. Which means that, at run time, you would often get invalid UIDs if you attempted to log in with such a user, triggering some logging or reporting you to Santa or whatever. And which means that sanitizers would immediately tell you that you forgot to initalialize the field if you ever try to use it (say, in is_root()). Both of these would flag the bug the moment that that kind of user attempts to log in, and make you dig into look_up_uid()'s body to figure out why it's not returning the UID when it's supposed to.

However, if C++ were to zero-initialize everything by default, then neither of those would be true - you would silently get a root user, which is capable of doing everything that nobody can do. And someone who reads the code wouldn't immediately know that you have such a bug; it would sit there idly until someone exploits it.


Replies

OrderlyTiamattoday at 8:26 PM

Thank you, I understand the example better now. Squashing the class of problems of using non-initialized variables by using zero-initialization causes potentially worse bugs since 0 could inadvertently be a correct value. That makes sense!

Going back to GP:

> being too good at solving one class of problems selects for other classes that are more resilient and harder to find

Rather than this being too good at solving this class of problem, it seems to me that zero-initialization is the wrong approach; if the default value were present in the program, it'd be eas(y|ier) to spot. Initializer checks can do that without introducing this issue. You're also using the fact that non-initialized values are "random" by default- we could also use fuzzer checkers for that.

I think the general lesson from the example is that the way you solve a class of problems could introduce more pernicious ones, rather than the fact that it's solved.

show 1 reply