C++ Core Guidelines Checkers: Preview of the Lifetime Safety checker

Edit 4 June 2016: We’ve now included the Lifetime checker–and any experimental checkers–in the regular CppCoreCheck package. See the blog post here for details: https://blogs.msdn.microsoft.com/vcblog/2016/06/03/c-core-guidelines-checkers-are-now-in-a-single-nuget-package/

In December we announced the first release of CppCoreCheck: code analysis tools to enforce some of the rules in the C++ Core Guidelines. But while there are three profiles currently mentioned in the C++ Core Guidelines, only two of the three are defined: the Bounds Safety Profile and the Type Safety Profile. There’s no definition yet of the Lifetime Safety Profile as it is still undergoing design and refinement.

Today, we’re happy to announce a preview of the Lifetime Safety checker. It’s being released in a separate package from the Bounds and Type Safety checkers because the lifetime checker is only a partial implementation and still in preview. The lifetime checker package will install the Bounds and Type Safety checker package as a subsidiary package. (Note: this makes three packages overall as the current CppCoreCheck package installs Microsoft’s implementation of the Guidelines Support Library. See the December blog post for details.)

It’s important to understand that the lifetime checker is still an experimental preview. The checker implements a small subset of the concepts set out in the design papers available in the C++ Core Guidelines repo in the docs directory. (See Lifetime Safety: Prevent Leaks and Dangling by Sutter and MacIntosh, and A brief introduction to C++’s model for type- and resource- safety, by Stroustrup, Sutter, and Dos Reis.) There are still likely to be bugs in the tool that result in false positive warnings, throughput issues, etc. See the section below on “Known Issues” for more details.

What is Lifetime Safety?

More complete descriptions of lifetime safety are available on the C++ Core Guidelines repo in the docs directory. Basically, though, lifetime safety is ensuring that the lifetime of any object matches its use. That is, don’t leak objects by forgetting to delete them in the case that they were allocated on the heap, and don’t access objects (e.g., through a pointer, reference or iterator) that no longer exist.

Let’s look at an example:

#include <vector>
bool CheckSomething();

void Demo(std::vector<int>& v)
   if (v.empty())
   int* p = &v[0];
   if (CheckSomething())
   *p = 13;

Running the lifetime checker over this code produces this error:

example.cpp(13) : warning C26400 Do not dereference an invalid pointer (lifetimes rule 1). 'p' was invalidated at line 11 by 'std::vector<int,std::allocator<int> >::push_back'. Path trace: 4, 6, 7, 8, 9, 11, 13, 14

In this example, the developer is sensibly ensuring that the vector has at least one element, so they can safely take the address of the first element and store it in p. Unfortunately, after doing so, they sometimes call push_back(), which can potentially reallocate the vector’s storage, invalidating any references to that storage. This will cause p to point at invalid storage, making the subsequent dereference of p potentially unsafe. This code is not wrong in the case that the path where the push_back() occurs is not taken. So on many executions it may be perfectly fine. Sometimes, even when the vector’s storage is reallocated by the push_back() it may still appear to behave correctly, by chance. This can make the bug very difficult to identify and fix with normal testing and debugging approaches. This is exactly the sort of subtle bug that the lifetimes checker aims to catch.

Known Issues

The lifetime checker won’t just identify hidden errors in your code. Like the bounds checker and type checker, it will identify potential errors in perfectly valid code. Think about C-style casts as an analogy. You can write perfectly safe code using C-style casts but should prefer C++ casts. The new casts exist to help catch errors that might be hard to find. Likewise, these checkers identify potential safety issues that you may not find through code review.

There are also some issues in our checkers—both the lifetime checker and the existing bounds and types checkers—that reveal the limitations of the underlying compiler. These limitations are being addressed. You might remember Jim Springfield’s excellent blog post on Rejuvenating the Microsoft C/C++ Compiler. As we do the work to rejuvenate the compiler, the code analysis checkers (both the PREfast checks and the C++ Core Guidelines checks) just get more accurate.

Want to try it out?

The lifetime checkers are published through NuGet, the same way the existing CppCoreCheck package is published. You can find detailed installation instructions on the earlier blog post: C++ Core Guidelines Checkers available for VS 2015 Update 1. The lifetime checker is in a separate DLL, ExperimentalCppCoreCheck.dll. Just search for it in the NuGet Package Manager as you would the CppCoreCheck.dll. The lifetime checker will install the CppCoreCheck package and GSL package as subsidiary packages.

If you’re interested in following our progress on checking your code against the C++ Core Guidelines you might also be interested the work clang is doing in the same area. There is a C++ “linter” tool called “clang-tidy” that is built on the clang C++ compiler. This is a link to details about the checks for the C++ Core Guidelines implemented in clang-tidy.

Send us your feedback!

As always, we want to hear your thoughts. The lifetime checkers are experimental so we’re not looking for the standard bug reports. But if you have feedback on their design and development, please send us mail at cppcorecheck@microsoft.com.