Webinar: Evaluation - 05.12
Static analysis cannot detect some types of program errors in a "general" way, or these errors may be extremely difficult to find. In this case, you may be better off learning how to identify many individual error patterns of the same type. In other words, one should look for special cases of errors. The easiest way to understand this concept is to illustrate it.
Both C and C++ have errors related to undefined behavior (UB) in code. So, static analyzers should detect code that causes UB. However, it is easier said than done.
Undefined behavior is not a specific type of a bug or typo. The C and C++ standards describe a wide variety of cases that lead to UB. Undefined behavior has been the subject of many articles and books. New language constructs appear as new C and C++ standards get released. If used incorrectly, they tend to cause undefined behavior in new ways.
So, in reality, the detection of undefined behavior is the creation of many separate diagnostic rules, each looking for its own patterns (cases) of errors. Here is an example:
Let's take a look at another example, which is a null pointer dereference. By the way, this is also a special case of undefined behavior, but everyone considers this error type separately.
The analyzer can find a lot of such errors by performing data flow and control flow analysis. Here's the simplest yet real-world example:
ViewProviderPage* vpp = getViewProviderPage(dView);
if (!vpp) {
return vpp->getQGVPage();
PVS-Studio warns (V522) that if we go into the if statement, a null pointer is dereferenced.
Unfortunately, the high computational complexity makes it impossible for static analyzers to trace all the paths of program execution and data transfer in general. That's why analyzers often can't determine whether a pointer can be null or not.
Warning a programmer about the use of pointers for which there is no information and "just in case" is a bad idea. It may lead to numerous false positives and render the analyzer useless. Helpful warnings may drown in an endless flood of useless ones, and the programmer will stop looking at them. So, at PVS-Studio, we stick to this philosophy: warn users only when there is a reason to suspect an error.
Is there something we can do if even the analyzer was unable to calculate whether a pointer can be null or not? Yes, there is. For example, PVS-Studio provides the following model for searching errors:
Ms::Segment* startSegment = score()->selection().startSegment();
startSegment->measure()->firstEnabled(); // <= (1)
if (!startSegment) { // <= (2)
return nullptr;
}
Let's assume that in this case, the analyzer failed to determine whether the startSegment pointer can be null or not. However, indirectly, we can certainly say that the code contains an error.
Note that the pointer is first dereferenced (1) and then checked (2). Since the pointer is checked, it can be equal to nullptr. So, a null pointer can be dereferenced here.
The PVS-Studio analyzer identifies such an erroneous pattern and issues the V595 warning. By the way, this is a very common antipattern, and we have already collected a large list of such bugs.
This is a very clear example. Theoretically, we may find the use of all null pointers by analyzing the data flow. However, the computational complexity of the task limits the capabilities of analyzers. The special case of an error detection—one of which we have just discussed—comes to the rescue. There are also other ways, such as the V757 diagnostic rule. By complementing each other, the diagnostic rules are generally good at finding null pointers.
Additional links:
0