Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to spot undefined behavior

Is there any way to know if you program has undefined behavior in C++ (or even C), short of memorizing the entire spec?

The reason I ask is that I've noticed a lot of cases of programs working in debug but not release being due to undefined behavior. It would be nice if there were a tool to at least help spot UB, so we know there's the potential for problems.

like image 668
BlueRaja - Danny Pflughoeft Avatar asked Jun 10 '10 15:06

BlueRaja - Danny Pflughoeft


2 Answers

Good coding standards. Protect you from yourself. Here are some ideas:

  1. The code must compile at the highest warning level... without warnings. (In other words, your code must not set off any warnings at all when set to the highest level.) Turn on the error on warning flag for all projects.

    This does mean some extra work when you use other peoples' libraries since they may not have done this. You will also find there are some warnings which are pointless... turn those off individually as your team decides.

  2. Always use RAII.

  3. Never use C style casts! Never! - I think there's like a couple rare cases when you have to break this but you will probably never find them.

  4. If you must reinterpret_cast or cast to void then use a wrapper to make sure you're always casting to/from the same type. In other words, wrap your pointer/object in a boost::any and cast a pointer to it into whatever you need and on the other side do the same. Why? Because you will always know what type to reinterpret_cast from and the boost::any will enforce that you've cast to the correct type after that. It's the safest you can get.

  5. Always initialize your variables at the point of declaration (or in constructor initializers when in a class).

There are more but those are some very important ones to start with.

Nobody can memorize the standard. What we intermediate to advanced C++ programmers do is use constructs we know are safe and protect ourselves from our human nature... and we don't use constructs that are not safe unless we have to and then we take extra care to make sure the danger is all wrapped up in a nice safe interface that is tested to hell and back.

One important thing to remember which is universal across all languages is to:

make your constructs easy to use correctly and difficult to use incorrectly

like image 81
Edward Strange Avatar answered Oct 13 '22 17:10

Edward Strange


It's not possible to detect undefined behavior in all cases. For example, consider x = x++ + 1;. If you're familiar with the language, you know it's UB. Now, *p = (*p)++ + 1; is obviously also UB, but what about *q = (*p)++ + 1;? That's UB if q == p, but other than that it's defined (if awkward-looking). In a given program, it might well be possible to prove that p and q will never be equal when reaching that line, but that can't be done in general.

To help spot UB, use all of the tools you've got. Good compilers will warn for at least the more obvious cases, although you may have to use some compiler options for best coverage. If you have further static analysis tools, use them.

Code reviews are also very good for spotting such problems. Use them, if you've got more than one developer available.

like image 26
David Thornley Avatar answered Oct 13 '22 18:10

David Thornley