SlideShare a Scribd company logo
1 of 9
Download to read offline
Checking 7-Zip with PVS-Studio analyzer
Author: Kirill Yudintsev
Date: 06.06.2016
One of the programs, which allows you to solve the problem of data compression, is a popular file
archiver 7-Zip, which I often use myself. Our readers have long asked us to check the code of this
application. Well, it's time to look at its source code, and see what PVS-Studio is able to detect in this
application.
Introduction
A couple of words about the project. 7-Zip is a free file archiver with a high data compression ratio,
written in C, and C++. The size of this project is 235,000 lines of code. It supports several compression
algorithms and a variety of data formats, including its own 7z format, with a highly effective LZMA
compression algorithm. It is in development since 1999, free, and open source. 7-Zip is the winner of the
SourceForge.net Community Choice Awards of the year 2007 in the categories "Best project" and "Best
technical design". We checked the 16.00 version, whose source code can be downloaded at this link -
http://www.7-zip.org/download.html
Analysis results.
To do the analysis of 7-Zip we used the static code analyzer, PVS-Studio v6.04. In this article we provide
the most interesting analyzer warnings. Let's have a look at them.
Typos in conditional statements
We see typos in conditional operators quite often. They can cause a lot of pain if there is a large number
of checks. Then static analyzer comes to our aid.
Here are some examples of this error.
V501 There are identical sub-expressions 'Id == k_PPC' to the left and to the right of the '||' operator.
7zupdate.cpp 41
void SetDelta()
{
if (Id == k_IA64)
Delta = 16;
else if (Id == k_ARM || Id == k_PPC || Id == k_PPC) //<==
Delta = 4;
else if (Id == k_ARMT)
Delta = 2;
else
Delta = 0;
}
The analyzer detected similar conditional expressions. At best, one of the conditions for Id == k_PPC is
redundant and does not affect the logic of the program. To fix this typo we should just remove this
condition, then the correct expression will be:
if (Id == k_IA64)
Delta = 16;
else if (Id == k_ARM || Id == k_PPC)
Delta = 4;
But there may be more serious consequences from such typos, if instead of a k_PPC constant, there
should be another in one of the repeated conditions. In this case, the program logic may be broken.
Here's another example of a typo in a conditional statement:
V501 There are identical sub-expressions to the left and to the right of the '||' operator: offs >=
nodeSize || offs >= nodeSize hfshandler.cpp 915
HRESULT CDatabase::LoadCatalog(....)
{
....
UInt32 nodeSize = (1 << hr.NodeSizeLog);
UInt32 offs = Get16(p + nodeOffset + nodeSize - (i + 1) * 2);
UInt32 offsNext = Get16(p + nodeOffset + nodeSize - (i + 2) * 2);
UInt32 recSize = offsNext - offs;
if (offs >= nodeSize
|| offs >= nodeSize //<==
|| offsNext < offs
|| recSize < 6)
return S_FALSE;
....
}
The problem is in the repeating condition offs >= nodeSize.
The typos most likely appeared because of using Copy-Paste to duplicate the code. It wouldn't make
sense to recommend not using the copy-paste method. It's too convenient and useful to reject such
functionality in the editor. We should just check the result we get more thoroughly.
Identical comparisons
The analyzer detected a potential error in a construction that consists of two conditional statements.
Here is an example.
V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error
presence. Check lines: 388, 390. archivecommandline.cpp 388
static void AddRenamePair(...., NRecursedType::EEnum type, ....)
{
....
if (type == NRecursedType::kRecursed)
val.AddAscii("-r");
else if (type == NRecursedType::kRecursed) //<==
val.AddAscii("-r0");
....
}
NRecursedType is defined in the following way in the code:
namespace NRecursedType {
enum EEnum {
kRecursed,
kWildcardOnlyRecursed,
kNonRecursed
};
}
As a result the second condition will never be fulfilled. Let's try to sort out this problem in detail. Based
on the description of the command-line parameters, the -r parameter signals usage of recursion for
subdirectories. But in the case of the -r0 parameter, the recursion is used only for the template names.
Comparing this with the definition NRecursedType we can draw the conclusion, that in the second case
we should use the type NRecursedType::kWildcardOnlyRecursed. Then the correct code will be like this:
static void AddRenamePair(...., NRecursedType::EEnum type, ....)
{
....
if (type == NRecursedType::kRecursed)
val.AddAscii("-r");
else if (type == NRecursedType::kWildcardOnlyRecursed) //<==
val.AddAscii("-r0");
....
}
Conditions that are always either true or false
You should always take into account the variable type - if it is signed or unsigned. Ignoring these
peculiarities can lead to unpleasant consequences.
V547 Expression 'newSize < 0' is always false. Unsigned type value is never < 0. update.cpp 254
Here is an example of where this language feature was ignored:
STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize)
{
if (newSize < 0) //<==
return E_INVALIDARG;
....
}
The thing is that newSize has unsigned type, and the condition will never be true. If a negative value gets
to the SetSize function, then this error will be ignored and the function will start using an incorrect size.
There were two more conditions in 7-Zip that are always either true or false because of the confusion
with signed/unsigned types.
 V547 Expression 'rec.SiAttr.SecurityId >= 0' is always true. Unsigned type value is always >= 0.
ntfshandler.cpp 2142
 V547 Expression 's.Len() >= 0' is always true. Unsigned type value is always >= 0. xarhandler.cpp
258
The same condition is checked twice.
The analyzer detected a potential bug, related to the fact that the same condition is checked twice.
V571 Recurring check. The 'if (Result != ((HRESULT) 0L))' condition was already verified in line 56.
extractengine.cpp 58
Here is a code fragment:
void Process2()
{
....
if (Result != S_OK)
{
if (Result != S_OK) //<==
ErrorMessage = kCantOpenArchive;
return;
}
....
}
Most likely, in this situation the second check is redundant, but there is also a possibility that a
programmer didn't change the second condition, and it turned out to be erroneous.
Another similar fragment in 7-Zip code:
 V571 Recurring check. The '!quoteMode' condition was already verified in line 18. stringutils.cpp
20
 V571 Recurring check. The 'IsVarStr(params[1], 22)' condition was already verified in line 3377.
nsisin.cpp 3381
Suspicious pointer handling
There were such bugs in 7-Zip code, where a pointer first gets dereferenced, and only then it is verified
against null.
V595 The 'outStreamSpec' pointer was utilized before it was verified against nullptr. Check lines: 753,
755. lzmaalone.cpp 753
It is a very common error in all programs. It usually appears because of negligence during the process of
refactoring. Accessing by a null pointer will result in undefined behavior. Let's look at a code fragment of
an application containing an error of this type:
static int main2(int numArgs, const char *args[])
{
....
if (!stdOutMode)
Print_Size("Output size: ", outStreamSpec->ProcessedSize); //<==
if (outStreamSpec) //<==
{
if (outStreamSpec->Close() != S_OK)
throw "File closing error";
}
....
}
The pointer outStreamSpec is dereferenced in the expression outStreamSpec->ProcessedSize. Then it is
verified against null. The check below in the code is either meaningless, or we should verify the pointer
in the code above against null. Here is a list of potentially buggy fragments in the program code:
 V595 The '_file' pointer was utilized before it was verified against nullptr. Check lines: 2099,
2112. bench.cpp 2099
 V595 The 'ai' pointer was utilized before it was verified against nullptr. Check lines: 204, 214.
updatepair.cpp 204
 V595 The 'options' pointer was utilized before it was verified against nullptr. Check lines: 631,
636. zipupdate.cpp 631
 V595 The 'volStreamSpec' pointer was utilized before it was verified against nullptr. Check lines:
856, 863. update.cpp 856
An exception inside a destructor
When an exception is thrown in a program, the stack beings to unwind, and objects get destroyed by
calling the destructors. If the destructor of an object being destroyed during the stack folding throws
another exception which leaves the destructor, the C++ library will immediately terminate the program
by calling the terminate() function. Therefore, the destructors should never throw exceptions. An
exception thrown inside a destructor must be handled inside the same destructor.
The analyzer issued the following message:
V509 The 'throw' operator inside the destructor should be placed within the try..catch block. Raising
exception inside the destructor is illegal. consoleclose.cpp 62
Here is the destructor that throws an exception:
CCtrlHandlerSetter::~CCtrlHandlerSetter()
{
#if !defined(UNDER_CE) && defined(_WIN32)
if (!SetConsoleCtrlHandler(HandlerRoutine, FALSE))
throw "SetConsoleCtrlHandler fails"; //<==
#endif
}
V509 message warns that if the CCtrlHandlerSetter object is destroyed during processing of the
exception handling, the new exception will cause an immediate crash of the program. This code should
be written in such a way, so as to report an error in the destructor without using the exception
mechanism. If the error is not critical, then it can be ignored.
CCtrlHandlerSetter::~CCtrlHandlerSetter()
{
#if !defined(UNDER_CE) && defined(_WIN32)
try
{
if (!SetConsoleCtrlHandler(HandlerRoutine, FALSE))
throw "SetConsoleCtrlHandler fails"; //<==
}
catch(...)
{
assert(false);
}
#endif
}
Increment of a bool type variable
Historically, the increment operation is possible for variable of bool type; the operation sets the value of
the variable to true. This feature is related to the fact that previously integer values were used to
represent boolean variables. Later this feature remained to support backwards compatibility. Starting
with the C++98 standard, it is marked as deprecated, and not recommended for use. In the upcoming
C++17 standard this possibility to use an increment for a boolean value is marked for deletion.
We found a couple of fragments where this obsolete feature is still used.
 V552 A bool type variable is being incremented: numMethods ++. Perhaps another variable
should be incremented instead. wimhandler.cpp 308
 V552 A bool type variable is being incremented: numMethods ++. Perhaps another variable
should be incremented instead. wimhandler.cpp 318
STDMETHODIMP CHandler::GetArchiveProperty(....)
{
....
bool numMethods = 0;
for (unsigned i = 0; i < ARRAY_SIZE(k_Methods); i++)
{
if (methodMask & ((UInt32)1 << i))
{
res.Add_Space_if_NotEmpty();
res += k_Methods[i];
numMethods++; //<==
}
}
if (methodUnknown != 0)
{
char temp[32];
ConvertUInt32ToString(methodUnknown, temp);
res.Add_Space_if_NotEmpty();
res += temp;
numMethods++; //<==
}
if (numMethods == 1 && chunkSizeBits != 0)
{
....
}
....
}
There are two possible variants in this situation. Either the numMethods is a flag, and it's better to use
initialization by a boolean value numMethods = true in this case. Or, judging by the variable, it is a
counter which should be an integer.
Checking incorrect memory allocation
The analyzer detected a situation, where the pointer value, returned by the new operator is compared
with zero. This usually means that the program won't behave in the way the programmer expects in the
case of it not being possible to allocate the memory.
V668 There is no sense in testing the 'plugin' pointer against null, as the memory was allocated using the
'new' operator. The exception will be generated in the case of memory allocation error. far.cpp 399
Here's how it looks in the code:
static HANDLE MyOpenFilePluginW(const wchar_t *name)
{
....
CPlugin *plugin = new CPlugin(
fullName,
// defaultName,
agent,
(const wchar_t *)archiveType
);
if (!plugin)
return INVALID_HANDLE_VALUE;
....
}
If the new operator was unable to allocate the memory, then according to a C++ standard, an exception
std::bad_alloc() is generated. Then the verification against null is pointless. The plugin pointer will never
be null. The function will never return a constant value INVALID_HANDLE_VALUE. If it is impossible to
allocate the memory, then we have an exception which should be handled on a higher level, and the
verification against null may be deleted. In case it's not desirable to have exceptions in the application,
we can use new operator which doesn't generate exceptions, and thus, the return value can be verified
against null. There were three more similar cheks:
 V668 There is no sense in testing the 'm_Formats' pointer against null, as the memory was
allocated using the 'new' operator. The exception will be generated in the case of memory
allocation error. enumformatetc.cpp 46
 V668 There is no sense in testing the 'm_States' pointer against null, as the memory was
allocated using the 'new' operator. The exception will be generated in the case of memory
allocation error. bzip2decoder.cpp 445
 V668 There is no sense in testing the 'ThreadsInfo' pointer against null, as the memory was
allocated using the 'new' operator. The exception will be generated in the case of memory
allocation error. bzip2encoder.cpp 170
Constructions requiring optimization
Now let's talk about some spots that can potentially be optimized. An object is passed to the function.
This object is passed by value, but doesn't get modified, because of an const keyword. Perhaps it would
be sensible to pass it with a constant reference in the C++ language, or with the help of a pointer in C.
Here is an example for the vector:
V801 Decreased performance. It is better to redefine the first function argument as a reference.
Consider replacing 'const .. pathParts' with 'const .. &pathParts'. wildcard.cpp 487
static unsigned GetNumPrefixParts(const UStringVector pathParts)
{
....
}
During the call of this function we'll have a call of a copy constructor for the UStringVector class. This can
significantly reduce the performance of an application if such object copying happens quite often. This
code can be easily optimized by adding a reference:
static unsigned GetNumPrefixParts(const UStringVector& pathParts)
{
....
}
Here are another similar fragments:
 V801 Decreased performance. It is better to redefine the first function argument as a reference.
Consider replacing 'const .. props' with 'const .. &props'. benchmarkdialog.cpp 766
 V801 Instantiate CRecordVector < CAttribIconPair >: Decreased performance. It is better to
redefine the first function argument as a reference. Consider replacing 'const .. item' with 'const
.. &item'. myvector.h 199
Conclusion
7-Zip is a small project, which has been developing for quite a while, so there wasn't much chance of
finding a large number of serious bugs. But still, there are some fragments that are worth reviewing,
and static code analyzer PVS-Studio can be of great help. If you develop a project in C, C++ or C#, I
suggest downloading PVS-Studio and checking your project.

More Related Content

What's hot

What's hot (20)

A Slipshod Check of the Visual C++ 2013 Library (update 3)
A Slipshod Check of the Visual C++ 2013 Library (update 3)A Slipshod Check of the Visual C++ 2013 Library (update 3)
A Slipshod Check of the Visual C++ 2013 Library (update 3)
 
Checking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-StudioChecking Clang 11 with PVS-Studio
Checking Clang 11 with PVS-Studio
 
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
 
Linux version of PVS-Studio couldn't help checking CodeLite
Linux version of PVS-Studio couldn't help checking CodeLiteLinux version of PVS-Studio couldn't help checking CodeLite
Linux version of PVS-Studio couldn't help checking CodeLite
 
Errors detected in the Visual C++ 2012 libraries
Errors detected in the Visual C++ 2012 librariesErrors detected in the Visual C++ 2012 libraries
Errors detected in the Visual C++ 2012 libraries
 
The Little Unicorn That Could
The Little Unicorn That CouldThe Little Unicorn That Could
The Little Unicorn That Could
 
Analyzing ReactOS One More Time
Analyzing ReactOS One More TimeAnalyzing ReactOS One More Time
Analyzing ReactOS One More Time
 
Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016Top 10 bugs in C++ open source projects, checked in 2016
Top 10 bugs in C++ open source projects, checked in 2016
 
Analysis of Microsoft Code Contracts
Analysis of Microsoft Code ContractsAnalysis of Microsoft Code Contracts
Analysis of Microsoft Code Contracts
 
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 projectPVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio is there to help CERN: analysis of Geant4 project
 
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' RequestChecking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
 
Checking Notepad++: five years later
Checking Notepad++: five years laterChecking Notepad++: five years later
Checking Notepad++: five years later
 
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Rechecking TortoiseSVN with the PVS-Studio Code AnalyzerRechecking TortoiseSVN with the PVS-Studio Code Analyzer
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
 
Checking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzerChecking the code of Valgrind dynamic analyzer by a static analyzer
Checking the code of Valgrind dynamic analyzer by a static analyzer
 
Brief analysis of Media Portal 2 bugs
Brief analysis of Media Portal 2 bugsBrief analysis of Media Portal 2 bugs
Brief analysis of Media Portal 2 bugs
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after Cppcheck
 
Reanalyzing the Notepad++ project
Reanalyzing the Notepad++ projectReanalyzing the Notepad++ project
Reanalyzing the Notepad++ project
 
Why Windows 8 drivers are buggy
Why Windows 8 drivers are buggyWhy Windows 8 drivers are buggy
Why Windows 8 drivers are buggy
 
Date Processing Attracts Bugs or 77 Defects in Qt 6
Date Processing Attracts Bugs or 77 Defects in Qt 6Date Processing Attracts Bugs or 77 Defects in Qt 6
Date Processing Attracts Bugs or 77 Defects in Qt 6
 
Analyzing Wine: One Year Later
Analyzing Wine: One Year LaterAnalyzing Wine: One Year Later
Analyzing Wine: One Year Later
 

Similar to Checking 7-Zip with PVS-Studio analyzer

Similar to Checking 7-Zip with PVS-Studio analyzer (20)

Analysis of Godot Engine's Source Code
Analysis of Godot Engine's Source CodeAnalysis of Godot Engine's Source Code
Analysis of Godot Engine's Source Code
 
Headache from using mathematical software
Headache from using mathematical softwareHeadache from using mathematical software
Headache from using mathematical software
 
Analyzing the Dolphin-emu project
Analyzing the Dolphin-emu projectAnalyzing the Dolphin-emu project
Analyzing the Dolphin-emu project
 
Documenting Bugs in Doxygen
Documenting Bugs in DoxygenDocumenting Bugs in Doxygen
Documenting Bugs in Doxygen
 
Analysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) projectAnalysis of the Trans-Proteomic Pipeline (TPP) project
Analysis of the Trans-Proteomic Pipeline (TPP) project
 
Checking OpenCV with PVS-Studio
Checking OpenCV with PVS-StudioChecking OpenCV with PVS-Studio
Checking OpenCV with PVS-Studio
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects 100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0
 
Checking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto GameChecking the Open-Source Multi Theft Auto Game
Checking the Open-Source Multi Theft Auto Game
 
Sony C#/.NET component set analysis
Sony C#/.NET component set analysisSony C#/.NET component set analysis
Sony C#/.NET component set analysis
 
PVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd CheckPVS-Studio vs Chromium. 3-rd Check
PVS-Studio vs Chromium. 3-rd Check
 
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
 
Why Windows 8 drivers are buggy
Why Windows 8 drivers are buggyWhy Windows 8 drivers are buggy
Why Windows 8 drivers are buggy
 
Checking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-StudioChecking the Source Code of FlashDevelop with PVS-Studio
Checking the Source Code of FlashDevelop with PVS-Studio
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
 
The Unicorn's Travel to the Microcosm
The Unicorn's Travel to the MicrocosmThe Unicorn's Travel to the Microcosm
The Unicorn's Travel to the Microcosm
 
Re-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large reportRe-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large report
 
A Spin-off: CryEngine 3 SDK Checked with CppCat
A Spin-off: CryEngine 3 SDK Checked with CppCatA Spin-off: CryEngine 3 SDK Checked with CppCat
A Spin-off: CryEngine 3 SDK Checked with CppCat
 
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
 

Recently uploaded

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 

Recently uploaded (20)

10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 

Checking 7-Zip with PVS-Studio analyzer

  • 1. Checking 7-Zip with PVS-Studio analyzer Author: Kirill Yudintsev Date: 06.06.2016 One of the programs, which allows you to solve the problem of data compression, is a popular file archiver 7-Zip, which I often use myself. Our readers have long asked us to check the code of this application. Well, it's time to look at its source code, and see what PVS-Studio is able to detect in this application. Introduction A couple of words about the project. 7-Zip is a free file archiver with a high data compression ratio, written in C, and C++. The size of this project is 235,000 lines of code. It supports several compression algorithms and a variety of data formats, including its own 7z format, with a highly effective LZMA compression algorithm. It is in development since 1999, free, and open source. 7-Zip is the winner of the SourceForge.net Community Choice Awards of the year 2007 in the categories "Best project" and "Best technical design". We checked the 16.00 version, whose source code can be downloaded at this link - http://www.7-zip.org/download.html Analysis results. To do the analysis of 7-Zip we used the static code analyzer, PVS-Studio v6.04. In this article we provide the most interesting analyzer warnings. Let's have a look at them. Typos in conditional statements We see typos in conditional operators quite often. They can cause a lot of pain if there is a large number of checks. Then static analyzer comes to our aid. Here are some examples of this error. V501 There are identical sub-expressions 'Id == k_PPC' to the left and to the right of the '||' operator. 7zupdate.cpp 41
  • 2. void SetDelta() { if (Id == k_IA64) Delta = 16; else if (Id == k_ARM || Id == k_PPC || Id == k_PPC) //<== Delta = 4; else if (Id == k_ARMT) Delta = 2; else Delta = 0; } The analyzer detected similar conditional expressions. At best, one of the conditions for Id == k_PPC is redundant and does not affect the logic of the program. To fix this typo we should just remove this condition, then the correct expression will be: if (Id == k_IA64) Delta = 16; else if (Id == k_ARM || Id == k_PPC) Delta = 4; But there may be more serious consequences from such typos, if instead of a k_PPC constant, there should be another in one of the repeated conditions. In this case, the program logic may be broken. Here's another example of a typo in a conditional statement: V501 There are identical sub-expressions to the left and to the right of the '||' operator: offs >= nodeSize || offs >= nodeSize hfshandler.cpp 915 HRESULT CDatabase::LoadCatalog(....) { .... UInt32 nodeSize = (1 << hr.NodeSizeLog); UInt32 offs = Get16(p + nodeOffset + nodeSize - (i + 1) * 2); UInt32 offsNext = Get16(p + nodeOffset + nodeSize - (i + 2) * 2); UInt32 recSize = offsNext - offs; if (offs >= nodeSize || offs >= nodeSize //<== || offsNext < offs || recSize < 6) return S_FALSE;
  • 3. .... } The problem is in the repeating condition offs >= nodeSize. The typos most likely appeared because of using Copy-Paste to duplicate the code. It wouldn't make sense to recommend not using the copy-paste method. It's too convenient and useful to reject such functionality in the editor. We should just check the result we get more thoroughly. Identical comparisons The analyzer detected a potential error in a construction that consists of two conditional statements. Here is an example. V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 388, 390. archivecommandline.cpp 388 static void AddRenamePair(...., NRecursedType::EEnum type, ....) { .... if (type == NRecursedType::kRecursed) val.AddAscii("-r"); else if (type == NRecursedType::kRecursed) //<== val.AddAscii("-r0"); .... } NRecursedType is defined in the following way in the code: namespace NRecursedType { enum EEnum { kRecursed, kWildcardOnlyRecursed, kNonRecursed }; } As a result the second condition will never be fulfilled. Let's try to sort out this problem in detail. Based on the description of the command-line parameters, the -r parameter signals usage of recursion for subdirectories. But in the case of the -r0 parameter, the recursion is used only for the template names. Comparing this with the definition NRecursedType we can draw the conclusion, that in the second case we should use the type NRecursedType::kWildcardOnlyRecursed. Then the correct code will be like this: static void AddRenamePair(...., NRecursedType::EEnum type, ....) { ....
  • 4. if (type == NRecursedType::kRecursed) val.AddAscii("-r"); else if (type == NRecursedType::kWildcardOnlyRecursed) //<== val.AddAscii("-r0"); .... } Conditions that are always either true or false You should always take into account the variable type - if it is signed or unsigned. Ignoring these peculiarities can lead to unpleasant consequences. V547 Expression 'newSize < 0' is always false. Unsigned type value is never < 0. update.cpp 254 Here is an example of where this language feature was ignored: STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) { if (newSize < 0) //<== return E_INVALIDARG; .... } The thing is that newSize has unsigned type, and the condition will never be true. If a negative value gets to the SetSize function, then this error will be ignored and the function will start using an incorrect size. There were two more conditions in 7-Zip that are always either true or false because of the confusion with signed/unsigned types.  V547 Expression 'rec.SiAttr.SecurityId >= 0' is always true. Unsigned type value is always >= 0. ntfshandler.cpp 2142  V547 Expression 's.Len() >= 0' is always true. Unsigned type value is always >= 0. xarhandler.cpp 258 The same condition is checked twice. The analyzer detected a potential bug, related to the fact that the same condition is checked twice. V571 Recurring check. The 'if (Result != ((HRESULT) 0L))' condition was already verified in line 56. extractengine.cpp 58 Here is a code fragment: void Process2() { .... if (Result != S_OK) { if (Result != S_OK) //<==
  • 5. ErrorMessage = kCantOpenArchive; return; } .... } Most likely, in this situation the second check is redundant, but there is also a possibility that a programmer didn't change the second condition, and it turned out to be erroneous. Another similar fragment in 7-Zip code:  V571 Recurring check. The '!quoteMode' condition was already verified in line 18. stringutils.cpp 20  V571 Recurring check. The 'IsVarStr(params[1], 22)' condition was already verified in line 3377. nsisin.cpp 3381 Suspicious pointer handling There were such bugs in 7-Zip code, where a pointer first gets dereferenced, and only then it is verified against null. V595 The 'outStreamSpec' pointer was utilized before it was verified against nullptr. Check lines: 753, 755. lzmaalone.cpp 753 It is a very common error in all programs. It usually appears because of negligence during the process of refactoring. Accessing by a null pointer will result in undefined behavior. Let's look at a code fragment of an application containing an error of this type: static int main2(int numArgs, const char *args[]) { .... if (!stdOutMode) Print_Size("Output size: ", outStreamSpec->ProcessedSize); //<== if (outStreamSpec) //<== { if (outStreamSpec->Close() != S_OK) throw "File closing error"; } .... } The pointer outStreamSpec is dereferenced in the expression outStreamSpec->ProcessedSize. Then it is verified against null. The check below in the code is either meaningless, or we should verify the pointer in the code above against null. Here is a list of potentially buggy fragments in the program code:
  • 6.  V595 The '_file' pointer was utilized before it was verified against nullptr. Check lines: 2099, 2112. bench.cpp 2099  V595 The 'ai' pointer was utilized before it was verified against nullptr. Check lines: 204, 214. updatepair.cpp 204  V595 The 'options' pointer was utilized before it was verified against nullptr. Check lines: 631, 636. zipupdate.cpp 631  V595 The 'volStreamSpec' pointer was utilized before it was verified against nullptr. Check lines: 856, 863. update.cpp 856 An exception inside a destructor When an exception is thrown in a program, the stack beings to unwind, and objects get destroyed by calling the destructors. If the destructor of an object being destroyed during the stack folding throws another exception which leaves the destructor, the C++ library will immediately terminate the program by calling the terminate() function. Therefore, the destructors should never throw exceptions. An exception thrown inside a destructor must be handled inside the same destructor. The analyzer issued the following message: V509 The 'throw' operator inside the destructor should be placed within the try..catch block. Raising exception inside the destructor is illegal. consoleclose.cpp 62 Here is the destructor that throws an exception: CCtrlHandlerSetter::~CCtrlHandlerSetter() { #if !defined(UNDER_CE) && defined(_WIN32) if (!SetConsoleCtrlHandler(HandlerRoutine, FALSE)) throw "SetConsoleCtrlHandler fails"; //<== #endif } V509 message warns that if the CCtrlHandlerSetter object is destroyed during processing of the exception handling, the new exception will cause an immediate crash of the program. This code should be written in such a way, so as to report an error in the destructor without using the exception mechanism. If the error is not critical, then it can be ignored. CCtrlHandlerSetter::~CCtrlHandlerSetter() { #if !defined(UNDER_CE) && defined(_WIN32) try { if (!SetConsoleCtrlHandler(HandlerRoutine, FALSE)) throw "SetConsoleCtrlHandler fails"; //<== } catch(...) {
  • 7. assert(false); } #endif } Increment of a bool type variable Historically, the increment operation is possible for variable of bool type; the operation sets the value of the variable to true. This feature is related to the fact that previously integer values were used to represent boolean variables. Later this feature remained to support backwards compatibility. Starting with the C++98 standard, it is marked as deprecated, and not recommended for use. In the upcoming C++17 standard this possibility to use an increment for a boolean value is marked for deletion. We found a couple of fragments where this obsolete feature is still used.  V552 A bool type variable is being incremented: numMethods ++. Perhaps another variable should be incremented instead. wimhandler.cpp 308  V552 A bool type variable is being incremented: numMethods ++. Perhaps another variable should be incremented instead. wimhandler.cpp 318 STDMETHODIMP CHandler::GetArchiveProperty(....) { .... bool numMethods = 0; for (unsigned i = 0; i < ARRAY_SIZE(k_Methods); i++) { if (methodMask & ((UInt32)1 << i)) { res.Add_Space_if_NotEmpty(); res += k_Methods[i]; numMethods++; //<== } } if (methodUnknown != 0) { char temp[32]; ConvertUInt32ToString(methodUnknown, temp); res.Add_Space_if_NotEmpty(); res += temp; numMethods++; //<== }
  • 8. if (numMethods == 1 && chunkSizeBits != 0) { .... } .... } There are two possible variants in this situation. Either the numMethods is a flag, and it's better to use initialization by a boolean value numMethods = true in this case. Or, judging by the variable, it is a counter which should be an integer. Checking incorrect memory allocation The analyzer detected a situation, where the pointer value, returned by the new operator is compared with zero. This usually means that the program won't behave in the way the programmer expects in the case of it not being possible to allocate the memory. V668 There is no sense in testing the 'plugin' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. far.cpp 399 Here's how it looks in the code: static HANDLE MyOpenFilePluginW(const wchar_t *name) { .... CPlugin *plugin = new CPlugin( fullName, // defaultName, agent, (const wchar_t *)archiveType ); if (!plugin) return INVALID_HANDLE_VALUE; .... } If the new operator was unable to allocate the memory, then according to a C++ standard, an exception std::bad_alloc() is generated. Then the verification against null is pointless. The plugin pointer will never be null. The function will never return a constant value INVALID_HANDLE_VALUE. If it is impossible to allocate the memory, then we have an exception which should be handled on a higher level, and the verification against null may be deleted. In case it's not desirable to have exceptions in the application, we can use new operator which doesn't generate exceptions, and thus, the return value can be verified against null. There were three more similar cheks:
  • 9.  V668 There is no sense in testing the 'm_Formats' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. enumformatetc.cpp 46  V668 There is no sense in testing the 'm_States' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. bzip2decoder.cpp 445  V668 There is no sense in testing the 'ThreadsInfo' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. bzip2encoder.cpp 170 Constructions requiring optimization Now let's talk about some spots that can potentially be optimized. An object is passed to the function. This object is passed by value, but doesn't get modified, because of an const keyword. Perhaps it would be sensible to pass it with a constant reference in the C++ language, or with the help of a pointer in C. Here is an example for the vector: V801 Decreased performance. It is better to redefine the first function argument as a reference. Consider replacing 'const .. pathParts' with 'const .. &pathParts'. wildcard.cpp 487 static unsigned GetNumPrefixParts(const UStringVector pathParts) { .... } During the call of this function we'll have a call of a copy constructor for the UStringVector class. This can significantly reduce the performance of an application if such object copying happens quite often. This code can be easily optimized by adding a reference: static unsigned GetNumPrefixParts(const UStringVector& pathParts) { .... } Here are another similar fragments:  V801 Decreased performance. It is better to redefine the first function argument as a reference. Consider replacing 'const .. props' with 'const .. &props'. benchmarkdialog.cpp 766  V801 Instantiate CRecordVector < CAttribIconPair >: Decreased performance. It is better to redefine the first function argument as a reference. Consider replacing 'const .. item' with 'const .. &item'. myvector.h 199 Conclusion 7-Zip is a small project, which has been developing for quite a while, so there wasn't much chance of finding a large number of serious bugs. But still, there are some fragments that are worth reviewing, and static code analyzer PVS-Studio can be of great help. If you develop a project in C, C++ or C#, I suggest downloading PVS-Studio and checking your project.