📜 ⬆️ ⬇️

Verone - static analyzer for C ++ with analysis on the fly

My name is Vladimir Plyashkun and in today's article I am going to present a free Verone static analyzer for C ++ 03 / C ++ 11 / C ++ 14, the main feature of which is on-the-fly analysis.

Today there is a large selection of static analysis tools for the C ++ language: Coverity / Klocwork / Parasoft / Clang / CppCheck / PVS-Studio and many others. And recently ReSharper C ++ was also released.

However, the task of analyzing on the fly is still relevant, since There are practically no solutions with this feature. The same ReSharper integrates with Visual Studio and finds many problems online (for example, unused header files), but it is still seen more as a means of refactoring than a classic static analyzer. R # has practically no diagnostics of semantic errors, copy-paste errors and typos, as well as run-time errors (memory leaks, buffer overflows, dereferencing null pointers, etc.)

Coverity / Klocwork / Parasoft - each of these tools calls itself the market leader, but it will be extremely difficult for an individual developer to get even a trial version of these analyzers, because the target orientation of these products is different and aimed at large companies. Therefore, it makes no sense to consider them.
')
Clang is mainly aimed at finding vulnerabilities in the source program. Available checks are available here .

It is disappointing that clang practically does not diagnose typos and semantic errors, and it is somewhat inconvenient to use clang at the moment outside of XCode. Of course, the analyzer can be launched from the console, but such a scheme requires manual configuration (specifying a list of files, paths to directories of header files, predefined macros, etc.). Therefore, such a periodic launch of an external utility outside the IDE can hardly be called convenient and useful.

PVS-Studio went a little further and integrated into the build system. Now the analysis is performed automatically after each build of the project. But again, the question arises, why not to perform analysis on the fly, highlighting all the errors found right during coding? Which is much more logical and convenient for the end user.

This is exactly the problem that the Verone analyzer solves. After installation, the analyzer will automatically start when you open Visual Studio. In the background thread, Verone will track the changes that the programmer makes during the workflow, analyze them, and display the result of the problems found. All diagnostics for the current file are indicated to the right of the editor in a special indent:



The color of the so-called glyph depends on the type of diagnosis (note, warning, error). To view all the problems found, just click on the analyzer icon on the toolbar, after which a window will appear with all the problems found:



At the moment, the analyzer has about 20 diagnostics aimed at finding semantic errors and typos. Of course, this is extremely small in order to compete with existing instruments, but it is worth understanding that this is only the first release and many developments simply did not enter it. Plans for the future will be at the end of the article. However, this is already enough to detect defects in real projects.

For example, the following problems were found in the LLVM source code:

else if (ArgTy->isStructureOrClassType()) return record_type_class; else if (ArgTy->isUnionType()) //<--- return union_type_class; else if (ArgTy->isArrayType()) return array_type_class; else if (ArgTy->isUnionType()) //<---- return union_type_class; 

Here is the condition

 ArgTy->isUnionType() 

repeated twice due to commonplace inattention. And if this fragment can hardly be considered a serious bug, then this code is already causing much more suspicion:

 if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY) //<---- outs() << "undefined [lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_UNDEFINED_LAZY) //<--- outs() << "undefined [private lazy bound]) "; else if ((NDesc & MachO::REFERENCE_TYPE) == MachO::REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY) outs() << "undefined [private]) "; else outs() << "undefined) "; 

The second block will never get control, because its condition is completely identical to the condition of the first block. After studying the source code, I can assume that in the second case there should be a condition of the form:

 (NDesc & MachO::REFERENCE_TYPE) == MachO:: REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY 

A similar problem was discovered by the libsass project.

 if (lhs_tail->combinator() != rhs_tail->combinator()) return false; if (lhs_tail->head() && !rhs_tail->head()) return false; if (!lhs_tail->head() && rhs_tail->head()) return false; if (lhs_tail->head() && lhs_tail->head()) { //<--- if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; } 

In this fragment, the last condition is mistaken:

 lhs_tail->head() && lhs_tail->head() 

Again, the code is written in such a way that the error did not make itself felt, but this does not mean that it does not need to be corrected, which was done by the authors in one of the latest commits.

A number of suspicious locations were found in the Torque 3D graphics engine.

 // Update accumulation texture if it changed. // Note: accumulation texture can be NULL, and must be updated. if ( passRI->accuTex != lastAccuTex ) { sgData.accuTex = passRI->accuTex; lastAccuTex = lastAccuTex; dirty = true; } 

Based on logic, the lastAccuTex pointer (GFXTextureObject *) refers to the last processed object passRI-> accuTex, but due to inattention of the variable, an eigenvalue is assigned.

And in this fragment:

 class ConnectionProtocol { public: ConnectionProtocol(); virtual void processRawPacket(BitStream *bstream); virtual Net::Error sendPacket(BitStream *bstream) = 0; virtual void keepAlive() = 0; virtual void handleConnectionEstablished() = 0; virtual void handleNotify(bool recvd) = 0; virtual void handlePacket(BitStream *bstream) = 0; ... }; 

The class is polymorphic, but it has a default destructor (not virtual). Therefore, it can lead to resource leaks in child classes, if the removal will use a pointer to the base class.

There are suspicious places with integer division, rejecting the fractional part, such as here:

 bm->avgfloat=PACKETBLOBS/2 

The variable bm-> avgfloat is a floating point type, but since the integer division is to the right of the assignment operator, the fractional part will be dropped. Not to say that this is a clear mistake, but requires attention.

In conclusion, it should be said that the development of a static analyzer is a very big task that requires a lot of resources and time. There are many developments that are not included in this release. Therefore, I want to describe several areas in which Verone will develop over the next six months or a year.

  1. More advanced analysis algorithm on the fly, based on the generation of mock files (thanks to Sasha Kokovin for the idea)
    Now this algorithm works extremely simply. In fact, everything is tied around the file with pairs <file name> - <last analysis date> . And the core of the analyzer just starts at a certain frequency. Since in the process of work a small number of files change, such a scheme still allowed to scale to large projects.
  2. Ability to analyze still unsaved changes in the editor
    The above scheme, unfortunately, is tied to files and does not take into account changes that have already been made in the editor, but not yet saved. This task is quite voluminous and requires thorough testing, and therefore was not implemented in the first release.
  3. Runtime error analysis
    One of the main directions is the analysis of run-time errors, which is not in the current version. Analysis of runtime errors, coupled with analysis on the fly, looks very promising, for I see, for example, dereferencing a null pointer at the time of encoding is very cool! Of course, some simplifications will have to be made, since the data flow analysis algorithms are quite resource intensive. But even, for example, local analysis will still bring tangible benefits, warning the developer about possible problems.

Of the more distant tasks, it is worth highlighting also:

  1. GUI Improvements
    Now there is no functionality for hiding diagnostics, searching for them and saving to a file, which would be useful in everyday work.
  2. Mastering other IDE and operating systems
  3. Standalone app
    Independent graphical application is useful, because not confined to any development environment. Already, you can safely use the console version of the analyzer, but the GUI version seems to be a more convenient solution for this purpose.

The analyzer is available for download at the link . At the moment, the analyzer is available only for Windows, a version is available for integration with Visual Studio 2015. Versions for Visual Studio 2012 and Visual Studio 2013 will appear in a week or two.

Ready to answer any questions. I would appreciate feedback and suggestions!

Source: https://habr.com/ru/post/272407/


All Articles