Static analysis of C++ source code
Karpov Andrey Nikolaevich candidate of science (PhD), CTO OOO Program Verification Systems (Co Ltd) E-mail: [email protected]
What is this report about
We all make mistakes while programming and spend a lot of time fixing them. One of the methods which allows for quick detection of defects is source code static analysis.
One should write a quality code from the beginning - it is not working in practice!
even the best developers make mistakes and typing errors; following are the examples of mistakes detected by static code analyzer in a well known projects; PVS-Studio tool was used to perform the analysis.
Priority of & and ! operations
Return to Castle Wolfenstein computer game, first person shooter, developed by id Software company. Game engine is available under GPL license.
#define SVF_CASTAI 0x00000010
if ( !ent->r.svFlags & SVF_CASTAI )
if ( ! (ent->r.svFlags & SVF_CASTAI) )
Usage of && instead of &
Stickies yellow sticky notes, just only on your monitor.
#define REO_INPLACEACTIVE #define REO_OPEN
(0x02000000L) (0x04000000L)
if (reObj.dwFlags && REO_INPLACEACTIVE) m_pRichEditOle->InPlaceDeactivate(); if(reObj.dwFlags && REO_OPEN) hr = reObj.poleobj->Close(OLECLOSE_NOSAVE);
Undefined behavior
Miranda IM (Miranda Instant Messenger) instant messaging software for Microsoft Windows.
while (*(n = ++s + strspn(s, EZXML_WS)) && *n != '>') {
Usage of `delete` for an array
Chromium open source web browser developed by Google. The development of Google Chrome browser is based upon Chromium.
auto_ptr<VARIANT> child_array(new VARIANT[child_count]); You should not use auto_ptr with arrays. Only one element is destroyed inside auto_ptr destructor: ~auto_ptr() { delete _Myptr; } For example you can use boost::scoped_array as an alternative.
Condition is always true
WinDjView is fast and small app for viewing files of DjVu format.
inline bool IsValidChar(int c) { return c == 0x9 || 0xA || c == 0xD || c >= 0x20 && c <= 0xD7FF || c >= 0xE000 && c <= 0xFFFD || c >= 0x10000 && c <= 0x10FFFF; }
Code formatting differs from it s own logic
Squirrel interpreted programming language, which is developed to be used as a scripting language in real time applications such as computer games.
if(pushval != 0) if(pushval) v->GetUp(-1) = t; else v->Pop(1); v->Pop(1); - will never be reached
Incidental local variable declaration
FCE Ultra open source Nintendo Entertainment System console emulator
int iNesSaveAs(char* name) { ... fp = fopen(name,"wb"); int x = 0; if (!fp) int x = 1; ... }
Using char as unsigned char
// 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; }
TortoiseSVN client of Subversion revision control system, implemented as Windows shell extension.
Incidental use of hexadecimal values
oCell._luminance = uint16(0.2220f*iPixel._red + 0.7067f*iPixel._blue + 0.0713f*iPixel._green); .... oCell._luminance = 2220*iPixel._red + 7067*iPixel._blue + 0713*iPixel._green;
eLynx Image Processing SDK and Lab
One variable is used for two loops
Lugaru first commercial game developed by Wolfire Games independent team.
static int i,j,k,l,m; ... for(j=0; j<numrepeats; j++){ ... for(i=0; i<num_joints; i++){ ... for(j=0;j<num_joints;j++){ if(joints[j].locked)freely=0; } ... } ... }
Array overrun
LAME free app for MP3 audio encoding.
#define SBMAX_l int l[1+SBMAX_l];
22
for (r0 = 0; r0 < 16; r0++) { ... for (r1 = 0; r1 < 8; r1++) { int a2 = gfc->scalefac_band.l[r0 + r1 + 2];
Priority of * and ++ operations
eMule is a client for ED2K file sharing network.
STDMETHODIMP CCustomAutoComplete::Next(..., ULONG *pceltFetched) { ... if (pceltFetched != NULL) *pceltFetched++; ... } (*pceltFetched)++;
Comparison mistake
WinMerge free open source software intended for the comparison and synchronization of files and directories.
BUFFERTYPE m_nBufferType[2]; ... // Handle unnamed buffers if ((m_nBufferType[nBuffer] == BUFFER_UNNAMED) || (m_nBufferType[nBuffer] == BUFFER_UNNAMED)) nSaveErrorCode = SAVE_NO_FILENAME; By reviewing the code close by, this should contain: (m_nBufferType[0] == BUFFER_UNNAMED) || (m_nBufferType[1] == BUFFER_UNNAMED)
Forgotten array index
void lNormalizeVector_32f_P3IM(..., Ipp32s* mask, ...) { Ipp32s i; Ipp32f norm; for(i=0; i<len; i++) { if(mask<0) continue; ... } } if(mask[i]<0) continue;
IPP Samples are samples demonstrating how to work with Intel Performance Primitives Library 7.0.
Identical source code branches
Notepad++ - free text editor for Windows supporting syntax highlight for a variety of programming languages.
if (!_isVertical) Flags |= DT_BOTTOM; else Flags |= DT_BOTTOM;
if (!_isVertical) Flags |= DT_VCENTER; else Flags |= DT_BOTTOM;
Calling incorrect function with similar name
What a beautiful comment. But it is sad that here we re doing not what was intended.
/** Deletes all previous field specifiers. * This should be used when dealing * with clients that send multiple NEP_PACKET_SPEC * messages, so only the last PacketSpec is taken * into account. */ int NEPContext::resetClientFieldSpecs(){ this->fspecs.empty(); return OP_SUCCESS; } /* End of resetClientFieldSpecs() */
Nmap Security Scanner free utility intended for diverse customizable scanning of IP-networks with any number of objects and for identification of the statuses of the objects belonging to the network which is being scanned.
Dangerous ?: operator
Newton Game Dynamics a well known physics engine which allows for reliable and fast simulation of environmental object s physical behavior.
den = dgFloat32 (1.0e-24f) * (den > dgFloat32(0.0f)) ? dgFloat32(1.0f) : dgFloat32(-1.0f); The priority of ?: is lower than that of multiplication operator *.
And so on, and so on
if (m_szPassword != NULL) { if (m_szPassword != '\0') { if (*m_szPassword != '\0')
Ultimate TCP/IP library
bleeding = 0; bleedx = 0,bleedy; direction = 0; bleedx = 0; bleedy = 0;
Lugaru
And so on, and so on
if((t=(char *)realloc( next->name, strlen(name+1)))) if((t=(char *)realloc( next->name, strlen(name)+1)))
FCE Ultra
minX=max(0,minX+mcLeftStart-2); minY=max(0,minY+mcTopStart-2); maxX=min((int)width,maxX+mcRightEnd-1); maxY=min((int)height,maxX+mcBottomEnd-1); minX=max(0,minX+mcLeftStart-2); minY=max(0,minY+mcTopStart-2); maxX=min((int)width,maxX+mcRightEnd-1); maxY=min((int)height,maxY+mcBottomEnd-1);
Low level memory management operations
I want to discuss separately the heritage of programs whish were using the following functions: ZeroMemory; memset; memcpy; memcmp;
Low level memory management operations
ID_INLINE mat3_t::mat3_t( float src[3][3] ) { memcpy( mat, src, sizeof( src ) ); } ID_INLINE mat3_t::mat3_t( float (&src)[3][3] ) { memcpy( mat, src, sizeof( src ) ); }
Return to Castle Wolfenstein
itemInfo_t *itemInfo; memset( itemInfo, 0, sizeof( &itemInfo ) ); memset( itemInfo, 0, sizeof( *itemInfo ) );
Low level memory management operations
CxImage open image processing library.
memset(tcmpt->stepsizes, 0, sizeof(tcmpt->numstepsizes * sizeof(uint_fast16_t))); memset(tcmpt->stepsizes, 0, tcmpt->numstepsizes * sizeof(uint_fast16_t));
Low level memory management operations
A beautiful example of 64-bit error:
dgInt32 faceOffsetHitogram[256]; dgSubMesh* mainSegmenst[256]; memset (faceOffsetHitogram, 0, sizeof (faceOffsetHitogram)); memset (mainSegmenst, 0, sizeof (faceOffsetHitogram));
This code was duplicated but was not entirely corrected. As a result the size of pointer will not be equal to the size of dgInt32 type on Win64 and we will flush only a fraction of mainSegmenst array.
Low level memory management operations
#define CONT_MAP_MAX 50 int _iContMap[CONT_MAP_MAX]; ... memset(_iContMap, -1, CONT_MAP_MAX); memset(_iContMap, -1, CONT_MAP_MAX * sizeof(int));
Low level memory management operations
OGRE open source Object-Oriented Graphics Rendering Engine written in C++.
Real w, x, y, z; ... inline Quaternion(Real* valptr) { memcpy(&w, valptr, sizeof(Real)*4); }
Yes, at present this is not a mistake. But it is a landmine!
The earlier the better
But why not the unit-tests only?
The verification of such code parts which rarely gain control; Detection of floating bugs (undefined behavior, heisenbugs); Not all variations of source code can be covered by unit tests:
Complicated calculation algorithms interface
Unit test will not be able to help you here, but static analysis will.
Fennec Media Project universal media-player intended for high definition audio and video playback.
OPENFILENAME lofn; ... lofn.lpstrFilter = uni("All Files (*.*)\0*.*"); lofn.lpstrFilter = uni("All Files (*.*)\0*.*\0");
Unit test will not be able to help you here, but static analysis will.
static INT_PTR CALLBACK DlgProcTrayOpts(...) { ... EnableWindow(GetDlgItem(hwndDlg,IDC_PRIMARYSTATUS),TRUE); EnableWindow(GetDlgItem(hwndDlg,IDC_CYCLETIMESPIN),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_CYCLETIME),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_ALWAYSPRIMARY),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_ALWAYSPRIMARY),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_CYCLE),FALSE); EnableWindow(GetDlgItem(hwndDlg,IDC_MULTITRAY),FALSE); ... }
Where can I more details about PVSStudio?
Product page: http://www.viva64.com/en/pvs-studio/ Trial version: http://www.viva64.com/en/pvs-studiodownload/ Documentation: http://www.viva64.com/en/d/
PVS-Studio static code analyzer intended for the detection of errors in the source code of applications developed with C/C++/C++0x. Language. PVS-Studio can be integrated into Visual Studio 2005/2008/2010 IDE.
Questions ?
Contacts: Karpov Andrey Nikolaevich candidate of science (PhD), CTO OOO Program Verification Systems (Co Ltd) Site: http://www.viva64.com E-mail: [email protected] Twitter: https://twitter.com/Code_Analysis