Thursday, August 31, 2017

How to Step Over Legacy and Start Using Static Code Analysis

by Svyatoslav Razmyslov 

Legacy code problems are familiar to the majority of software developers. The process of transforming code in legacy is inevitable, because progress in programming moves on. Projects either "die" forever, or require constant support and writing new functions. Thus, in any project in any programming language legacy code appears and brings different inconveniences when the process of further development. On the example of PVS-Studio, in this article I'll show you how to immediately start using the static code analyzer in your project.
Picture 1

Introduction


To start, let us deal with the definition of legacy code and get acquainted with PVS-Studio static analyzer.
A term "legacy" is not always related to the age of code. Here are some of the most common definitions:
  • Code is not covered by tests - it is usually a very serious reason to make less changes in such code;
  • Legacy code - it was forgotten how it works long time ago. In addition, if it is not covered by tests, the situation is very sad, because the code is still used and requires support.
  • Third-party code - is similar to the previous definition, but even in the new third-party code these problems may occur;
  • The code is saved for compatibility - this code is usually old and, probably, is not tested with compatible programs;
  • Other similar definitions.
In any of these situations, developers are trying to modify code as carefully as possible when designing a project not to crash something. Additionally, a project can also use techniques and tools for writing more effective code of high quality. In this case, the old code may need to make much more changes. A using of modern standard language, conformity to a style guide and implementation of the static code analyzer are a few examples. Unfortunately, any of these ideas faces such an obstacle as legacy-code. It can be everywhere: it is impossible to separate it like test files or third-library files. Almost all tools, offering modifications of code, have the ability to mark up the code to disable the warnings, but in practice, this method cannot be applied. The first time you use such a tool, no one will let you mark up hundreds of thousands of comments in source code files - it is a very large amount of changed code because of which a view of changes in the systems of SCM (Source Control Management) will become very complicated. So, for PVS-Studio static analyzer a system of mass warnings suppression has been developed so that there would be no need to modify the project source files. This feature allows you to easily embed the analyzer in any project and IMMEDIATELY start to benefit from this, i.e. to find new bugs.
PVS-Studio is a static analyzer for bug detection in the source code of programs, written in C, C++ and C#. It can be run on Windows and Linux, integrate into build systems, various IDE systems and continuous integration systems.

Operation principles

Suppression mechanism is based on the use of special files "bases" of analyzer messages that are added next to the project. These files contain messages, tagged for this project as "unnecessary". During the next files checks of this type by the analyzer, it will automatically verify the existence of such files next to the project, and in case of their detecting will not show the messages, which are included in such a "base". We should note that a modifying of the source file that contains the tagged messages, and, in particular, lines shift, would not lead to the re-emergence of these messages. However, the edit of the line containing this message can lead to its repeated occurrence, since this message has already become "new".

The Usage of Suppression Mechanism in Visual Studio (Windows)

For Microsoft Visual Studio PVS-Studio plugin is available, which is conveniently integrates in IDE. It allows you to run a check of the entire solution, specific projects or files, and it supports incremental analysis.
In PVS-Studio menu (Figure 1) a point "Suppress Messages ..." is available, which opens the window for working with suppressed analyzer warnings (Figure 2).
Figure 1 - PVS-Studio Menu in Visual Studio
Figure 1 - PVS-Studio Menu in Visual Studio
Figure 2 - Window for analyzer warnings suppression
Figure 2 - Window for analyzer warnings suppression
In the opening window, several actions are available:
  • Suppress Current Messages - suppressing of all analyzer warnings ;
  • Clear Selected Files - restoring of hidden warnings for all or some of the projects;
  • Display Suppressed Messages - a displaying of hidden analyzer warnings in a window (PVS-Studio Output Window) with the rest of the warnings. In this mode, you can return to correcting previously suppressed warnings. Such messages will be marked, strikethrough in a special way, so they cannot be confused with others.
To view analysis results in Visual Studio, there is a special window (Figure 3).
Figure 3 - PVS-Studio Output Window
Figure 3 - PVS-Studio Output Window
Special window allows navigating along found warnings and jump to the code to fix them. PVS-Studio window provides a wide range of options for filtering and sorting the results. It is also possible to quickly navigate to the documentation of the selected diagnostic.
Additional opportunities of working with each message are available in the context menu by clicking the right mouse button on the message (Figure 4). Here the command for suppressing a selected warning is available. When menu entry at the already suppressed warning, false alarm mark will be available for reverting.
Figure 4 - A context menu of analyzer warning
Figure 4 - A context menu of analyzer warning

The usage of the Suppression Mechanism in Standalone (Windows)

PVS-Studio can be used regardless of the integrated Visual Studio development environment. Standalone provides opportunities to test the code regardless of the used compiler or build system, and then lets you work with analysis results providing a user interface similar to PVS-Studio plugin for Visual Studio (Figure 5).
Figure 5 - PVS-Studio Standalone
Figure 5 - PVS-Studio Standalone
Standalone also allows you to work with the analyzer report, obtained by its direct integration into the build system, when a user does not have the Visual Studio environment.
Standalone menu for the analysis run and warnings suppression looks as follows (Figure 6).
Figure 6- Menu Tools of Standalone utility
Figure 6- Menu Tools of Standalone utility
When choosing a point in menu for analyzer run, the window "Compiler Monitoring (C/C++)" will appear (Figure 7).
Figure 7 - Dialog of build monitoring run
Figure 7 - Dialog of build monitoring run
For analyzer warnings filtration, before the analysis a programmer has to specify the file with warnings, suppressed earlier. You can create and maintain such a file through the menu "Message Suppression...", which is the same as the one presented in a section about Visual Studio at Figure 2. After the completion of the analysis in a PVS-Studio window, only new errors will be shown. Without specifying the file, the analyzer will show all the results.

The Usage of Suppression Mechanism in Linux

In Linux commands of suppression and filtration of analyzer warnings are performed only in console, but when their automating on server, the mechanism of suppression becomes very convenient.
There are several ways of using this mechanism, depending on the integration of the analyzer.

Analysis using pvs-studio-analyzer Utility

To suppress all analyzer warnings (first time and in subsequent occasions) you need to execute the command:
pvs-studio-analyzer suppress /path/to/report.log
Analysis of the project can be run as before. At the same time suppressed warnings will be filtered:
pvs-studio-analyzer analyze ... -o /path/to/report.log
plog-converter ...
With this run suppressed warnings will be saved in the current directory in a file named uppress_base.json, which should be stored with the project. New suppressed warnings will be recorded to the file. If there is a need to specify a different name or location of the file, than the commands above may be supplemented by specifying the path to the file with suppressed warnings.

Direct Integration of the Analyzer in the Build System

Direct integration might look as follows:
.cpp.o:
  $(CXX) $(CFLAGS) $(DFLAGS) $(INCLUDES) $< -o $@
  pvs-studio --cfg $(CFG_PATH) --source-file $< --language C++
     --cl-params $(CFLAGS) $(DFLAGS) $(INCLUDES) $<
In this mode, the analyzer cannot verify source files and filter them simultaneously. So, filtration and warnings suppression would require additional commands.
To suppress all the warnings, you must also run the command:
pvs-studio-analyzer suppress /path/to/report.log
To filter a new log, you must use the following commands:
pvs-studio-analyzer filter-suppressed /path/to/report.log
plog-converter ...
File with suppressed warnings also has the default name suppress_base.json, for which you can optionally specify an arbitrary name.

The use of the Suppression Mechanism in SonarQube

SonarQube (formerly Sonar) is an open source platform for continuous inspection of code quality.
PVS-Studio plugin is available to users of this system. SonarQube combines the results of the analysis to a single dashboard, taking the history of runs, allowing to see the overall trend of software quality during a development. An additional advantage is the ability to combine the results of different analyzers.
So, after receiving the results of the analysis of one or more analyzers, you must go to the list of warnings and click on "Bulk Change", which opens the next menu (Figure 8).
Figure 8- A massive change of errors status
Figure 8- A massive change of errors status
In this window, you can mark up all warnings of the analyzer as "won't fix" and further work only with new errors.

What can we do after suppressing all warnings?

Configure static analysis on the build server and developers' computers. Correct new warnings of the analyzer and not let them accumulate. It is also worth to plan search and correction of errors among the suppressed warnings.
Additional control over code quality will help to ensure sending results by mail. It is possible to send warnings to only those programmers who made erroneous code using BlameNotifier utility, which is included in PVS-Studio distribution package.
For some users it may be convenient to load results in Jenkins or TeamCity using PVS-Studio plugin, and send a link to this page.

Conclusion

Missing of analyzer warnings on legacy-code is not the only scenario of presented mechanisms of using. Mass warnings suppression allows you to easily embed the analyzer in any project and immediately start to benefit from this, i.e. to find new bugs. Such implementation allows you to plan correcting of missed warnings in future, without distracting developers from performing their current tasks. This mechanism can also be used in future to suppress false alarms and warnings, which will not be corrected for various reasons.
This article discusses all the possible ways of suppressing analyzer warnings now. The collected material is based on documentation for the PVS-Studio analyzer, but the details on that topic were considered more than in documentation. General information may not be very informative for new users, so you should be familiar with the documentation below.
An alternative way of nullifying the number of analyzer warnings is a correction of all the analyzer warnings at the initial stage. Epic Games company used this way in Unreal Engine project. Details on this can be found in the article: How the PVS-Studio Team Improved Unreal Engine's Code

Wednesday, August 30, 2017

Useful Improvements in the PVS-Studio 6.17 Release

Today we released a new version of PVS-Studio 6.17 static analyzer. In this version there are improvements, which, in my opinion, deserve a small note. I suggest to get acquainted with them, and then download the latest version of the distribution package.
Рисунок 1
We continue to develop our analyzer in relation to Linux. In other words, the Linux version is overtaking Windows version of analyzer on its abilities. The next step was the implementation of a plugin for SonarQube quality control system and adding a mode of mass warnings suppression for Linux. More detailed information can be received from the documentation sections "Integration of PVS-Studio analysis results into SonarQube" and "How to run PVS-Studio on Linux".
In the command line module PVS-Studio_Cmd.exe a new incremental analysis mode "AppendScan" was added. The usage of this mode will let "accumulate" information about the files that need to be analyzed. Thus, more options of performing the analysis become available that will let you configure the analyzer in terms of the needs of the project. More details are given in the article "PVS-Studio's incremental analysis mode".
Several new diagnostics were added, but nothing remarkable:
  • C++. V821. The variable can be constructed in a lower level scope.
  • C++. V1001. The variable is assigned but is not used until the end of the function.
  • C#. V3135. The initial value of the index in the nested loop equals 'i'. Consider using 'i + 1' instead.
  • C#. V3136. Constant expression in switch statement.
  • C#. V3137. The variable is assigned but is not used until the end of the function.
A much more interesting feature is that a mechanism of virtual values was significantly redesigned in the kernel of C++ analyzer. For example, now the analyzer performs a double loop passage, which allows it to define the range of possible values of variables, changing in a loop, more accurately. So don't be surprised if the analyzer starts issuing many warnings for that code which used to seem correct for the analyzer. We definitely recommend to upgrade the analyzer version because this way you will be able to find new bugs.
Another interesting improvement is the ability to make C++ analyzer think that malloc function cannot return NULL. Some programmers don't want to deal with the situation when the malloc returns a null pointer. That is why, pointers are used without prior check and, as a consequence, PVS-Studio analyzer issues plenty of warnings. Developers consider them as false positives and they asked for the ability to modify the behavior of the analyzer. An example of such an approach is described in the article "Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries". To configure the behavior of the analyzer special comments are used that are described in the section "Additional diagnostics configuration".
Also a plugin has been added in the distribution package which allows displaying analysis results in a Jenkins continuous integration system (so far available on Windows). This will allow you a better monitoring of the amount of potentially dangerous places in the project. A using the plugin for Jenkins is described in more detail in the article "Integrating PVS-Studio into the Continuous Integration Process" in the section "PVS-Studio plugin for Jenkins".
Other improvements:
  • The ability is also added to save reports of analyzer work from Visual Studio plugin and Standalone in txt\csv\html formats without a need to manually call PlogConverter.
  • License and file settings are now saved in UTF-8 encoding. For compatibility the analyzer supports files in UTF-16 format that we used previously.
  • A list of recently opened logs is added in a Visual Studio plugin menu.
  • Support of Visual Studio 2017 15.3 update.
Download and try the new version of the analyzer! Also, I'd like to take this opportunity to remind that we wrote a series of articles dedicated to a verification of a Tizen operating system code and hope they were interesting. If you missed the information about their publications, I suggest go over the links and get acquainted with these articles:

Thursday, August 17, 2017

Use PVS-Studio to Find Errors in Your Colleagues' Code

by Andrey Karpov

We noticed that many people read our articles, dedicated to the abilities of PVS-Studio static code analyzer. Despite this, a small number of readers reach the download page with a distribution package. We would like to assure that downloading and trying PVS-Studio is very simple. On our site there is a demo version that you can download without filling in different forms and immediately begin detecting errors in code. To encourage our readers we decided to give an idea - to search errors in colleagues' code. This can become an interesting competition.
Рисунок 2
PVS-Studio performs static code analysis and generates warnings that help a programmer to find and fix bugs. We regularly demonstrate abilities of the analyzer in the articles, dedicated to the check of open source projects. Here are some recent publications:

The articles are really meaningful, people read them with great interest. At the same time we were disappointed by three messages, which we received last month. They had similar ideas such as:
For a year/two years I've been reading your articles with great interest. Finally, I decided to try your analyzer. I really liked it, but I have a question...
Aah! Why wait for 2 years? The analyzer is so simple to download and try. Very simple:
  • download;
  • install;
  • run;
  • study the warnings and ask yourself and others, how this code could actually work :)
So, I inspire not to postpone for years, just try it. You will like it! You will find a lot of interesting bugs both in your code and in code of your colleagues.
I suggest to have some fun! You find an error, and then show the related fragment to the author of the code. Let him try to find this error. It is a great way to appreciate the analyzer.
Download PVS-Studio and find errors.

Tuesday, August 15, 2017

Story of One Exception or This is How We Have to Debug Other People's Code

by Paul Eremeev and Sergey Vasiliev 
Using third-party libraries allows you to get the functionality you want, without wasting time on the development of the corresponding logic. Take and use it! Of course, such an approach doesn't include only the merits, that's why it has another "dark" side. One of the problems inherent to using third-party libraries is the lack of control over things that are going on inside. It all started with a user who wrote about an unhandled exception, appearing when checking C# project...
Before moving to a debriefing session, one has to understand at least approximately about interacting between PVS-Studio, Roslyn and MSBuild. To keep it short - to open C++ and C# projects, PVS-Studio uses MSBuild libraries, and for C# projects Roslyn libraries are additionally used (which, in turn, uses MSBuild as well). If you would like to get more details about the interaction of these components, you can find them in the article: "Support of Visual Studio 2017 and Roslyn 2.0 in PVS-Studio: sometimes it's not that easy to use ready-made solutions as it may seem".
As I've already said it all started with a crash with this warning:
Unhandled Exception: System.IO.FileNotFoundException: Could not load 
file or assembly 'Microsoft.Build.Framework, Version=15.1.0.0, 
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its 
dependencies. The system cannot find the file specified. ---> 
System.IO.FileNotFoundException: Could not load file or assembly
'Microsoft.Build.Framework, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The 
system cannot find the file specified.
The most interesting thing here is that PVS-Studio is not mentioned in the error message, that made us think that we are not directly related to the crash. That's good. But when verifying the project, PVS-Studio still crashes, which is bad. Moreover, the exception was thrown in a way, that it couldn't be caught and handled properly on our side. So, we had to investigate and dig deep, e.g. configure debug projects to explore source code of Roslyn and MSBuild libraries.
It was clear at once that the exception is coming from within the Roslyn, while trying to open a project, but the Antlr4.Build.Tasks mention gave the idea, that the problem is more specific.
Gradually, exploring the code and making a way in the wilds of third-party dependencies connected with PVS-Studio, this call chain was reconstructed:
PVS-Studio -> Roslyn -> MSBuild -> Antlr4
It should be explained how Antlr4 dependencies got here, if PVS-Studio does not use them. The project file, parsing of which had generated an exception, utilizes the Antlr4 code generatortask, which is a user-defined MSBuild task. Such a task can be imported into any standard MSBuild project and it can be called as a separate build step. Thus, to open the project, in addition to Roslyn and MSBuild, we find ourselves tied by such third party components necessary for a build. Since the Roslyn, in addition to its evaluation, runs various build steps to open the project, code from Antlr4 is also executed inside the process of our analyzer.
Task's source code, throwing an exception, is available as an open source at GitHub. This allowed us to rebuild a task's .dll with debug symbols and see what was going on inside.
The most interesting (and useful for us) from what was inside, was a way in which an external process is launched - it took place in another application domain. During the instantiation of a type containing a method for processing the stdout of the aforementioned external process, the task requested the Microsoft.Build.Framework.dll library. Here comes the most interesting part. This library is already loaded by the process of our analyzer - it is needed both by us (for MSBuild files parsing) and by Roslyn. However, as you may remember, we are now in another AppDomain and the library is not loaded into it yet. The crash occurs while attempting to load this very library.
A careful reader may immediately have a question - why there is a problem with loading this library now, if in the main AppDomain it was just fine? The answer is how this AppDomain was created. A directory, passed to its constructor as base one, was the one containing the task's dll, but not a directory with the executable PVS-Studio_Cmd.exe file. This, in turn, led to a change in the behavior of the Fusion subsystem, which is responsible for finding and loading dependent .NET assemblies. Fusion searches for the assembly in the directory of the executable file (which is usually an exe file) and in the Global Assembly Cache. Creation of an AppDomain with another base directory made Fusion search for dll file in another directory, but not in the directory of executable PVS-Studio_Cmd.exe file. Yes, it is worth noting that PVS-Studio in its distribution has a significant number of MSBuild libraries (including the one which we are talking about) - the reason why it happened so can also be found in the article about our support of Visual Studio 2017, the link to which is given above. At the same time, starting with MSBuild version 15, its libraries are not registered at GAC. These two reasons (lack of a library in the GAC and change of the AppDomain base directory, in which Fusion looks for dependencies) led to the crash of the program.
As the exception occurred in another AppDomain, it is not possible to catch and handle it in analyzer application domain. More precisely, it is possible, but the application will still crash. The most we can do is to undertake actions for a softer crash, for example, inform about the source of the problem (what we did).
By the way, the same error occurs in the Microsoft Visual Studio 2017 development environment when trying to build this project. Most likely, it has existed in the Antlr4 task, but remained unnoticed because previous versions of MSBuild registered its dependencies in GAC, and Fusion found them there. At the moment, the bug is fixed in the Antlr4 repository. Do you know what decision did Antlr4 task developers made to fix this problem? Not to create a separate AppDomain, and do all necessary operations in the default one. Why it was necessary to complicate logic from the very beginning is an open question.
Overall, it turned out to be very ironic. The AppDomain technology, created with the aim of increasing the fault tolerance of the applications by creating isolated areas of execution, eventually led to the inability to protect ourselves in this situation. Of course, it is clear that such a situation could arise in case of an unhandled exception in a thread created in a third-party library as well, even when this thread is created in the default domain.
Anyway, we can learn several lessons from this situation:
  • There is no need to reinvent the bicycle. The more complex code, the more likely you will make a mistake.
  • Get ready to pay a certain price for the use of third-party libraries. Sometimes it can be quite huge.
  • Even if the problem is not in your code, but in some of the dependencies, the problem will automatically be your headache when it reveals itself.
  • The world is not perfect - unfortunately, there are things out of our control. For example, the exceptions, thrown from other application domains or threads.

Tuesday, August 1, 2017

Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, 10-15% of False Positives

by Andrey Karpov 

After I wrote quite a big article about the analysis of the Tizen OS code, I received a large number of questions concerning the percentage of false positives and the density of errors (how many errors PVS-Studio detects per 1000 lines of code). Apparently, my reasoning that it strongly depends on the project to be analyzed and the settings of the analyzer didn't seem sufficient enough. Therefore, I decided to provide specific figures by doing a more thorough investigation of one of the project of the Tizen OS. I decided that it would be quite interesting to take EFL Core Libraries, because one of the developers, Carsten Haitzler, took an active part in the discussion of my articles. I hope this article would prove to Carsten that PVS-Studio is a worthy tool.
Рисунок 3

Prehistory



If there were people who missed the news, then I just inform that recently I have written an open letter to the Tizen developers, and then a monumental article "27000 Errors in the Tizen Operating System".
After that, there were several news post on various resources and quite vivid discussions. Here are some of them:
I would express special gratitude to Carsten Haitzler once more, for his attention to my post and active discussion of it.
There were various topics raised, some of them were covered in more details in the post "Tizen: summing up".
However, there are two eternal questions which continue haunting me.
  • What is the percentage of false positives?
  • How many errors does PVS-Studio find per 1000 lines of code?
Those programmers, who are well aware of what is the methodology of static analysis would agree with me that such generalized questions don't have any sense at all. It all depends of the project we are working with. Asking such questions is like trying to measure an average temperature of all patients in a hospital.
So I'll give the answer on the example of a specific project. I chose EFL Core Libraries. Firstly, this project is part of Tizen. Secondly, as I have already said, one of the developers is Carsten Haitzler, who would probably find these results interesting.
I could also check Enlightenment, but I didn't have enough energy for it. I feel that this article will be already rather long.
The Enlightenment Foundation Libraries (EFL) are a set of graphics libraries that grew out of the development of Enlightenment, a window manager and Wayland compositor.
To check the EFL Core Libraries I used the recent code, taken from the repository https://git.enlightenment.org/.
It's worth mentioning that this project is checked by Coverity static code analyzer. Here is a comment on this topic:
I will say that we take checking seriously. Coverity reports a bug rate of 0 for Enlightenment upstream (we've fixed all issues Coverity points out or dismissed them as false after taking a good look) and the bug rate for EFL is 0.04 issues per 1k lines of code which is pretty small (finding issues is easy enough i the codebase is large). They are mostly not so big impacting things. Every release we do has our bug rates go down and we tend to go through a bout of "fixing the issues" in the weeks prior to a release.
So, let's see what PVS-Studio can show us.

Characteristics

After proper configuration, PVS-Studio will issue 10-15% of false positives during the analysis of EFL Core Libraries.
The density of the detectable errors in EFL Core Libraries is 0.71 errors per 1000 lines of code at this point.

The Way I did the calculations

The project EFL Core Libraries at the moment of analysis has about 1 616 000 lines of code written in C and C++. 17.7% of them are comments. Thus, the number of code lines without comments - 1 330 000.
After the first run I saw the following number of general analysis warnings (GA):
  • High level of certainty: 605
  • Medium level of certainty: 3924
  • Low level of certainty: 1186
Of course, this is a bad result. That's why I don't like to write abstract results of measurements. The work requires proper analyzer settings, this time I decided to spend some time on it.
Almost the whole project is written in C, and as a result, macros are widely used in it. They are the cause of most of the false positives. I spent about 40 minutes on a quick review of the report and came up with the file efl_settings.txt.
The file contains the necessary settings. To use them during the project analysis, it's necessary to specify in the configuration file of the analyzer (for example, in PVS-Studio.cfg) the following:
rules-config=/path/to/efl_settings.txt
The analyzer can be run in the following way:
pvs-studio-analyzer analyze ... --cfg /path/to/PVS-Studio.cfg ...
or like this:
pvs-studio ... --cfg /patn/to/PVS-Studio.cfg ...
depending on the way of integration.
With the help of these settings I specified in the analyzer, so that it doesn't issue some warnings for those code lines, in which there are names of certain macros or expressions. I have also disabled several diagnostics at all. For example, I disabled V505. It's not great to use the alloca function in the loops, but it's not a crucial error. I don't want to debate a lot, whether a certain warning is a false positive, so I thought it would be easier to disable something.
Yes, it should be noted that I reviewed and set up only the warnings of the first two certainty levels. Further on, I will review only them. We aren't going to consider warnings of low certainty level. At least, it would be irrational to start using the analyzer and review warnings of this level. Only after sorting out the warnings of the first two levels, you can take a look at the third and choose the useful warnings at your glance.
The second run had the following results:
  • High level of certainty: 189
  • Medium level of certainty: 1186
  • Low level of certainty: 1186
The number 1186 is repeated twice. This is not a typo. These numbers have really turned up to be the same.
So, having spent 40 minutes to set up the analyzer, I significantly reduced the number of false positives. Of course, I have a lot of experience in it, it would probably take more time it were a programmer who is new to it, but there is nothing terrible and difficult in the configuring of the analyzer.
In total I got 189 +1186 = 1375 messages (High + Medium) with which I started working.
After I reviewed these warnings, I suppose that the analyzer detected 950 fragments of code, containing errors. In other words, I found 950 fragments that require fixing. I will give more details about these errors in the next chapter.
Let's evaluate the density of the detected errors.
950*1000/1330000 = about 0,71 errors per 1000 lines of code.
Now, let's evaluate the percentage of false positives:
((1375-950) / 1375) * 100% = 30%
Well, wait! In the beginning of the article there was a number 10-15% of false positives. Here it's 30%.
Let me explain. So, reviewing the report of 1375 warnings, I came to the conclusion that 950 of them indicate errors. There were 425 warnings left.
But not all these 425 warnings are false positives. There are a lot of messages, reviewing which it is impossible to say if there is an error or not.
Let's consider one example of a message that I decided to skip.
....
uint64_t callback_mask;
....
static void
_check_event_catcher_add(void *data, const Efl_Event *event)
{
  ....
  Evas_Callback_Type type = EVAS_CALLBACK_LAST;
  ....
  else if ((type = _legacy_evas_callback_type(array[i].desc)) !=
           EVAS_CALLBACK_LAST)
  {
    obj->callback_mask |= (1 << type);
  }
  ....
}
PVS-Studio warning: V629 Consider inspecting the '1 << type' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. evas_callbacks.c 709
Let's take a closer look at this line:
obj->callback_mask |= (1 << type);
It is used to write 1 to the necessary bit of the variable callback_mask. Pay attention that the variable callback_mask is of 64-bit type.
The statement (1 << type) has an int type, that's why you can change only the bits in the lower part of the variable. Bits [32-63] cannot be changed.
To understand, if there is a bug or not, we need to understand what range of values may the function _legacy_evas_callback_type return. Can it return a value greater than 31? I don't know, so I skip this warning.
Try to understand this case. I see the code for the first time and have no idea what it's doing. In addition, hundreds of the analyzer messages are still waiting for me. I just cannot deal with every case like this.
Comment by Carsten Haitzler. Above - actually is a bug that's a result of an optimization that setting bits to decide if it should bother trying to map new event types to old ones (we're refactoring huge chunks of our internals around a new object system and so we have to do this to retain compatibility, but like with any refactoring... stuff happens). Yes - it wraps the bitshift and does the extra work of a whole bunch of if's because the same bits in the mask are re-used for now 2 events due to wrap around. As such this doesn't lead to a bug, just slightly less micro optimizations when set as now that bit means "it has an event callback for type A OR B" not just "type A" ... the following code actually does the complete check/mapping. It surely was not intended to wrap so this was a catch but the way it's used means it was actually pretty harmless.
Among those 425 ones left, there will be warnings, pointing to errors. For now I just skipped them.
If it comes to the regular use of PVS-Studio, it will be possible to continue setting it up. As I have already said, I spent just 40 minutes on the settings. But it doesn't mean that I did everything I could. The number of false positives can be reduced even more by disabling the diagnostics for certain programming constructs.
After careful review of the remaining warnings and additional settings, there will be 10-15% of false positives. This is a good result.

Bugs found

Now let's take a look at the bugs I found. I cannot describe all the 950 errors, so I will limit myself to describing a pair of warnings of each type. The remaining warnings I will provide a list or a separate file.
The reader can also look at all the warnings by opening the report file: zip-archive with the report. Note that I have left only the General warnings of high and medium level of certainty.
I reviewed this report in Windows using PVS-Studio Standalone utility.
In Linux you can use a utility Plog Converter that converts the report into one of the following formats:
  • xml - a convenient format for further processing of the results of the analysis, which is supported by the plugin for SonarQube;
  • csv - a text format for providing data as a table;
  • errorfile is the output format of the gcc and clang;
  • tasklist - an error format that can be opened in QtCreator.
Further on, to view the reports, you can use QtCreator, Vim/gVim, GNU Emacs, LibreOffice Calc. The documentation "How to run PVS-Studio on Linux" gives a detailed description of this process. (see "Filtering and viewing the analyzer report").

V501 (1 error)

The V501 diagnostic detected just one error, but a very nice one. The error is in the comparison function, which echoes the topic of a recent article "Evil in the comparison functions".
static int
_ephysics_body_evas_stacking_sort_cb(const void *d1,
                                     const void *d2)
{
   const EPhysics_Body_Evas_Stacking *stacking1, *stacking2;

   stacking1 = (const EPhysics_Body_Evas_Stacking *)d1;
   stacking2 = (const EPhysics_Body_Evas_Stacking *)d2;

   if (!stacking1) return 1;
   if (!stacking2) return -1;

   if (stacking1->stacking < stacking2->stacking) return -1;
   if (stacking2->stacking > stacking2->stacking) return 1;

   return 0;
}
PVS-Studio warning: V501 There are identical sub-expressions 'stacking2->stacking' to the left and to the right of the '>' operator. ephysics_body.cpp 450
A Typo. The last comparison should be as follows:
if (stacking1->stacking > stacking2->stacking) return 1;

V512 (8 errors)

Firstly, let's take a look at the definition of the Eina_Array structure.
typedef struct _Eina_Array Eina_Array;
struct _Eina_Array
{
   int version;
   void **data;
   unsigned int total;
   unsigned int count;
   unsigned int step;
   Eina_Magic __magic;
};
There is no need to take a very close look at it. It's just a structure with some fields.
Now let's look at the definition of the structure Eina_Accessor_Array:
typedef struct _Eina_Accessor_Array Eina_Accessor_Array;
struct _Eina_Accessor_Array
{
   Eina_Accessor accessor;
   const Eina_Array *array;
   Eina_Magic __magic;
};
Pay attention that the pointer to the structure Eina_Array is stored in the in the structure Eina_Accessor_Array. Apart from this, these structures are in no way connected with each other and have different sizes.
Now, here is the code fragment that was detected by the analyzer, and which I cannot understand.
static Eina_Accessor *
eina_array_accessor_clone(const Eina_Array *array)
{
   Eina_Accessor_Array *ac;
   EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL);
   EINA_MAGIC_CHECK_ARRAY(array);
   ac = calloc(1, sizeof (Eina_Accessor_Array));
   if (!ac) return NULL;
   memcpy(ac, array, sizeof(Eina_Accessor_Array));
   return &ac->accessor;
}
PVS-Studio warning: V512 A call of the 'memcpy' function will lead to the 'array' buffer becoming out of range. eina_array.c 186
Let me remove all unnecessary details to make it easier:
.... eina_array_accessor_clone(const Eina_Array *array)
{
   Eina_Accessor_Array *ac = calloc(1, sizeof (Eina_Accessor_Array));
   memcpy(ac, array, sizeof(Eina_Accessor_Array));
}
The memory is allocated for the object of the Eina_Accessor_Array type. Further on there is a strange thing.
An object of the Eina_Array type is copied to the allocated memory buffer.
Рисунок 1
I don't know what this function is supposed to do, but it does something strange.
Firstly, there is an index out of source bounds (of the structure Eina_Array).
Secondly, this copying has no sense at all. Structures have the set of members of completely different types.
Comment by Carsten Haitzler. Function content correct - Type in param is wrong. It didn't actually matter because the function is assigned to a func ptr that does have the correct type and since it's a generic "parent class" the assignment casts to a generic accessor type thus the compiler didn't complain and this seemed to work.
Let's consider the following error:
static Eina_Bool _convert_etc2_rgb8_to_argb8888(....)
{
   const uint8_t *in = src;
   uint32_t *out = dst;
   int out_step, x, y, k;
   unsigned int bgra[16];
   ....
   for (k = 0; k < 4; k++)
     memcpy(out + x + k * out_step, bgra + k * 16, 16);
   ....
}
PVS-Studio warning: V512 A call of the 'memcpy' function will lead to overflow of the buffer 'bgra + k * 16'. draw_convert.c 318
It's all very simple. A usual array index out of bounds.
The array bgra consists of 16 elements of the unsigned int type.
The variable k takes values from 0 to 3 in the loop.
Take a look at the expression: bgra + k * 16.
When the variable k takes the value greater than 0, we'll have the evaluation of a pointer that points outside the array.
However, some messages V512 indicate some code fragments that do not have a real error. Still, I don't think that these are false positives of the analyzer. This code is pretty bad and should be fixed. Let's consider such a case.
#define MATRIX_XX(m) (m)->xx
typedef struct _Eina_Matrix4 Eina_Matrix4;
struct _Eina_Matrix4
{
   double xx;
   double xy;
   double xz;
   double xw;

   double yx;
   double yy;
   double yz;
   double yw;

   double zx;
   double zy;
   double zz;
   double zw;

   double wx;
   double wy;
   double wz;
   double ww;
};

EAPI void
eina_matrix4_array_set(Eina_Matrix4 *m, const double *v)
{
   memcpy(&MATRIX_XX(m), v, sizeof(double) * 16);
}
PVS-Studio warning: V512 A call of the 'memcpy' function will lead to overflow of the buffer '& (m)->xx'. eina_matrix.c 1003
The programmer could just copy the array to the structure. Instead of it, the address of the first xx member is used. Probably, it is assumed that further on there will be another fields in the beginning of the structure. This method is used to avoid the crash of the program behavior.
Comment by Carsten Haitzler. Above and related memcpy's - not a bug: taking advantage of guaranteed mem layout in structs.
I don't like it, actually. I recommend to write something like this:
struct _Eina_Matrix4
{
  union {
    struct {
      double xx;
      double xy;
      double xz;
      double xw;

      double yx;
      double yy;
      double yz;
      double yw;

      double zx;
      double zy;
      double zz;
      double zw;

      double wx;
      double wy;
      double wz;
      double ww;
    };
    double RawArray[16];
  };
};

EAPI void
void eina_matrix4_array_set(Eina_Matrix4 *m, const double *v)
{
  memcpy(m->RawArray, v, sizeof(double) * 16);
}
This is a little longer, but ideologically more correct. If there is no wish to fix the code, the warning can be suppressed using one of the following methods.
The first method. Add a comment to the code:
memcpy(&MATRIX_XX(m), v, sizeof(double) * 16); //-V512
The second method. Add a line to the settings file:
//-V:MATRIX_:512
The third method. Use a markup base.
Other errors:
  • V512 A call of the 'memcpy' function will lead to overflow of the buffer '& (m)->xx'. eina_matrix.c 1098
  • V512 A call of the 'memcpy' function will lead to overflow of the buffer '& (m)->xx'. eina_matrix.c 1265
  • V512 A call of the 'memcpy' function will lead to the '& pd->projection.xx' buffer becoming out of range. evas_canvas3d_camera.c 120
  • V512 A call of the 'memcpy' function will lead to the '& pd->projection.xx' buffer becoming out of range. evas_canvas3d_light.c 270
  • V512 A call of the 'memcpy' function will lead to overflow of the buffer 'bgra + k * 16'. draw_convert.c 350

V517 (3 errors)

static Eina_Bool
evas_image_load_file_head_bmp(void *loader_data,
                              Evas_Image_Property *prop,
                              int *error)
{
  ....
  if (header.comp == 0) // no compression
  {
    // handled
  }
  else if (header.comp == 3) // bit field
  {
    // handled
  }
  else if (header.comp == 4) // jpeg - only printer drivers
    goto close_file;
  else if (header.comp == 3) // png - only printer drivers
    goto close_file;
  else
    goto close_file;
  ....
}
PVS-Studio warning: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 433, 439. evas_image_load_bmp.c 433
The variable header.comp is compared with the constant 3 twice.
Other errors:
  • V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 1248, 1408. evas_image_load_bmp.c 1248
  • V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 426, 432. parser.c 426

V519 (1 error)

EOLIAN static Efl_Object *
_efl_net_ssl_context_efl_object_finalize(....)
{
  Efl_Net_Ssl_Ctx_Config cfg;
  ....
  cfg.load_defaults = pd->load_defaults;                // <=
  cfg.certificates = &pd->certificates;
  cfg.private_keys = &pd->private_keys;
  cfg.certificate_revocation_lists =
          &pd->certificate_revocation_lists;
  cfg.certificate_authorities = &pd->certificate_authorities;
  cfg.load_defaults = pd->load_defaults;                // <=
  ....
}
PVS-Studio warning: V519 The 'cfg.load_defaults' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 304, 309. efl_net_ssl_context.c 309
Repeated assignment. One assignment is extra here, or something else was just not copied.
Comment by Carsten Haitzler. Not a bug. Just an overzealous copy & paste of the line.
One more simple case:
EAPI Eina_Bool
edje_edit_size_class_add(Evas_Object *obj, const char *name)
{
  Eina_List *l;
  Edje_Size_Class *sc, *s;
  ....
  /* set default values for max */
  s->maxh = -1;
  s->maxh = -1;
  ....
}
PVS-Studio warning: V519 The 's->maxh' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 8132, 8133. edje_edit.c 8133
Of course, not cases are so obvious. Nevertheless, I think that the warnings listed below most likely point to errors:
  • V519 The 'pdata->seat->object.in' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1519, 1521. evas_events.c 1521
  • V519 The 'pdata->seat->object.in' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2597, 2599. evas_events.c 2599
  • V519 The 'b->buffer[r]' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 348, 353. evas_image_load_pmaps.c 353
  • V519 The 'attr_amount' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 13891, 13959. edje_edit.c 13959
  • V519 The 'async_loader_running' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 152, 165. evas_gl_preload.c 165
  • V519 The 'textlen' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 86, 87. elm_code_widget_undo.c 87
  • V519 The 'content' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 313, 315. elm_dayselector.c 315
  • V519 The 'wd->resize_obj' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3099, 3105. elm_entry.c 3105
  • V519 The 'wd->resize_obj' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3125, 3131. elm_entry.c 3131
  • V519 The 'idata->values' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 128, 129. elm_view_list.c 129
  • V519 The 'wd->resize_obj' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2602, 2608. efl_ui_text.c 2608
  • V519 The 'wd->resize_obj' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2628, 2634. efl_ui_text.c 2634
  • V519 The 'finfo' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 706, 743. evas_image_load_gif.c 743
  • V519 The 'current_program_lookups' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 15819, 15820. edje_cc_handlers.c 15820
Note. Carsten Haitzler, commenting on the article, wrote that V519 warnings, given in the list, are false positives. I do not agree with such approach. Perhaps, code works correctly, but it is still worth paying attention and fixing. I decided to leave the list in the article, so that readers could estimate themselves, if repeats of variables assignment are false positives or not. But if Carsten says, that they are not errors, I will not take them into account.

V522 (563 errors)

Picture 4
The EFL project has a big problem - the checks if the memory was allocated or not. In general, there are such checks. Example:
if (!(el = malloc(sizeof(Evas_Stringshare_El) + slen + 1)))
  return NULL;
Moreover, sometimes they are in those places, where they aren't really needed (see about the warning V668 below).
But in a huge number of cases there are no checks at all. Let's take a look at a couple of the analyzer warnings.
Comment by Carsten Haitzler. OK so this is a general acceptance that at least on Linux which was always our primary focus and for a long time was our only target, returns from malloc/calloc/realloc can't be trusted especially for small amounts. Linux overcommits memory by default. That means you get new memory but the kernel has not actually assigned real physical memory pages to it yet. Only virtual space. Not until you touch it. If the kernel cannot service this request your program crashes anyway trying to access memory in what looks like a valid pointer. So all in all the value of checking returns of allocs that are small at least on Linux is low. Sometimes we do it... sometimes not. But the returns cannot be trusted in general UNLESS its for very large amounts of memory and your alloc is never going to be serviced - e.g. your alloc cannot fit in virtual address space at all (happens sometimes on 32bit). Yes overcommit can be tuned but it comes at a cost that most people never want to pay or no one even knows they can tune. Secondly, fi an alloc fails for a small chunk of memory - e.g. a linked list node... realistically if NULL is returned... crashing is about as good as anything you can do. Your memory is so low that you can crash, call abort() like glib does with g_malloc because if you can't allocate 20-40 bytes ... your system is going to fall over anyway as you have no working memory left anyway. I'm not talking about tiny embedded systems here, but large machines with virtual memory and a few megabytes of memory etc. which has been our target. I can see why PVS-Studio doesn't like this. Strictly it is actually correct, but in reality code spent on handling this stuff is kind of a waste of code given the reality of the situation. I'll get more into that later.
static Eina_Debug_Session *
_session_create(int fd)
{
   Eina_Debug_Session *session = calloc(1, sizeof(*session));
   session->dispatch_cb = eina_debug_dispatch;
   session->fd = fd;
   // start the monitor thread
   _thread_start(session);
   return session;
}
Comment by Carsten Haitzler. This is brand new code that arrived 2 months ago and still is being built out and tested and not ready for prime time. It's part of our live debugging infra where any app using EFL can be controlled by a debugger daemon (if it is run) and controlled (inspect all objects in memory and the object tree and their state with introspection live as it runs), collect execution timeline logs (how much time is spent in what function call tree where while rendering in which thread - what threads are using what cpu time at which slots down to the ms and below level, correlated with function calls, state of animation system and when wakeup events happen and the device timestamp that triggered the wakeup, and so on ... so given that scenario ... if you can't calloc a tiny session struct while debugging a crash accessing the first page of memory is pretty much about as good as anything... as above on memory and aborts etc.
Comment by Andrey Karpov. It is not very clear why it is mentioned here, that this is new and non-tested code. In the first place, static analyzers are intended to detect bugs in new code :).
PVS-Studio warning: V522 There might be dereferencing of a potential null pointer 'session'. eina_debug.c 440
The programmer allocated the memory with the calloc function and used it right away.
Another example:
static Reference *
_entry_reference_add(Entry *entry, Client *client,
                     unsigned int client_entry_id)
{
   Reference *ref;

   // increase reference for this file
   ref = malloc(sizeof(*ref));
   ref->client = client;
   ref->entry = entry;
   ref->client_entry_id = client_entry_id;
   ref->count = 1;
   entry->references = eina_list_append(entry->references, ref);

   return ref;
}
PVS-Studio warning: V522 There might be dereferencing of a potential null pointer 'ref'. evas_cserve2_cache.c 1404
The same situation repeated 563 times. I cannot provide them all in the article. Here is a link to the file: EFL_V522.txt.

V547 (39 errors)

static void
_ecore_con_url_dialer_error(void *data, const Efl_Event *event)
{
   Ecore_Con_Url *url_con = data;
   Eina_Error *perr = event->info;
   int status;

   status = 
     efl_net_dialer_http_response_status_get(url_con->dialer);

   if ((status < 500) && (status > 599))
   {
      DBG("HTTP error %d reset to 1", status);
      status = 1; /* not a real HTTP error */
   }
 
   WRN("HTTP dialer error url='%s': %s",
       efl_net_dialer_address_dial_get(url_con->dialer),
       eina_error_msg_get(*perr));

   _ecore_con_event_url_complete_add(url_con, status);
}
PVS-Studio warning: V547 Expression '(status < 500) && (status > 599)' is always false. ecore_con_url.c 351
Correct variant of the check should be as follows:
if ((status < 500) || (status > 599))
A fragment of code with this error was copied in another two fragments:
  • V547 Expression '(status < 500) && (status > 599)' is always false. ecore_con_url.c 658
  • V547 Expression '(status < 500) && (status > 599)' is always false. ecore_con_url.c 1340
Another erroneous situation:
EAPI void
eina_rectangle_pool_release(Eina_Rectangle *rect)
{
  Eina_Rectangle *match;  
  Eina_Rectangle_Alloc *new;
  ....
  match = (Eina_Rectangle *) (new + 1);
  if (match)
    era->pool->empty = _eina_rectangle_skyline_list_update(
                          era->pool->empty, match);
  ....
}
PVS-Studio warning: V547 Expression 'match' is always true. eina_rectangle.c 798
After the pointer was added 1, there is no point in checking it against NULL.
The pointer match can become equal to null, only if there is an overflow upon the addition. However, the pointer overflow is thought to be undefined behavior, so this variant shouldn't be considered.
And another case.
EAPI const void *
evas_object_smart_interface_get(const Evas_Object *eo_obj,
                                const char *name)
{
  Evas_Smart *s;
  ....
  s = evas_object_smart_smart_get(eo_obj);
  if (!s) return NULL;

  if (s)
  ....
}
PVS-Studio warning: V547 Expression 's' is always true. evas_object_smart.c 160
If the pointer is NULL, then there is an exit from the function. The repeated check has no sense.
Other errors: EFL_V547.txt.
There are warnings V547 that I skipped and didn't note them down, as it wasn't much interesting to sort them out. There can be several more errors among them.

V556 (8 errors)

All of them are issued for one code fragment. First let's take a look at the declaration of two enumerations.
typedef enum _Elm_Image_Orient_Type
{
  ELM_IMAGE_ORIENT_NONE = 0,
  ELM_IMAGE_ORIENT_0 = 0,
  ELM_IMAGE_ROTATE_90 = 1,
  ELM_IMAGE_ORIENT_90 = 1,
  ELM_IMAGE_ROTATE_180 = 2,
  ELM_IMAGE_ORIENT_180 = 2,
  ELM_IMAGE_ROTATE_270 = 3,
  ELM_IMAGE_ORIENT_270 = 3,
  ELM_IMAGE_FLIP_HORIZONTAL = 4,
  ELM_IMAGE_FLIP_VERTICAL = 5,
  ELM_IMAGE_FLIP_TRANSPOSE = 6,
  ELM_IMAGE_FLIP_TRANSVERSE = 7
} Elm_Image_Orient;

typedef enum
{
  EVAS_IMAGE_ORIENT_NONE = 0,
  EVAS_IMAGE_ORIENT_0 = 0,
  EVAS_IMAGE_ORIENT_90 = 1,
  EVAS_IMAGE_ORIENT_180 = 2,
  EVAS_IMAGE_ORIENT_270 = 3,
  EVAS_IMAGE_FLIP_HORIZONTAL = 4,
  EVAS_IMAGE_FLIP_VERTICAL = 5,
  EVAS_IMAGE_FLIP_TRANSPOSE = 6,
  EVAS_IMAGE_FLIP_TRANSVERSE = 7
} Evas_Image_Orient;
As you see, the names of these constants in the enumerations are similar. This is what was failing for a programmer.
EAPI void
elm_image_orient_set(Evas_Object *obj, Elm_Image_Orient orient)
{
  Efl_Orient dir;
  Efl_Flip flip;

  EFL_UI_IMAGE_DATA_GET(obj, sd);
  sd->image_orient = orient;

  switch (orient)
  {
    case EVAS_IMAGE_ORIENT_0:
    ....
    case EVAS_IMAGE_ORIENT_90:
    ....
    case EVAS_IMAGE_FLIP_HORIZONTAL:
    ....
    case EVAS_IMAGE_FLIP_VERTICAL:
    ....
}
PVS-Studio warnings:
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2141
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2145
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2149
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2153
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2157
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2161
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2165
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B: ... }. efl_ui_image.c 2169
Instances from different enumerations are compared eight times.
At the same time, thanks to luck, these comparisons work correctly. The constants are the same:
  • ELM_IMAGE_ORIENT_NONE = 0 ; EVAS_IMAGE_ORIENT_NONE = 0,
  • ELM_IMAGE_ORIENT_0 = 0 ; EVAS_IMAGE_ORIENT_0 = 0
  • ELM_IMAGE_ROTATE_90 = 1 ; EVAS_IMAGE_ORIENT_90 = 1
  • and so on.
The function will work correctly, but still, these are errors.
Comment by Carsten Haitzler. All of the above orient/rotate enum stuff is intentional. We had to cleanup duplication of enums and we ensured they had the same values so they were interchangeable - we moved from rotate to orient and kept the compatibility. It's part of our move over to the new object system and a lot of code auto-generation etc. that is still underway and beta. It's not an error but intended to do this as part of transitioning, so it's a false positive.
Comment by Andrey Karpov. I do not agree that these are false positives in this case and in some other ones. Following such logic, it turns out that a warning for incorrect but, for some reason, working code is false positive.

V558 (4 errors)

accessor_iterator<T>& operator++(int)
{
  accessor_iterator<T> tmp(*this);
  ++*this;
  return tmp;
}
PVS-Studio warning: V558 Function returns the reference to temporary local object: tmp. eina_accessor.hh 519
To fix the code, you should remove from the function declaration:
accessor_iterator<T> operator++(int)
Other errors:
  • V558 Function returns the reference to temporary local object: tmp. eina_accessor.hh 535
  • V558 Function returns the reference to temporary local object: tmp. eina_accessor.hh 678
  • V558 Function returns the reference to temporary local object: tmp. eina_accessor.hh 694

V560 (32 errors)

static unsigned int read_compressed_channel(....)
{
  ....
  signed char headbyte;
  ....
  if (headbyte >= 0)
  {
    ....
  }
  else if (headbyte >= -127 && headbyte <= -1)     // <=
  ....
}
PVS-Studio warning: V560 A part of conditional expression is always true: headbyte <= - 1. evas_image_load_psd.c 221
If the variable headbyte was >= 0, then there is no sense in the check <= -1.
Let's have a look at a different case.
static Eeze_Disk_Type
_eeze_disk_type_find(Eeze_Disk *disk)
{
  const char *test;
  ....
  test = udev_device_get_property_value(disk->device, "ID_BUS");
  if (test)
  {
    if (!strcmp(test, "ata")) return EEZE_DISK_TYPE_INTERNAL;
    if (!strcmp(test, "usb")) return EEZE_DISK_TYPE_USB;
    return EEZE_DISK_TYPE_UNKNOWN;
  }
  if ((!test) && (!filesystem))                     // <=
  ....
}
PVS-Studio warning: V560 A part of conditional expression is always true: (!test). eeze_disk.c 55
The second condition is redundant. If the test pointer were non-null pointer, then the function would have exited.
Other errors: EFL_V560.txt.

V568 (3 errors)

EOLIAN static Eina_Error
_efl_net_server_tcp_efl_net_server_fd_socket_activate(....)
{
  ....
  struct sockaddr_storage *addr;
  socklen_t addrlen;
  ....
  addrlen = sizeof(addr);
  if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
  ....
}
PVS-Studio warning: V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'addr' class object. efl_net_server_tcp.c 192
I have a suspicion that here the size of the structure should be evaluated, not the pointer size. Then the correct code should be as follows:
addrlen = sizeof(*addr);
Other errors:
  • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'addr' class object. efl_net_server_udp.c 228
  • V568 It's odd that 'sizeof()' operator evaluates the size of a pointer to a class, but not the size of the 'addr' class object. efl_net_server_unix.c 198

V571 (6 errors)

EAPI void eeze_disk_scan(Eeze_Disk *disk)
{
  ....
  if (!disk->cache.vendor)
    if (!disk->cache.vendor)
      disk->cache.vendor = udev_device_get_sysattr_value(....);
  ....
}
PVS-Studio warning: V571 Recurring check. The 'if (!disk->cache.vendor)' condition was already verified in line 298. eeze_disk.c 299
A redundant or incorrect check.
Other errors:
  • V571 Recurring check. The 'if (!disk->cache.model)' condition was already verified in line 302. eeze_disk.c 303
  • V571 Recurring check. The 'if (priv->last_buffer)' condition was already verified in line 150. emotion_sink.c 152
  • V571 Recurring check. The 'if (pd->editable)' condition was already verified in line 892. elm_code_widget.c 894
  • V571 Recurring check. The 'if (mnh >= 0)' condition was already verified in line 279. els_box.c 281
  • V571 Recurring check. The 'if (mnw >= 0)' condition was already verified in line 285. els_box.c 287
Note. Carsten Haitzler does not consider them as erroneous. He thinks that such warnings are recommendations on micro optimizations. But I think that this code is incorrect and it needs to be fixed. In my opinion, these are errors. We have disagreement about this issue, how to consider these analyzer warnings.

V575 (126 errors)

The diagnostic is triggered when strange factual arguments are passed to the function. Let's consider several variants of how this diagnostic is triggered.
static void
free_buf(Eina_Evlog_Buf *b)
{
   if (!b->buf) return;
   b->size = 0;
   b->top = 0;
# ifdef HAVE_MMAP
   munmap(b->buf, b->size);
# else
   free(b->buf);
# endif
   b->buf = NULL;
}
PVS-Studio warning: V575 The 'munmap' function processes '0' elements. Inspect the second argument. eina_evlog.c 117
First, 0 was written to the variable b->size, then it was passed to the function munmap.
It seems to me that it should be written differently:
static void
free_buf(Eina_Evlog_Buf *b)
{
   if (!b->buf) return;
   b->top = 0;
# ifdef HAVE_MMAP
   munmap(b->buf, b->size);
# else
   free(b->buf);
# endif
   b->buf = NULL;
   b->size = 0;
}
Let's continue.
EAPI Eina_Bool
eina_simple_xml_parse(....)
{
  ....
  else if ((itr + sizeof("<!>") - 1 < itr_end) &&
           (!memcmp(itr + 2, "", sizeof("") - 1)))
  ....
}
PVS-Studio warning: V575 The 'memcmp' function processes '0' elements. Inspect the third argument. eina_simple_xml_parser.c 355
It's unclear why the programmer compares 0 bytes of memory.
Let's continue.
static void
_edje_key_down_cb(....)
{
  ....
  char *compres = NULL, *string = (char *)ev->string;
  ....
  if (compres)
  {
    string = compres;
    free_string = EINA_TRUE;
  }
  else free(compres);
  ....
}
PVS-Studio warning: V575 The null pointer is passed into 'free' function. Inspect the first argument. edje_entry.c 2306
If the compress pointer is null, then there is no need to free the memory. The line
else free(compres);
can be removed.
Comment by Carsten Haitzler. Not a bug but indeed some extra if paranoia like code that isn't needed. Micro optimizations again?
Comment by Andrey Karpov. In this case, we also have different opinions. I consider this warning as useful, which points at the error. Probably, another pointer should have been freed and it is absolutely right that the analyzer points at this code. Even though there is no error, code should be fixed so that it would not confuse the analyzer and programmers.
Similarly:
  • V575 The null pointer is passed into 'free' function. Inspect the first argument. efl_ui_internal_text_interactive.c 1022
  • V575 The null pointer is passed into 'free' function. Inspect the first argument. edje_cc_handlers.c 15962
But most of the V575 diagnostic warnings are related to the use of potentially null pointers. These errors are quite similar to the ones we had a look at when we spoke about the V522 diagnostic.
static void _fill_all_outs(char **outs, const char *val)
{
  size_t vlen = strlen(val);
  for (size_t i = 0; i < (sizeof(_dexts) / sizeof(char *)); ++i)
  {
    if (outs[i])
      continue;
    size_t dlen = strlen(_dexts[i]);
    char *str = malloc(vlen + dlen + 1);
    memcpy(str, val, vlen);
    memcpy(str + vlen, _dexts[i], dlen);
    str[vlen + dlen] = '\0';
    outs[i] = str;
  }
}
PVS-Studio warning: V575 The potential null pointer is passed into 'memcpy' function. Inspect the first argument. main.c 112
We use a pointer without checking if the memory was allocated.
Other errors: EFL_V575.txt.

V587 (2 errors)

void
_ecore_x_event_handle_focus_in(XEvent *xevent)
{
  ....
   e->time = _ecore_x_event_last_time;
   _ecore_x_event_last_time = e->time;
  ....
}
PVS-Studio warning: V587 An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 1006, 1007. ecore_x_events.c 1007
Comment by Carsten Haitzler. Not bugs as such - looks like just overzealous storing of last timestamp. This is adding a timestamp to an event when no original timestamp exists so we can keep a consistent structure for events with timestamps, but it is code clutter and a micro optimization.
Comment by Andrey Karpov. Apparently, we cannot agree about some issues. Some of the cases are erroneous in my view, and inaccurate in Carsten's. As I said, I do not agree with it and, for this reason, I do not include some similar comments in the article.
Another error: V587 An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 1050, 1051. ecore_x_events.c 1051

V590 (3 errors)

static int command(void)
{
  ....
  while (*lptr == ' ' && *lptr != '\0')
    lptr++; /* skip whitespace */
  ....
}
PVS-Studio warning: V590 Consider inspecting the '* lptr == ' ' && * lptr != '\0'' expression. The expression is excessive or contains a misprint. embryo_cc_sc2.c 944
Redundant check. It can be simplified:
while (*lptr == ' ')
Two more similar warnings:
  • V590 Consider inspecting the 'sym->ident == 9 || sym->ident != 10' expression. The expression is excessive or contains a misprint. embryo_cc_sc3.c 1782
  • V590 Consider inspecting the '* p == '\n' || * p != '\"'' expression. The expression is excessive or contains a misprint. cpplib.c 4012

V591 (1 error)

_self_type& operator=(_self_type const& other)
{
  _base_type::operator=(other);
}
PVS-Studio warning: V591 Non-void function should return a value. eina_accessor.hh 330

V595 (4 errors)

static void
eng_image_size_get(void *engine EINA_UNUSED, void *image,
int *w, int *h)
{
   Evas_GL_Image *im;
   if (!image)
     {
        *w = 0;                                    // <=
        *h = 0;                                    // <=
        return;
     }
   im = image;
   if (im->orient == EVAS_IMAGE_ORIENT_90 ||
       im->orient == EVAS_IMAGE_ORIENT_270 ||
       im->orient == EVAS_IMAGE_FLIP_TRANSPOSE ||
       im->orient == EVAS_IMAGE_FLIP_TRANSVERSE)
     {
        if (w) *w = im->h;
        if (h) *h = im->w;
     }
   else
     {
        if (w) *w = im->w;
        if (h) *h = im->h;
     }
}
PVS-Studio warnings:
  • V595 The 'w' pointer was utilized before it was verified against nullptr. Check lines: 575, 585. evas_engine.c 575
  • V595 The 'h' pointer was utilized before it was verified against nullptr. Check lines: 576, 586. evas_engine.c 576
The if (w) and if (h) checks give a hint to the analyzer, that input arguments w and hmay be equal to NULL. It is dangerous that in the beginning of the function they are used without verification.
If you call the eng_image_size_get function, as follows:
eng_image_size_get(NULL, NULL, NULL, NULL);
it won't be ready to it and the null pointer dereference will occur.
The warnings, which, in my opinion, also indicate errors:
  • V595 The 'cur->node' pointer was utilized before it was verified against nullptr. Check lines: 9889, 9894. evas_object_textblock.c 9889
  • V595 The 'subtype' pointer was utilized before it was verified against nullptr. Check lines: 2200, 2203. eet_data.c 2200

V597 (6 errors)

EAPI Eina_Binbuf *
emile_binbuf_decipher(Emile_Cipher_Algorithm algo,
                      const Eina_Binbuf *data,
                      const char *key,
                      unsigned int length)
{
  ....
  Eina_Binbuf *result = NULL;
  unsigned int *over;
  EVP_CIPHER_CTX *ctx = NULL;
  unsigned char ik[MAX_KEY_LEN];
  unsigned char iv[MAX_IV_LEN];
  ....
on_error:
   memset(iv, 0, sizeof (iv));
   memset(ik, 0, sizeof (ik));

   if (ctx)
     EVP_CIPHER_CTX_free(ctx);

   eina_binbuf_free(result);

   return NULL;
}
PVS-Studio warnings:
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'iv' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 293
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'ik' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 294
I have already written in articles many times, why the compiler can delete the calls of memset functions in such code. That's why I don't want to repeat myself. If someone is not familiar with this issue, I suggest reading the description of the V597 diagnostics.
Comment by Carsten Haitzler. Above 2 - totally familiar with the issue. The big problem is memset_s is not portable or easily available, thus why we don't use it yet. You have to do special checks for it to see if it exists as it does not exist everywhere. Just as a simple example add AC_CHECK_FUNCS([memset_s]) to your configure.ac and memset_s is not found you have to jump through some more hoops like define __STDC_WANT_LIB_EXT1__ 1 before including system headers ... and it's still not declared. On my pretty up to date Arch system memset_s is not defined by any system headers, same on debian testing... warning: implicit declaration of function 'memset_s'; did you mean memset'? [-Wimplicit-function-declaration], and then compile failure ... no matter what I do. A grep -r of all my system includes shows no memset_s declared ... so I think advising people to use memset_s is only a viable advice if its widely available and usable. Be aware of this.
Other errors:
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'key_material' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 144
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'iv' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 193
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'ik' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 194
  • V597 The compiler could delete the 'memset' function call, which is used to flush 'key_material' buffer. The memset_s() function should be used to erase the private data. emile_cipher_openssl.c 249

V609 (1 error)

First of all, let's take a look at eina_value_util_type_size function:
static inline size_t
eina_value_util_type_size(const Eina_Value_Type *type)
{
   if (type == EINA_VALUE_TYPE_INT)
     return sizeof(int32_t);
   if (type == EINA_VALUE_TYPE_UCHAR)
     return sizeof(unsigned char);
   if ((type == EINA_VALUE_TYPE_STRING) ||
       (type == EINA_VALUE_TYPE_STRINGSHARE))
     return sizeof(char*);
   if (type == EINA_VALUE_TYPE_TIMESTAMP)
     return sizeof(time_t);
   if (type == EINA_VALUE_TYPE_ARRAY)
     return sizeof(Eina_Value_Array);
   if (type == EINA_VALUE_TYPE_DOUBLE)
     return sizeof(double);
   if (type == EINA_VALUE_TYPE_STRUCT)
     return sizeof(Eina_Value_Struct);
   return 0;
}
Pay attention that the function may return 0. Now let's see, how this function is used.
static inline unsigned int
eina_value_util_type_offset(const Eina_Value_Type *type,
                            unsigned int base)
{
   unsigned size, padding;
   size = eina_value_util_type_size(type);
   if (!(base % size))
     return base;
   padding = ( (base > size) ? (base - size) : (size - base));
   return base + padding;
}
PVS-Studio warning: V609 Mod by zero. Denominator range [0..24]. eina_inline_value_util.x 60
Potential division by zero. I am not sure if there can be a real situation when eina_value_util_type_size function returns 0. In any case, the code is dangerous.
Comment by Carsten Haitzler. The 0 return would only happen if you have provided totally invalid input, like again strdup(NULL) ... So I call this a false positive as you cant have an eina_value generic value that is not valid without bad stuff happening - validate you passes a proper value in first. eina_value is performance sensitive btw so every check here costs something. it's like adding if() checks to the add opcode.

V610 (1 error)

void fetch_linear_gradient(....)
{
  ....
  if (t + inc*length < (float)(INT_MAX >> (FIXPT_BITS + 1)) &&
      t+inc*length > (float)(INT_MIN >> (FIXPT_BITS + 1)))
  ....
}
PVS-Studio warning: V610 Unspecified behavior. Check the shift operator '>>'. The left operand '(- 0x7fffffff - 1)' is negative. ector_software_gradient.c 412

V614 (1 error)

extern struct tm *gmtime (const time_t *__timer)
  __attribute__ ((__nothrow__ , __leaf__));

static void
_set_headers(Evas_Object *obj)
{
  static char part[] = "ch_0.text";
  int i;
  struct tm *t;
  time_t temp;
  ELM_CALENDAR_DATA_GET(obj, sd);

  elm_layout_freeze(obj);

  sd->filling = EINA_TRUE;

  t = gmtime(&temp);            // <=
  ....
}
PVS-Studio warning: V614 Uninitialized variable 'temp' used. Consider checking the first actual argument of the 'gmtime' function. elm_calendar.c 720

V621 (1 error)

static void
_opcodes_unregister_all(Eina_Debug_Session *session)
{
  Eina_List *l;
  int i;
  _opcode_reply_info *info = NULL;

  if (!session) return;
  session->cbs_length = 0;
  for (i = 0; i < session->cbs_length; i++)
    eina_list_free(session->cbs[i]);
  ....
}
PVS-Studio warning: V621 Consider inspecting the 'for' operator. It's possible that the loop will be executed incorrectly or won't be executed at all. eina_debug.c 405

V630 (2 errors)

There is an ordinary btVector3 class with a constructor. However, this constructor does nothing.
class btVector3
{
public:
  ....
  btScalar m_floats[4];
  inline btVector3() { }
  ....
};
There is also such a Simulation_Msg structure:
typedef struct _Simulation_Msg Simulation_Msg;
struct _Simulation_Msg {
     EPhysics_Body *body_0;
     EPhysics_Body *body_1;
     btVector3 pos_a;
     btVector3 pos_b;
     Eina_Bool tick:1;
};
Pay attention that some class members are of btVector3 type. Now let's see how the structure is created:
_ephysics_world_tick_dispatch(EPhysics_World *world)
{
   Simulation_Msg *msg;

   if (!world->ticked)
     return;

   world->ticked = EINA_FALSE;
   world->pending_ticks++;

   msg = (Simulation_Msg *) calloc(1, sizeof(Simulation_Msg));
   msg->tick = EINA_TRUE;
   ecore_thread_feedback(world->cur_th, msg);
}
PVS-Studio warning: V630 The 'calloc' function is used to allocate memory for an array of objects which are classes containing constructors. ephysics_world.cpp 299
A structure, which contains non-POD members, is created using a call of callocfunction.
In practice, this code will work, but it is generally incorrect. Technically, the usage of this structure will end up with undefined behavior.
Another error: V630 The 'calloc' function is used to allocate memory for an array of objects which are classes containing constructors. ephysics_world.cpp 471
Comment by Carsten Haitzler. Because the other end of the pipe is C code that is passing around a raw ptr as the result from thread A to thread B, it's a mixed c and c++ environment. In the end we'd be sending raw ptr's around no matter what...

V654 (2 errors)

int
evas_mem_free(int mem_required EINA_UNUSED)
{
   return 0;
}

int
evas_mem_degrade(int mem_required EINA_UNUSED)
{
   return 0;
}

void * 
evas_mem_calloc(int size)
{
   void *ptr;

   ptr = calloc(1, size);
   if (ptr) return ptr;
   MERR_BAD();
   while ((!ptr) && (evas_mem_free(size))) ptr = calloc(1, size);
   if (ptr) return ptr;
   while ((!ptr) && (evas_mem_degrade(size))) ptr = calloc(1, size);
   if (ptr) return ptr;
   MERR_FATAL();
   return NULL;
}
PVS-Studio warnings:
  • V654 The condition '(!ptr) && (evas_mem_free(size))' of loop is always false. main.c 44
  • V654 The condition '(!ptr) && (evas_mem_degrade(size))' of loop is always false. main.c 46
Obviously, this is incomplete code.
Comment by Carsten Haitzler. Old old code because caching was implemented, so it was basically a lot of NOP's waiting to be filled in. since evas speculatively cached data (megabytes of it) the idea was that if allocs fail - free up some cache and try again... if that fails then actually try nuke some non-cached data that could be reloaded/rebuilt but with more cost... and only fail after that. But because of overcommit this didn't end up practical as allocs would succeed then just fall over often enough if you did hit a really low memory situation, so I gave up. it's not a bug. it's a piece of history :).
The next case is more interesting and odd.
Picture 5
EAPI void evas_common_font_query_size(....)
{
  ....
  size_t cluster = 0;
  size_t cur_cluster = 0;
  ....
  do
  {
    cur_cluster = cluster + 1;
    
    glyph--;
    if (cur_w > ret_w)
    {
      ret_w = cur_w;
    }
  }
  while ((glyph > first_glyph) && (cur_cluster == cluster));
  ....
}
PVS-Studio warning: V654 The condition of loop is always false. evas_font_query.c 376
This assignment is executed in the loop:
cur_cluster = cluster + 1;
This means that the comparison (cur_cluster == cluster) is always evaluated as false.
Comment by Carsten Haitzler. Above ... it seems you built without harfbuzz support... we highly don't recommend that. it's not tested. Building without basically nukes almost all of the interesting unicode/intl support for text layout. You do have to explicitly - disable it ... because with harfbuzz support we have opentype enabled and a different bit of code is executed due to ifdefs.. if you actually check history of the code before adding opentype support it didn't loop over clusters at all or even glyphs .. so really the ifdef just ensures the loop only loops one and avoids more ifdefs later in the loop conditions making the code easier to maintain - beware the ifdefs!

V668 (21 errors)

As we found out earlier, there are hundreds fragments of code where there is no checking of the pointer after the memory is allocated using malloc calloc function.
Against this background the checks after the usage of the new operator seem like a joke.
Picture 7
There are some harmless errors:
static EPhysics_Body *
_ephysics_body_rigid_body_add(....)
{
  ....
  motion_state = new btDefaultMotionState();
  if (!motion_state)
  {
    ERR("Couldn't create a motion state.");
    goto err_motion_state;
  }
  ....
}
PVS-Studio warning: V668 There is no sense in testing the 'motion_state' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. ephysics_body.cpp 837
There is no point in checking, as in case of impossibility of the memory allocation, the std::bad_alloc exception will be generated.
Comment by Carsten Haitzler. Fair enough, but be aware some compiler DON'T throw exceptions... they return NULL on new... so not totally useless code depending on the compiler. I believe VSC6 didn't throw an exception - so before exceptions were a thing this actually was correct behavior, also I depends on the allocator func if it throws and exception or not, so all in all, very minor harmless code.
Comment by Andrey KarpovI do not agree. See the next comment.
There are more serious errors:
EAPI EPhysics_Constraint *
ephysics_constraint_linked_add(EPhysics_Body *body1,
                               EPhysics_Body *body2)
{
  ....
  constraint->bt_constraint = new btGeneric6DofConstraint(
     *ephysics_body_rigid_body_get(body1),
     *ephysics_body_rigid_body_get(body2),
     btTransform(), btTransform(), false);
  if (!constraint->bt_constraint)
  {
    ephysics_world_lock_release(constraint->world);
    free(constraint);
    return NULL;
  }
  ....
}  
PVS-Studio warning: V668 There is no sense in testing the 'constraint->bt_constraint' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. ephysics_constraints.cpp 382
If the exception is thrown, not only the logic in the work will get broken. What is more, the memory leak will occur, as the free function will not be called.
Comment by Carsten Haitzler. Same as previous new + NULL check.
Comment by Andrey Karpov. It is not clear for me why it is said here about the Visual C++ 6.0. Yes, it does not through an exception while using a new operator, as well as other old compilers. Yes, if the old compiler is used, the program will work correctly. But Tizen will never be compiled using Visual C++ 6.0! There is no point in thinking about it. A new compiler will through an exception and this will lead to errors. I will emphasize one more time that this is not an extra check. The program works not the way the programmer expects. Moreover, in the second example there is a memory-leak. If we consider a case when new does not through an exception, new(nothrow) should be used. Then the analyzer will not complain. In any way, this code is incorrect.
Other errors: EFL_V668.txt.

V674 (2 errors)

First, let's see how the abs function is declared:
extern int abs (int __x) __attribute__ ((__nothrow__ , __leaf__))
                         __attribute__ ((__const__)) ;
As you can see, it possesses and returners the int values.
Now let's see, how this function is used.
#define ELM_GESTURE_MINIMUM_MOMENTUM 0.001

typedef int Evas_Coord;

struct _Elm_Gesture_Momentum_Info
{
  ....
  Evas_Coord mx;
  Evas_Coord my;
  ....
};

static void
_momentum_test(....)
{
  ....
  if ((abs(st->info.mx) > ELM_GESTURE_MINIMUM_MOMENTUM) ||
      (abs(st->info.my) > ELM_GESTURE_MINIMUM_MOMENTUM))
    state_to_report = ELM_GESTURE_STATE_END;
  ....
}
PVS-Studio warnings:
  • V674 The '0.001' literal of the 'double' type is compared to a value of the 'int' type. Consider inspecting the 'abs(st->info.mx) > 0.001' expression. elm_gesture_layer.c 2533
  • V674 The '0.001' literal of the 'double' type is compared to a value of the 'int' type. Consider inspecting the 'abs(st->info.my) > 0.001' expression. elm_gesture_layer.c 2534
It is weird to compare the int values with constant 0.001. There is definitely a bug here.

V686 (3 errors)

static Image_Entry *
_scaled_image_find(Image_Entry *im, ....)
{
   size_t               pathlen, keylen, size;
   char                 *hkey;
   Evas_Image_Load_Opts  lo;
   Image_Entry          *ret;

   if (((!im->file) || ((!im->file) && (!im->key))) || (!im->data1) ||
       ((src_w == dst_w) && (src_h == dst_h)) ||
       ((!im->flags.alpha) && (!smooth))) return NULL;
  ....
}
PVS-Studio warning: V686 A pattern was detected: (!im->file) || ((!im->file) && ...). The expression is excessive or contains a logical error. evas_cache2.c 825
Most likely this is not a real error, but redundant code. Let me explain this using a simple example.
if (A || (A && B) || C)
The expression can be simplified to:
if (A || C)
Although, it is possible that in the expression there is a typo and something important is not checked. In this very case, it is an error. It is hard for me to judge about this unfamiliar code.
Similar redundant conditions:
  • V686 A pattern was detected: (!im->file) || ((!im->file) && ...). The expression is excessive or contains a logical error. evas_cache2.c 905
  • V686 A pattern was detected: (nextc == '*') || ((nextc == '*') && ...). The expression is excessive or contains a logical error. cpplib.c 1022

V694 (2 errors)

#define CPP_PREV_BUFFER(BUFFER) ((BUFFER)+1)

static void
initialize_builtins(cpp_reader * pfile)
{
  ....
  cpp_buffer *pbuffer = CPP_BUFFER(pfile);

  while (CPP_PREV_BUFFER(pbuffer))
    pbuffer = CPP_PREV_BUFFER(pbuffer);
  ....
}
PVS-Studio warning: V694 The condition ((pbuffer) + 1) is only false if there is pointer overflow which is undefined behavior anyway. cpplib.c 2496
I will expand the macro to make it clearer.
cpp_buffer *pbuffer = ....;
while (pbuffer + 1)
  ....
The loop condition is always true. Formally, it can become false in case, if the pointer is equal to its limit. In addition, an overflow occurs. This is considered as undefined behavior, it means that a compiler has the right not to take into account such situation. The compiler can delete the conditional check, and then we will get:
while (true)
  pbuffer = CPP_PREV_BUFFER(pbuffer);
It's a very interesting bug.
Another incorrect check: V694 The condition ((ip) + 1) is only false if there is pointer overflow which is undefined behavior anyway. cpplib.c 2332
Comment by Carsten Haitzler. This old code indeed has issues. There should be checks against CPP_NULL_BUFFER(pfile) because if its a linked list this is a null heck, if its a static buffer array as a stack, it checks stack end position - interestingly in decades it's never been triggered that I know of.

V701 (69 errors)

static void
_efl_vg_gradient_efl_gfx_gradient_stop_set(
                          ...., Efl_VG_Gradient_Data *pd, ....)
{
   pd->colors = realloc(pd->colors,
                        length * sizeof(Efl_Gfx_Gradient_Stop));
   if (!pd->colors)
     {
        pd->colors_count = 0;
        return ;
     }

   memcpy(pd->colors, colors,
          length * sizeof(Efl_Gfx_Gradient_Stop));
   pd->colors_count = length;

   _efl_vg_changed(obj);
}
PVS-Studio warning: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'pd->colors' is lost. Consider assigning realloc() to a temporary pointer. evas_vg_gradient.c 14
This line contains the error:
pd->colors = realloc(pd->colors, ....);
The old value of the pointer pd->colors does not retain anywhere. A memory leak will occur, if it isn't possible to allocate a new memory block. The address of the previous buffer will be lost, because in pd->colors value NULL will be written.
In some cases, the memory leak is complemented also by a null pointer dereference. If a null pointer dereference isn't handled in any way, the program will abort. If it is handled, the program will continue working, but the memory leak will occur. Here is an example of such code:
EOLIAN void _evas_canvas_key_lock_add(
  Eo *eo_e, Evas_Public_Data *e, const char *keyname)
{
   if (!keyname) return;
   if (e->locks.lock.count >= 64) return;
   evas_key_lock_del(eo_e, keyname);
   e->locks.lock.count++;

   e->locks.lock.list =
     realloc(e->locks.lock.list,
             e->locks.lock.count * sizeof(char *));

   e->locks.lock.list[e->locks.lock.count - 1] = strdup(keyname);

   eina_hash_free_buckets(e->locks.masks);
}
PVS-Studio warning: V701 realloc() possible leak: when realloc() fails in allocating memory, original pointer 'e->locks.lock.list' is lost. Consider assigning realloc() to a temporary pointer. evas_key.c 142
Other errors: EFL_701.txt.

V728 (4 errors)

static Eina_Bool
_evas_textblock_node_text_adjust_offsets_to_start(....)
{
  Evas_Object_Textblock_Node_Format *last_node, *itr;
  ....
  if (!itr || (itr && (itr->text_node != n)))
  ....
}
PVS-Studio warning: V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!itr' and 'itr'. evas_object_textblock.c 9505
This is not a bug, but an unnecessarily complicated condition. It expression can be simplified:
if (!itr || (itr->text_node != n))
Other redundant conditions:
  • V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!p' and 'p'. elm_theme.c 447
  • V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!ss' and 'ss'. config.c 3932
  • V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!icon_version' and 'icon_version'. efreet_icon_cache_create.c 917

V769 (11 errors)

We have already considered the V522 warning in case when there is no checking of the pointer value after the memory allocation. We will now consider a similar error.
EAPI Eina_Bool
edje_edit_sound_sample_add(
  Evas_Object *obj, const char *name, const char *snd_src)
{
   ....
   ed->file->sound_dir->samples =
     realloc(ed->file->sound_dir->samples,
             sizeof(Edje_Sound_Sample) *
             ed->file->sound_dir->samples_count);

   sound_sample = ed->file->sound_dir->samples +
     ed->file->sound_dir->samples_count - 1;
   sound_sample->name = (char *)eina_stringshare_add(name);
   ....
}
PVS-Studio warning: V769 The 'ed->file->sound_dir->samples' pointer in the expression could be nullptr. In such case, resulting value of arithmetic operations on this pointer will be senseless and it should not be used. edje_edit.c 1271
The allocation memory function is called. The resulting pointer is not dereferenced, but is used in an expression. A value is added to the pointer, so it becomes incorrect to say that the null pointer is used. The pointer is non-null in any case, but it can be invalid if the memory was not allocated. Exactly this code is shown by the considered diagnostic.
By the way, such pointers are more dangerous than null ones. The null pointer dereference will be definitely revealed by the operating system. Using the (NULL + N) pointer one can get to the memory location, which can be modified and stores some data.
Other errors:
  • V769 The 'new_txt' pointer in the 'new_txt + outlen' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. eina_str.c 539
  • V769 The 'new_txt' pointer in the 'new_txt + outlen' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. eina_str.c 611
  • V769 The 'tmp' pointer in the 'tmp ++' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. evas_object_textblock.c 11131
  • V769 The 'dst' pointer in the 'dst += sizeof (int)' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. evas_font_compress.c 218
  • V769 The 'content' pointer in the 'content + position' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. elm_code_line.c 78
  • V769 The 'newtext' pointer in the 'newtext + length1' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. elm_code_line.c 102
  • V769 The 'tmp' pointer in the 'tmp + dirlen' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. elm_code_file.c 101
  • V769 The 'ptr' pointer in the 'ptr += strlen(first) + newline_len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. elm_code_widget_text.c 72
  • V769 The 'content' pointer in the 'content + 319' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. test_store.c 198
  • V769 The 'pos' pointer in the 'pos += sizeof (msg)' expression could be nullptr. In such case, resulting value will be senseless and it should not be used. evas_cserve2_cache.c 2534

V779 (19 errors)

Sometimes the V779 diagnostic indicates the code which is harmless but written incorrectly. For example:
EAPI Eina_Bool
ecore_x_xinerama_screen_geometry_get(int screen,
                                     int *x, int *y,
                                     int *w, int *h)
{
  LOGFN(__FILE__, __LINE__, __FUNCTION__);

#ifdef ECORE_XINERAMA
  if (_xin_info)
  {
    int i;
    for (i = 0; i < _xin_scr_num; i++)
    {
      if (_xin_info[i].screen_number == screen)
      {
        if (x) *x = _xin_info[i].x_org;
        if (y) *y = _xin_info[i].y_org;
        if (w) *w = _xin_info[i].width;
        if (h) *h = _xin_info[i].height;
        return EINA_TRUE;
      }
    }
  }
#endif /* ifdef ECORE_XINERAMA */

  if (x) *x = 0; 
  if (y) *y = 0;
  if (w) *w = DisplayWidth(_ecore_x_disp, 0);
  if (h) *h = DisplayHeight(_ecore_x_disp, 0);

  return EINA_FALSE;
  screen = 0;                          // <=
}
PVS-Studio warning: V779 Unreachable code detected. It is possible that an error is present. ecore_x_xinerama.c 92
Under certain conditions, in the function body the screen argument is not used anywhere. Apparently a compiler or an analyzer warned about the argument which is not used. That's why the programmer wrote the assignment which is actually never performed to please that tool.
In my opinion it would be better to mark the argument using EINA_UNUSED.
Now let's look at a more difficult case.
extern void _exit (int __status) __attribute__ ((__noreturn__));

static void _timeout(int val)
{
  _exit(-1);
  if (val) return;
}
PVS-Studio warning: V779 Unreachable code detected. It is possible that an error is present. timeout.c 30
_exit function does not return the control. This code is incredibly strange. It seems to me, the function must be written as follows:
static void _timeout(int val)
{
  if (val) return;
  _exit(-1);
}
Comment by Carsten Haitzler. Not a bug. it's also an unused param thing from before the macros. The timeout has the process self exit in case it takes too long (assuming the decoder lib is stuck if a timeout happens).
Comment by Andrey Karpov. I do not agree. Perhaps, the program works correctly, but from professional programmer's point of view, such code is unacceptable. I think is it a mistake to write such code for suppressing false positives. It will confuse a person who will maintain such code.
Other errors: EFL_V779.txt.

V1001 (6 errors)

static Elocation_Address *address = NULL;

EAPI Eina_Bool
elocation_address_get(Elocation_Address *address_shadow)
{
   if (!address) return EINA_FALSE;
   if (address == address_shadow) return EINA_TRUE;

   address_shadow = address;
   return EINA_TRUE;
}
PVS-Studio warning: V1001 The 'address_shadow' variable is assigned but is not used until the end of the function. elocation.c 1122
This is very strange code. It seems to me that in fact, something should be written here:
*address_shadow = *address;
Other errors:
  • V1001 The 'screen' variable is assigned but is not used until the end of the function. ecore_x_xinerama.c 92
  • V1001 The 'ret' variable is assigned but is not used until the end of the function. edje_edit.c 12774
  • V1001 The 'ret' variable is assigned but is not used until the end of the function. edje_edit.c 15884
  • V1001 The 'position_shadow' variable is assigned but is not used until the end of the function. elocation.c 1133
  • V1001 The 'status_shadow' variable is assigned but is not used until the end of the function. elocation.c 1144

Comment By Carsten Haitzler

PVS-Studio seems to find different bugs to Coverity. So far it seems to be noisier than Coverity (more non-bugs pointed out as issues) BUT some of these are bugs so if you're willing to go through all the false positivies there are indeed some gems there. The text reports are far less useful than Coverity but they get the job done. I'd actually consider adding PVS-Studio as a second line of defense after Coverity as it can catch some things Coverity cannot and if you are willing to invest the time, that's a good thing. I've already addressed some of the above issues, others will take time, and some are just non-issues that a tool like PVS-Studio or Coverity will complain about, but being able to ignore it is the best solution.

Conclusion

I would like to remind one more time, that visitors can explore the analyzer reportto make sure that the analyzer characteristics given in the article, correspond to reality.
Among the errors described in the article there is nothing epic, but this is not surprising. As I have already said, the EFL project is regularly checked using Coverity. Despite this, PVS-Studio Analyzer still managed to identify many errors. I think that PVS-Studio would have shown itself better if it had been possible to return to past and swap the analyzers :). I mean, if PVS-Studio had been used first, and then Coverity, PVS-Studio would have shown itself brighter.
Рисунок 2