SlideShare a Scribd company logo
1 of 66
Download to read offline
100 bugs in Open Source C/C++ projects
Authors: Andrey Karpov, Evgeniy Ryzhkov
Date: 16.03.2012
Abstract
This article demonstrates capabilities of the static code analysis methodology. The readers are offered
to study the samples of one hundred errors found in open-source projects in C/C++. All the errors have
been found with the PVS-Studio static code analyzer.
Introduction
We won't tire you programmers by making you read texts and will pass to the error samples right away.
Those who want to know what static code analysis is, please follow the link. Those who want to know
what PVS-Studio is and download the trial version, see this page: http://www.viva64.com/en/pvs-
studio/.
Yes, one more thing. Please see our post "FAQ for those who have read our articles".
Samples of errors detected in various open-source projects
The samples of detected errors will be divided into several groups. This division is rather relative. One
and the same error can often be referred to misprints and incorrect array handling at a time.
Of course, we have taken just a few errors from each of the projects. If we described all the found
defects, it would be a reference book. This is the list of analyzed projects:
• Apache HTTP Server - http://httpd.apache.org/
• Audacity - http://audacity.sourceforge.net/
• Chromium - http://www.chromium.org/
• Clang - http://clang-analyzer.llvm.org/
• CMake - http://www.cmake.org/
• Crystal Space 3D SDK - http://www.crystalspace3d.org/main/Main_Page
• Emule - http://www.emule.com/
• FAR Manager - http://www.farmanager.com/
• FCE Ultra - http://fceux.com/web/home.html
• Fennec Media Project - http://fennec.sourceforge.net/
• G3D Content Pak - http://sourceforge.net/projects/g3d-cpp/
• IPP Samples - http://www.viva64.com/go.php?url=449
• Lugaru - http://www.wolfire.com/lugaru
• Miranda IM - http://www.miranda-im.org/
• MySQL - http://www.mysql.com/
• Newton Game Dynamics - http://newtondynamics.com/forum/newton.php
• Notepad++ - http://notepad-plus-plus.org/
• Pixie - http://www.renderpixie.com/
• PNG library - http://libpng.org/pub/png/
• QT - http://qt.nokia.com/products/
• ReactOS - http://www.reactos.org/en/
• Shareaza - http://www.shareaza.com/
• SMTP Client with SSL/TLS - http://www.codeproject.com/KB/IP/smtp_ssl.aspx
• StrongDC++ - http://strongdc.sourceforge.net/index.php?lang=eng
• Swiss-Army Knife of Trace - http://www.codeproject.com/KB/trace/tracetool.aspx
• TortoiseSVN - http://tortoisesvn.net/
• Ultimate TCP/IP - http://www.codeproject.com/KB/MFC/UltimateTCPIP.aspx
• VirtualDub - http://www.virtualdub.org/
• WinDjView - http://windjview.sourceforge.net/
• WinMerge - http://winmerge.org/
• Wolfenstein 3D - http://en.wikipedia.org/wiki/Wolfenstein_3D
• Crypto++ - http://www.cryptopp.com/
• Quake-III-Arena - https://github.com/id-Software/Quake-III-Arena
• And some others.
Errors of array and string handling
Errors of array and string handling are the largest class of defects in C/C++ programs. This is the price for
the capability of effective low-level memory handling available to programmers. In the article we will
show just a small part of these errors found by the PVS-Studio analyzer. But we think any C/C++
programmer understands how numerous and insidious they are.
Example 1. Wolfenstein 3D project. Only part of an object is cleared.
void CG_RegisterItemVisuals( int itemNum ) {
...
itemInfo_t *itemInfo;
...
memset( itemInfo, 0, sizeof( &itemInfo ) );
...
}
The error was found through the V568 diagnostic: It's odd that the argument of sizeof() operator is the
'&itemInfo' expression. cgame cg_weapons.c 1467.
The sizeof() operator calculates the size of the pointer instead of the 'itemInfo_t' structure's size. It is
"sizeof(*itemInfo)" that must be written.
Example 2. Wolfenstein 3D project. Only part of a matrix is cleared.
ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
memcpy( mat, src, sizeof( src ) );
}
The error was found through the V511: The sizeof() operator returns size of the pointer, and not of the
array, in 'sizeof(src)' expression. Splines math_matrix.h 94
Usually programmers expect 'sizeof(src)' to return the size of an array equal to "3*3*sizeof(float)" bytes.
But according to the language standard, 'src' is just a pointer, not an array. Thus, the matrix will be
copied only partly. The 'memcpy' function will copy 4 or 8 bytes (the pointer size) depending on whether
the code is 32-bit or 64-bit.
If you want the whole matrix to be copied, you may pass a reference to the array into the function. This
is the correct code:
ID_INLINE mat3_t::mat3_t( float (&src)[3][3] )
{
memcpy( mat, src, sizeof( src ) );
}
Example 3. FAR Manager project. Only part of an array is cleared.
struct TreeItem
{
int *Last;
size_t LastCount;
...
void Clear()
{
strName.Clear();
memset(Last, 0, sizeof(Last));
Depth=0;
}
};
The error was found through the V579: diagnostic The memset function receives the pointer and its size
as arguments. It is probably a mistake. Inspect the third argument. far treelist.hpp 66
Most likely, there is a missing operation of multiplication by the number of items being cleared, and the
code must look as follows: "memset(Last, 0, LastCount * sizeof(*Last));".
Example 4. ReactOS project. Incorrect calculation of a string length.
static const PCHAR Nv11Board = "NV11 (GeForce2) Board";
static const PCHAR Nv11Chip = "Chip Rev B2";
static const PCHAR Nv11Vendor = "NVidia Corporation";
BOOLEAN
IsVesaBiosOk(...)
{
...
if (!(strncmp(Vendor, Nv11Vendor, sizeof(Nv11Vendor))) &&
!(strncmp(Product, Nv11Board, sizeof(Nv11Board))) &&
!(strncmp(Revision, Nv11Chip, sizeof(Nv11Chip))) &&
(OemRevision == 0x311))
...
}
The error was found through the V579 diagnostic: The strncmp function receives the pointer and its size
as arguments. It is probably a mistake. Inspect the third argument. vga vbe.c 57
Calls of the 'strncmp' function in this code compare only the first several characters, not whole strings.
The error here is this: the sizeof() operator, absolutely inappropriate in this situation, is used to calculate
string lengths. The sizeof() operator actually calculates the pointer size instead of the number of bytes in
a string.
What is the most unpleasant and insidious about this error is that this code almost works as intended. In
99% of cases, comparison of the first several characters is enough. But the remaining 1% can bring you
much fun and long debugging.
Example 5. VirtualDub project. Array overrun (explicit index).
struct ConvoluteFilterData {
long m[9];
long bias;
void *dyna_func;
DWORD dyna_size;
DWORD dyna_old_protect;
BOOL fClip;
};
static unsigned long __fastcall do_conv(
unsigned long *data,
const ConvoluteFilterData *cfd,
long sflags, long pit)
{
long rt0=cfd->m[9], gt0=cfd->m[9], bt0=cfd->m[9];
...
}
The code was found through the V557 diagnostic: Array overrun is possible. The '9' index is pointing
beyond array bound. VirtualDub f_convolute.cpp 73
It is not a real error, but good diagnostic. Explanation: http://www.viva64.com/go.php?url=756.
Example 6. CPU Identifying Tool project. Array overrun (index in a macro).
#define FINDBUFFLEN 64 // Max buffer find/replace size
...
int WINAPI Sticky (...)
{
...
static char findWhat[FINDBUFFLEN] = {'0'};
...
findWhat[FINDBUFFLEN] = '0';
...
}
The error was found through the V557 diagnostic: Array overrun is possible. The '64' index is pointing
beyond array bound. stickies stickies.cpp 7947
This error is a kind of the previous one. The terminal null is written outside the array. The correct code
is: "findWhat[FINDBUFFLEN - 1] = '0';".
Example 7. Wolfenstein 3D project. Array overrun (incorrect expression).
typedef struct bot_state_s
{
...
char teamleader[32]; //netname of the team leader
...
} bot_state_t;
void BotTeamAI( bot_state_t *bs ) {
...
bs->teamleader[sizeof( bs->teamleader )] = '0';
...
}
The error was found through the V557 diagnostic: Array overrun is possible. The 'sizeof (bs-
>teamleader)' index is pointing beyond array bound. game ai_team.c 548
Here is one more example of an array overrun when using an explicitly declared index. These samples
show that such simple at first sight errors are much more widely-spread than it may seem.
The terminal null is written outside the 'teamleader' array. This is the correct code:
bs->teamleader[
sizeof(bs->teamleader) / sizeof(bs->teamleader[0]) - 1
] = '0';
Example 8. Miranda IM project. Only part of a string is copied.
typedef struct _textrangew
{
CHARRANGE chrg;
LPWSTR lpstrText;
} TEXTRANGEW;
const wchar_t* Utils::extractURLFromRichEdit(...)
{
...
::CopyMemory(tr.lpstrText, L"mailto:", 7);
...
}
The error was found through the V512 diagnostic: A call of the 'memcpy' function will lead to a buffer
overflow or underflow. tabsrmm utils.cpp 1080
If Unicode-strings are used, one character occupies 2 or 4 bytes (depending on the data model being
used in compiler) instead of one byte. Unfortunately, programmers easily forget about it, and you can
often see defects like our example in programs.
The 'CopyMemory' function will copy only part of the L"mailto:" string since it handles bytes, not
characters. You can fix the code by using a more appropriate function for string copying or, at least,
multiplying number 7 by sizeof(wchar_t).
Example 9. CMake project. Array overrun inside a loop.
static const struct {
DWORD winerr;
int doserr;
} doserrors[] =
{
...
};
static void
la_dosmaperr(unsigned long e)
{
...
for (i = 0; i < sizeof(doserrors); i++)
{
if (doserrors[i].winerr == e)
{
errno = doserrors[i].doserr;
return;
}
}
...
}
The error was found through the V557 diagnostic: Array overrun is possible. The value of 'i' index could
reach 367. cmlibarchive archive_windows.c 1140, 1142
The error handler itself contains an error. The sizeof() operator returns the array size in bytes and not
the number of items inside it. As a result, the program will try to search much more items than it should
in the loop. This is the correct loop:
for (i = 0; i < sizeof(doserrors) / sizeof(*doserrors); i++)
Example 10. CPU Identifying Tool project. A string is printed into itself.
char * OSDetection ()
{
...
sprintf(szOperatingSystem,
"%sversion %d.%d %s (Build %d)",
szOperatingSystem,
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.szCSDVersion,
osvi.dwBuildNumber & 0xFFFF);
...
sprintf (szOperatingSystem, "%s%s(Build %d)",
szOperatingSystem, osvi.szCSDVersion,
osvi.dwBuildNumber & 0xFFFF);
...
}
This error was found through the V541 diagnostic: It is dangerous to print the string 'szOperatingSystem'
into itself. stickies camel.cpp 572, 603
An attempt of formatted printing of a string into itself can lead to bad consequences. The result of
executing this code depends on the input data, and you cannot predict what will happen. Most likely,
the result will be a meaningless string or an Access Violation will occur.
This error can be referred to the category "code vulnerabilities". In some programs, by feeding special
data to code, you can exploit such code fragments to cause a buffer overflow or other effects an
intruder needs.
Example 11. FCE Ultra project. A string gets less memory than needed.
int FCEUI_SetCheat(...)
{
...
if((t=(char *)realloc(next->name,strlen(name+1))))
...
}
The error was found through the V518 diagnostic: The 'realloc' function allocates strange amount of
memory calculated by 'strlen(expr)'. Perhaps the correct variant is 'strlen(expr) + 1'. fceux cheat.cpp 609
This error is caused by a misprint. It is the 'name' pointer instead of the "name+1" expression that must
be the argument of the strlen() function. As a result, the realloc function allocates 2 bytes less memory
than needed: one byte is lost because 1 is not added to the string length; another byte is lost because
the 'strlen' function calculates the string length skipping the first character.
Example 12. Notepad++ project. Partial array clearing.
#define CONT_MAP_MAX 50
int _iContMap[CONT_MAP_MAX];
...
DockingManager::DockingManager()
{
...
memset(_iContMap, -1, CONT_MAP_MAX);
...
}
The error was found through the V512 diagnostic: A call of the memset function will lead to a buffer
overflow or underflow. notepadPlus DockingManager.cpp 60
That's one more example of how the number of array items is mixed up with an array size. A
multiplication by sizeof(int) is missing.
We can go on and on showing you errors of array handling we have found in various programs. But we
have to stop somewhere.
Undefined behavior
A bit of theory at first.
Undefined behavior is a property of certain programming languages (most prominent in C and C++) to
produce a result in certain situations that depends on compiler implementation or specified
optimization switches. In other words, the specification does not define the language's behavior in any
possible situations but says: "at A condition, the result of B operation is undefined". It is considered a
mistake to allow such a situation in your program even if it is executed well at some particular compiler.
Such a program will not be crossplatform and may cause failures on a different computer, operating
system and even at different compiler's settings.
A sequence point in programming is any point in a program where it is guaranteed that the side effects
of all the previous calculations have already emerged while there are no side effects of the following
calculations yet. To learn more about sequence points and cases of undefined behavior related to
sequence points, see this post: http://www.viva64.com/en/t/0065/.
Example 1. Chromium project. Incorrect use of smart pointer.
void AccessibleContainsAccessible(...)
{
...
auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
...
}
The error was found through the V554 diagnostic: Incorrect use of auto_ptr. The memory allocated with
'new []' will be cleaned using 'delete'. interactive_ui_tests accessibility_win_browsertest.cc 171
This example demonstrates the case when using a smart pointer can cause undefined behavior. It may
be expressed through heap damage, program crash, incomplete object destruction or any other failure.
The error is this: memory is allocated by the new [] operator and released by the delete operator in the
'auto_ptr' class' destructor:
~auto_ptr() {
delete _Myptr;
}
To fix these issues, you should use a more appropriate class, for instance, boost::scoped_array.
Example 2. IPP Samples project. Classic Undefined behavior.
template<typename T, Ipp32s size> void HadamardFwdFast(...)
{
Ipp32s *pTemp;
...
for(j=0;j<4;j++) {
a[0] = pTemp[0*4] + pTemp[1*4];
a[1] = pTemp[0*4] - pTemp[1*4];
a[2] = pTemp[2*4] + pTemp[3*4];
a[3] = pTemp[2*4] - pTemp[3*4];
pTemp = pTemp++;
...
}
...
}
The error was found through the V567 diagnostic: Undefined behavior. The 'pTemp' variable is modified
while being used twice between sequence points. me umc_me_cost_func.h 168
This is a classic example of undefined program behavior. It is this construct which is used to
demonstrate Undefined behavior in various articles. It is unknown whether 'pTemp' will be incremented
by one or not. Two actions of changing pTemp variable's value are located in one sequence point. It
means that the compiler may create the following code:
pTemp = pTemp + 1;
pTemp = pTemp;
Or it may create another version of the code:
TMP = pTemp;
pTemp = pTemp + 1;
pTemp = TMP;
Which of the two code versions will be created depends on the compiler and optimization switches.
Example 3. Fennec Media Project project. Complex expression.
uint32 CUnBitArrayOld::DecodeValueRiceUnsigned(uint32 k)
{
...
while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &
Powers_of_Two_Reversed[m_nCurrentBitIndex++ & 31])) {}
...
}
The error was found through the V567 diagnostic: Undefined behavior. The 'm_nCurrentBitIndex'
variable is modified while being used twice at single sequence point. MACLib unbitarrayold.cpp 78
There are no sequence points between two instances of using the 'm_nCurrentBitIndex' variable. It
means that the standard does not specify the moment when this variable is incremented.
Correspondingly, this code may work differently depending on the compiler and optimization switches.
Example 4. Miranda IM project. Complex expression.
short ezxml_internal_dtd(ezxml_root_t root,
char *s, size_t len)
{
...
while (*(n = ++s + strspn(s, EZXML_WS)) && *n != '>') {
...
}
The error was found through the V567 diagnostic: Undefined behavior. The 's' variable is modified while
being used twice between sequence points.msne zxml.c 371
Prefix increment of the variable is used here. But it does not mean anything: it cannot be guaranteed
that the 's' variable will be incremented before calling the strspn() function.
Errors relating to operation priorities.
To make understanding of examples easier, let's recall the operation priorities table.
Example 1. MySQL project. Priorities of ! and & operations.
int ha_innobase::create(...)
{
...
if (srv_file_per_table
&& !mysqld_embedded
&& (!create_info->options & HA_LEX_CREATE_TMP_TABLE)) {
...
}
The error was found through the V564 diagnostic: The '&' operator is applied to bool type value. You've
probably forgotten to include parentheses or intended to use the '&&' operator. innobase ha_innodb.cc
6789
The programmer wanted a part of the expression to check that a certain bit in the 'create_info->options'
variable is equal to zero. But the priority of the '!' operation is higher than that of the '&' operation,
that's why the expression works by this algorithm:
((!create_info->options) & HA_LEX_CREATE_TMP_TABLE)
We should use additional parentheses if we want the code to work properly:
(!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
Or, what we find nicer, write the code in the following way:
((create_info->options & HA_LEX_CREATE_TMP_TABLE) == 0)
Example 2. Emule project. Priorities of * and ++ operations.
STDMETHODIMP
CCustomAutoComplete::Next(..., ULONG *pceltFetched)
{
...
if (pceltFetched != NULL)
*pceltFetched++;
...
}
The error was found through the V532 diagnostic: Consider inspecting the statement of '*pointer++'
pattern. Probably meant: '(*pointer)++'. emule customautocomplete.cpp 277
If 'pceltFetched' is not a null pointer, the function must increment the variable of the ULONG type this
pointer refers to. The error is this: the priority of the '++' operation is higher than that of '*' operation
(pointer dereferencing). The "*pceltFetched++;" line is identical to the following code:
TMP = pceltFetched + 1;
*pceltFetched;
pceltFetched = TMP;
Virtually it is just increment of the pointer. To make the code correct, we must add parentheses:
"(*pceltFetched)++;".
Example 3. Chromium project. Priorities of & and != operations.
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
...
info->is_directory =
file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0;
...
}
The error was found through the V564 diagnostic: The '&' operator is applied to bool type value. You've
probably forgotten to include parentheses or intended to use the '&&' operator. base
platform_file_win.cc 216
Programmers easily forget that the priority of the '!=' operation is higher than that of '&'. This is what
happened in our case. As a result, we have the following expression:
info->is_directory =
file_info.dwFileAttributes & (0x00000010 != 0);
Let's simplify the expression:
info->is_directory = file_info.dwFileAttributes & (true);
Let's simplify it once again:
info->is_directory = file_info.dwFileAttributes & 1;
It turns out that we have tested the first bit instead of the fifth bit. To fix this, we need to add
parentheses.
Example 4. BCmenu project. IF and ELSE mixed up.
void BCMenu::InsertSpaces(void)
{
if(IsLunaMenuStyle())
if(!xp_space_accelerators) return;
else
if(!original_space_accelerators) return;
...
}
The error was found through the V563 diagnostic: It is possible that this 'else' branch must apply to the
previous 'if' statement. fire bcmenu.cpp 1853
This is not an error of operation priorities, but one relative to it. The programmer does not take into
account that the 'else' branch refers to the nearest 'if' operator. We can see that the code justification
as if it works by the following algorithm:
if(IsLunaMenuStyle()) {
if(!xp_space_accelerators) return;
} else {
if(!original_space_accelerators) return;
}
But actually it is equivalent to the following construct:
if(IsLunaMenuStyle())
{
if(!xp_space_accelerators) {
return;
} else {
if(!original_space_accelerators) return;
}
}
Example 5. IPP Samples project. Priorities of ?: and | operations.
vm_file* vm_file_fopen(...)
{
...
mds[3] = FILE_ATTRIBUTE_NORMAL |
(islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING;
...
}
The error was found through the V502 diagnostic: Perhaps the '?:' operator works in a different way
than it was expected. The '?:' operator has a lower priority than the '|' operator. vm vm_file_win.c 393
Depending on the 'islog' variable's value, the expression must be either equal to
"FILE_ATTRIBUTE_NORMAL" or "FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING". But it does
not happen. Priority of the '?:' operation is lower than that of '|'. As a result, the code acts as follows:
mds[3] = (FILE_ATTRIBUTE_NORMAL | (islog == 0)) ?
0 : FILE_FLAG_NO_BUFFERING;
Let's simplify the expression:
mds[3] = (0x00000080 | ...) ? 0 : FILE_FLAG_NO_BUFFERING;
Since FILE_ATTRIBUTE_NORMAL equals 0x00000080, the condition is always true. It means that 0 will
always be written into mds[3].
Example 6. Newton Game Dynamics project. Priorities of ?: and * operations.
dgInt32 CalculateConvexShapeIntersection (...)
{
...
den = dgFloat32 (1.0e-24f) *
(den > dgFloat32 (0.0f)) ?
dgFloat32 (1.0f) : dgFloat32 (-1.0f);
...
}
The error was found through the V502 diagnostic: Perhaps the '?:' operator works in a different way
than it was expected. The '?:' operator has a lower priority than the '*' operator. physics
dgminkowskiconv.cpp 1061
The error in this code again relates to the low priority of the '?:' operation. The condition for the '?:'
operator is expressed by a meaningless subexpression "dgFloat32 (1.0e-24f) * (den > dgFloat32 (0.0f))".
Adding parentheses will solve the issue.
By the way, programmers often forget how cunning the '?:' operator is. Here is a post on this topic:
"How to make fewer errors at the stage of code writing. Part N2".
Formatted output errors
Examples of these errors are boring and alike, so we will examine only a few samples. The point is that
functions with a variable number of arguments accept actual arguments incompatible with the format
string. Any programmer who uses such functions as printf() is familiar with this type of errors.
Example 1. ReactOS project. Incorrect printing of a WCHAR-character.
static void REGPROC_unescape_string(WCHAR* str)
{
...
default:
fprintf(stderr,
"Warning! Unrecognized escape sequence: %c'n",
str[str_idx]);
...
}
The error was found through the V576 diagnostic: Incorrect format. Consider checking the third actual
argument of the 'fprintf' function. The char type argument is expected. regedit regproc.c 293
The fprinf() function must print a character of the char type. But the third argument is a character of the
WCHAR type. The user will get an incorrectly generated message. To fix the code, we should replace '%c'
with '%C' in the format string.
Example 2. Intel AMT SDK project. Character '%' missing.
void addAttribute(...)
{
...
int index = _snprintf(temp, 1023,
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
"%02x%02x:02x%02x:%02x%02x:%02x%02x",
value[0],value[1],value[2],value[3],value[4],
value[5],value[6],value[7],value[8],
value[9],value[10],value[11],value[12],
value[13],value[14],value[15]);
...
}
The error was found through the V576 diagnostic: Incorrect format. A different number of actual
arguments is expected while calling '_snprintf' function. Expected: 18. Present: 19. mod_pvs
mod_pvs.cpp 308
It is not easy to find an error here at first sight. However, the PVS-Studio analyzer does not get tired and
notices that the function takes more actual arguments than specified in the format string. The reason is
that the '%' character is missing in one place. Let's single out this fragment:
"%02x%02x:[HERE]02x%02x:%02x%02x:%02x%02x",
Example 3. Intel AMT SDK project. Unused argument.
bool GetUserValues(...)
{
...
printf("Error: illegal value. Aborting.n", tmp);
return false;
}
The error was found through the V576 diagnostic: Incorrect format. A different number of actual
arguments is expected while calling 'printf' function. Expected: 1. Present: 2. RemoteControlSample
remotecontrolsample.cpp 792
The error is this: the 'tmp' variable is not used in any way when printing the information message.
Example 4. G3D Content Pak project. Printing of meaningless data.
class Matrix3 {
...
inline float* operator[] (int iRow) {
...
};
void AnyVal::serialize(G3D::TextOutput& t) const {
...
const Matrix3& m = *(Matrix3*)m_value;
...
t.printf("%10.5f, %10.5f, %10.5f,n
%10.5f, %10.5f, %10.5f,n
%10.5f, %10.5f, %10.5f)",
m[0, 0], m[0, 1], m[0, 2],
m[1, 0], m[1, 1], m[1, 2],
m[2, 0], m[2, 1], m[2, 2]);
...
}
The error was found through the V520 diagnostic: The comma operator ',' in array index expression '[0,
0]'. graphics3D anyval.cpp 275
The program prints meaningless values instead of the matrix. You may write such a code when you work
with different programming languages and sometimes forget how to access an item in a two-
dimensional array in the C language.
Let's see how the 'm[0, 1]' expression works. At first, expression"0, 1" is calculated. The result of this
expression is 1. Then the 'operator[]' function is called in the Matrix3 class. The function takes the actual
argument 1 and returns the pointer to the first string in the matrix. It is the value of this pointer that will
be printed by the 'printf()' function though it expects a value of the float-type.
This is the correct code:
t.printf("%10.5f, %10.5f, %10.5f,n
%10.5f, %10.5f, %10.5f,n
%10.5f, %10.5f, %10.5f)",
m[0][0], m[0][1], m[0][2],
m[1][0], m[1][1], m[1][2],
m[2][0], m[2][1], m[2][2]);
Examples of misprints found in code
A lot of programming errors are caused by misprints. Most of these errors are quickly detected at the
early stages of testing. But there are some defects of this kind that remain in code for a long time
causing troubles both to programmers and users.
You can make these errors much fewer using the PVS-Studio analyzer. It will find them before testing
starts, which will significantly reduce the cost of defect detection and elimination.
Example 1. Miranda IM project. Assignment inside IF.
void CIcqProto::handleUserOffline(BYTE *buf, WORD wLen)
{
...
else if (wTLVType = 0x29 && wTLVLen == sizeof(DWORD))
...
}
The error was found through the V560 diagnostic: A part of conditional expression is always true: 0x29.
icqoscar8 fam_03buddy.cpp 632
Because of a misprint, there is an assignment taking place inside the condition of the 'if' operator. This is
the correct condition: "if (wTLVType == 0x29 && wTLVLen == sizeof(DWORD))".
Example 2. ReactOS project. Assignment error.
BOOL WINAPI GetMenuItemInfoA(...)
{
...
mii->cch = mii->cch;
...
}
The error was found through the V570 diagnostic: The 'mii->cch' variable is assigned to itself. user32
menu.c 4347
The value of the variable is assigned to itself. The programmer apparently intended to write it in this
way: "mii->cch = miiW->cch;".
Example 3. Clang project. Object name misprinted.
static Value *SimplifyICmpInst(...) {
...
case Instruction::Shl: {
bool NUW =
LBO->hasNoUnsignedWrap() && LBO->hasNoUnsignedWrap();
bool NSW =
LBO->hasNoSignedWrap() && RBO->hasNoSignedWrap();
...
}
The error was found through the V501 diagnostic: There are identical sub-expressions 'LBO-
>hasNoUnsignedWrap ()' to the left and to the right of the '&&' operator. LLVMAnalysis
instructionsimplify.cpp 1891
There is a misprint when using variables with similar names. In the first line, both LBO and RBO variables
must be used. This is the correct code:
bool NUW = LBO->hasNoUnsignedWrap() && RBO->hasNoUnsignedWrap();
Example 4. Notepad++ project. Incorrect state test.
bool _isPointXValid;
bool _isPointYValid;
...
bool isPointValid() {
return _isPointXValid && _isPointXValid;
};
The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to
the right of the '&&' operator. _isPointXValid && _isPointXValid
The name '_isPointXValid' is used twice. The function must actually return this code: "_isPointXValid &&
_isPointYValid".
Example 5. StrongDC++ project. Unsuccessful check of rn.
static void getContentLengthAndHeaderLength(...)
{
...
while(line[linelen] != 'r' && line[linelen] != 'r')
...
}
The error was found through the V501 diagnostic: There are identical sub-expressions 'line [linelen] !=
'r'' to the left and to the right of the '&&' operator. miniupnpc miniupnpc.c 153
Because of a misprint, presence of the 'r' character is checked twice. Actually presence of the 'n'
character must be checked too.
Example 6. G3D Content Pak project. A closing parenthesis in a wrong place.
bool Matrix4::operator==(const Matrix4& other) const {
if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
return true;
}
...
}
The error was found through the V575 diagnostic: The 'memcmp' function processes '0' elements.
Inspect the 'third' argument. graphics3D matrix4.cpp 269
One closing parenthesis is in a wrong place. It turns out that the size of the memory area being
compared is calculated by the "sizeof(Matrix4) == 0" expression. This expression always has the 'false'
result. Then 'false' turns into an integer value equal to 0. This is the correct code:
if (memcmp(this, &other, sizeof(Matrix4)) == 0) {
Example 7. QT project. Error of structure member copying.
PassRefPtr<Structure>
Structure::getterSetterTransition(Structure* structure)
{
...
transition->m_propertyStorageCapacity =
structure->m_propertyStorageCapacity;
transition->m_hasGetterSetterProperties =
transition->m_hasGetterSetterProperties;
transition->m_hasNonEnumerableProperties =
structure->m_hasNonEnumerableProperties;
transition->m_specificFunctionThrashCount =
structure->m_specificFunctionThrashCount;
...
}
The error was found through the V570 diagnostic: The 'transition->m_hasGetterSetterProperties'
variable is assigned to itself. QtScript structure.cpp 512
It is not easy to find an error looking at this code. But it is there. The field 'm_hasGetterSetterProperties'
is copied into itself. This is the correct code:
transition->m_hasGetterSetterProperties =
structure->m_hasGetterSetterProperties;
Example 8. Apache HTTP Server project. Extra sizeof operator.
PSECURITY_ATTRIBUTES GetNullACL(void)
{
PSECURITY_ATTRIBUTES sa;
sa = (PSECURITY_ATTRIBUTES)
LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));
...
}
The error was found through the V568 diagnostic: It's odd that the argument of sizeof() operator is the
'sizeof (SECURITY_ATTRIBUTES)' expression. libhttpd util_win32.c 115
The field 'nLength' must contain the size of the 'SECURITY_ATTRIBUTES' structure. There is a misprint in
the code: the 'sizeof' operator is used twice. As a result, the field 'nLength' stores a size of the 'size_t'
type. This is the correct code:
sa->nLength = sizeof(SECURITY_ATTRIBUTES);
Example 9. FCE Ultra project. Double variable declaration.
int iNesSaveAs(char* name)
{
...
fp = fopen(name,"wb");
int x = 0;
if (!fp)
int x = 1;
...
}
The error was found through the V561 diagnostic: It's probably better to assign value to 'x' variable than
to declare it anew. Previous daclaration: ines.cpp, line 960. fceuxines.cpp 962
The 'x' variable must store information whether or not a file was opened successfully. Because of a
misprint, a new variable named 'x' is created and initialized instead of assigning 1 to the existing
variable. This is how the correct code must look:
if (!fp)
x = 1;
Example 10. Notepad++ project. Using && operator instead of &.
TCHAR GetASCII(WPARAM wParam, LPARAM lParam)
{
...
result=ToAscii(wParam,
(lParam >> 16) && 0xff, keys,&dwReturnedValue,0);
...
}
The error was found through the V560 diagnostic: A part of conditional expression is always true: 0xff.
notepadPlus babygrid.cpp 694
The "(lParam >> 16) && 0xff" expression is meaningless and is always equal to 1 (true). A misprint here
is in using the '&&' operator instead of '&'.
Example 11. WinDjView project. Incomplete condition.
inline bool IsValidChar(int c)
{
return c == 0x9 || 0xA || c == 0xD || c >= 0x20 &&
c <= 0xD7FF || c >= 0xE000 && c <= 0xFFFD ||
c >= 0x10000 && c <= 0x10FFFF;
}
The error was found through the V560 diagnostic: A part of conditional expression is always true: 0xA.
WinDjView xmlparser.cpp 45 False
The IsValidChar function always returns 'true'. Comparison is missing in one place because of a misprint:
"... || 0xA || ...".
Example 12. Fennec Media Project project. Extra semicolon.
int settings_default(void)
{
...
for(i=0; i<16; i++);
for(j=0; j<32; j++)
{
settings.conversion.equalizer_bands.boost[i][j] = 0.0;
settings.conversion.equalizer_bands.preamp[i] = 0.0;
}
}
The error was found through the V529 diagnostic: Odd semicolon ';' after 'for' operator. settings.c 483
All the C and C++ programmers know how dangerous an extra semicolon ';' is. Unfortunately, this
knowledge does not prevent them from making such misprints. There is an extra semicolon after the
first 'for' operator, which makes this program fragment unable to execute.
Example 13. QT project. Missing break operator.
int QCleanlooksStyle::pixelMetric(...)
{
...
case PM_SpinBoxFrameWidth:
ret = 3;
break;
case PM_MenuBarItemSpacing:
ret = 6;
case PM_MenuBarHMargin:
ret = 0;
break;
...
}
The error was found through the V519 diagnostic: The 'ret' variable is assigned values twice successively.
Perhaps this is a mistake. Check lines: 3765, 3767. QtGui qcleanlooksstyle.cpp 3767
This is a classic error - 'break' is missing inside the 'switch' operator. I think you do not need any further
comments here.
Example 14. Miranda IM project. Assignment instead of comparison.
int FindItem(...)
{
...
int ret;
ret=FindItem(hwnd,dat,hItem,
(struct ClcContact ** )&z,
(struct ClcGroup ** )&isv,NULL);
if (ret=0) {return (0);}
...
}
The error was found through the V559 diagnostic: Suspicious assignment inside the condition expression
of 'if' operator: ret = 0. clist_mw clcidents.c 179
There is a misprint inside the condition of the 'if' operator: '=' is written instead of '=='. The function will
handle the situation incorrectly when a certain item is not found.
Example 15. IPP Samples project. Incorrect index.
struct AVS_MB_INFO
{
...
Ipp8u refIdx[AVS_DIRECTIONS][4];
...
};
void AVSCompressor::GetRefIndiciesBSlice(void){
...
if (m_pMbInfo->predType[0] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0];
iRefNum += 1;
}
if (m_pMbInfo->predType[1] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1];
iRefNum += 1;
}
if (m_pMbInfo->predType[2] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2];
iRefNum += 1;
}
if (m_pMbInfo->predType[3] & predType)
{
m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30];
iRefNum += 1;
}
...
}
The error was found through the V557 diagnostic: Array overrun is possible. The '30' index is pointing
beyond array bound. avs_enc umc_avs_enc_compressor_enc_b.cpp 495
Consider this fragment: "m_pMbInfo->refIdx[dir][30]". Because of a misprint, number 30 is written
instead of index 3. By the way, this sample shows well how relative our division of errors into categories
is. This error might well be referred to the category "Errors of array and string handling". The division is
relative and is made to show diversity of errors the PVS-Studio analyzer can detect.
Example 16. ReactOS project. Misprint in a macro.
#define SWAP(a,b,c) c = a;
a = b;
a = c
The error was found through the V519 diagnostic: The 'v2' variable is assigned values twice successively.
Perhaps this is a mistake. Check lines: 343, 343. win32k gradient.c 343
It is a rather funny misprint in a macro intended to swap values in two variables. Look closely at the code
and you will see what I mean. This is the correct code:
#define SWAP(a,b,c) c = a;
a = b;
b = c
Example 17. Quake-III-Arena project. Misprint. Comma instead of multiplication operator.
void Q1_AllocMaxBSP(void)
{
...
q1_allocatedbspmem +=
Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t);
...
q1_allocatedbspmem +=
Q1_MAX_MAP_EDGES , sizeof(q1_dedge_t);
...
q1_allocatedbspmem +=
Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short);
...
}
The error has been found with rule V521: Such expressions using the ',' operator are dangerous. Make
sure the expression is correct. bspc l_bsp_q1.c 136
It's a funny misprint. Look at the line in the middle of the code. ',' written instead of '*'. As a result, the
'sizeof(q1_dedge_t)' value is always added to the 'q1_allocatedbspmem' variable. I have no suggestions
how this misprint could have occurred.
Example 18. LibXml project. Misprint =+.
static int
xmlXPathCompOpEvalFirst(...)
{
...
total += xmlXPathCompOpEvalFirst(...);
...
total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
...
}
The error has been found with rule V588: The expression of the 'A =+ B' kind is utilized. Consider
reviewing it, as it is possible that 'A += B' was meant. libxml xpath.c 12676
In one place, "=+" is written instead of "+=" by mistake. They look similar but the result is quite different.
Such errors are rather difficult to find just reviewing the code.
Many errors in software are caused by misprints. There are much more errors of this kind than
programmers think. We could go on and on in this section but we decide to stop at the 18-th example at
last.
Incorrect use of base functions and classes
Example 1. Fennec Media Project. Two terminal nulls absent.
int JoiningProc(HWND hwnd,UINT uMsg,
WPARAM wParam,LPARAM lParam)
{
...
OPENFILENAME lofn;
memset(&lofn, 0, sizeof(lofn));
...
lofn.lpstrFilter = uni("All Files (*.*)0*.*");
...
}
The error was found through the V540 diagnostic: Member 'lpstrFilter' should point to string terminated
by two 0 characters. base windows.c 5309
In Windows API there are structures in which pointers to strings must end with two null characters. It is
that very kind of string the 'lpstrFilter' member in the OPENFILENAME structure points to.
Description of 'lpstrFilter' in MSDN:
LPCTSTR
A buffer containing pairs of null-terminated filter strings. The last string in the buffer must be terminated
by two NULL characters.
If you forget to write an additional null at the end, the dialogue of file handling may contain garbage in
the filter fields. This is the correct code:
lofn.lpstrFilter = uni("All Files (*.*)0*.*0");
Example 2. TortoiseSVN project. Incorrect use of 'remove' function.
STDMETHODIMP CShellExt::Initialize(....)
{
...
ignoredprops = UTF8ToWide(st.c_str());
// remove all escape chars ('')
std::remove(ignoredprops.begin(), ignoredprops.end(), '');
break;
...
}
The error was found through the V530 diagnostic: The return value of function 'remove' is required to
be utilized. contextmenu.cpp 442
The std::remove function does not remove items from the container. It only shifts the items and returns
the iterator to the beginning of trash. Assume we have a vector<int> container that contains items
1,2,3,1,2,3,1,2,3. If we execute the code "remove( v.begin(), v.end(), 2 )", the container will contain
items 1,3,1,3,X,X,X, where X is some trash. The function will return the iterator to the first trash item, so
if we want to remove these trash items, we need to write the code: "v.erase(remove(v.begin(), v.end(),
2), v.end())".
Example 3. TortoiseSVN project. Using 'empty' function instead of 'clear'.
CMailMsg& CMailMsg::SetFrom(string sAddress,
string sName)
{
if (initIfNeeded())
{
// only one sender allowed
if (m_from.size())
m_from.empty();
m_from.push_back(TStrStrPair(sAddress,sName));
}
return *this;
}
The error was found through the V530 diagnostic: The return value of function 'empty' is required to be
utilized. mailmsg.cpp 40
The error here is this: the vector::empty() function is called by mistake instead of vector::clear(), and the
array's contents remain the same. It is a very frequent error because the words 'clear' and 'empty' are
rather close in meaning, and you might easily mix them up.
Example 4. WinMerge project. Using 'empty' function instead of 'clear'.
void CDirView::GetItemFileNames(int sel,
String& strLeft, String& strRight) const
{
UINT_PTR diffpos = GetItemKey(sel);
if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS)
{
strLeft.empty();
strRight.empty();
}
else
{
...
}
}
The error was found through the V530 diagnostic: The return value of function 'empty' is required to be
utilized WinMerge DirActions.cpp 1307, 1308
Again, the reason is in using the empty() function instead of clear(). We could cite examples of such
errors from other projects as well: InstantVNC, IPP Samples, Chromium, Intel AMT SDK, etc.
Unfortunately, all these samples are alike, and there is nothing interesting about examining them. But
trust me, you can see these defects in serious projects developed by professional programmers.
Example 5. Pixie project. Using 'alloca' function inside loops.
inline void triangulatePolygon(...) {
...
for (i=1;i<nloops;i++) {
...
do {
...
do {
...
CTriVertex *snVertex =
(CTriVertex *)alloca(2*sizeof(CTriVertex));
...
} while(dVertex != loops[0]);
...
} while(sVertex != loops[i]);
...
}
...
}
The error was found through the V505 diagnostic: The 'alloca' function is used inside the loop. This can
quickly overflow stack. ri polygons.cpp 1120
The alloca function allocates memory inside the stack, so calling it many times inside the loop body may
suddenly cause a stack overflow. And we have several nested loops here. This code may exhaust stack
memory very quickly.
Example 6. Miranda IM project. Arguments mixed up.
static BOOL ImageArray_Alloc(LP_IMAGE_ARRAY_DATA iad, int size)
{
...
memset(&iad->nodes[iad->nodes_allocated_size],
(size_grow - iad->nodes_allocated_size) *
sizeof(IMAGE_ARRAY_DATA_NODE),
0);
...
}
The error was found through the V575 diagnostic: Function receives an odd argument. clist_modern
modern_image_array.cpp 59
The 'memset' function handles 0 items, i.e. actually does nothing. The reason is in mixed up arguments.
This is how the correct call of the memset function should be written:
memset(&iad->nodes[iad->nodes_allocated_size],
0,
(size_grow - iad->nodes_allocated_size) *
sizeof(IMAGE_ARRAY_DATA_NODE));
Examples of meaningless code
Example 1. IPP Samples project. Incomplete condition.
void lNormalizeVector_32f_P3IM(Ipp32f *vec[3],
Ipp32s* mask, Ipp32s len)
{
Ipp32s i;
Ipp32f norm;
for(i=0; i<len; i++) {
if(mask<0) continue;
norm = 1.0f/sqrt(vec[0][i]*vec[0][i]+
vec[1][i]*vec[1][i]+vec[2][i]*vec[2][i]);
vec[0][i] *= norm; vec[1][i] *= norm; vec[2][i] *= norm;
}
}
The error was found through the V503 diagnostic: This is a nonsensical comparison: pointer < 0.
ipprsample ippr_sample.cpp 501
I do not know how it happened, but there are 3 characters "[i]" missing in this code. As a result, the code
performs a meaningless check that the pointer is below zero instead of checking the mask array.
The correct check should be written in this way: if(mask[i] < 0).
Example 2. Pc Ps2 Emulator project. Incorrect switch.
LRESULT CALLBACK IOP_DISASM(...)
{
...
switch(LOWORD(wParam))
{
case (IDOK || IDCANCEL):
EndDialog(hDlg,TRUE);
return(TRUE);
break;
}
...
}
The error was found through the V560 diagnostic: A part of conditional expression is always true: 2.
pcsx2 debugger.cpp 321
This code does not have any meaning. The programmer must have intended to write it this way:
switch(LOWORD(wParam))
{
case IDOK: //no break
case IDCANCEL:
EndDialog(hDlg,TRUE);
return(TRUE);
break;
}
Example 3. CPU Identifying Tool project. A too strict condition.
void projillum(short* wtab, int xdots, int ydots, double dec)
{
...
s = sin(-dtr(dec));
x = -s * sin(th);
y = cos(th);
...
lon = (y == 0 && x == 0) ? 0.0 : rtd(atan2(y, x));
}
The error was found through the V550 diagnostic: An odd precise comparison: x == 0. It's probably
better to use a comparison with defined precision: fabs(A - B) '<' Epsilon. clock_dll sunalgo.cpp 155
It is strange to expect that the result will be strictly 0 after executing all these complex calculations using
'sin' and 'cos' functions. Most likely, there must be comparison to be performed with certain accuracy.
Example 4. Lugaru. Double assignment.
int Game::DrawGLScene(void)
{
...
radius=fast_sqrt(maxdistance);
radius=110;
...
}
The error was found through the V519 diagnostic: The 'radius' object is assigned values twice
successively. Perhaps this is a mistake. Lugaru gamedraw.cpp 1505
The programmer must have deliberately written value 110 into the 'radius' variable for the sake of
experiment and then forgot to remove this line. As a result, we have a meaningless and maybe even
invalid code.
Example 5. QT project. Duplicated check.
Q3TextCustomItem* Q3TextDocument::parseTable(...)
{
...
while (end < length
&& !hasPrefix(doc, length, end, QLatin1String("</td"))
&& !hasPrefix(doc, length, end, QLatin1String("<td"))
&& !hasPrefix(doc, length, end, QLatin1String("</th"))
&& !hasPrefix(doc, length, end, QLatin1String("<th"))
&& !hasPrefix(doc, length, end, QLatin1String("<td"))
&& !hasPrefix(doc, length, end, QLatin1String("</tr"))
&& !hasPrefix(doc, length, end, QLatin1String("<tr"))
&& !hasPrefix(doc, length, end, QLatin1String("</table"))) {
...
}
The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to
the right of the '&&' operator. Qt3Support q3richtext.cpp 6978
Presence of the "<td" prefix is checked twice in the condition. It is meaningless. Maybe it is an extra
check or there should be some other prefix instead of the second "<td".
Example 6. Audacity project. Strange check.
int sf_error (SNDFILE *sndfile)
{
...
if (!sndfile)
{
if (sf_error != 0)
return sf_errno;
return 0;
} ;
...
}
The error was found through the V516 diagnostic: Consider inspecting an odd expression. Non-null
function pointer is compared to null: 'sf_error != 0'. libsndfile sndfile.c 491
The "sf_error != 0" check always returns true, since 'sf_error' is the name of the function in which the
code is executed.
Example 7. IPP Samples project. Strange code inside a loop.
static IppStatus mp2_HuffmanTableInitAlloc(Ipp32s *tbl, ...)
{
...
for (i = 0; i < num_tbl; i++) {
*tbl++;
}
...
}
The error was found through the V532 diagnostic: Consider inspecting the statement of '*pointer++'
pattern. Probably meant: '(*pointer)++'. mpeg2_dec umc_mpeg2_dec.cpp 59
The loop body is probably incomplete because it is meaningless in the current form.
Always true or always false conditions
It is a very large and widely-spread type of errors. These errors also vary greatly depending on the
importance level. To non-dangerous errors we may refer incorrect conditions in ASSERT that actually do
not check anything. To dangerous errors, incorrect checks of buffer size or index size are referred.
Example 1. Shareaza project. Value range of char type.
void CRemote::Output(LPCTSTR pszName)
{
...
CHAR* pBytes = new CHAR[ nBytes ];
hFile.Read( pBytes, nBytes );
...
if ( nBytes > 3 && pBytes[0] == 0xEF &&
pBytes[1] == 0xBB && pBytes[2] == 0xBF )
{
pBytes += 3;
nBytes -= 3;
bBOM = true;
}
...
}
The error was found through the V547 diagnostic: Expression 'pBytes [ 0 ] == 0xEF' is always false. The
value range of signed char type: [-128, 127]. Shareaza remote.cpp 350
In this code, the 'TCHAR' type is the 'char' type. The value range of char is from -128 to 127 inclusive.
Value 0xEF in the variable of the char type is nothing else than number -17. When comparing the char
variable with number 0xEF, its type is extended up to the 'int' type. But the value still lies inside the
range [-128..127]. The "pBytes[0] == 0xEF" ("-17 == 0xEF") condition is always false, and the program
does not work as intended.
This is the correct comparison:
if ( nBytes > 3 && pBytes[0] == TCHAR(0xEF) &&
pBytes[1] == TCHAR(0xBB) &&
pBytes[2] == TCHAR(0xBF) )
Example 2. TortoiseSVN project. Value range of char type.
BOOL TortoiseBlame::OpenFile(const TCHAR *fileName)
{
...
// check each line for illegal utf8 sequences.
// If one is found, we treat
// the file as ASCII, otherwise we assume
// an UTF8 file.
char * utf8CheckBuf = lineptr;
while ((bUTF8)&&(*utf8CheckBuf))
{
if ((*utf8CheckBuf == 0xC0)||
(*utf8CheckBuf == 0xC1)||
(*utf8CheckBuf >= 0xF5))
{
bUTF8 = false;
break;
}
...
}
...
}
The error was found through the V547 diagnostic: Expression '* utf8CheckBuf == 0xC0' is always false.
The value range of signed char type: [-128, 127]. tortoiseblame.cpp 310
While the defect in the previous example seems to be caused through mere inattention, in this case it is
not so. Here is another identical example where a condition is always false. This is a very widely-spread
type of errors in various projects.
Example 3. VirtualDub project. Unsigned type is always >= 0.
typedef unsigned short wint_t;
...
void lexungetc(wint_t c) {
if (c < 0)
return;
g_backstack.push_back(c);
}
The error was found through the V547 diagnostic: Expression 'c < 0' is always false. Unsigned type value
is never < 0. Ami lexer.cpp 225
The "c < 0" condition is always false because the variable of the unsigned type is always above or equal
to 0.
Example 4. Swiss-Army Knife of Trace project. Socket handling.
static UINT_PTR m_socketHandle;
void TTrace::LoopMessages(void)
{
...
// Socket creation
if ( (m_socketHandle = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
continue;
}
...
}
The error was found through the V547 diagnostic: Expression '(m_socketHandle = socket (2, 1, 0)) < 0' is
always false. Unsigned type value is never < 0. Vs8_Win_Lib tracetool.cpp 871
An attempt to check that a socket was created successfully is performed incorrectly. If a socket cannot
be created, this situation is not handled in any way. To make the check work correctly, we should use
the INVALID_SOCKET constant:
m_socketHandle = socket(AF_INET,SOCK_STREAM, 0);
if (m_socketHandle == INVALID_SOCKET)
...
Example 5. Chromium project. Time handling.
IdleState CalculateIdleState(...) {
...
DWORD current_idle_time = 0;
...
// Will go -ve if we have been idle for
// a long time (2gb seconds).
if (current_idle_time < 0)
current_idle_time = INT_MAX;
...
}
The error was found through the V547 diagnostic: Expression 'current_idle_time < 0' is always false.
Unsigned type value is never < 0. browser idle_win.cc 23
To handle time, a variable of the unsigned type is used. As a result, check of too large values does not
work. This is the correct code:
if (current_idle_time > INT_MAX)
current_idle_time = INT_MAX;
Example 6. ICU project. Error in condition.
U_CDECL_BEGIN static const char* U_CALLCONV
_processVariableTop(...)
{
...
if(i == locElementCapacity &&
(*string != 0 || *string != '_'))
{
*status = U_BUFFER_OVERFLOW_ERROR;
}
...
}
The error was found through the V547 diagnostic: Expression '*string != 0 || *string != '_'' is always true.
Probably the '&&' operator should be used here. icui18n ucol_sit.cpp 242
The condition contains a logical error. The "(*string != 0 || *string != '_')" subexpression is always true. It
is impossible that one and the same string character is not equal to 0 and '_' at a time.
Example 7. QT project. Dangerous loop.
bool equals( class1* val1, class2* val2 ) const{
{
...
size_t size = val1->size();
...
while ( --size >= 0 ){
if ( !comp(*itr1,*itr2) )
return false;
itr1++;
itr2++;
}
...
}
The error was found through the V547 diagnostic: Expression '--size >= 0' is always true. Unsigned type
value is always >= 0. QtCLucene arrays.h 154
The (--size >= 0) condition is always true, since the size variable has the unsigned type. It means that if
two sequences being compared are alike, we will get an overflow that will in its turn cause Access
Violation or other program failures.
This is the correct code:
for (size_t i = 0; i != size; i++){
if ( !comp(*itr1,*itr2) )
return false;
itr1++;
itr2++;
}
Example 8. MySQL project. Error in condition.
enum enum_mysql_timestamp_type
str_to_datetime(...)
{
...
else if (str[0] != 'a' || str[0] != 'A')
continue; /* Not AM/PM */
...
}
The error was found through the V547 diagnostic: Expression 'str [0] != 'a' || str [0] != 'A'' is always true.
Probably the '&&' operator should be used here. clientlib my_time.c 340
The condition is always true because the character is always either not equal to 'a' or to 'A'. This is the
correct check:
else if (str[0] != 'a' && str[0] != 'A')
Example 9. QT project. Incorrect count of references.
STDMETHODIMP QEnumPins::QueryInterface(const IID &iid,void **out)
{
...
if (S_OK)
AddRef();
return hr;
}
The error was found through the V545 diagnostic: Such conditional expression of 'if' operator is
incorrect for the HRESULT type value '(HRESULT) 0L'. The SUCCEEDED or FAILED macro should be used
instead. phonon_ds9 qbasefilter.cpp 60
The check condition is represented by the S_OK constant. Since S_OK is 0, the AddRef() function will
never be called. This is how this check must look: if (hr == S_OK).
Example 10. TickerTape project. Incorrect tornado.
void GetWindAtSingleTornado(...)
{
...
if(radius < THRESH * 5)
*yOut = THRESH * 10 / radius;
else if (radius < THRESH * 5)
*yOut = -3.0f / (THRESH * 5.0f) *
(radius - THRESH * 5.0f) + 3.0f;
else
*yOut = 0.0f;
...
}
The error was found through the V517 diagnostic: The use of 'if (A) {...} else if (A) {...}' pattern was
detected. There is a probability of logical error presence. TickerTape wind.cpp 118
The second condition is always false. The reason is that the first condition coincides with the second.
There must be a misprint here.
Example 11. Apache HTTP Server project. Error of socket handling in Windows.
typedef UINT_PTR SOCKET;
static unsigned int __stdcall win9x_accept(void * dummy)
{
SOCKET csd;
...
do {
clen = sizeof(sa_client);
csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
} while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
...
}
The error was found through the V547 diagnostic: Expression 'csd < 0' is always false. Unsigned type
value is never < 0. libhttpd child.c 404
Socket handling errors very often emerge in crossplatform programs built under Windows. In Linux,
socket descriptors are represented by the signed type, while in Windows it is the unsigned type.
Programmers often forget about this and check the error status by comparing the value to 0. This is
incorrect; you must use specialized constants.
Example 12. QT project. Misprint in comparisons.
QStringList ProFileEvaluator::Private::values(...)
{
...
else if (ver == QSysInfo::WV_NT)
ret = QLatin1String("WinNT");
else if (ver == QSysInfo::WV_2000)
ret = QLatin1String("Win2000");
else if (ver == QSysInfo::WV_2000) <<--
ret = QLatin1String("Win2003");
else if (ver == QSysInfo::WV_XP)
ret = QLatin1String("WinXP");
...
}
The error was found through the V517 diagnostic: The use of 'if (A) {...} else if (A) {...}' pattern was
detected. There is a probability of logical error presence. Check lines: 2303, 2305. lrelease
profileevaluator.cpp 2303
In the string we have marked, there must be the text "ver == QSysInfo::WV_2003". Because of this error,
the "ret = QLatin1String("Win2003")" statement will never be executed.
Code vulnerabilities
Of course, errors leading to code vulnerabilities are actually misprints, incorrect conditions and incorrect
array handling. But we decided to single out certain errors into a separate group because they relate to
the notion of software vulnerabilities. An intruder, using such errors, can try to disturb program
operation, perform an attack to gain extended rights or carry out any other actions he/she needs.
Example 1. Ultimate TCP/IP project. Incorrect check of an empty string.
char *CUT_CramMd5::GetClientResponse(LPCSTR ServerChallenge)
{
...
if (m_szPassword != NULL)
{
...
if (m_szPassword != '0')
{
...
}
The error was found through the V528 diagnostic: It is odd that pointer to 'char' type is compared with
the '0' value. Probably meant: *m_szPassword != '0'. UTMail ut_crammd5.cpp 333
This code fragment must check that the pointer to the password is not equal to NULL and that the string
is not empty. But instead, the code checks twice that the pointer is not equal to NULL. The check of the
string does not work. The "if (m_szPassword != '0')" condition was intended to check that there is a
terminal null in the very beginning of the string, which means that the string is empty. But a pointer
dereferencing operation is missing here, and it is the pointer itself which is compared to zero. This is the
correct code:
if (m_szPassword != NULL)
{
...
if (*m_szPassword != '0')
Example 2. Chromium project. Null pointer handling.
bool ChromeFrameNPAPI::Invoke(...)
{
ChromeFrameNPAPI* plugin_instance =
ChromeFrameInstanceFromNPObject(header);
if (!plugin_instance &&
(plugin_instance->automation_client_.get()))
return false;
...
}
The error was found through the V522 diagnostic: Dereferencing of the null pointer 'plugin_instance'
might take place. Check the logical condition. chrome_frame_npapi chrome_frame_npapi.cc 517
The condition that checks the null pointer is written incorrectly. As a result, we have a segmentation
error. This is the correct code:
if (plugin_instance &&
(plugin_instance->automation_client_.get()))
return false;
Example 3. SMTP Client with SSL/TLS project. Incomplete buffer clearing.
void MD5::finalize () {
...
uint1 buffer[64];
...
// Zeroize sensitive information
memset (buffer, 0, sizeof(*buffer));
...
}
The error was found through the V512 diagnostic: A call of the 'memset' function will lead to a buffer
overflow or underflow. CSmtp md5.cpp 212
For security purposes, the function tries to clear the buffer containing sensitive information. But it fails.
Only the first byte will be cleared in the buffer. The error is this: the 'sizeof' operator calculates the size
of the 'uint1' type instead of buffer. This is the correct code:
memset (buffer, 0, sizeof(buffer));
Generally, errors of incomplete memory clearing are rather frequent. Consider some other cases like
this.
Example 4. Chromium. Incomplete buffer clearing.
void Time::Explode(..., Exploded* exploded) const {
...
ZeroMemory(exploded, sizeof(exploded));
...
}
The error was found through the V512 diagnostic: A call of the 'memset' function will lead to underflow
of the buffer '(exploded)'. base time_win.cc 227
The ZeroMemory function clears only part of the Exploded structure. The reason is that the 'sizeof'
operator returns the pointer size. To fix the error, we must dereference the pointer:
ZeroMemory(exploded, sizeof(*exploded));
Example 5. Apache HTTP Server project. Incomplete buffer clearing.
#define MEMSET_BZERO(p,l) memset((p), 0, (l))
void apr__SHA256_Final(..., SHA256_CTX* context) {
...
MEMSET_BZERO(context, sizeof(context));
...
}
The error was found through the V512 diagnostic: A call of the 'memset' function will lead to underflow
of the buffer '(context)'. apr sha2.c 560
The error is completely identical to the previous one. The 'sizeof' operator calculates the pointer size. To
fix it, we must write: "sizeof(*context)".
Example 6. Miranda IM project. Incorrect string handling.
static char *_skipblank(char * str)
{
char * endstr=str+strlen(str);
while ((*str==' ' || *str=='t') && str!='0') str++;
while ((*endstr==' ' || *endstr=='t') &&
endstr!='0' && endstr<str)
endstr--;
...
}
The error was found through the diagnostics: V528 It is odd that pointer to 'char' type is compared with
the '0' value. Probably meant: *str != '0'. clist_modern modern_skinbutton.cpp 282
V528 It is odd that pointer to 'char' type is compared with the '0' value. Probably meant: *endstr !=
'0'. clist_modern modern_skinbutton.cpp 283
This code is rather dangerous because it incorrectly determines the string end. It may cause a string
overflow and, as a consequence, an Access Violation exception. The error lies here: "str!='0'" and here:
"endstr!='0'". A pointer dereferencing operation is missing. This is the correct code:
while ((*str==' ' || *str=='t') && *str!='0') str++;
while ((*endstr==' ' || *endstr=='t') &&
*endstr!='0' && endstr<str)
endstr--;
Example 7. PNG library project. Accidental pointer clearing.
png_size_t
png_check_keyword(png_structp png_ptr, png_charp key,
png_charpp new_key)
{
...
if (key_len > 79)
{
png_warning(png_ptr, "keyword length must be 1 - 79 characters");
new_key[79] = '0';
key_len = 79;
}
...
}
The error was found through the V527 diagnostic: It is odd that the '0' value is assigned to 'char' type
pointer. Probably meant: *new_key [79] = '0'. graphics3D pngwutil.c 1283
This sample demonstrates a mistake when the programmer accidentally clears the pointer instead of
truncating the string length. The point is that 'new_key' is a pointer to a string. And it means that we
should write our code as follows to truncate it to 79 characters:
(*new_key)[79] = '0';
Example 8. Intel AMT SDK project. Unverified user name.
static void
wsman_set_subscribe_options(...)
{
...
if (options->delivery_certificatethumbprint ||
options->delivery_password ||
options->delivery_password) {
...
}
The error was found through the V501 diagnostic: There are identical sub-expressions 'options-
>delivery_password' to the left and to the right of the '||' operator. OpenWsmanLib wsman-client.c 631
Because of the developer's inattention, presence of password is checked twice, while presence of user
name is not checked at all. This is the correct code:
if (options->delivery_certificatethumbprint ||
options->delivery_username ||
options->delivery_password) {
Example 9. Ultimate TCP/IP project. Incorrect handling of empty strings.
void CUT_StrMethods::RemoveCRLF(LPSTR buf)
{
// v4.2 changed to size_t
size_t len, indx = 1;
if(buf != NULL){
len = strlen(buf);
while((len - indx) >= 0 && indx <= 2) {
if(buf[len - indx] == 'r' ||
buf[len - indx] == 'n')
buf[len - indx] = 0;
++indx;
}
}
}
The error was found through the V547 diagnostic: Expression '(len - indx) >= 0' is always true. Unsigned
type value is always >= 0. UTDns utstrlst.cpp 58
The "len - indx" expression has the unsigned type 'size_t' and is always >= 0. Let's look what it will result
in, if we send an empty string to the input.
If the string is empty, then: len = 0, indx = 1.
The len - indx expression is equal to 0xFFFFFFFFu.
Since 0xFFFFFFFFu > 0 and indx <= 2, an array access is performed
"buf[len - indx]".
The "buf[0xFFFFFFFFu]" operation will cause Access Violation.
Example 10. Miranda IM project. Underflow protection does not work.
void Append( PCXSTR pszSrc, int nLength )
{
...
UINT nOldLength = GetLength();
if (nOldLength < 0)
{
// protects from underflow
nOldLength = 0;
}
...
}
The error was found through the V547 diagnostic: Expression 'nOldLength < 0' is always false. Unsigned
type value is never < 0. IRC mstring.h 229
The check "if (nOldLength < 0)" does not work since the nOldLength variable has the unsigned type.
Example 11. Apache HTTP Server project. Incorrect handling of negative values.
typedef size_t apr_size_t;
APU_DECLARE(apr_status_t) apr_memcache_getp(...)
{
...
apr_size_t len = 0;
...
len = atoi(length);
...
if (len < 0) {
*new_length = 0;
*baton = NULL;
}
else {
...
}
}
The error was found through the V547 diagnostic: Expression 'len < 0' is always false. Unsigned type
value is never < 0. aprutil apr_memcache.c 814
The check "if (len < 0)" does not work because the 'len' variable has the unsigned type.
Example 12. Ultimate TCP/IP project. Incorrect condition of loop termination.
void CUT_StrMethods::RemoveSpaces(LPSTR szString) {
...
size_t loop, len = strlen(szString);
// Remove the trailing spaces
for(loop = (len-1); loop >= 0; loop--) {
if(szString[loop] != ' ')
break;
}
...
}
The error was found through the V547 diagnostic: Expression 'loop >= 0' is always true. Unsigned type
value is always >= 0. UTDns utstrlst.cpp 430
Suppose the whole string consists only of spaces. While searching the characters, the program will reach
the null item of the string, and the 'loop' variable will equal to zero. Then it will be decremented once
again. Since this variable is of unsigned type, its value will be 0xFFFFFFFFu or 0xFFFFFFFFFFFFFFFFu
(depending on the architecture). This value is 'naturally >= 0', and a new loop iteration will start. There
will be an attempt of memory access by szString[0xFFFFFFFFu] address - the consequences of this are
familiar to every C/C++ programmer.
Example 13. Crypto++ project. Private data clearing error.
void CAST256::Base::UncheckedSetKey(const byte *userKey,
unsigned int keylength, const NameValuePairs &)
{
AssertValidKeyLength(keylength);
word32 kappa[8];
...
memset(kappa, 0, sizeof(kappa));
}
The error has been found with rule V597: The compiler could delete the 'memset' function call, which is
used to flush 'kappa' buffer. The RtlSecureZeroMemory() function should be used to erase the private
data. cryptlib cast.cpp 293
The problem is in the memset() function. The arguments passed into the function are correct. If a
programmer looks how the Debug-version of this code works in the debugger, he/she won't notice the
trouble either. The error occurs in the Release version of the project. The data that should have been
cleared will remain in memory. The reason is that the compiler has the right to delete the call of the
memset() function during optimization, and this is what it does. If you want know why it happens, read
the article "Overwriting memory - why?".
Copy-Paste
Developers should not also underestimate Copy-Paste errors as well as common misprints. They are
very-very numerous. Programmers spend much time on debugging them.
Of course, misprints and Copy-Paste errors are similar, but there is a difference between them that
caused us to place them into different groups in this article. Misprints often result in using a wrong
variable instead of the needed one. And in the case of copy-paste, programmers simply forget to edit
copied and pasted lines.
Example 1. Fennec Media Project project. Mistake while handling array items.
void* tag_write_setframe(char *tmem,
const char *tid, const string dstr)
{
...
if(lset)
{
fhead[11] = '0';
fhead[12] = '0';
fhead[13] = '0';
fhead[13] = '0';
}
...
}
The error was found through the V525 diagnostic: The code containing the collection of similar blocks.
Check items '11', '12', '13', '13' in lines 716, 717, 718, 719. id3 editor.c 716
The four similar lines must have appeared in the code through the copy-paste method. When the
programmer started editing the indexes, he/she made a mistake that causes zero to be written into
'fhead[13] ' twice and not be written into 'fhead[14] '.
Example 2. MySQL project. Mistake while handling array items.
static int rr_cmp(uchar *a,uchar *b)
{
if (a[0] != b[0])
return (int) a[0] - (int) b[0];
if (a[1] != b[1])
return (int) a[1] - (int) b[1];
if (a[2] != b[2])
return (int) a[2] - (int) b[2];
if (a[3] != b[3])
return (int) a[3] - (int) b[3];
if (a[4] != b[4])
return (int) a[4] - (int) b[4];
if (a[5] != b[5])
return (int) a[1] - (int) b[5];
if (a[6] != b[6])
return (int) a[6] - (int) b[6];
return (int) a[7] - (int) b[7];
}
The error was found through the V525 diagnostic: The code containing the collection of similar blocks.
Check items '0', '1', '2', '3', '4', '1', '6' in lines 680, 682, 684, 689, 691, 693, 695. sql records.cc 680
It is not apparent at first sight, so let's single it out:
return (int) a[1] - (int) b[5];
Actually there must be the following code:
return (int) a[5] - (int) b[5];
Example 3. TortoiseSVN project. File name not corrected.
BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS)
{
return(GetInMemoryFileVersion(("DBGHELP.DLL"),
dwMS,
dwLS)) ;
}
BOOL GetDbgHelpVersion(DWORD &dwMS, DWORD &dwLS)
{
return(GetInMemoryFileVersion(("DBGHELP.DLL"),
dwMS,
dwLS)) ;
}
The error was found through the V524 diagnostic: It is odd that the 'GetDbgHelpVersion' function is fully
equivalent to the 'GetImageHlpVersion' function (SymbolEngine.h, line 98). symbolengine.h 105
The 'GetImageHlpVersion' function must have appeared through copying and pasting the
'GetInMemoryFileVersion' function. The error is this: the programmer forgot to fix the file name in the
copied and pasted function. This is the correct code:
BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS)
{
return(GetInMemoryFileVersion(("IMAGEHLP.DLL"),
dwMS,
dwLS)) ;
}
Example 4. Clang project. Identical function bodies.
MapTy PerPtrTopDown;
MapTy PerPtrBottomUp;
void clearBottomUpPointers() {
PerPtrTopDown.clear();
}
void clearTopDownPointers() {
PerPtrTopDown.clear();
}
The error was found through the V524 diagnostic: It is odd that the body of 'clearTopDownPointers'
function is fully equivalent to the body of 'clearBottomUpPointers' function (ObjCARC.cpp, line 1318).
LLVMScalarOpts objcarc.cpp 1322
The body of the clearBottomUpPointers function seems to be incorrect; this function should be written
as follows:
void clearBottomUpPointers() {
PerPtrBottomUp.clear();
}
Example 5. QT. Unsuccessful swap.
bool qt_testCollision(...)
{
...
t=x1; x1=x2; x2=t;
t=y1; x1=y2; y2=t;
...
}
The error was found through the V519 diagnostic: The 'x1' variable is assigned values twice successively.
Perhaps this is a mistake. Check lines: 2218, 2219. Qt3Support q3canvas.cpp 2219
The first line is absolutely correct and swaps values in the x1 and x2 variables. In the second line,
variables y1 and y2 must be swapped. This line is probably a copy of the previous one. All the 'x' letters
must be replaced with letters 'y'. Unfortunately, the programmer forgot to do that in one place: "...
x1=y2; ...".
Correct code:
t=x1; x1=x2; x2=t;
t=y1; y1=y2; y2=t;
Example 6. Crystal Space 3D SDK project. Identical subexpressions.
inline_ bool Contains(const LSS& lss)
{
return Contains(Sphere(lss.mP0, lss.mRadius)) &&
Contains(Sphere(lss.mP0, lss.mRadius));
}
The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to
the right of the '&&' operator. plgcsopcode icelss.h 69
The error is this: the 'lss.mP0.' variable is used twice here. There must be 'lss.mP1' in the first part of the
expression.
Example 7. Notepad++ project. Setting an incorrect style.
void KeyWordsStyleDialog::updateDlg()
{
...
Style & w1Style =
_pUserLang->_styleArray.getStyler(STYLE_WORD1_INDEX);
styleUpdate(w1Style, _pFgColour[0], _pBgColour[0],
IDC_KEYWORD1_FONT_COMBO, IDC_KEYWORD1_FONTSIZE_COMBO,
IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,
IDC_KEYWORD1_UNDERLINE_CHECK);
Style & w2Style =
_pUserLang->_styleArray.getStyler(STYLE_WORD2_INDEX);
styleUpdate(w2Style, _pFgColour[1], _pBgColour[1],
IDC_KEYWORD2_FONT_COMBO, IDC_KEYWORD2_FONTSIZE_COMBO,
IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,
IDC_KEYWORD2_UNDERLINE_CHECK);
Style & w3Style =
_pUserLang->_styleArray.getStyler(STYLE_WORD3_INDEX);
styleUpdate(w3Style, _pFgColour[2], _pBgColour[2],
IDC_KEYWORD3_FONT_COMBO, IDC_KEYWORD3_FONTSIZE_COMBO,
IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK,
IDC_KEYWORD3_UNDERLINE_CHECK);
Style & w4Style =
_pUserLang->_styleArray.getStyler(STYLE_WORD4_INDEX);
styleUpdate(w4Style, _pFgColour[3], _pBgColour[3],
IDC_KEYWORD4_FONT_COMBO, IDC_KEYWORD4_FONTSIZE_COMBO,
IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,
IDC_KEYWORD4_UNDERLINE_CHECK);
...
}
The error was found through the V525 diagnostic: The code containing the collection of similar blocks.
Check items '7', '7', '6', '7' in lines 576, 580, 584, 588
It is almost unreal to find this error by sight, so let's abridge the text to single out the most interesting
fragments:
styleUpdate(...
IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK,
...);
styleUpdate(...
IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK,
...);
styleUpdate(...
IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK, <<--
...);
styleUpdate(...
IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,
...);
By mistake, IDC_KEYWORD3_BOLD_CHECK is used instead of IDC_KEYWORD3_ITALIC_CHECK.
Example 8. ReactOS object. Choosing a wrong object.
void CardButton::DrawRect(HDC hdc, RECT *rect, bool fNormal)
{
...
HPEN hhi = CreatePen(0, 0, MAKE_PALETTERGB(crHighlight));
HPEN hsh = CreatePen(0, 0, MAKE_PALETTERGB(crShadow));
...
if(fNormal)
hOld = SelectObject(hdc, hhi);
else
hOld = SelectObject(hdc, hhi);
...
}
The error was found through the V523 diagnostic: The 'then' statement is equivalent to the 'else'
statement. cardlib cardbutton.cpp 83
The 'hsh' object is not used, while 'hhi' is used twice. This is the correct code:
if(fNormal)
hOld = SelectObject(hdc, hhi);
else
hOld = SelectObject(hdc, hsh);
Example 9. IPP Samples project. Incorrect check.
Status VC1VideoDecoder::ResizeBuffer()
{
...
if(m_pContext && m_pContext->m_seqLayerHeader &&
m_pContext->m_seqLayerHeader->heightMB &&
m_pContext->m_seqLayerHeader->heightMB)
...
}
The error was found through the V501 diagnostic: There are identical sub-expressions 'm_pContext-
>m_seqLayerHeader->heightMB' to the left and to the right of the '&&' operator. vc1_dec
umc_vc1_video_decoder.cpp 1347
Correct code:
if(m_pContext && m_pContext->m_seqLayerHeader &&
m_pContext->m_seqLayerHeader->heightMB &&
m_pContext->m_seqLayerHeader->widthMB)
Example 10. ReactOS project. Mistake in a variable name.
BOOL APIENTRY
GreStretchBltMask(...)
{
...
MaskPoint.x += DCMask->ptlDCOrig.x;
MaskPoint.y += DCMask->ptlDCOrig.x;
...
}
The error was found through the V537 diagnostic: Consider reviewing the correctness of 'x' item's usage.
win32k bitblt.c 670
This is a very good example where you can see that a line was copied and pasted. After that, the
programmer fixed the first name 'x' but forgot to fix the second. This is the correct code:
MaskPoint.x += DCMask->ptlDCOrig.x;
MaskPoint.y += DCMask->ptlDCOrig.y;
Late check of null pointers
C/C++ programmers have to check numerous pointers all the time to make sure that they are not equal
to zero. Since these checks are numerous, the chance to make a mistake is also big. It often happens
that a pointer is used first and only then is compared to NULL. Errors of this type reveal themselves very
rarely. Usually the program works correctly in the standard mode and fails only in case of a non-
standard situation. Instead of correctly processing a null pointer in normal mode, an Access Violation
will occur and an exception will be thrown.
Example 1. Quake-III-Arena project. Late check.
void Item_Paint(itemDef_t *item) {
vec4_t red;
menuDef_t *parent = (menuDef_t*)item->parent;
red[0] = red[3] = 1;
red[1] = red[2] = 0;
if (item == NULL) {
return;
}
...
}
The error has been found with rule V595: The 'item' pointer was utilized before it was verified against
nullptr. Check lines: 3865, 3869. cgame ui_shared.c 3865
The 'item' pointer is used first and only then is compared to NULL.
Example 2. LAME Ain't an MP3 Encoder project. Late check.
static int
check_vbr_header(PMPSTR mp, int bytes)
{
...
buf = buf->next;
pos = buf->pos;
if(!buf) return -1; /* fatal error */
...
}
The error has been found with rule V595: The 'buf' pointer was utilized before it was verified against
nullptr. Check lines: 226, 227. mpglib interface.c 226
If 'buf' equals NULL, an exception will be thrown instead of returning the error code. And if exceptions
are not used, the program will crash.
Example 3. daoParanoia library project. Late check.
static long i_stage2_each(root_block *root,
v_fragment *v, void(*callback)(long,int))
{
cdrom_paranoia *p=v->p;
long dynoverlap=p->dynoverlap/2*2;
if (!v || !v->one) return(0);
...
}
The error has been found with rule V595: The 'v' pointer was utilized before it was verified against
nullptr. Check lines: 532, 535. daoParanoia paranoia.c 532
The situation here is identical to the previous ones.
Example 4. TrinityCore project. Late check.
bool OnCheck(Player* player, Unit* /*target*/)
{
bool checkArea =
player->GetAreaId() == AREA_ARGENT_TOURNAMENT_FIELDS ||
player->GetAreaId() == AREA_RING_OF_ASPIRANTS ||
player->GetAreaId() == AREA_RING_OF_ARGENT_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_ALLIANCE_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_HORDE_VALIANTS ||
player->GetAreaId() == AREA_RING_OF_CHAMPIONS;
return player && checkArea && player->duel &&
player->duel->isMounted;
}
The error has been found with rule V595: The 'player' pointer was utilized before it was verified against
nullptr. Check lines: 310, 312. scripts achievement_scripts.cpp 310
As you can see from the "player && ..." condition, the 'player' pointer can be equal to zero. However,
this check, like in all the previous examples, is too late.
We could cite many examples of such errors, but they are all alike. If you have seen a couple of such
errors, be sure you've seen them all.
Miscellaneous
Example 1. Image Processing SDK project. Octal number.
inline
void elxLuminocity(const PixelRGBus& iPixel,
LuminanceCell< PixelRGBus >& oCell)
{
oCell._luminance = uint16(0.2220f*iPixel._red +
0.7067f*iPixel._blue + 0.0713f*iPixel._green);
oCell._pixel = iPixel;
}
inline
void elxLuminocity(const PixelRGBi& iPixel,
LuminanceCell< PixelRGBi >& oCell)
{
oCell._luminance = 2220*iPixel._red +
7067*iPixel._blue + 0713*iPixel._green;
oCell._pixel = iPixel;
}
The error was found through the V536 diagnostic: Be advised that the utilized constant value is
represented by an octal form. Oct: 0713, Dec: 459. IFF plugins pixelservices.inl 146
If you examine the second function, you will see that the programmer intended to use number 713, not
0713. Number 0713 is declared in the octal numeral system. You can easily forget about it if you seldom
use octal constants.
Example 2. IPP Samples project. One variable for two loops.
JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void)
{
...
for(c = 0; c < m_scan_ncomps; c++)
{
block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU)));
// skip any relevant components
for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++)
{
block += (DCTSIZE2*m_ccomp[c].m_nblocks);
}
...
}
The error was found through the V535 diagnostic: The variable 'c' is being used for this loop and for the
outer loop. jpegcodec jpegdec.cpp 4652
One and the same variable is used for the outer loop and the inner loop. As a result, this code will
handle only part of the data or cause an eternal loop.
Example 3. Quake-III-Arena project. Missing return.
static ID_INLINE int BigLong(int l)
{ LongSwap(l); }
The error has been found with rule V591: Non-void function should return a value. botlib q_shared.h
155
This code is written in C. It means that the compiler doesn't require that return should be necessarily
present. But it is really necessary here. However, the code can work well due to sheer luck. Everything
depends on what the EAX register contains. But it's just luck and nothing more. The function body
should have been written this way: { return LongSwap(l); }.
Example 4. Notepad++ project. Odd condition.
int Notepad_plus::getHtmlXmlEncoding(....) const
{
...
if (langT != L_XML && langT != L_HTML && langT == L_PHP)
return -1;
...
}
The error has been found with rule V590: Consider inspecting this expression. The expression is
excessive or contains a misprint. Notepad++ notepad_plus.cpp 853
Perhaps this error is just a misprint, but it also could have appeared during factoring. However, it is
obvious. The condition can be simplified: if (langT == L_PHP). It means that the code must have looked
this way:
if (langT != L_XML && langT != L_HTML && langT != L_PHP)
References
1. PVS-Studio Main Product Page. http://www.viva64.com/en/pvs-studio/
2. Download the fully functional trial. http://www.viva64.com/en/pvs-studio-download/
3. Buy PVS-Studio. http://www.viva64.com/en/order/
4. PVS-Studio Documentation. http://www.viva64.com/en/d/
5. Feedback. http://www.viva64.com/en/about-feedback/
6. Twitter. http://twitter.com/Code_Analysis

More Related Content

What's hot

Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.
Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.
Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.PVS-Studio
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckAndrey Karpov
 
How to avoid bugs using modern C++
How to avoid bugs using modern C++How to avoid bugs using modern C++
How to avoid bugs using modern C++PVS-Studio
 
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) projectPVS-Studio
 
PVS-Studio vs Chromium - Continuation
PVS-Studio vs Chromium - ContinuationPVS-Studio vs Chromium - Continuation
PVS-Studio vs Chromium - ContinuationPVS-Studio
 
Cppcheck and PVS-Studio compared
Cppcheck and PVS-Studio comparedCppcheck and PVS-Studio compared
Cppcheck and PVS-Studio comparedPVS-Studio
 
The CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitThe CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitAndrey Karpov
 
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 CodePVS-Studio
 
Mathematicians: Trust, but Verify
Mathematicians: Trust, but VerifyMathematicians: Trust, but Verify
Mathematicians: Trust, but VerifyAndrey Karpov
 
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible IssuesPVS-Studio
 
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 AnalyzerAndrey Karpov
 
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 CodeLitePVS-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 correctionAndrey Karpov
 
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 correctionPVS-Studio
 
Exploring Microoptimizations Using Tizen Code as an Example
Exploring Microoptimizations Using Tizen Code as an ExampleExploring Microoptimizations Using Tizen Code as an Example
Exploring Microoptimizations Using Tizen Code as an ExamplePVS-Studio
 
Dusting the globe: analysis of NASA World Wind project
Dusting the globe: analysis of NASA World Wind projectDusting the globe: analysis of NASA World Wind project
Dusting the globe: analysis of NASA World Wind projectPVS-Studio
 
LibRaw, Coverity SCAN, PVS-Studio
LibRaw, Coverity SCAN, PVS-StudioLibRaw, Coverity SCAN, PVS-Studio
LibRaw, Coverity SCAN, PVS-StudioAndrey Karpov
 
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ..."Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...PVS-Studio
 

What's hot (20)

Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.
Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.
Celebrating 30-th anniversary of the first C++ compiler: let's find bugs in it.
 
Picking Mushrooms after Cppcheck
Picking Mushrooms after CppcheckPicking Mushrooms after Cppcheck
Picking Mushrooms after Cppcheck
 
How to avoid bugs using modern C++
How to avoid bugs using modern C++How to avoid bugs using modern C++
How to avoid bugs using modern C++
 
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
 
Analyzing Firebird 3.0
Analyzing Firebird 3.0Analyzing Firebird 3.0
Analyzing Firebird 3.0
 
PVS-Studio vs Chromium - Continuation
PVS-Studio vs Chromium - ContinuationPVS-Studio vs Chromium - Continuation
PVS-Studio vs Chromium - Continuation
 
Cppcheck and PVS-Studio compared
Cppcheck and PVS-Studio comparedCppcheck and PVS-Studio compared
Cppcheck and PVS-Studio compared
 
The CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitThe CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGit
 
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
 
Mathematicians: Trust, but Verify
Mathematicians: Trust, but VerifyMathematicians: Trust, but Verify
Mathematicians: Trust, but Verify
 
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues64-Bit Code in 2015: New in the Diagnostics of Possible Issues
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
 
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
 
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
 
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
 
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
 
Exploring Microoptimizations Using Tizen Code as an Example
Exploring Microoptimizations Using Tizen Code as an ExampleExploring Microoptimizations Using Tizen Code as an Example
Exploring Microoptimizations Using Tizen Code as an Example
 
Dusting the globe: analysis of NASA World Wind project
Dusting the globe: analysis of NASA World Wind projectDusting the globe: analysis of NASA World Wind project
Dusting the globe: analysis of NASA World Wind project
 
LibRaw, Coverity SCAN, PVS-Studio
LibRaw, Coverity SCAN, PVS-StudioLibRaw, Coverity SCAN, PVS-Studio
LibRaw, Coverity SCAN, PVS-Studio
 
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ..."Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
 
Cppcheck
CppcheckCppcheck
Cppcheck
 

Viewers also liked

Prototyping is an attitude
Prototyping is an attitudePrototyping is an attitude
Prototyping is an attitudeWith Company
 
50 Essential Content Marketing Hacks (Content Marketing World)
50 Essential Content Marketing Hacks (Content Marketing World)50 Essential Content Marketing Hacks (Content Marketing World)
50 Essential Content Marketing Hacks (Content Marketing World)Heinz Marketing Inc
 
20 Ideas for your Website Homepage Content
20 Ideas for your Website Homepage Content20 Ideas for your Website Homepage Content
20 Ideas for your Website Homepage ContentBarry Feldman
 
10 Insightful Quotes On Designing A Better Customer Experience
10 Insightful Quotes On Designing A Better Customer Experience10 Insightful Quotes On Designing A Better Customer Experience
10 Insightful Quotes On Designing A Better Customer ExperienceYuan Wang
 
Learn BEM: CSS Naming Convention
Learn BEM: CSS Naming ConventionLearn BEM: CSS Naming Convention
Learn BEM: CSS Naming ConventionIn a Rocket
 
How to Build a Dynamic Social Media Plan
How to Build a Dynamic Social Media PlanHow to Build a Dynamic Social Media Plan
How to Build a Dynamic Social Media PlanPost Planner
 
SEO: Getting Personal
SEO: Getting PersonalSEO: Getting Personal
SEO: Getting PersonalKirsty Hulse
 

Viewers also liked (7)

Prototyping is an attitude
Prototyping is an attitudePrototyping is an attitude
Prototyping is an attitude
 
50 Essential Content Marketing Hacks (Content Marketing World)
50 Essential Content Marketing Hacks (Content Marketing World)50 Essential Content Marketing Hacks (Content Marketing World)
50 Essential Content Marketing Hacks (Content Marketing World)
 
20 Ideas for your Website Homepage Content
20 Ideas for your Website Homepage Content20 Ideas for your Website Homepage Content
20 Ideas for your Website Homepage Content
 
10 Insightful Quotes On Designing A Better Customer Experience
10 Insightful Quotes On Designing A Better Customer Experience10 Insightful Quotes On Designing A Better Customer Experience
10 Insightful Quotes On Designing A Better Customer Experience
 
Learn BEM: CSS Naming Convention
Learn BEM: CSS Naming ConventionLearn BEM: CSS Naming Convention
Learn BEM: CSS Naming Convention
 
How to Build a Dynamic Social Media Plan
How to Build a Dynamic Social Media PlanHow to Build a Dynamic Social Media Plan
How to Build a Dynamic Social Media Plan
 
SEO: Getting Personal
SEO: Getting PersonalSEO: Getting Personal
SEO: Getting Personal
 

Similar to 100 bugs in Open Source C/C++ projects

How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.PVS-Studio
 
Monitoring a program that monitors computer networks
Monitoring a program that monitors computer networksMonitoring a program that monitors computer networks
Monitoring a program that monitors computer networksAndrey Karpov
 
PVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's codePVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's codePVS-Studio
 
Monitoring a program that monitors computer networks
Monitoring a program that monitors computer networksMonitoring a program that monitors computer networks
Monitoring a program that monitors computer networksPVS-Studio
 
How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.PVS-Studio
 
How to make fewer errors at the stage of code writing. Part N1
How to make fewer errors at the stage of code writing. Part N1How to make fewer errors at the stage of code writing. Part N1
How to make fewer errors at the stage of code writing. Part N1Andrey Karpov
 
Tesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition SoftwareTesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition SoftwareAndrey Karpov
 
Valgrind debugger Tutorial
Valgrind debugger TutorialValgrind debugger Tutorial
Valgrind debugger TutorialAnurag Tomar
 
Consequences of using the Copy-Paste method in C++ programming and how to dea...
Consequences of using the Copy-Paste method in C++ programming and how to dea...Consequences of using the Copy-Paste method in C++ programming and how to dea...
Consequences of using the Copy-Paste method in C++ programming and how to dea...Andrey Karpov
 
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 CheckAndrey Karpov
 
PVS-Studio vs Chromium
PVS-Studio vs ChromiumPVS-Studio vs Chromium
PVS-Studio vs ChromiumPVS-Studio
 
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Andrey Karpov
 
Search for Vulnerabilities Using Static Code Analysis
Search for Vulnerabilities Using Static Code AnalysisSearch for Vulnerabilities Using Static Code Analysis
Search for Vulnerabilities Using Static Code AnalysisAndrey Karpov
 
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderA Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderAndrey Karpov
 
Analyzing the Blender project with PVS-Studio
Analyzing the Blender project with PVS-StudioAnalyzing the Blender project with PVS-Studio
Analyzing the Blender project with PVS-StudioPVS-Studio
 
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 2016PVS-Studio
 
The operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzerThe operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzerAndrey Karpov
 
A Collection of Examples of 64-bit Errors in Real Programs
A Collection of Examples of 64-bit Errors in Real ProgramsA Collection of Examples of 64-bit Errors in Real Programs
A Collection of Examples of 64-bit Errors in Real ProgramsPVS-Studio
 
200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis ExperienceAndrey Karpov
 
The Ultimate Question of Programming, Refactoring, and Everything
The Ultimate Question of Programming, Refactoring, and EverythingThe Ultimate Question of Programming, Refactoring, and Everything
The Ultimate Question of Programming, Refactoring, and EverythingAndrey Karpov
 

Similar to 100 bugs in Open Source C/C++ projects (20)

How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.
 
Monitoring a program that monitors computer networks
Monitoring a program that monitors computer networksMonitoring a program that monitors computer networks
Monitoring a program that monitors computer networks
 
PVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's codePVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's code
 
Monitoring a program that monitors computer networks
Monitoring a program that monitors computer networksMonitoring a program that monitors computer networks
Monitoring a program that monitors computer networks
 
How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.
 
How to make fewer errors at the stage of code writing. Part N1
How to make fewer errors at the stage of code writing. Part N1How to make fewer errors at the stage of code writing. Part N1
How to make fewer errors at the stage of code writing. Part N1
 
Tesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition SoftwareTesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition Software
 
Valgrind debugger Tutorial
Valgrind debugger TutorialValgrind debugger Tutorial
Valgrind debugger Tutorial
 
Consequences of using the Copy-Paste method in C++ programming and how to dea...
Consequences of using the Copy-Paste method in C++ programming and how to dea...Consequences of using the Copy-Paste method in C++ programming and how to dea...
Consequences of using the Copy-Paste method in C++ programming and how to dea...
 
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
 
PVS-Studio vs Chromium
PVS-Studio vs ChromiumPVS-Studio vs Chromium
PVS-Studio vs Chromium
 
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
 
Search for Vulnerabilities Using Static Code Analysis
Search for Vulnerabilities Using Static Code AnalysisSearch for Vulnerabilities Using Static Code Analysis
Search for Vulnerabilities Using Static Code Analysis
 
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderA Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
 
Analyzing the Blender project with PVS-Studio
Analyzing the Blender project with PVS-StudioAnalyzing the Blender project with PVS-Studio
Analyzing the Blender project with PVS-Studio
 
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
 
The operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzerThe operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzer
 
A Collection of Examples of 64-bit Errors in Real Programs
A Collection of Examples of 64-bit Errors in Real ProgramsA Collection of Examples of 64-bit Errors in Real Programs
A Collection of Examples of 64-bit Errors in Real Programs
 
200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience200 Open Source Projects Later: Source Code Static Analysis Experience
200 Open Source Projects Later: Source Code Static Analysis Experience
 
The Ultimate Question of Programming, Refactoring, and Everything
The Ultimate Question of Programming, Refactoring, and EverythingThe Ultimate Question of Programming, Refactoring, and Everything
The Ultimate Question of Programming, Refactoring, and Everything
 

Recently uploaded

Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentationphoebematthew05
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfjimielynbastida
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 

Recently uploaded (20)

Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
costume and set research powerpoint presentation
costume and set research powerpoint presentationcostume and set research powerpoint presentation
costume and set research powerpoint presentation
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdf
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 

100 bugs in Open Source C/C++ projects

  • 1. 100 bugs in Open Source C/C++ projects Authors: Andrey Karpov, Evgeniy Ryzhkov Date: 16.03.2012 Abstract This article demonstrates capabilities of the static code analysis methodology. The readers are offered to study the samples of one hundred errors found in open-source projects in C/C++. All the errors have been found with the PVS-Studio static code analyzer. Introduction We won't tire you programmers by making you read texts and will pass to the error samples right away. Those who want to know what static code analysis is, please follow the link. Those who want to know what PVS-Studio is and download the trial version, see this page: http://www.viva64.com/en/pvs- studio/. Yes, one more thing. Please see our post "FAQ for those who have read our articles". Samples of errors detected in various open-source projects The samples of detected errors will be divided into several groups. This division is rather relative. One and the same error can often be referred to misprints and incorrect array handling at a time. Of course, we have taken just a few errors from each of the projects. If we described all the found defects, it would be a reference book. This is the list of analyzed projects: • Apache HTTP Server - http://httpd.apache.org/ • Audacity - http://audacity.sourceforge.net/ • Chromium - http://www.chromium.org/ • Clang - http://clang-analyzer.llvm.org/ • CMake - http://www.cmake.org/ • Crystal Space 3D SDK - http://www.crystalspace3d.org/main/Main_Page • Emule - http://www.emule.com/ • FAR Manager - http://www.farmanager.com/ • FCE Ultra - http://fceux.com/web/home.html
  • 2. • Fennec Media Project - http://fennec.sourceforge.net/ • G3D Content Pak - http://sourceforge.net/projects/g3d-cpp/ • IPP Samples - http://www.viva64.com/go.php?url=449 • Lugaru - http://www.wolfire.com/lugaru • Miranda IM - http://www.miranda-im.org/ • MySQL - http://www.mysql.com/ • Newton Game Dynamics - http://newtondynamics.com/forum/newton.php • Notepad++ - http://notepad-plus-plus.org/ • Pixie - http://www.renderpixie.com/ • PNG library - http://libpng.org/pub/png/ • QT - http://qt.nokia.com/products/ • ReactOS - http://www.reactos.org/en/ • Shareaza - http://www.shareaza.com/ • SMTP Client with SSL/TLS - http://www.codeproject.com/KB/IP/smtp_ssl.aspx • StrongDC++ - http://strongdc.sourceforge.net/index.php?lang=eng • Swiss-Army Knife of Trace - http://www.codeproject.com/KB/trace/tracetool.aspx • TortoiseSVN - http://tortoisesvn.net/ • Ultimate TCP/IP - http://www.codeproject.com/KB/MFC/UltimateTCPIP.aspx • VirtualDub - http://www.virtualdub.org/ • WinDjView - http://windjview.sourceforge.net/ • WinMerge - http://winmerge.org/ • Wolfenstein 3D - http://en.wikipedia.org/wiki/Wolfenstein_3D • Crypto++ - http://www.cryptopp.com/ • Quake-III-Arena - https://github.com/id-Software/Quake-III-Arena • And some others. Errors of array and string handling Errors of array and string handling are the largest class of defects in C/C++ programs. This is the price for the capability of effective low-level memory handling available to programmers. In the article we will show just a small part of these errors found by the PVS-Studio analyzer. But we think any C/C++ programmer understands how numerous and insidious they are. Example 1. Wolfenstein 3D project. Only part of an object is cleared. void CG_RegisterItemVisuals( int itemNum ) { ... itemInfo_t *itemInfo; ... memset( itemInfo, 0, sizeof( &itemInfo ) ); ... }
  • 3. The error was found through the V568 diagnostic: It's odd that the argument of sizeof() operator is the '&itemInfo' expression. cgame cg_weapons.c 1467. The sizeof() operator calculates the size of the pointer instead of the 'itemInfo_t' structure's size. It is "sizeof(*itemInfo)" that must be written. Example 2. Wolfenstein 3D project. Only part of a matrix is cleared. ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) { memcpy( mat, src, sizeof( src ) ); } The error was found through the V511: The sizeof() operator returns size of the pointer, and not of the array, in 'sizeof(src)' expression. Splines math_matrix.h 94 Usually programmers expect 'sizeof(src)' to return the size of an array equal to "3*3*sizeof(float)" bytes. But according to the language standard, 'src' is just a pointer, not an array. Thus, the matrix will be copied only partly. The 'memcpy' function will copy 4 or 8 bytes (the pointer size) depending on whether the code is 32-bit or 64-bit. If you want the whole matrix to be copied, you may pass a reference to the array into the function. This is the correct code: ID_INLINE mat3_t::mat3_t( float (&src)[3][3] ) { memcpy( mat, src, sizeof( src ) ); } Example 3. FAR Manager project. Only part of an array is cleared. struct TreeItem { int *Last; size_t LastCount; ... void Clear() { strName.Clear(); memset(Last, 0, sizeof(Last));
  • 4. Depth=0; } }; The error was found through the V579: diagnostic The memset function receives the pointer and its size as arguments. It is probably a mistake. Inspect the third argument. far treelist.hpp 66 Most likely, there is a missing operation of multiplication by the number of items being cleared, and the code must look as follows: "memset(Last, 0, LastCount * sizeof(*Last));". Example 4. ReactOS project. Incorrect calculation of a string length. static const PCHAR Nv11Board = "NV11 (GeForce2) Board"; static const PCHAR Nv11Chip = "Chip Rev B2"; static const PCHAR Nv11Vendor = "NVidia Corporation"; BOOLEAN IsVesaBiosOk(...) { ... if (!(strncmp(Vendor, Nv11Vendor, sizeof(Nv11Vendor))) && !(strncmp(Product, Nv11Board, sizeof(Nv11Board))) && !(strncmp(Revision, Nv11Chip, sizeof(Nv11Chip))) && (OemRevision == 0x311)) ... } The error was found through the V579 diagnostic: The strncmp function receives the pointer and its size as arguments. It is probably a mistake. Inspect the third argument. vga vbe.c 57 Calls of the 'strncmp' function in this code compare only the first several characters, not whole strings. The error here is this: the sizeof() operator, absolutely inappropriate in this situation, is used to calculate string lengths. The sizeof() operator actually calculates the pointer size instead of the number of bytes in a string. What is the most unpleasant and insidious about this error is that this code almost works as intended. In 99% of cases, comparison of the first several characters is enough. But the remaining 1% can bring you much fun and long debugging.
  • 5. Example 5. VirtualDub project. Array overrun (explicit index). struct ConvoluteFilterData { long m[9]; long bias; void *dyna_func; DWORD dyna_size; DWORD dyna_old_protect; BOOL fClip; }; static unsigned long __fastcall do_conv( unsigned long *data, const ConvoluteFilterData *cfd, long sflags, long pit) { long rt0=cfd->m[9], gt0=cfd->m[9], bt0=cfd->m[9]; ... } The code was found through the V557 diagnostic: Array overrun is possible. The '9' index is pointing beyond array bound. VirtualDub f_convolute.cpp 73 It is not a real error, but good diagnostic. Explanation: http://www.viva64.com/go.php?url=756. Example 6. CPU Identifying Tool project. Array overrun (index in a macro). #define FINDBUFFLEN 64 // Max buffer find/replace size ... int WINAPI Sticky (...) { ... static char findWhat[FINDBUFFLEN] = {'0'};
  • 6. ... findWhat[FINDBUFFLEN] = '0'; ... } The error was found through the V557 diagnostic: Array overrun is possible. The '64' index is pointing beyond array bound. stickies stickies.cpp 7947 This error is a kind of the previous one. The terminal null is written outside the array. The correct code is: "findWhat[FINDBUFFLEN - 1] = '0';". Example 7. Wolfenstein 3D project. Array overrun (incorrect expression). typedef struct bot_state_s { ... char teamleader[32]; //netname of the team leader ... } bot_state_t; void BotTeamAI( bot_state_t *bs ) { ... bs->teamleader[sizeof( bs->teamleader )] = '0'; ... } The error was found through the V557 diagnostic: Array overrun is possible. The 'sizeof (bs- >teamleader)' index is pointing beyond array bound. game ai_team.c 548 Here is one more example of an array overrun when using an explicitly declared index. These samples show that such simple at first sight errors are much more widely-spread than it may seem. The terminal null is written outside the 'teamleader' array. This is the correct code: bs->teamleader[ sizeof(bs->teamleader) / sizeof(bs->teamleader[0]) - 1 ] = '0';
  • 7. Example 8. Miranda IM project. Only part of a string is copied. typedef struct _textrangew { CHARRANGE chrg; LPWSTR lpstrText; } TEXTRANGEW; const wchar_t* Utils::extractURLFromRichEdit(...) { ... ::CopyMemory(tr.lpstrText, L"mailto:", 7); ... } The error was found through the V512 diagnostic: A call of the 'memcpy' function will lead to a buffer overflow or underflow. tabsrmm utils.cpp 1080 If Unicode-strings are used, one character occupies 2 or 4 bytes (depending on the data model being used in compiler) instead of one byte. Unfortunately, programmers easily forget about it, and you can often see defects like our example in programs. The 'CopyMemory' function will copy only part of the L"mailto:" string since it handles bytes, not characters. You can fix the code by using a more appropriate function for string copying or, at least, multiplying number 7 by sizeof(wchar_t). Example 9. CMake project. Array overrun inside a loop. static const struct { DWORD winerr; int doserr; } doserrors[] = { ... };
  • 8. static void la_dosmaperr(unsigned long e) { ... for (i = 0; i < sizeof(doserrors); i++) { if (doserrors[i].winerr == e) { errno = doserrors[i].doserr; return; } } ... } The error was found through the V557 diagnostic: Array overrun is possible. The value of 'i' index could reach 367. cmlibarchive archive_windows.c 1140, 1142 The error handler itself contains an error. The sizeof() operator returns the array size in bytes and not the number of items inside it. As a result, the program will try to search much more items than it should in the loop. This is the correct loop: for (i = 0; i < sizeof(doserrors) / sizeof(*doserrors); i++) Example 10. CPU Identifying Tool project. A string is printed into itself. char * OSDetection () { ... sprintf(szOperatingSystem, "%sversion %d.%d %s (Build %d)", szOperatingSystem, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.szCSDVersion,
  • 9. osvi.dwBuildNumber & 0xFFFF); ... sprintf (szOperatingSystem, "%s%s(Build %d)", szOperatingSystem, osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF); ... } This error was found through the V541 diagnostic: It is dangerous to print the string 'szOperatingSystem' into itself. stickies camel.cpp 572, 603 An attempt of formatted printing of a string into itself can lead to bad consequences. The result of executing this code depends on the input data, and you cannot predict what will happen. Most likely, the result will be a meaningless string or an Access Violation will occur. This error can be referred to the category "code vulnerabilities". In some programs, by feeding special data to code, you can exploit such code fragments to cause a buffer overflow or other effects an intruder needs. Example 11. FCE Ultra project. A string gets less memory than needed. int FCEUI_SetCheat(...) { ... if((t=(char *)realloc(next->name,strlen(name+1)))) ... } The error was found through the V518 diagnostic: The 'realloc' function allocates strange amount of memory calculated by 'strlen(expr)'. Perhaps the correct variant is 'strlen(expr) + 1'. fceux cheat.cpp 609 This error is caused by a misprint. It is the 'name' pointer instead of the "name+1" expression that must be the argument of the strlen() function. As a result, the realloc function allocates 2 bytes less memory than needed: one byte is lost because 1 is not added to the string length; another byte is lost because the 'strlen' function calculates the string length skipping the first character. Example 12. Notepad++ project. Partial array clearing. #define CONT_MAP_MAX 50 int _iContMap[CONT_MAP_MAX];
  • 10. ... DockingManager::DockingManager() { ... memset(_iContMap, -1, CONT_MAP_MAX); ... } The error was found through the V512 diagnostic: A call of the memset function will lead to a buffer overflow or underflow. notepadPlus DockingManager.cpp 60 That's one more example of how the number of array items is mixed up with an array size. A multiplication by sizeof(int) is missing. We can go on and on showing you errors of array handling we have found in various programs. But we have to stop somewhere. Undefined behavior A bit of theory at first. Undefined behavior is a property of certain programming languages (most prominent in C and C++) to produce a result in certain situations that depends on compiler implementation or specified optimization switches. In other words, the specification does not define the language's behavior in any possible situations but says: "at A condition, the result of B operation is undefined". It is considered a mistake to allow such a situation in your program even if it is executed well at some particular compiler. Such a program will not be crossplatform and may cause failures on a different computer, operating system and even at different compiler's settings. A sequence point in programming is any point in a program where it is guaranteed that the side effects of all the previous calculations have already emerged while there are no side effects of the following calculations yet. To learn more about sequence points and cases of undefined behavior related to sequence points, see this post: http://www.viva64.com/en/t/0065/. Example 1. Chromium project. Incorrect use of smart pointer. void AccessibleContainsAccessible(...) { ... auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
  • 11. ... } The error was found through the V554 diagnostic: Incorrect use of auto_ptr. The memory allocated with 'new []' will be cleaned using 'delete'. interactive_ui_tests accessibility_win_browsertest.cc 171 This example demonstrates the case when using a smart pointer can cause undefined behavior. It may be expressed through heap damage, program crash, incomplete object destruction or any other failure. The error is this: memory is allocated by the new [] operator and released by the delete operator in the 'auto_ptr' class' destructor: ~auto_ptr() { delete _Myptr; } To fix these issues, you should use a more appropriate class, for instance, boost::scoped_array. Example 2. IPP Samples project. Classic Undefined behavior. template<typename T, Ipp32s size> void HadamardFwdFast(...) { Ipp32s *pTemp; ... for(j=0;j<4;j++) { a[0] = pTemp[0*4] + pTemp[1*4]; a[1] = pTemp[0*4] - pTemp[1*4]; a[2] = pTemp[2*4] + pTemp[3*4]; a[3] = pTemp[2*4] - pTemp[3*4]; pTemp = pTemp++; ... } ... } The error was found through the V567 diagnostic: Undefined behavior. The 'pTemp' variable is modified while being used twice between sequence points. me umc_me_cost_func.h 168 This is a classic example of undefined program behavior. It is this construct which is used to demonstrate Undefined behavior in various articles. It is unknown whether 'pTemp' will be incremented
  • 12. by one or not. Two actions of changing pTemp variable's value are located in one sequence point. It means that the compiler may create the following code: pTemp = pTemp + 1; pTemp = pTemp; Or it may create another version of the code: TMP = pTemp; pTemp = pTemp + 1; pTemp = TMP; Which of the two code versions will be created depends on the compiler and optimization switches. Example 3. Fennec Media Project project. Complex expression. uint32 CUnBitArrayOld::DecodeValueRiceUnsigned(uint32 k) { ... while (!(m_pBitArray[m_nCurrentBitIndex >> 5] & Powers_of_Two_Reversed[m_nCurrentBitIndex++ & 31])) {} ... } The error was found through the V567 diagnostic: Undefined behavior. The 'm_nCurrentBitIndex' variable is modified while being used twice at single sequence point. MACLib unbitarrayold.cpp 78 There are no sequence points between two instances of using the 'm_nCurrentBitIndex' variable. It means that the standard does not specify the moment when this variable is incremented. Correspondingly, this code may work differently depending on the compiler and optimization switches. Example 4. Miranda IM project. Complex expression. short ezxml_internal_dtd(ezxml_root_t root, char *s, size_t len) { ... while (*(n = ++s + strspn(s, EZXML_WS)) && *n != '>') { ...
  • 13. } The error was found through the V567 diagnostic: Undefined behavior. The 's' variable is modified while being used twice between sequence points.msne zxml.c 371 Prefix increment of the variable is used here. But it does not mean anything: it cannot be guaranteed that the 's' variable will be incremented before calling the strspn() function. Errors relating to operation priorities. To make understanding of examples easier, let's recall the operation priorities table. Example 1. MySQL project. Priorities of ! and & operations. int ha_innobase::create(...) { ... if (srv_file_per_table && !mysqld_embedded && (!create_info->options & HA_LEX_CREATE_TMP_TABLE)) { ... } The error was found through the V564 diagnostic: The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. innobase ha_innodb.cc 6789 The programmer wanted a part of the expression to check that a certain bit in the 'create_info->options' variable is equal to zero. But the priority of the '!' operation is higher than that of the '&' operation, that's why the expression works by this algorithm: ((!create_info->options) & HA_LEX_CREATE_TMP_TABLE) We should use additional parentheses if we want the code to work properly: (!(create_info->options & HA_LEX_CREATE_TMP_TABLE)) Or, what we find nicer, write the code in the following way: ((create_info->options & HA_LEX_CREATE_TMP_TABLE) == 0) Example 2. Emule project. Priorities of * and ++ operations. STDMETHODIMP CCustomAutoComplete::Next(..., ULONG *pceltFetched)
  • 14. { ... if (pceltFetched != NULL) *pceltFetched++; ... } The error was found through the V532 diagnostic: Consider inspecting the statement of '*pointer++' pattern. Probably meant: '(*pointer)++'. emule customautocomplete.cpp 277 If 'pceltFetched' is not a null pointer, the function must increment the variable of the ULONG type this pointer refers to. The error is this: the priority of the '++' operation is higher than that of '*' operation (pointer dereferencing). The "*pceltFetched++;" line is identical to the following code: TMP = pceltFetched + 1; *pceltFetched; pceltFetched = TMP; Virtually it is just increment of the pointer. To make the code correct, we must add parentheses: "(*pceltFetched)++;". Example 3. Chromium project. Priorities of & and != operations. #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { ... info->is_directory = file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0; ... } The error was found through the V564 diagnostic: The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. base platform_file_win.cc 216 Programmers easily forget that the priority of the '!=' operation is higher than that of '&'. This is what happened in our case. As a result, we have the following expression: info->is_directory =
  • 15. file_info.dwFileAttributes & (0x00000010 != 0); Let's simplify the expression: info->is_directory = file_info.dwFileAttributes & (true); Let's simplify it once again: info->is_directory = file_info.dwFileAttributes & 1; It turns out that we have tested the first bit instead of the fifth bit. To fix this, we need to add parentheses. Example 4. BCmenu project. IF and ELSE mixed up. void BCMenu::InsertSpaces(void) { if(IsLunaMenuStyle()) if(!xp_space_accelerators) return; else if(!original_space_accelerators) return; ... } The error was found through the V563 diagnostic: It is possible that this 'else' branch must apply to the previous 'if' statement. fire bcmenu.cpp 1853 This is not an error of operation priorities, but one relative to it. The programmer does not take into account that the 'else' branch refers to the nearest 'if' operator. We can see that the code justification as if it works by the following algorithm: if(IsLunaMenuStyle()) { if(!xp_space_accelerators) return; } else { if(!original_space_accelerators) return; } But actually it is equivalent to the following construct: if(IsLunaMenuStyle()) { if(!xp_space_accelerators) {
  • 16. return; } else { if(!original_space_accelerators) return; } } Example 5. IPP Samples project. Priorities of ?: and | operations. vm_file* vm_file_fopen(...) { ... mds[3] = FILE_ATTRIBUTE_NORMAL | (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING; ... } The error was found through the V502 diagnostic: Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '|' operator. vm vm_file_win.c 393 Depending on the 'islog' variable's value, the expression must be either equal to "FILE_ATTRIBUTE_NORMAL" or "FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING". But it does not happen. Priority of the '?:' operation is lower than that of '|'. As a result, the code acts as follows: mds[3] = (FILE_ATTRIBUTE_NORMAL | (islog == 0)) ? 0 : FILE_FLAG_NO_BUFFERING; Let's simplify the expression: mds[3] = (0x00000080 | ...) ? 0 : FILE_FLAG_NO_BUFFERING; Since FILE_ATTRIBUTE_NORMAL equals 0x00000080, the condition is always true. It means that 0 will always be written into mds[3]. Example 6. Newton Game Dynamics project. Priorities of ?: and * operations. dgInt32 CalculateConvexShapeIntersection (...) { ... den = dgFloat32 (1.0e-24f) *
  • 17. (den > dgFloat32 (0.0f)) ? dgFloat32 (1.0f) : dgFloat32 (-1.0f); ... } The error was found through the V502 diagnostic: Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '*' operator. physics dgminkowskiconv.cpp 1061 The error in this code again relates to the low priority of the '?:' operation. The condition for the '?:' operator is expressed by a meaningless subexpression "dgFloat32 (1.0e-24f) * (den > dgFloat32 (0.0f))". Adding parentheses will solve the issue. By the way, programmers often forget how cunning the '?:' operator is. Here is a post on this topic: "How to make fewer errors at the stage of code writing. Part N2". Formatted output errors Examples of these errors are boring and alike, so we will examine only a few samples. The point is that functions with a variable number of arguments accept actual arguments incompatible with the format string. Any programmer who uses such functions as printf() is familiar with this type of errors. Example 1. ReactOS project. Incorrect printing of a WCHAR-character. static void REGPROC_unescape_string(WCHAR* str) { ... default: fprintf(stderr, "Warning! Unrecognized escape sequence: %c'n", str[str_idx]); ... } The error was found through the V576 diagnostic: Incorrect format. Consider checking the third actual argument of the 'fprintf' function. The char type argument is expected. regedit regproc.c 293
  • 18. The fprinf() function must print a character of the char type. But the third argument is a character of the WCHAR type. The user will get an incorrectly generated message. To fix the code, we should replace '%c' with '%C' in the format string. Example 2. Intel AMT SDK project. Character '%' missing. void addAttribute(...) { ... int index = _snprintf(temp, 1023, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" "%02x%02x:02x%02x:%02x%02x:%02x%02x", value[0],value[1],value[2],value[3],value[4], value[5],value[6],value[7],value[8], value[9],value[10],value[11],value[12], value[13],value[14],value[15]); ... } The error was found through the V576 diagnostic: Incorrect format. A different number of actual arguments is expected while calling '_snprintf' function. Expected: 18. Present: 19. mod_pvs mod_pvs.cpp 308 It is not easy to find an error here at first sight. However, the PVS-Studio analyzer does not get tired and notices that the function takes more actual arguments than specified in the format string. The reason is that the '%' character is missing in one place. Let's single out this fragment: "%02x%02x:[HERE]02x%02x:%02x%02x:%02x%02x", Example 3. Intel AMT SDK project. Unused argument. bool GetUserValues(...) { ... printf("Error: illegal value. Aborting.n", tmp); return false; }
  • 19. The error was found through the V576 diagnostic: Incorrect format. A different number of actual arguments is expected while calling 'printf' function. Expected: 1. Present: 2. RemoteControlSample remotecontrolsample.cpp 792 The error is this: the 'tmp' variable is not used in any way when printing the information message. Example 4. G3D Content Pak project. Printing of meaningless data. class Matrix3 { ... inline float* operator[] (int iRow) { ... }; void AnyVal::serialize(G3D::TextOutput& t) const { ... const Matrix3& m = *(Matrix3*)m_value; ... t.printf("%10.5f, %10.5f, %10.5f,n %10.5f, %10.5f, %10.5f,n %10.5f, %10.5f, %10.5f)", m[0, 0], m[0, 1], m[0, 2], m[1, 0], m[1, 1], m[1, 2], m[2, 0], m[2, 1], m[2, 2]); ... } The error was found through the V520 diagnostic: The comma operator ',' in array index expression '[0, 0]'. graphics3D anyval.cpp 275 The program prints meaningless values instead of the matrix. You may write such a code when you work with different programming languages and sometimes forget how to access an item in a two- dimensional array in the C language. Let's see how the 'm[0, 1]' expression works. At first, expression"0, 1" is calculated. The result of this expression is 1. Then the 'operator[]' function is called in the Matrix3 class. The function takes the actual argument 1 and returns the pointer to the first string in the matrix. It is the value of this pointer that will be printed by the 'printf()' function though it expects a value of the float-type.
  • 20. This is the correct code: t.printf("%10.5f, %10.5f, %10.5f,n %10.5f, %10.5f, %10.5f,n %10.5f, %10.5f, %10.5f)", m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); Examples of misprints found in code A lot of programming errors are caused by misprints. Most of these errors are quickly detected at the early stages of testing. But there are some defects of this kind that remain in code for a long time causing troubles both to programmers and users. You can make these errors much fewer using the PVS-Studio analyzer. It will find them before testing starts, which will significantly reduce the cost of defect detection and elimination. Example 1. Miranda IM project. Assignment inside IF. void CIcqProto::handleUserOffline(BYTE *buf, WORD wLen) { ... else if (wTLVType = 0x29 && wTLVLen == sizeof(DWORD)) ... } The error was found through the V560 diagnostic: A part of conditional expression is always true: 0x29. icqoscar8 fam_03buddy.cpp 632 Because of a misprint, there is an assignment taking place inside the condition of the 'if' operator. This is the correct condition: "if (wTLVType == 0x29 && wTLVLen == sizeof(DWORD))". Example 2. ReactOS project. Assignment error. BOOL WINAPI GetMenuItemInfoA(...) { ... mii->cch = mii->cch;
  • 21. ... } The error was found through the V570 diagnostic: The 'mii->cch' variable is assigned to itself. user32 menu.c 4347 The value of the variable is assigned to itself. The programmer apparently intended to write it in this way: "mii->cch = miiW->cch;". Example 3. Clang project. Object name misprinted. static Value *SimplifyICmpInst(...) { ... case Instruction::Shl: { bool NUW = LBO->hasNoUnsignedWrap() && LBO->hasNoUnsignedWrap(); bool NSW = LBO->hasNoSignedWrap() && RBO->hasNoSignedWrap(); ... } The error was found through the V501 diagnostic: There are identical sub-expressions 'LBO- >hasNoUnsignedWrap ()' to the left and to the right of the '&&' operator. LLVMAnalysis instructionsimplify.cpp 1891 There is a misprint when using variables with similar names. In the first line, both LBO and RBO variables must be used. This is the correct code: bool NUW = LBO->hasNoUnsignedWrap() && RBO->hasNoUnsignedWrap(); Example 4. Notepad++ project. Incorrect state test. bool _isPointXValid; bool _isPointYValid; ... bool isPointValid() { return _isPointXValid && _isPointXValid; };
  • 22. The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to the right of the '&&' operator. _isPointXValid && _isPointXValid The name '_isPointXValid' is used twice. The function must actually return this code: "_isPointXValid && _isPointYValid". Example 5. StrongDC++ project. Unsuccessful check of rn. static void getContentLengthAndHeaderLength(...) { ... while(line[linelen] != 'r' && line[linelen] != 'r') ... } The error was found through the V501 diagnostic: There are identical sub-expressions 'line [linelen] != 'r'' to the left and to the right of the '&&' operator. miniupnpc miniupnpc.c 153 Because of a misprint, presence of the 'r' character is checked twice. Actually presence of the 'n' character must be checked too. Example 6. G3D Content Pak project. A closing parenthesis in a wrong place. bool Matrix4::operator==(const Matrix4& other) const { if (memcmp(this, &other, sizeof(Matrix4) == 0)) { return true; } ... } The error was found through the V575 diagnostic: The 'memcmp' function processes '0' elements. Inspect the 'third' argument. graphics3D matrix4.cpp 269 One closing parenthesis is in a wrong place. It turns out that the size of the memory area being compared is calculated by the "sizeof(Matrix4) == 0" expression. This expression always has the 'false' result. Then 'false' turns into an integer value equal to 0. This is the correct code: if (memcmp(this, &other, sizeof(Matrix4)) == 0) { Example 7. QT project. Error of structure member copying.
  • 23. PassRefPtr<Structure> Structure::getterSetterTransition(Structure* structure) { ... transition->m_propertyStorageCapacity = structure->m_propertyStorageCapacity; transition->m_hasGetterSetterProperties = transition->m_hasGetterSetterProperties; transition->m_hasNonEnumerableProperties = structure->m_hasNonEnumerableProperties; transition->m_specificFunctionThrashCount = structure->m_specificFunctionThrashCount; ... } The error was found through the V570 diagnostic: The 'transition->m_hasGetterSetterProperties' variable is assigned to itself. QtScript structure.cpp 512 It is not easy to find an error looking at this code. But it is there. The field 'm_hasGetterSetterProperties' is copied into itself. This is the correct code: transition->m_hasGetterSetterProperties = structure->m_hasGetterSetterProperties; Example 8. Apache HTTP Server project. Extra sizeof operator. PSECURITY_ATTRIBUTES GetNullACL(void) { PSECURITY_ATTRIBUTES sa; sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES)); sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES)); ... }
  • 24. The error was found through the V568 diagnostic: It's odd that the argument of sizeof() operator is the 'sizeof (SECURITY_ATTRIBUTES)' expression. libhttpd util_win32.c 115 The field 'nLength' must contain the size of the 'SECURITY_ATTRIBUTES' structure. There is a misprint in the code: the 'sizeof' operator is used twice. As a result, the field 'nLength' stores a size of the 'size_t' type. This is the correct code: sa->nLength = sizeof(SECURITY_ATTRIBUTES); Example 9. FCE Ultra project. Double variable declaration. int iNesSaveAs(char* name) { ... fp = fopen(name,"wb"); int x = 0; if (!fp) int x = 1; ... } The error was found through the V561 diagnostic: It's probably better to assign value to 'x' variable than to declare it anew. Previous daclaration: ines.cpp, line 960. fceuxines.cpp 962 The 'x' variable must store information whether or not a file was opened successfully. Because of a misprint, a new variable named 'x' is created and initialized instead of assigning 1 to the existing variable. This is how the correct code must look: if (!fp) x = 1; Example 10. Notepad++ project. Using && operator instead of &. TCHAR GetASCII(WPARAM wParam, LPARAM lParam) { ... result=ToAscii(wParam, (lParam >> 16) && 0xff, keys,&dwReturnedValue,0); ...
  • 25. } The error was found through the V560 diagnostic: A part of conditional expression is always true: 0xff. notepadPlus babygrid.cpp 694 The "(lParam >> 16) && 0xff" expression is meaningless and is always equal to 1 (true). A misprint here is in using the '&&' operator instead of '&'. Example 11. WinDjView project. Incomplete condition. inline bool IsValidChar(int c) { return c == 0x9 || 0xA || c == 0xD || c >= 0x20 && c <= 0xD7FF || c >= 0xE000 && c <= 0xFFFD || c >= 0x10000 && c <= 0x10FFFF; } The error was found through the V560 diagnostic: A part of conditional expression is always true: 0xA. WinDjView xmlparser.cpp 45 False The IsValidChar function always returns 'true'. Comparison is missing in one place because of a misprint: "... || 0xA || ...". Example 12. Fennec Media Project project. Extra semicolon. int settings_default(void) { ... for(i=0; i<16; i++); for(j=0; j<32; j++) { settings.conversion.equalizer_bands.boost[i][j] = 0.0; settings.conversion.equalizer_bands.preamp[i] = 0.0; } } The error was found through the V529 diagnostic: Odd semicolon ';' after 'for' operator. settings.c 483
  • 26. All the C and C++ programmers know how dangerous an extra semicolon ';' is. Unfortunately, this knowledge does not prevent them from making such misprints. There is an extra semicolon after the first 'for' operator, which makes this program fragment unable to execute. Example 13. QT project. Missing break operator. int QCleanlooksStyle::pixelMetric(...) { ... case PM_SpinBoxFrameWidth: ret = 3; break; case PM_MenuBarItemSpacing: ret = 6; case PM_MenuBarHMargin: ret = 0; break; ... } The error was found through the V519 diagnostic: The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3765, 3767. QtGui qcleanlooksstyle.cpp 3767 This is a classic error - 'break' is missing inside the 'switch' operator. I think you do not need any further comments here. Example 14. Miranda IM project. Assignment instead of comparison. int FindItem(...) { ... int ret; ret=FindItem(hwnd,dat,hItem, (struct ClcContact ** )&z, (struct ClcGroup ** )&isv,NULL);
  • 27. if (ret=0) {return (0);} ... } The error was found through the V559 diagnostic: Suspicious assignment inside the condition expression of 'if' operator: ret = 0. clist_mw clcidents.c 179 There is a misprint inside the condition of the 'if' operator: '=' is written instead of '=='. The function will handle the situation incorrectly when a certain item is not found. Example 15. IPP Samples project. Incorrect index. struct AVS_MB_INFO { ... Ipp8u refIdx[AVS_DIRECTIONS][4]; ... }; void AVSCompressor::GetRefIndiciesBSlice(void){ ... if (m_pMbInfo->predType[0] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][0]; iRefNum += 1; } if (m_pMbInfo->predType[1] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][1]; iRefNum += 1; } if (m_pMbInfo->predType[2] & predType) {
  • 28. m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][2]; iRefNum += 1; } if (m_pMbInfo->predType[3] & predType) { m_refIdx[iRefNum] = m_pMbInfo->refIdx[dir][30]; iRefNum += 1; } ... } The error was found through the V557 diagnostic: Array overrun is possible. The '30' index is pointing beyond array bound. avs_enc umc_avs_enc_compressor_enc_b.cpp 495 Consider this fragment: "m_pMbInfo->refIdx[dir][30]". Because of a misprint, number 30 is written instead of index 3. By the way, this sample shows well how relative our division of errors into categories is. This error might well be referred to the category "Errors of array and string handling". The division is relative and is made to show diversity of errors the PVS-Studio analyzer can detect. Example 16. ReactOS project. Misprint in a macro. #define SWAP(a,b,c) c = a; a = b; a = c The error was found through the V519 diagnostic: The 'v2' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 343, 343. win32k gradient.c 343 It is a rather funny misprint in a macro intended to swap values in two variables. Look closely at the code and you will see what I mean. This is the correct code: #define SWAP(a,b,c) c = a; a = b; b = c Example 17. Quake-III-Arena project. Misprint. Comma instead of multiplication operator. void Q1_AllocMaxBSP(void) {
  • 29. ... q1_allocatedbspmem += Q1_MAX_MAP_CLIPNODES * sizeof(q1_dclipnode_t); ... q1_allocatedbspmem += Q1_MAX_MAP_EDGES , sizeof(q1_dedge_t); ... q1_allocatedbspmem += Q1_MAX_MAP_MARKSURFACES * sizeof(unsigned short); ... } The error has been found with rule V521: Such expressions using the ',' operator are dangerous. Make sure the expression is correct. bspc l_bsp_q1.c 136 It's a funny misprint. Look at the line in the middle of the code. ',' written instead of '*'. As a result, the 'sizeof(q1_dedge_t)' value is always added to the 'q1_allocatedbspmem' variable. I have no suggestions how this misprint could have occurred. Example 18. LibXml project. Misprint =+. static int xmlXPathCompOpEvalFirst(...) { ... total += xmlXPathCompOpEvalFirst(...); ... total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first); ... } The error has been found with rule V588: The expression of the 'A =+ B' kind is utilized. Consider reviewing it, as it is possible that 'A += B' was meant. libxml xpath.c 12676 In one place, "=+" is written instead of "+=" by mistake. They look similar but the result is quite different. Such errors are rather difficult to find just reviewing the code.
  • 30. Many errors in software are caused by misprints. There are much more errors of this kind than programmers think. We could go on and on in this section but we decide to stop at the 18-th example at last. Incorrect use of base functions and classes Example 1. Fennec Media Project. Two terminal nulls absent. int JoiningProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam) { ... OPENFILENAME lofn; memset(&lofn, 0, sizeof(lofn)); ... lofn.lpstrFilter = uni("All Files (*.*)0*.*"); ... } The error was found through the V540 diagnostic: Member 'lpstrFilter' should point to string terminated by two 0 characters. base windows.c 5309 In Windows API there are structures in which pointers to strings must end with two null characters. It is that very kind of string the 'lpstrFilter' member in the OPENFILENAME structure points to. Description of 'lpstrFilter' in MSDN: LPCTSTR A buffer containing pairs of null-terminated filter strings. The last string in the buffer must be terminated by two NULL characters. If you forget to write an additional null at the end, the dialogue of file handling may contain garbage in the filter fields. This is the correct code: lofn.lpstrFilter = uni("All Files (*.*)0*.*0"); Example 2. TortoiseSVN project. Incorrect use of 'remove' function. STDMETHODIMP CShellExt::Initialize(....)
  • 31. { ... ignoredprops = UTF8ToWide(st.c_str()); // remove all escape chars ('') std::remove(ignoredprops.begin(), ignoredprops.end(), ''); break; ... } The error was found through the V530 diagnostic: The return value of function 'remove' is required to be utilized. contextmenu.cpp 442 The std::remove function does not remove items from the container. It only shifts the items and returns the iterator to the beginning of trash. Assume we have a vector<int> container that contains items 1,2,3,1,2,3,1,2,3. If we execute the code "remove( v.begin(), v.end(), 2 )", the container will contain items 1,3,1,3,X,X,X, where X is some trash. The function will return the iterator to the first trash item, so if we want to remove these trash items, we need to write the code: "v.erase(remove(v.begin(), v.end(), 2), v.end())". Example 3. TortoiseSVN project. Using 'empty' function instead of 'clear'. CMailMsg& CMailMsg::SetFrom(string sAddress, string sName) { if (initIfNeeded()) { // only one sender allowed if (m_from.size()) m_from.empty(); m_from.push_back(TStrStrPair(sAddress,sName)); } return *this; } The error was found through the V530 diagnostic: The return value of function 'empty' is required to be utilized. mailmsg.cpp 40
  • 32. The error here is this: the vector::empty() function is called by mistake instead of vector::clear(), and the array's contents remain the same. It is a very frequent error because the words 'clear' and 'empty' are rather close in meaning, and you might easily mix them up. Example 4. WinMerge project. Using 'empty' function instead of 'clear'. void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const { UINT_PTR diffpos = GetItemKey(sel); if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS) { strLeft.empty(); strRight.empty(); } else { ... } } The error was found through the V530 diagnostic: The return value of function 'empty' is required to be utilized WinMerge DirActions.cpp 1307, 1308 Again, the reason is in using the empty() function instead of clear(). We could cite examples of such errors from other projects as well: InstantVNC, IPP Samples, Chromium, Intel AMT SDK, etc. Unfortunately, all these samples are alike, and there is nothing interesting about examining them. But trust me, you can see these defects in serious projects developed by professional programmers. Example 5. Pixie project. Using 'alloca' function inside loops. inline void triangulatePolygon(...) { ... for (i=1;i<nloops;i++) { ... do {
  • 33. ... do { ... CTriVertex *snVertex = (CTriVertex *)alloca(2*sizeof(CTriVertex)); ... } while(dVertex != loops[0]); ... } while(sVertex != loops[i]); ... } ... } The error was found through the V505 diagnostic: The 'alloca' function is used inside the loop. This can quickly overflow stack. ri polygons.cpp 1120 The alloca function allocates memory inside the stack, so calling it many times inside the loop body may suddenly cause a stack overflow. And we have several nested loops here. This code may exhaust stack memory very quickly. Example 6. Miranda IM project. Arguments mixed up. static BOOL ImageArray_Alloc(LP_IMAGE_ARRAY_DATA iad, int size) { ... memset(&iad->nodes[iad->nodes_allocated_size], (size_grow - iad->nodes_allocated_size) * sizeof(IMAGE_ARRAY_DATA_NODE), 0); ... } The error was found through the V575 diagnostic: Function receives an odd argument. clist_modern modern_image_array.cpp 59
  • 34. The 'memset' function handles 0 items, i.e. actually does nothing. The reason is in mixed up arguments. This is how the correct call of the memset function should be written: memset(&iad->nodes[iad->nodes_allocated_size], 0, (size_grow - iad->nodes_allocated_size) * sizeof(IMAGE_ARRAY_DATA_NODE)); Examples of meaningless code Example 1. IPP Samples project. Incomplete condition. void lNormalizeVector_32f_P3IM(Ipp32f *vec[3], Ipp32s* mask, Ipp32s len) { Ipp32s i; Ipp32f norm; for(i=0; i<len; i++) { if(mask<0) continue; norm = 1.0f/sqrt(vec[0][i]*vec[0][i]+ vec[1][i]*vec[1][i]+vec[2][i]*vec[2][i]); vec[0][i] *= norm; vec[1][i] *= norm; vec[2][i] *= norm; } } The error was found through the V503 diagnostic: This is a nonsensical comparison: pointer < 0. ipprsample ippr_sample.cpp 501 I do not know how it happened, but there are 3 characters "[i]" missing in this code. As a result, the code performs a meaningless check that the pointer is below zero instead of checking the mask array. The correct check should be written in this way: if(mask[i] < 0). Example 2. Pc Ps2 Emulator project. Incorrect switch. LRESULT CALLBACK IOP_DISASM(...)
  • 35. { ... switch(LOWORD(wParam)) { case (IDOK || IDCANCEL): EndDialog(hDlg,TRUE); return(TRUE); break; } ... } The error was found through the V560 diagnostic: A part of conditional expression is always true: 2. pcsx2 debugger.cpp 321 This code does not have any meaning. The programmer must have intended to write it this way: switch(LOWORD(wParam)) { case IDOK: //no break case IDCANCEL: EndDialog(hDlg,TRUE); return(TRUE); break; } Example 3. CPU Identifying Tool project. A too strict condition. void projillum(short* wtab, int xdots, int ydots, double dec) { ... s = sin(-dtr(dec)); x = -s * sin(th); y = cos(th);
  • 36. ... lon = (y == 0 && x == 0) ? 0.0 : rtd(atan2(y, x)); } The error was found through the V550 diagnostic: An odd precise comparison: x == 0. It's probably better to use a comparison with defined precision: fabs(A - B) '<' Epsilon. clock_dll sunalgo.cpp 155 It is strange to expect that the result will be strictly 0 after executing all these complex calculations using 'sin' and 'cos' functions. Most likely, there must be comparison to be performed with certain accuracy. Example 4. Lugaru. Double assignment. int Game::DrawGLScene(void) { ... radius=fast_sqrt(maxdistance); radius=110; ... } The error was found through the V519 diagnostic: The 'radius' object is assigned values twice successively. Perhaps this is a mistake. Lugaru gamedraw.cpp 1505 The programmer must have deliberately written value 110 into the 'radius' variable for the sake of experiment and then forgot to remove this line. As a result, we have a meaningless and maybe even invalid code. Example 5. QT project. Duplicated check. Q3TextCustomItem* Q3TextDocument::parseTable(...) { ... while (end < length && !hasPrefix(doc, length, end, QLatin1String("</td")) && !hasPrefix(doc, length, end, QLatin1String("<td")) && !hasPrefix(doc, length, end, QLatin1String("</th")) && !hasPrefix(doc, length, end, QLatin1String("<th"))
  • 37. && !hasPrefix(doc, length, end, QLatin1String("<td")) && !hasPrefix(doc, length, end, QLatin1String("</tr")) && !hasPrefix(doc, length, end, QLatin1String("<tr")) && !hasPrefix(doc, length, end, QLatin1String("</table"))) { ... } The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to the right of the '&&' operator. Qt3Support q3richtext.cpp 6978 Presence of the "<td" prefix is checked twice in the condition. It is meaningless. Maybe it is an extra check or there should be some other prefix instead of the second "<td". Example 6. Audacity project. Strange check. int sf_error (SNDFILE *sndfile) { ... if (!sndfile) { if (sf_error != 0) return sf_errno; return 0; } ; ... } The error was found through the V516 diagnostic: Consider inspecting an odd expression. Non-null function pointer is compared to null: 'sf_error != 0'. libsndfile sndfile.c 491 The "sf_error != 0" check always returns true, since 'sf_error' is the name of the function in which the code is executed. Example 7. IPP Samples project. Strange code inside a loop. static IppStatus mp2_HuffmanTableInitAlloc(Ipp32s *tbl, ...)
  • 38. { ... for (i = 0; i < num_tbl; i++) { *tbl++; } ... } The error was found through the V532 diagnostic: Consider inspecting the statement of '*pointer++' pattern. Probably meant: '(*pointer)++'. mpeg2_dec umc_mpeg2_dec.cpp 59 The loop body is probably incomplete because it is meaningless in the current form. Always true or always false conditions It is a very large and widely-spread type of errors. These errors also vary greatly depending on the importance level. To non-dangerous errors we may refer incorrect conditions in ASSERT that actually do not check anything. To dangerous errors, incorrect checks of buffer size or index size are referred. Example 1. Shareaza project. Value range of char type. void CRemote::Output(LPCTSTR pszName) { ... CHAR* pBytes = new CHAR[ nBytes ]; hFile.Read( pBytes, nBytes ); ... if ( nBytes > 3 && pBytes[0] == 0xEF && pBytes[1] == 0xBB && pBytes[2] == 0xBF ) { pBytes += 3; nBytes -= 3; bBOM = true; }
  • 39. ... } The error was found through the V547 diagnostic: Expression 'pBytes [ 0 ] == 0xEF' is always false. The value range of signed char type: [-128, 127]. Shareaza remote.cpp 350 In this code, the 'TCHAR' type is the 'char' type. The value range of char is from -128 to 127 inclusive. Value 0xEF in the variable of the char type is nothing else than number -17. When comparing the char variable with number 0xEF, its type is extended up to the 'int' type. But the value still lies inside the range [-128..127]. The "pBytes[0] == 0xEF" ("-17 == 0xEF") condition is always false, and the program does not work as intended. This is the correct comparison: if ( nBytes > 3 && pBytes[0] == TCHAR(0xEF) && pBytes[1] == TCHAR(0xBB) && pBytes[2] == TCHAR(0xBF) ) Example 2. TortoiseSVN project. Value range of char type. BOOL TortoiseBlame::OpenFile(const TCHAR *fileName) { ... // check each line for illegal utf8 sequences. // If one is found, we treat // the file as ASCII, otherwise we assume // an UTF8 file. char * utf8CheckBuf = lineptr; while ((bUTF8)&&(*utf8CheckBuf)) { if ((*utf8CheckBuf == 0xC0)|| (*utf8CheckBuf == 0xC1)|| (*utf8CheckBuf >= 0xF5)) { bUTF8 = false; break; }
  • 40. ... } ... } The error was found through the V547 diagnostic: Expression '* utf8CheckBuf == 0xC0' is always false. The value range of signed char type: [-128, 127]. tortoiseblame.cpp 310 While the defect in the previous example seems to be caused through mere inattention, in this case it is not so. Here is another identical example where a condition is always false. This is a very widely-spread type of errors in various projects. Example 3. VirtualDub project. Unsigned type is always >= 0. typedef unsigned short wint_t; ... void lexungetc(wint_t c) { if (c < 0) return; g_backstack.push_back(c); } The error was found through the V547 diagnostic: Expression 'c < 0' is always false. Unsigned type value is never < 0. Ami lexer.cpp 225 The "c < 0" condition is always false because the variable of the unsigned type is always above or equal to 0. Example 4. Swiss-Army Knife of Trace project. Socket handling. static UINT_PTR m_socketHandle; void TTrace::LoopMessages(void) { ...
  • 41. // Socket creation if ( (m_socketHandle = socket(AF_INET,SOCK_STREAM,0)) < 0) { continue; } ... } The error was found through the V547 diagnostic: Expression '(m_socketHandle = socket (2, 1, 0)) < 0' is always false. Unsigned type value is never < 0. Vs8_Win_Lib tracetool.cpp 871 An attempt to check that a socket was created successfully is performed incorrectly. If a socket cannot be created, this situation is not handled in any way. To make the check work correctly, we should use the INVALID_SOCKET constant: m_socketHandle = socket(AF_INET,SOCK_STREAM, 0); if (m_socketHandle == INVALID_SOCKET) ... Example 5. Chromium project. Time handling. IdleState CalculateIdleState(...) { ... DWORD current_idle_time = 0; ... // Will go -ve if we have been idle for // a long time (2gb seconds). if (current_idle_time < 0) current_idle_time = INT_MAX; ... } The error was found through the V547 diagnostic: Expression 'current_idle_time < 0' is always false. Unsigned type value is never < 0. browser idle_win.cc 23 To handle time, a variable of the unsigned type is used. As a result, check of too large values does not work. This is the correct code: if (current_idle_time > INT_MAX)
  • 42. current_idle_time = INT_MAX; Example 6. ICU project. Error in condition. U_CDECL_BEGIN static const char* U_CALLCONV _processVariableTop(...) { ... if(i == locElementCapacity && (*string != 0 || *string != '_')) { *status = U_BUFFER_OVERFLOW_ERROR; } ... } The error was found through the V547 diagnostic: Expression '*string != 0 || *string != '_'' is always true. Probably the '&&' operator should be used here. icui18n ucol_sit.cpp 242 The condition contains a logical error. The "(*string != 0 || *string != '_')" subexpression is always true. It is impossible that one and the same string character is not equal to 0 and '_' at a time. Example 7. QT project. Dangerous loop. bool equals( class1* val1, class2* val2 ) const{ { ... size_t size = val1->size(); ... while ( --size >= 0 ){ if ( !comp(*itr1,*itr2) ) return false; itr1++; itr2++;
  • 43. } ... } The error was found through the V547 diagnostic: Expression '--size >= 0' is always true. Unsigned type value is always >= 0. QtCLucene arrays.h 154 The (--size >= 0) condition is always true, since the size variable has the unsigned type. It means that if two sequences being compared are alike, we will get an overflow that will in its turn cause Access Violation or other program failures. This is the correct code: for (size_t i = 0; i != size; i++){ if ( !comp(*itr1,*itr2) ) return false; itr1++; itr2++; } Example 8. MySQL project. Error in condition. enum enum_mysql_timestamp_type str_to_datetime(...) { ... else if (str[0] != 'a' || str[0] != 'A') continue; /* Not AM/PM */ ... } The error was found through the V547 diagnostic: Expression 'str [0] != 'a' || str [0] != 'A'' is always true. Probably the '&&' operator should be used here. clientlib my_time.c 340 The condition is always true because the character is always either not equal to 'a' or to 'A'. This is the correct check: else if (str[0] != 'a' && str[0] != 'A')
  • 44. Example 9. QT project. Incorrect count of references. STDMETHODIMP QEnumPins::QueryInterface(const IID &iid,void **out) { ... if (S_OK) AddRef(); return hr; } The error was found through the V545 diagnostic: Such conditional expression of 'if' operator is incorrect for the HRESULT type value '(HRESULT) 0L'. The SUCCEEDED or FAILED macro should be used instead. phonon_ds9 qbasefilter.cpp 60 The check condition is represented by the S_OK constant. Since S_OK is 0, the AddRef() function will never be called. This is how this check must look: if (hr == S_OK). Example 10. TickerTape project. Incorrect tornado. void GetWindAtSingleTornado(...) { ... if(radius < THRESH * 5) *yOut = THRESH * 10 / radius; else if (radius < THRESH * 5) *yOut = -3.0f / (THRESH * 5.0f) * (radius - THRESH * 5.0f) + 3.0f; else *yOut = 0.0f; ... } The error was found through the V517 diagnostic: The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. TickerTape wind.cpp 118 The second condition is always false. The reason is that the first condition coincides with the second. There must be a misprint here.
  • 45. Example 11. Apache HTTP Server project. Error of socket handling in Windows. typedef UINT_PTR SOCKET; static unsigned int __stdcall win9x_accept(void * dummy) { SOCKET csd; ... do { clen = sizeof(sa_client); csd = accept(nsd, (struct sockaddr *) &sa_client, &clen); } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error())); ... } The error was found through the V547 diagnostic: Expression 'csd < 0' is always false. Unsigned type value is never < 0. libhttpd child.c 404 Socket handling errors very often emerge in crossplatform programs built under Windows. In Linux, socket descriptors are represented by the signed type, while in Windows it is the unsigned type. Programmers often forget about this and check the error status by comparing the value to 0. This is incorrect; you must use specialized constants. Example 12. QT project. Misprint in comparisons. QStringList ProFileEvaluator::Private::values(...) { ... else if (ver == QSysInfo::WV_NT) ret = QLatin1String("WinNT"); else if (ver == QSysInfo::WV_2000) ret = QLatin1String("Win2000"); else if (ver == QSysInfo::WV_2000) <<-- ret = QLatin1String("Win2003");
  • 46. else if (ver == QSysInfo::WV_XP) ret = QLatin1String("WinXP"); ... } The error was found through the V517 diagnostic: The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 2303, 2305. lrelease profileevaluator.cpp 2303 In the string we have marked, there must be the text "ver == QSysInfo::WV_2003". Because of this error, the "ret = QLatin1String("Win2003")" statement will never be executed. Code vulnerabilities Of course, errors leading to code vulnerabilities are actually misprints, incorrect conditions and incorrect array handling. But we decided to single out certain errors into a separate group because they relate to the notion of software vulnerabilities. An intruder, using such errors, can try to disturb program operation, perform an attack to gain extended rights or carry out any other actions he/she needs. Example 1. Ultimate TCP/IP project. Incorrect check of an empty string. char *CUT_CramMd5::GetClientResponse(LPCSTR ServerChallenge) { ... if (m_szPassword != NULL) { ... if (m_szPassword != '0') { ... } The error was found through the V528 diagnostic: It is odd that pointer to 'char' type is compared with the '0' value. Probably meant: *m_szPassword != '0'. UTMail ut_crammd5.cpp 333 This code fragment must check that the pointer to the password is not equal to NULL and that the string is not empty. But instead, the code checks twice that the pointer is not equal to NULL. The check of the string does not work. The "if (m_szPassword != '0')" condition was intended to check that there is a terminal null in the very beginning of the string, which means that the string is empty. But a pointer
  • 47. dereferencing operation is missing here, and it is the pointer itself which is compared to zero. This is the correct code: if (m_szPassword != NULL) { ... if (*m_szPassword != '0') Example 2. Chromium project. Null pointer handling. bool ChromeFrameNPAPI::Invoke(...) { ChromeFrameNPAPI* plugin_instance = ChromeFrameInstanceFromNPObject(header); if (!plugin_instance && (plugin_instance->automation_client_.get())) return false; ... } The error was found through the V522 diagnostic: Dereferencing of the null pointer 'plugin_instance' might take place. Check the logical condition. chrome_frame_npapi chrome_frame_npapi.cc 517 The condition that checks the null pointer is written incorrectly. As a result, we have a segmentation error. This is the correct code: if (plugin_instance && (plugin_instance->automation_client_.get())) return false; Example 3. SMTP Client with SSL/TLS project. Incomplete buffer clearing. void MD5::finalize () { ... uint1 buffer[64]; ... // Zeroize sensitive information
  • 48. memset (buffer, 0, sizeof(*buffer)); ... } The error was found through the V512 diagnostic: A call of the 'memset' function will lead to a buffer overflow or underflow. CSmtp md5.cpp 212 For security purposes, the function tries to clear the buffer containing sensitive information. But it fails. Only the first byte will be cleared in the buffer. The error is this: the 'sizeof' operator calculates the size of the 'uint1' type instead of buffer. This is the correct code: memset (buffer, 0, sizeof(buffer)); Generally, errors of incomplete memory clearing are rather frequent. Consider some other cases like this. Example 4. Chromium. Incomplete buffer clearing. void Time::Explode(..., Exploded* exploded) const { ... ZeroMemory(exploded, sizeof(exploded)); ... } The error was found through the V512 diagnostic: A call of the 'memset' function will lead to underflow of the buffer '(exploded)'. base time_win.cc 227 The ZeroMemory function clears only part of the Exploded structure. The reason is that the 'sizeof' operator returns the pointer size. To fix the error, we must dereference the pointer: ZeroMemory(exploded, sizeof(*exploded)); Example 5. Apache HTTP Server project. Incomplete buffer clearing. #define MEMSET_BZERO(p,l) memset((p), 0, (l)) void apr__SHA256_Final(..., SHA256_CTX* context) { ... MEMSET_BZERO(context, sizeof(context)); ... }
  • 49. The error was found through the V512 diagnostic: A call of the 'memset' function will lead to underflow of the buffer '(context)'. apr sha2.c 560 The error is completely identical to the previous one. The 'sizeof' operator calculates the pointer size. To fix it, we must write: "sizeof(*context)". Example 6. Miranda IM project. Incorrect string handling. static char *_skipblank(char * str) { char * endstr=str+strlen(str); while ((*str==' ' || *str=='t') && str!='0') str++; while ((*endstr==' ' || *endstr=='t') && endstr!='0' && endstr<str) endstr--; ... } The error was found through the diagnostics: V528 It is odd that pointer to 'char' type is compared with the '0' value. Probably meant: *str != '0'. clist_modern modern_skinbutton.cpp 282 V528 It is odd that pointer to 'char' type is compared with the '0' value. Probably meant: *endstr != '0'. clist_modern modern_skinbutton.cpp 283 This code is rather dangerous because it incorrectly determines the string end. It may cause a string overflow and, as a consequence, an Access Violation exception. The error lies here: "str!='0'" and here: "endstr!='0'". A pointer dereferencing operation is missing. This is the correct code: while ((*str==' ' || *str=='t') && *str!='0') str++; while ((*endstr==' ' || *endstr=='t') && *endstr!='0' && endstr<str) endstr--; Example 7. PNG library project. Accidental pointer clearing. png_size_t png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) {
  • 50. ... if (key_len > 79) { png_warning(png_ptr, "keyword length must be 1 - 79 characters"); new_key[79] = '0'; key_len = 79; } ... } The error was found through the V527 diagnostic: It is odd that the '0' value is assigned to 'char' type pointer. Probably meant: *new_key [79] = '0'. graphics3D pngwutil.c 1283 This sample demonstrates a mistake when the programmer accidentally clears the pointer instead of truncating the string length. The point is that 'new_key' is a pointer to a string. And it means that we should write our code as follows to truncate it to 79 characters: (*new_key)[79] = '0'; Example 8. Intel AMT SDK project. Unverified user name. static void wsman_set_subscribe_options(...) { ... if (options->delivery_certificatethumbprint || options->delivery_password || options->delivery_password) { ... } The error was found through the V501 diagnostic: There are identical sub-expressions 'options- >delivery_password' to the left and to the right of the '||' operator. OpenWsmanLib wsman-client.c 631 Because of the developer's inattention, presence of password is checked twice, while presence of user name is not checked at all. This is the correct code: if (options->delivery_certificatethumbprint ||
  • 51. options->delivery_username || options->delivery_password) { Example 9. Ultimate TCP/IP project. Incorrect handling of empty strings. void CUT_StrMethods::RemoveCRLF(LPSTR buf) { // v4.2 changed to size_t size_t len, indx = 1; if(buf != NULL){ len = strlen(buf); while((len - indx) >= 0 && indx <= 2) { if(buf[len - indx] == 'r' || buf[len - indx] == 'n') buf[len - indx] = 0; ++indx; } } } The error was found through the V547 diagnostic: Expression '(len - indx) >= 0' is always true. Unsigned type value is always >= 0. UTDns utstrlst.cpp 58 The "len - indx" expression has the unsigned type 'size_t' and is always >= 0. Let's look what it will result in, if we send an empty string to the input. If the string is empty, then: len = 0, indx = 1. The len - indx expression is equal to 0xFFFFFFFFu. Since 0xFFFFFFFFu > 0 and indx <= 2, an array access is performed "buf[len - indx]". The "buf[0xFFFFFFFFu]" operation will cause Access Violation. Example 10. Miranda IM project. Underflow protection does not work. void Append( PCXSTR pszSrc, int nLength )
  • 52. { ... UINT nOldLength = GetLength(); if (nOldLength < 0) { // protects from underflow nOldLength = 0; } ... } The error was found through the V547 diagnostic: Expression 'nOldLength < 0' is always false. Unsigned type value is never < 0. IRC mstring.h 229 The check "if (nOldLength < 0)" does not work since the nOldLength variable has the unsigned type. Example 11. Apache HTTP Server project. Incorrect handling of negative values. typedef size_t apr_size_t; APU_DECLARE(apr_status_t) apr_memcache_getp(...) { ... apr_size_t len = 0; ... len = atoi(length); ... if (len < 0) { *new_length = 0; *baton = NULL; } else { ... }
  • 53. } The error was found through the V547 diagnostic: Expression 'len < 0' is always false. Unsigned type value is never < 0. aprutil apr_memcache.c 814 The check "if (len < 0)" does not work because the 'len' variable has the unsigned type. Example 12. Ultimate TCP/IP project. Incorrect condition of loop termination. void CUT_StrMethods::RemoveSpaces(LPSTR szString) { ... size_t loop, len = strlen(szString); // Remove the trailing spaces for(loop = (len-1); loop >= 0; loop--) { if(szString[loop] != ' ') break; } ... } The error was found through the V547 diagnostic: Expression 'loop >= 0' is always true. Unsigned type value is always >= 0. UTDns utstrlst.cpp 430 Suppose the whole string consists only of spaces. While searching the characters, the program will reach the null item of the string, and the 'loop' variable will equal to zero. Then it will be decremented once again. Since this variable is of unsigned type, its value will be 0xFFFFFFFFu or 0xFFFFFFFFFFFFFFFFu (depending on the architecture). This value is 'naturally >= 0', and a new loop iteration will start. There will be an attempt of memory access by szString[0xFFFFFFFFu] address - the consequences of this are familiar to every C/C++ programmer. Example 13. Crypto++ project. Private data clearing error. void CAST256::Base::UncheckedSetKey(const byte *userKey, unsigned int keylength, const NameValuePairs &) { AssertValidKeyLength(keylength); word32 kappa[8]; ...
  • 54. memset(kappa, 0, sizeof(kappa)); } The error has been found with rule V597: The compiler could delete the 'memset' function call, which is used to flush 'kappa' buffer. The RtlSecureZeroMemory() function should be used to erase the private data. cryptlib cast.cpp 293 The problem is in the memset() function. The arguments passed into the function are correct. If a programmer looks how the Debug-version of this code works in the debugger, he/she won't notice the trouble either. The error occurs in the Release version of the project. The data that should have been cleared will remain in memory. The reason is that the compiler has the right to delete the call of the memset() function during optimization, and this is what it does. If you want know why it happens, read the article "Overwriting memory - why?". Copy-Paste Developers should not also underestimate Copy-Paste errors as well as common misprints. They are very-very numerous. Programmers spend much time on debugging them. Of course, misprints and Copy-Paste errors are similar, but there is a difference between them that caused us to place them into different groups in this article. Misprints often result in using a wrong variable instead of the needed one. And in the case of copy-paste, programmers simply forget to edit copied and pasted lines. Example 1. Fennec Media Project project. Mistake while handling array items. void* tag_write_setframe(char *tmem, const char *tid, const string dstr) { ... if(lset) { fhead[11] = '0'; fhead[12] = '0'; fhead[13] = '0'; fhead[13] = '0'; } ... }
  • 55. The error was found through the V525 diagnostic: The code containing the collection of similar blocks. Check items '11', '12', '13', '13' in lines 716, 717, 718, 719. id3 editor.c 716 The four similar lines must have appeared in the code through the copy-paste method. When the programmer started editing the indexes, he/she made a mistake that causes zero to be written into 'fhead[13] ' twice and not be written into 'fhead[14] '. Example 2. MySQL project. Mistake while handling array items. static int rr_cmp(uchar *a,uchar *b) { if (a[0] != b[0]) return (int) a[0] - (int) b[0]; if (a[1] != b[1]) return (int) a[1] - (int) b[1]; if (a[2] != b[2]) return (int) a[2] - (int) b[2]; if (a[3] != b[3]) return (int) a[3] - (int) b[3]; if (a[4] != b[4]) return (int) a[4] - (int) b[4]; if (a[5] != b[5]) return (int) a[1] - (int) b[5]; if (a[6] != b[6]) return (int) a[6] - (int) b[6]; return (int) a[7] - (int) b[7]; } The error was found through the V525 diagnostic: The code containing the collection of similar blocks. Check items '0', '1', '2', '3', '4', '1', '6' in lines 680, 682, 684, 689, 691, 693, 695. sql records.cc 680 It is not apparent at first sight, so let's single it out: return (int) a[1] - (int) b[5]; Actually there must be the following code: return (int) a[5] - (int) b[5];
  • 56. Example 3. TortoiseSVN project. File name not corrected. BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS) { return(GetInMemoryFileVersion(("DBGHELP.DLL"), dwMS, dwLS)) ; } BOOL GetDbgHelpVersion(DWORD &dwMS, DWORD &dwLS) { return(GetInMemoryFileVersion(("DBGHELP.DLL"), dwMS, dwLS)) ; } The error was found through the V524 diagnostic: It is odd that the 'GetDbgHelpVersion' function is fully equivalent to the 'GetImageHlpVersion' function (SymbolEngine.h, line 98). symbolengine.h 105 The 'GetImageHlpVersion' function must have appeared through copying and pasting the 'GetInMemoryFileVersion' function. The error is this: the programmer forgot to fix the file name in the copied and pasted function. This is the correct code: BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS) { return(GetInMemoryFileVersion(("IMAGEHLP.DLL"), dwMS, dwLS)) ; } Example 4. Clang project. Identical function bodies. MapTy PerPtrTopDown; MapTy PerPtrBottomUp;
  • 57. void clearBottomUpPointers() { PerPtrTopDown.clear(); } void clearTopDownPointers() { PerPtrTopDown.clear(); } The error was found through the V524 diagnostic: It is odd that the body of 'clearTopDownPointers' function is fully equivalent to the body of 'clearBottomUpPointers' function (ObjCARC.cpp, line 1318). LLVMScalarOpts objcarc.cpp 1322 The body of the clearBottomUpPointers function seems to be incorrect; this function should be written as follows: void clearBottomUpPointers() { PerPtrBottomUp.clear(); } Example 5. QT. Unsuccessful swap. bool qt_testCollision(...) { ... t=x1; x1=x2; x2=t; t=y1; x1=y2; y2=t; ... } The error was found through the V519 diagnostic: The 'x1' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2218, 2219. Qt3Support q3canvas.cpp 2219 The first line is absolutely correct and swaps values in the x1 and x2 variables. In the second line, variables y1 and y2 must be swapped. This line is probably a copy of the previous one. All the 'x' letters must be replaced with letters 'y'. Unfortunately, the programmer forgot to do that in one place: "... x1=y2; ...". Correct code: t=x1; x1=x2; x2=t;
  • 58. t=y1; y1=y2; y2=t; Example 6. Crystal Space 3D SDK project. Identical subexpressions. inline_ bool Contains(const LSS& lss) { return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius)); } The error was found through the V501 diagnostic: There are identical sub-expressions to the left and to the right of the '&&' operator. plgcsopcode icelss.h 69 The error is this: the 'lss.mP0.' variable is used twice here. There must be 'lss.mP1' in the first part of the expression. Example 7. Notepad++ project. Setting an incorrect style. void KeyWordsStyleDialog::updateDlg() { ... Style & w1Style = _pUserLang->_styleArray.getStyler(STYLE_WORD1_INDEX); styleUpdate(w1Style, _pFgColour[0], _pBgColour[0], IDC_KEYWORD1_FONT_COMBO, IDC_KEYWORD1_FONTSIZE_COMBO, IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK, IDC_KEYWORD1_UNDERLINE_CHECK); Style & w2Style = _pUserLang->_styleArray.getStyler(STYLE_WORD2_INDEX); styleUpdate(w2Style, _pFgColour[1], _pBgColour[1], IDC_KEYWORD2_FONT_COMBO, IDC_KEYWORD2_FONTSIZE_COMBO, IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK, IDC_KEYWORD2_UNDERLINE_CHECK);
  • 59. Style & w3Style = _pUserLang->_styleArray.getStyler(STYLE_WORD3_INDEX); styleUpdate(w3Style, _pFgColour[2], _pBgColour[2], IDC_KEYWORD3_FONT_COMBO, IDC_KEYWORD3_FONTSIZE_COMBO, IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_UNDERLINE_CHECK); Style & w4Style = _pUserLang->_styleArray.getStyler(STYLE_WORD4_INDEX); styleUpdate(w4Style, _pFgColour[3], _pBgColour[3], IDC_KEYWORD4_FONT_COMBO, IDC_KEYWORD4_FONTSIZE_COMBO, IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK, IDC_KEYWORD4_UNDERLINE_CHECK); ... } The error was found through the V525 diagnostic: The code containing the collection of similar blocks. Check items '7', '7', '6', '7' in lines 576, 580, 584, 588 It is almost unreal to find this error by sight, so let's abridge the text to single out the most interesting fragments: styleUpdate(... IDC_KEYWORD1_BOLD_CHECK, IDC_KEYWORD1_ITALIC_CHECK, ...); styleUpdate(... IDC_KEYWORD2_BOLD_CHECK, IDC_KEYWORD2_ITALIC_CHECK, ...); styleUpdate(... IDC_KEYWORD3_BOLD_CHECK, IDC_KEYWORD3_BOLD_CHECK, <<-- ...); styleUpdate(... IDC_KEYWORD4_BOLD_CHECK, IDC_KEYWORD4_ITALIC_CHECK,
  • 60. ...); By mistake, IDC_KEYWORD3_BOLD_CHECK is used instead of IDC_KEYWORD3_ITALIC_CHECK. Example 8. ReactOS object. Choosing a wrong object. void CardButton::DrawRect(HDC hdc, RECT *rect, bool fNormal) { ... HPEN hhi = CreatePen(0, 0, MAKE_PALETTERGB(crHighlight)); HPEN hsh = CreatePen(0, 0, MAKE_PALETTERGB(crShadow)); ... if(fNormal) hOld = SelectObject(hdc, hhi); else hOld = SelectObject(hdc, hhi); ... } The error was found through the V523 diagnostic: The 'then' statement is equivalent to the 'else' statement. cardlib cardbutton.cpp 83 The 'hsh' object is not used, while 'hhi' is used twice. This is the correct code: if(fNormal) hOld = SelectObject(hdc, hhi); else hOld = SelectObject(hdc, hsh); Example 9. IPP Samples project. Incorrect check. Status VC1VideoDecoder::ResizeBuffer() { ... if(m_pContext && m_pContext->m_seqLayerHeader && m_pContext->m_seqLayerHeader->heightMB &&
  • 61. m_pContext->m_seqLayerHeader->heightMB) ... } The error was found through the V501 diagnostic: There are identical sub-expressions 'm_pContext- >m_seqLayerHeader->heightMB' to the left and to the right of the '&&' operator. vc1_dec umc_vc1_video_decoder.cpp 1347 Correct code: if(m_pContext && m_pContext->m_seqLayerHeader && m_pContext->m_seqLayerHeader->heightMB && m_pContext->m_seqLayerHeader->widthMB) Example 10. ReactOS project. Mistake in a variable name. BOOL APIENTRY GreStretchBltMask(...) { ... MaskPoint.x += DCMask->ptlDCOrig.x; MaskPoint.y += DCMask->ptlDCOrig.x; ... } The error was found through the V537 diagnostic: Consider reviewing the correctness of 'x' item's usage. win32k bitblt.c 670 This is a very good example where you can see that a line was copied and pasted. After that, the programmer fixed the first name 'x' but forgot to fix the second. This is the correct code: MaskPoint.x += DCMask->ptlDCOrig.x; MaskPoint.y += DCMask->ptlDCOrig.y; Late check of null pointers C/C++ programmers have to check numerous pointers all the time to make sure that they are not equal to zero. Since these checks are numerous, the chance to make a mistake is also big. It often happens that a pointer is used first and only then is compared to NULL. Errors of this type reveal themselves very rarely. Usually the program works correctly in the standard mode and fails only in case of a non-
  • 62. standard situation. Instead of correctly processing a null pointer in normal mode, an Access Violation will occur and an exception will be thrown. Example 1. Quake-III-Arena project. Late check. void Item_Paint(itemDef_t *item) { vec4_t red; menuDef_t *parent = (menuDef_t*)item->parent; red[0] = red[3] = 1; red[1] = red[2] = 0; if (item == NULL) { return; } ... } The error has been found with rule V595: The 'item' pointer was utilized before it was verified against nullptr. Check lines: 3865, 3869. cgame ui_shared.c 3865 The 'item' pointer is used first and only then is compared to NULL. Example 2. LAME Ain't an MP3 Encoder project. Late check. static int check_vbr_header(PMPSTR mp, int bytes) { ... buf = buf->next; pos = buf->pos; if(!buf) return -1; /* fatal error */ ... } The error has been found with rule V595: The 'buf' pointer was utilized before it was verified against nullptr. Check lines: 226, 227. mpglib interface.c 226
  • 63. If 'buf' equals NULL, an exception will be thrown instead of returning the error code. And if exceptions are not used, the program will crash. Example 3. daoParanoia library project. Late check. static long i_stage2_each(root_block *root, v_fragment *v, void(*callback)(long,int)) { cdrom_paranoia *p=v->p; long dynoverlap=p->dynoverlap/2*2; if (!v || !v->one) return(0); ... } The error has been found with rule V595: The 'v' pointer was utilized before it was verified against nullptr. Check lines: 532, 535. daoParanoia paranoia.c 532 The situation here is identical to the previous ones. Example 4. TrinityCore project. Late check. bool OnCheck(Player* player, Unit* /*target*/) { bool checkArea = player->GetAreaId() == AREA_ARGENT_TOURNAMENT_FIELDS || player->GetAreaId() == AREA_RING_OF_ASPIRANTS || player->GetAreaId() == AREA_RING_OF_ARGENT_VALIANTS || player->GetAreaId() == AREA_RING_OF_ALLIANCE_VALIANTS || player->GetAreaId() == AREA_RING_OF_HORDE_VALIANTS || player->GetAreaId() == AREA_RING_OF_CHAMPIONS; return player && checkArea && player->duel && player->duel->isMounted; }
  • 64. The error has been found with rule V595: The 'player' pointer was utilized before it was verified against nullptr. Check lines: 310, 312. scripts achievement_scripts.cpp 310 As you can see from the "player && ..." condition, the 'player' pointer can be equal to zero. However, this check, like in all the previous examples, is too late. We could cite many examples of such errors, but they are all alike. If you have seen a couple of such errors, be sure you've seen them all. Miscellaneous Example 1. Image Processing SDK project. Octal number. inline void elxLuminocity(const PixelRGBus& iPixel, LuminanceCell< PixelRGBus >& oCell) { oCell._luminance = uint16(0.2220f*iPixel._red + 0.7067f*iPixel._blue + 0.0713f*iPixel._green); oCell._pixel = iPixel; } inline void elxLuminocity(const PixelRGBi& iPixel, LuminanceCell< PixelRGBi >& oCell) { oCell._luminance = 2220*iPixel._red + 7067*iPixel._blue + 0713*iPixel._green; oCell._pixel = iPixel; } The error was found through the V536 diagnostic: Be advised that the utilized constant value is represented by an octal form. Oct: 0713, Dec: 459. IFF plugins pixelservices.inl 146 If you examine the second function, you will see that the programmer intended to use number 713, not 0713. Number 0713 is declared in the octal numeral system. You can easily forget about it if you seldom use octal constants.
  • 65. Example 2. IPP Samples project. One variable for two loops. JERRCODE CJPEGDecoder::DecodeScanBaselineNI(void) { ... for(c = 0; c < m_scan_ncomps; c++) { block = m_block_buffer + (DCTSIZE2*m_nblock*(j+(i*m_numxMCU))); // skip any relevant components for(c = 0; c < m_ccomp[m_curr_comp_no].m_comp_no; c++) { block += (DCTSIZE2*m_ccomp[c].m_nblocks); } ... } The error was found through the V535 diagnostic: The variable 'c' is being used for this loop and for the outer loop. jpegcodec jpegdec.cpp 4652 One and the same variable is used for the outer loop and the inner loop. As a result, this code will handle only part of the data or cause an eternal loop. Example 3. Quake-III-Arena project. Missing return. static ID_INLINE int BigLong(int l) { LongSwap(l); } The error has been found with rule V591: Non-void function should return a value. botlib q_shared.h 155 This code is written in C. It means that the compiler doesn't require that return should be necessarily present. But it is really necessary here. However, the code can work well due to sheer luck. Everything depends on what the EAX register contains. But it's just luck and nothing more. The function body should have been written this way: { return LongSwap(l); }. Example 4. Notepad++ project. Odd condition.
  • 66. int Notepad_plus::getHtmlXmlEncoding(....) const { ... if (langT != L_XML && langT != L_HTML && langT == L_PHP) return -1; ... } The error has been found with rule V590: Consider inspecting this expression. The expression is excessive or contains a misprint. Notepad++ notepad_plus.cpp 853 Perhaps this error is just a misprint, but it also could have appeared during factoring. However, it is obvious. The condition can be simplified: if (langT == L_PHP). It means that the code must have looked this way: if (langT != L_XML && langT != L_HTML && langT != L_PHP) References 1. PVS-Studio Main Product Page. http://www.viva64.com/en/pvs-studio/ 2. Download the fully functional trial. http://www.viva64.com/en/pvs-studio-download/ 3. Buy PVS-Studio. http://www.viva64.com/en/order/ 4. PVS-Studio Documentation. http://www.viva64.com/en/d/ 5. Feedback. http://www.viva64.com/en/about-feedback/ 6. Twitter. http://twitter.com/Code_Analysis