SlideShare a Scribd company logo
Miranda NG Project to Get the "Wild 
Pointers" Award (Part 1) 
Author: Andrey Karpov 
Date: 25.11.2014 
I have recently got to the Miranda NG project and checked it with the PVS-Studio code analyzer. And I'm 
afraid this is the worst project in regard to memory and pointers handling issues I've ever seen. 
Although I didn't study the analysis results too thoroughly, there still were so many errors that I had to 
split the material into 2 articles. The first of them is devoted to pointers and the second to all the rest 
stuff. Enjoy reading and don't forget your popcorn. 
Checking Miranda NG 
The Miranda NG project is a successor of the multi-protocol IM-client for Windows, Miranda IM. 
Well, I didn't actually plan to check Miranda NG at first. It's just that we need a few actively developing 
projects to test one PVS-Studio's new feature on. It is about using a special database storing all the 
information about messages that shouldn't be displayed. To learn more about it, see this article. In brief, 
the idea behind it is the following. It is sometimes difficult to integrate static analysis into a large project 
because the analyzer generates too many warnings and one has a hard time trying to sort it all out while 
still wishing to start seeing the benefit right away. That's why you can hide all the warnings and check 
only fresh ones generated while writing new code or doing refactoring. And then, if you really feel like 
that, you can start gradually fixing errors in the old code. 
Miranda NG appeared to be one of the actively developing projects. But when I saw the analysis results 
generated by PVS-Studio after the first launch, I knew for sure I had got enough material for a new 
article. 
So, let's see what the PVS-Studio static code analyzer has found in Miranda NG's source codes. 
To do this check, we took the Trunk from the repository. Please keep in mind that I was just scanning 
through the analysis report and may have well missed much. I only checked the general diagnostics of 
the 1-st and 2-nd severity levels and didn't even bother to take a look at the 3-rd level. You see, the first 
two were just more than enough. 
Part 1. Pointers and memory handling 
Null pointer dereferencing 
void CMLan::OnRecvPacket(u_char* mes, int len, in_addr from) 
{ 
.... 
TContact* cont = m_pRootContact;
.... 
if (!cont) 
RequestStatus(true, cont->m_addr.S_un.S_addr); 
.... 
} 
PVS-Studio's diagnostic message: V522 Dereferencing of the null pointer 'cont' might take place. 
EmLanProto mlan.cpp 342 
It's all simple here. Since the pointer equals NULL, then let's dereference it and see if anything funny 
comes out of it. 
First using the pointer, then checking it 
There are numbers and numbers of errors of this kind in Miranda NG, just like in any other application. 
Such code usually works well because the function receives a non-null pointer. But if it is null, functions 
aren't ready to handle it. 
Here's a typical example: 
void TSAPI BB_InitDlgButtons(TWindowData *dat) 
{ 
.... 
HWND hdlg = dat->hwnd; 
.... 
if (dat == 0 || hdlg == 0) { return; } 
.... 
} 
PVS-Studio's diagnostic message: V595 The 'dat' pointer was utilized before it was verified against 
nullptr. Check lines: 428, 430. TabSRMM buttonsbar.cpp 428 
If you pass NULL into the BB_InitDlgButtons() function, the check will be done too late. The analyzer 
generated 164 more messages like this on Miranda NG's code. Citing them all in this article won't make 
any sense, so here they are all in a file: MirandaNG-595.txt. 
Potentially uninitialized pointer 
BSTR IEView::getHrefFromAnchor(IHTMLElement *element) 
{ 
.... 
if (SUCCEEDED(....) { 
VARIANT variant; 
BSTR url; 
if (SUCCEEDED(element->getAttribute(L"href", 2, &variant) && 
variant.vt == VT_BSTR))
{ 
url = mir_tstrdup(variant.bstrVal); 
SysFreeString(variant.bstrVal); 
} 
pAnchor->Release(); 
return url; 
} 
.... 
} 
PVS-Studio's diagnostic message: V614 Potentially uninitialized pointer 'url' used. IEView ieview.cpp 
1117 
If the if (SUCCEEDED(....)) condition is wrong, the 'url' variable will remain uninitialized and the function 
will have to return god knows what. The situation is much trickier though. The code contains another 
error: a closing parenthesis is put in a wrong place. It will result in the SUCCEEDED macro being applied 
only to the expression of the 'bool' type, which doesn't make any sense. 
The second bug makes up for the first. Let's see what the SUCCEEDED macro really is in itself: 
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) 
An expression of the 'bool' type evaluates to 0 or 1. In its turn, 0 or 1 are always >= 0. So it turns out that 
the SUCCEEDED macro will always return the truth value thus enabling the 'url' variable to be initialized 
all the time. 
So now we've just seen a very nice example of how one bug makes up for another. If we fix the 
condition, the bug with the uninitialized variable will show up. 
If we fix both, the code will look like this: 
BSTR url = NULL; 
if (SUCCEEDED(element->getAttribute(L"href", 2, &variant)) && 
variant.vt == VT_BSTR) 
The analyzer suspects something to be wrong in 20 more fragments. Here they are: MirandaNG-614.txt. 
Array size and item number mixed up 
The number of items in an array and the array size in bytes are two different entities. However, if you 
are not careful enough, you may easily mix them up. The Miranda NG project offers a handful of various 
ways to do that. 
Most harmful of all was the SIZEOF macro: 
#define SIZEOF(X) (sizeof(X)/sizeof(X[0])) 
This macro calculates the number of items in an array. But the programmer seems to treat it as a fellow 
of the sizeof() operator. I don't know, though, why use a macro instead of the standard sizeof() then, so I 
have another version - the programmer doesn't know how to use the memcpy() function. 
Here is a typical example:
int CheckForDuplicate(MCONTACT contact_list[], MCONTACT lparam) 
{ 
MCONTACT s_list[255] = { 0 }; 
memcpy(s_list, contact_list, SIZEOF(s_list)); 
for (int i = 0;; i++) { 
if (s_list[i] == lparam) 
return i; 
if (s_list[i] == 0) 
return -1; 
} 
return 0; 
} 
PVS-Studio's diagnostic message: V512 A call of the 'memcpy' function will lead to underflow of the 
buffer 's_list'. Sessions utils.cpp 288 
The memcpy() function will copy only part of the array as the third argument specifies the array size in 
bytes. 
In the same incorrect way, the SIZEOF() macro is used in 8 more places: MirandaNG-512-1.txt. 
The next trouble. Programmers often forget to fix memset()/memcpy() calls when using Unicode in their 
code: 
void checkthread(void*) 
{ 
.... 
WCHAR msgFrom[512]; 
WCHAR msgSubject[512]; 
ZeroMemory(msgFrom,512); 
ZeroMemory(msgSubject,512); 
.... 
} 
PVS-Studio's diagnostic messages: 
• V512 A call of the 'memset' function will lead to underflow of the buffer 'msgFrom'. LotusNotify 
lotusnotify.cpp 760 
• V512 A call of the 'memset' function will lead to underflow of the buffer 'msgSubject'. 
LotusNotify lotusnotify.cpp 761 
The ZeroMemoty() function will clear only half of the buffer as characters of the WCHAR type occupy 2 
bytes. 
And here is an example of partial string copying:
INT_PTR CALLBACK DlgProcMessage(....) 
{ 
.... 
CopyMemory(tr.lpstrText, _T("mailto:"), 7); 
.... 
} 
PVS-Studio's diagnostic message: V512 A call of the 'memcpy' function will lead to underflow of the 
buffer 'L"mailto:"'. TabSRMM msgdialog.cpp 2085 
Only part of the string will be copied. Each string character occupies 2 bytes, so 14 bytes instead of 7 
should have been copied. 
Other similar issues: 
• userdetails.cpp 206 
• weather_conv.cpp 476 
• dirent.c 138 
The next mistake was made due to mere inattentiveness: 
#define MSGDLGFONTCOUNT 22 
LOGFONTA logfonts[MSGDLGFONTCOUNT + 2]; 
void TSAPI CacheLogFonts() 
{ 
int i; 
HDC hdc = GetDC(NULL); 
logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); 
ReleaseDC(NULL, hdc); 
ZeroMemory(logfonts, sizeof(LOGFONTA) * MSGDLGFONTCOUNT + 2); 
.... 
} 
PVS-Studio's diagnostic message: V512 A call of the 'memset' function will lead to underflow of the 
buffer 'logfonts'. TabSRMM msglog.cpp 134 
The programmer must have been in a hurry, for he mixed up the object size and number of objects. 2 
should be added before the multiplication. Here's the fixed code: 
ZeroMemory(logfonts, sizeof(LOGFONTA) * (MSGDLGFONTCOUNT + 2)); 
In the next sample, the programmer tried his best to make it all work right using sizeof() but eventually 
ended up mixing sizes up again. The resulting value is larger than needed.
BOOL HandleLinkClick(....) 
{ 
.... 
MoveMemory(tr.lpstrText + sizeof(TCHAR)* 7, 
tr.lpstrText, 
sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 1)); 
.... 
} 
PVS-Studio's diagnostic message: V620 It's unusual that the expression of sizeof(T)*N kind is being 
summed with the pointer to T type. Scriver input.cpp 387 
The 'tr.lpstrText' variable points to a string consisting of characters of the wchat_t type. If you want to 
skip 7 characters, you just need to add 7; no need to multiply it by sizeof(wchar_t). 
Another similar error: ctrl_edit.cpp 351 
It's not over, I'm afraid. What about one more way of making a mistake: 
INT_PTR CALLBACK DlgProcThemeOptions(....) 
{ 
.... 
str = (TCHAR *)malloc(MAX_PATH+1); 
.... 
} 
PVS-Studio's diagnostic message: V641 The size of the allocated memory buffer is not a multiple of the 
element size. KeyboardNotify options.cpp 718 
Multiplication by sizeof(TCHAR) is missing. There are 2 more errors in the same file, lines 819 and 1076. 
And finally the last code fragment with an error related to the number of items: 
void createProcessList(void) 
{ 
.... 
ProcessList.szFileName[i] = 
(TCHAR *)malloc(wcslen(dbv.ptszVal) + 1); 
if (ProcessList.szFileName[i]) 
wcscpy(ProcessList.szFileName[i], dbv.ptszVal); 
.... 
}
PVS-Studio's diagnostic messages: V635 Consider inspecting the expression. The length should probably 
be multiplied by the sizeof(wchar_t). KeyboardNotify main.cpp 543 
Missing multiplication by sizeof(TCHAR) can also be found in the following fragments: options.cpp 1177, 
options.cpp 1204. 
Now we're done with sizes, let's pass on to other methods of shooting yourself in the foot with a 
pointer. 
Array index out of bounds 
INT_PTR CALLBACK DlgProcFiles(....) 
{ 
.... 
char fn[6], tmp[MAX_PATH]; 
.... 
SetDlgItemTextA(hwnd, IDC_WWW_TIMER, 
_itoa(db_get_w(NULL, MODNAME, strcat(fn, "_timer"), 60), 
tmp, 10)); 
.... 
} 
V512 A call of the 'strcat' function will lead to overflow of the buffer 'fn'. NimContact files.cpp 290 
The "_timer" string doesn't fit into the 'fn' array. Although it consists of 6 characters only, mind the 
terminal null character (NUL). Theoretically, we've got undefined behavior here. In practice, it appears 
that the 'tmp' array will be affected: '0' will be written into the null element of the 'tmp' array. 
The next example is even worse. In the code below, HANDLE of some icon will be spoiled: 
typedef struct 
{ 
int cbSize; 
char caps[0x10]; 
HANDLE hIcon; 
char name[MAX_CAPNAME]; 
} ICQ_CUSTOMCAP; 
void InitCheck() 
{ 
.... 
strcpy(cap.caps, "GPG AutoExchange"); 
....
} 
PVS-Studio's diagnostic message: V512 A call of the 'strcpy' function will lead to overflow of the buffer 
'cap.caps'. New_GPG main.cpp 2246 
The end-of-string character is again not taken into account. I guess it would be better to use the 
memcpy() function here. 
Other similar issues: 
• main.cpp 2261 
• messages.cpp 541 
• messages.cpp 849 
• utilities.cpp 547 
The Great and Powerful strncat() function 
Many heard about the danger of using the strcat() function and therefore prefer to use a seemingly 
safer strncat() function instead. But few can really handle it right. This function is much more dangerous 
than you might think. You see, the third argument specifies the amount of free space left in the buffer, 
not the buffer's maximum length. 
The following code is totally incorrect: 
BOOL ExportSettings(....) 
{ 
.... 
char header[512], buff[1024], abuff[1024]; 
.... 
strncat(buff, abuff, SIZEOF(buff)); 
.... 
} 
PVS-Studio's diagnostic message: V645 The 'strncat' function call could lead to the 'buff' buffer overflow. 
The bounds should not contain the size of the buffer, but a number of characters it can hold. Miranda 
fontoptions.cpp 162 
If only half of 'buff' is occupied, the code will show no care about it and allow adding 1000 more 
characters thus causing an array overrun - and quite a large one indeed. After all, the programmer could 
simply use strcat() to get the same result. 
Well, to be exact, the statement strncat(...., ...., SIZEOF(X)) is fundamentally incorrect. It implies that the 
array ALWAYS has some free space left. 
There are 48 more fragments in Miranda NG where the strncat() function is misused. Here they are: 
MirandaNG-645-1.txt. 
By the way, such issues in the code can well be treated as potential vulnerabilities. 
In defense of Miranda NG programmers, I should note that some of them did read the description of the 
strncat() function. These guys write their code in the following way: 
void __cdecl GGPROTO::dccmainthread(void*)
{ 
.... 
strncat(filename, (char*)local_dcc->file_info.filename, 
sizeof(filename) - strlen(filename)); 
.... 
} 
PVS-Studio's diagnostic message: V645 The 'strncat' function call could lead to the 'filename' buffer 
overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. 
GG filetransfer.cpp 273 
Unfortunately, it's wrong again. At least, there is a risk of spoiling 1 byte outside the array. And I think 
you have already guessed that the reason is that very ill-fated end-of-string character that wasn't taken 
into account. 
Let me explain this error by a simple example: 
char buf[5] = "ABCD"; 
strncat(buf, "E", 5 - strlen(buf)); 
The buffer doesn't have any more space left for new characters. It is keeping 4 characters and an end-of-string 
character. The "5 - strlen(buf)" expression evaluates to 1. The strncpy() function will copy the "E" 
character into the last item of the 'buf' array and the end-of-string character will be written outside the 
buffer bounds. 
Other 34 issues are collected in this file: MirandaNG-645-2.txt. 
Erotica with new[] and delete 
Someone of the Miranda NG team keeps constantly forgetting to write square brackets for the delete 
operator: 
extern "C" int __declspec(dllexport) Load(void) 
{ 
int wdsize = GetCurrentDirectory(0, NULL); 
TCHAR *workingDir = new TCHAR[wdsize]; 
GetCurrentDirectory(wdsize, workingDir); 
Utils::convertPath(workingDir); 
workingDirUtf8 = mir_utf8encodeT(workingDir); 
delete workingDir; 
.... 
} 
PVS-Studio's diagnostic message: V611 The memory was allocated using 'new T[]' operator but was 
released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] 
workingDir;'. IEView ieview_main.cpp 68 
Here are 20 more issues of the kind: MirandaNG-611-1.txt.
Well, errors like that don't usually have any serious effects though. That's why I put them into the 
"erotica" category. More hard-core things are shown further. 
Perverted new, malloc, delete and free 
The programmer mixed up methods of memory allocation and freeing: 
void CLCDLabel::UpdateCutOffIndex() 
{ 
.... 
int *piWidths = new int[(*--m_vLines.end()).length()]; 
.... 
free(piWidths); 
.... 
} 
PVS-Studio's diagnostic message: V611 The memory was allocated using 'new' operator but was 
released using the 'free' function. Consider inspecting operation logics behind the 'piWidths' variable. 
MirandaG15 clcdlabel.cpp 209 
11 more Kama Sutra positions can be studied here: MirandaNG-611-2.txt. 
Meaningless checks 
In case of a memory shortage issue, the ordinary 'new' operator throws an exception. That's why it 
doesn't make sense checking a pointer returned by 'new' for being null. 
Such an excessive check is usually harmless. However, you may sometimes come across code fragments 
like the following one: 
int CIcqProto::GetAvatarData(....) 
{ 
.... 
ar = new avatars_request(ART_GET); // get avatar 
if (!ar) { // out of memory, go away 
m_avatarsMutex->Leave(); 
return 0; 
} 
.... 
} 
PVS-Studio's diagnostic message: V668 There is no sense in testing the 'ar' pointer against null, as the 
memory was allocated using the 'new' operator. The exception will be generated in the case of memory 
allocation error. ICQ icq_avatar.cpp 608 
If the error occurs, the mutex should be freed. But it won't happen. If an object can't be created, things 
will go quite a different way than the programmer expects.
I suggest checking the rest 83 analyzer's warnings of this kind: MirandaNG-668.txt. 
SIZEOF() and _tcslen() mixed up 
#define SIZEOF(X) (sizeof(X)/sizeof(X[0])) 
.... 
TCHAR *ptszVal; 
.... 
int OnButtonPressed(WPARAM wParam, LPARAM lParam) 
{ 
.... 
int FinalLen = slen + SIZEOF(dbv.ptszVal) + 1; 
.... 
} 
PVS-Studio's diagnostic message: V514 Dividing sizeof a pointer 'sizeof (dbv.ptszVal)' by another value. 
There is a probability of logical error presence. TranslitSwitcher layoutproc.cpp 827 
Something strange is written here. The SIZEOF() macro is applied to a pointer, which makes no sense at 
all. I suspect that the programmer really wanted to calculate the string length. Then he should have used 
the _tcslen() function. 
Other similar fragments: 
• layoutproc.cpp 876 
• layoutproc.cpp 924 
• main.cpp 1300 
vptr spoiled 
class CBaseCtrl 
{ 
.... 
virtual void Release() { } 
virtual BOOL OnInfoChanged(MCONTACT hContact, LPCSTR pszProto); 
.... 
}; 
CBaseCtrl::CBaseCtrl() 
{ 
ZeroMemory(this, sizeof(*this)); 
_cbSize = sizeof(CBaseCtrl); 
}
PVS-Studio's diagnostic message: V598 The 'memset' function is used to nullify the fields of 'CBaseCtrl' 
class. Virtual method table will be damaged by this. UInfoEx ctrl_base.cpp 77 
The programmer was too lazy and settled for the ZeroMemory() function to zero the class fields. He 
didn't take into account, however, that the class contains a pointer to a virtual method table. In the base 
class, many methods are declared as virtual. Spoiling a pointer to a virtual method table will lead to 
undefined behavior when handling an object initialized in such a crude manner. 
Other similar issues: 
• ctrl_base.cpp 87 
• ctrl_base.cpp 103. 
Object lifetime 
static INT_PTR CALLBACK DlgProcFindAdd(....) 
{ 
.... 
case IDC_ADD: 
{ 
ADDCONTACTSTRUCT acs = {0}; 
if (ListView_GetSelectedCount(hwndList) == 1) { 
.... 
} 
else { 
.... 
PROTOSEARCHRESULT psr = { 0 }; <<<--- 
psr.cbSize = sizeof(psr); 
psr.flags = PSR_TCHAR; 
psr.id = str; 
acs.psr = &psr; <<<--- 
acs.szProto = (char*)SendDlgItemMessage(....); 
} 
acs.handleType = HANDLE_SEARCHRESULT; 
CallService(MS_ADDCONTACT_SHOW, 
(WPARAM)hwndDlg, (LPARAM)&acs); 
} 
break;
.... 
} 
PVS-Studio's diagnostic message: V506 Pointer to local variable 'psr' is stored outside the scope of this 
variable. Such a pointer will become invalid. Miranda findadd.cpp 777 
The 'psr' object will cease to exist when the program leaves the else branch. However, the pointer to 
this object will have been already saved by the time and will be used further in the program. This is an 
example of a genuine "wild pointer". The results of handling it cannot be predicted. 
Another similar example: 
HMENU BuildRecursiveMenu(....) 
{ 
.... 
if (GetKeyState(VK_CONTROL) & 0x8000) { 
TCHAR str[256]; 
mir_sntprintf(str, SIZEOF(str), 
_T("%s (%d, id %x)"), mi->pszName, 
mi->position, mii.dwItemData); 
mii.dwTypeData = str; 
} 
.... 
} 
PVS-Studio's diagnostic message: V507 Pointer to local array 'str' is stored outside the scope of this 
array. Such a pointer will become invalid. Miranda genmenu.cpp 973 
The text is printed into a temporary array which is destroyed right after. But the pointer to this array will 
be used in some other part of the program. 
I wonder how programs like this work at all! Check other 9 fragments inhabited by wild pointers: 
MirandaNG-506-507.txt. 
Torments of 64-bit pointers 
I didn't examine the 64-bit diagnostics. I look only to V220 warnings. Almost each of them indicates a 
genuine bug. 
Here's an example of incorrect code from the viewpoint of the 64-bit mode: 
typedef LONG_PTR LPARAM; 
LRESULT 
WINAPI 
SendMessageA(
__in HWND hWnd, 
__in UINT Msg, 
__in WPARAM wParam, 
__in LPARAM lParam); 
static INT_PTR CALLBACK DlgProcOpts(....) 
{ 
.... 
SendMessageA(hwndCombo, CB_ADDSTRING, 0, (LONG)acc[i].name); 
.... 
} 
PVS-Studio's diagnostic message: V220 Suspicious sequence of types castings: memsize -> 32-bit integer 
-> memsize. The value being casted: 'acc[i].name'. GmailNotifier options.cpp 55 
A 64-bit pointer is to be passed somewhere. To do this, it must be cast to the LPARAM type. But instead, 
this pointer is forced to turn into the 32-bit LONG type and only after that automatically expanded to 
LONG_PTR. This error dates back to the times of 32 bits when the LONG and LPARAM types' sizes 
coincided. Nowadays they no longer do. The most significant 32 bits will be spoiled in the 64-bit pointer. 
What is especially unpleasant about bugs like this is that they do not eagerly reveal themselves. You will 
be lucky while memory is allocated within the low addresses. 
Here are 20 more fragments where 64-bit pointers get spoiled: MirandaNG-220.txt. 
Non-erased private data 
void CAST256::Base::UncheckedSetKey(....) 
{ 
AssertValidKeyLength(keylength); 
word32 kappa[8]; 
.... 
memset(kappa, 0, sizeof(kappa)); 
} 
PVS-Studio's diagnostic message: 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 compiler will delete the call of the memset() function in the release version. To find out why, see 
the diagnostic description. 
There are 6 more fragments where private data won't be erased: MirandaNG-597.txt. 
Miscellaneous 
There are another couple of analyzer's warnings which I'd like to discuss together.
void LoadStationData(...., WIDATA *Data) 
{ 
.... 
ZeroMemory(Data, sizeof(Data)); 
.... 
} 
PVS-Studio's diagnostic message: V512 A call of the 'memset' function will lead to underflow of the 
buffer 'Data'. Weather weather_ini.cpp 250 
What the 'sizeof(Data)' expression returns is the size of the pointer, not WIDATA. Only part of the object 
will be zeroed. A correct way to write this code is as follows: sizeof(*Data). 
void CSametimeProto::CancelFileTransfer(HANDLE hFt) 
{ 
.... 
FileTransferClientData* ftcd = ....; 
if (ftcd) { 
while (mwFileTransfer_isDone(ftcd->ft) && ftcd) 
ftcd = ftcd->next; 
.... 
} 
PVS-Studio's diagnostic message: V713 The pointer ftcd was utilized in the logical expression before it 
was verified against nullptr in the same logical expression. Sametime files.cpp 423 
In the loop condition, the 'ftcd' pointer is first dereferenced and only then checked. I guess the 
expression should be rewritten in the following way: 
while (ftcd && mwFileTransfer_isDone(ftcd->ft)) 
Conclusion 
Handling pointers and memory is not the only aspect of C++ programs. In the next article, we'll discuss 
other types of bugs found in Miranda NG. There are not as many of them, but still quite a lot.

More Related Content

PDF
Analyzing Firebird 3.0
PDF
How to make fewer errors at the stage of code writing. Part N1.
PDF
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
PDF
How to make fewer errors at the stage of code writing. Part N4.
PDF
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
PDF
CppCat Static Analyzer Review
PDF
Tesseract. Recognizing Errors in Recognition Software
PDF
PVS-Studio vs Chromium - Continuation
Analyzing Firebird 3.0
How to make fewer errors at the stage of code writing. Part N1.
64-Bit Code in 2015: New in the Diagnostics of Possible Issues
How to make fewer errors at the stage of code writing. Part N4.
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
CppCat Static Analyzer Review
Tesseract. Recognizing Errors in Recognition Software
PVS-Studio vs Chromium - Continuation

What's hot (20)

PDF
Source code of WPF samples by Microsoft was checked
PDF
A few words about OpenSSL
PDF
A fresh eye on Oracle VM VirtualBox
PDF
Static Analysis of Mozilla Thunderbird's Code by PVS-Studio
PDF
Analysis of the Trans-Proteomic Pipeline (TPP) project
PDF
Picking Mushrooms after Cppcheck
PDF
Dusting the globe: analysis of NASA World Wind project
PDF
Linux version of PVS-Studio couldn't help checking CodeLite
PDF
Checking WinMerge with PVS-Studio for the second time
PDF
Checking VirtualDub
PDF
Intel IPP Samples for Windows - error correction
PDF
Intel IPP Samples for Windows - error correction
PDF
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PDF
How to make fewer errors at the stage of code writing. Part N3.
PDF
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
PDF
Analyzing the Blender project with PVS-Studio
PDF
Linux Kernel, tested by the Linux-version of PVS-Studio
PDF
Mathematicians: Trust, but Verify
PDF
PVS-Studio delved into the FreeBSD kernel
PDF
PVS-Studio vs Chromium
Source code of WPF samples by Microsoft was checked
A few words about OpenSSL
A fresh eye on Oracle VM VirtualBox
Static Analysis of Mozilla Thunderbird's Code by PVS-Studio
Analysis of the Trans-Proteomic Pipeline (TPP) project
Picking Mushrooms after Cppcheck
Dusting the globe: analysis of NASA World Wind project
Linux version of PVS-Studio couldn't help checking CodeLite
Checking WinMerge with PVS-Studio for the second time
Checking VirtualDub
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
How to make fewer errors at the stage of code writing. Part N3.
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
Analyzing the Blender project with PVS-Studio
Linux Kernel, tested by the Linux-version of PVS-Studio
Mathematicians: Trust, but Verify
PVS-Studio delved into the FreeBSD kernel
PVS-Studio vs Chromium
Ad

Viewers also liked (6)

PDF
Breizhcamp NoSQL
PDF
Pentaho Reporting Solutions_Sigma Infosolutions
PDF
Keynote The Digital Challenge Nordiska Museet April 2011
PDF
Rebellious leadership
PDF
The Unicorn's Travel to the Microcosm
PPTX
2010/10 - Database Architechs presentation
Breizhcamp NoSQL
Pentaho Reporting Solutions_Sigma Infosolutions
Keynote The Digital Challenge Nordiska Museet April 2011
Rebellious leadership
The Unicorn's Travel to the Microcosm
2010/10 - Database Architechs presentation
Ad

Similar to Miranda NG Project to Get the "Wild Pointers" Award (Part 1) (20)

PDF
Analyzing the Dolphin-emu project
PDF
Analyzing Firebird 3.0
PDF
Top 10 C# projects errors found in 2016
PDF
Checking Intel IPP Samples for Windows - Continuation
PDF
Grounded Pointers
PDF
Optimization in the world of 64-bit errors
PDF
Checking the Open-Source Multi Theft Auto Game
PDF
Consequences of using the Copy-Paste method in C++ programming and how to dea...
PDF
Handling False Positives in PVS-Studio and CppCat
PDF
The Ultimate Question of Programming, Refactoring, and Everything
PDF
The Ultimate Question of Programming, Refactoring, and Everything
PDF
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
PDF
Checking OpenCV with PVS-Studio
PDF
How to make fewer errors at the stage of code writing. Part N1
PDF
How to make fewer errors at the stage of code writing. Part N1.
PDF
Monitoring a program that monitors computer networks
PDF
Intel IPP Samples for Windows - error correction
PDF
100 bugs in Open Source C/C++ projects
PDF
Checking the code of Valgrind dynamic analyzer by a static analyzer
PDF
LibRaw, Coverity SCAN, PVS-Studio
Analyzing the Dolphin-emu project
Analyzing Firebird 3.0
Top 10 C# projects errors found in 2016
Checking Intel IPP Samples for Windows - Continuation
Grounded Pointers
Optimization in the world of 64-bit errors
Checking the Open-Source Multi Theft Auto Game
Consequences of using the Copy-Paste method in C++ programming and how to dea...
Handling False Positives in PVS-Studio and CppCat
The Ultimate Question of Programming, Refactoring, and Everything
The Ultimate Question of Programming, Refactoring, and Everything
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
Checking OpenCV with 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 N1.
Monitoring a program that monitors computer networks
Intel IPP Samples for Windows - error correction
100 bugs in Open Source C/C++ projects
Checking the code of Valgrind dynamic analyzer by a static analyzer
LibRaw, Coverity SCAN, PVS-Studio

More from Andrey Karpov (20)

PDF
60 антипаттернов для С++ программиста
PDF
60 terrible tips for a C++ developer
PPTX
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PDF
PVS-Studio in 2021 - Error Examples
PDF
PVS-Studio in 2021 - Feature Overview
PDF
PVS-Studio в 2021 - Примеры ошибок
PDF
PVS-Studio в 2021
PPTX
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
PPTX
Best Bugs from Games: Fellow Programmers' Mistakes
PPTX
Does static analysis need machine learning?
PPTX
Typical errors in code on the example of C++, C#, and Java
PPTX
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
PPTX
Game Engine Code Quality: Is Everything Really That Bad?
PPTX
C++ Code as Seen by a Hypercritical Reviewer
PPTX
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
PPTX
Static Code Analysis for Projects, Built on Unreal Engine
PPTX
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
PPTX
The Great and Mighty C++
PPTX
Static code analysis: what? how? why?
PDF
Zero, one, two, Freddy's coming for you
60 антипаттернов для С++ программиста
60 terrible tips for a C++ developer
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Feature Overview
PVS-Studio в 2021 - Примеры ошибок
PVS-Studio в 2021
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
Best Bugs from Games: Fellow Programmers' Mistakes
Does static analysis need machine learning?
Typical errors in code on the example of C++, C#, and Java
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
Game Engine Code Quality: Is Everything Really That Bad?
C++ Code as Seen by a Hypercritical Reviewer
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
Static Code Analysis for Projects, Built on Unreal Engine
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
The Great and Mighty C++
Static code analysis: what? how? why?
Zero, one, two, Freddy's coming for you

Recently uploaded (20)

PPTX
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
PDF
Cost to Outsource Software Development in 2025
PDF
Complete Guide to Website Development in Malaysia for SMEs
PDF
Autodesk AutoCAD Crack Free Download 2025
PDF
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
PDF
AutoCAD Professional Crack 2025 With License Key
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
medical staffing services at VALiNTRY
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
17 Powerful Integrations Your Next-Gen MLM Software Needs
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
L1 - Introduction to python Backend.pptx
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Download FL Studio Crack Latest version 2025 ?
PPTX
Patient Appointment Booking in Odoo with online payment
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PPTX
AMADEUS TRAVEL AGENT SOFTWARE | AMADEUS TICKETING SYSTEM
PDF
Digital Systems & Binary Numbers (comprehensive )
PPTX
Reimagine Home Health with the Power of Agentic AI​
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
Cost to Outsource Software Development in 2025
Complete Guide to Website Development in Malaysia for SMEs
Autodesk AutoCAD Crack Free Download 2025
iTop VPN 6.5.0 Crack + License Key 2025 (Premium Version)
AutoCAD Professional Crack 2025 With License Key
Adobe Illustrator 28.6 Crack My Vision of Vector Design
medical staffing services at VALiNTRY
CHAPTER 2 - PM Management and IT Context
17 Powerful Integrations Your Next-Gen MLM Software Needs
Internet Downloader Manager (IDM) Crack 6.42 Build 41
L1 - Introduction to python Backend.pptx
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Download FL Studio Crack Latest version 2025 ?
Patient Appointment Booking in Odoo with online payment
Design an Analysis of Algorithms II-SECS-1021-03
AMADEUS TRAVEL AGENT SOFTWARE | AMADEUS TICKETING SYSTEM
Digital Systems & Binary Numbers (comprehensive )
Reimagine Home Health with the Power of Agentic AI​
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises

Miranda NG Project to Get the "Wild Pointers" Award (Part 1)

  • 1. Miranda NG Project to Get the "Wild Pointers" Award (Part 1) Author: Andrey Karpov Date: 25.11.2014 I have recently got to the Miranda NG project and checked it with the PVS-Studio code analyzer. And I'm afraid this is the worst project in regard to memory and pointers handling issues I've ever seen. Although I didn't study the analysis results too thoroughly, there still were so many errors that I had to split the material into 2 articles. The first of them is devoted to pointers and the second to all the rest stuff. Enjoy reading and don't forget your popcorn. Checking Miranda NG The Miranda NG project is a successor of the multi-protocol IM-client for Windows, Miranda IM. Well, I didn't actually plan to check Miranda NG at first. It's just that we need a few actively developing projects to test one PVS-Studio's new feature on. It is about using a special database storing all the information about messages that shouldn't be displayed. To learn more about it, see this article. In brief, the idea behind it is the following. It is sometimes difficult to integrate static analysis into a large project because the analyzer generates too many warnings and one has a hard time trying to sort it all out while still wishing to start seeing the benefit right away. That's why you can hide all the warnings and check only fresh ones generated while writing new code or doing refactoring. And then, if you really feel like that, you can start gradually fixing errors in the old code. Miranda NG appeared to be one of the actively developing projects. But when I saw the analysis results generated by PVS-Studio after the first launch, I knew for sure I had got enough material for a new article. So, let's see what the PVS-Studio static code analyzer has found in Miranda NG's source codes. To do this check, we took the Trunk from the repository. Please keep in mind that I was just scanning through the analysis report and may have well missed much. I only checked the general diagnostics of the 1-st and 2-nd severity levels and didn't even bother to take a look at the 3-rd level. You see, the first two were just more than enough. Part 1. Pointers and memory handling Null pointer dereferencing void CMLan::OnRecvPacket(u_char* mes, int len, in_addr from) { .... TContact* cont = m_pRootContact;
  • 2. .... if (!cont) RequestStatus(true, cont->m_addr.S_un.S_addr); .... } PVS-Studio's diagnostic message: V522 Dereferencing of the null pointer 'cont' might take place. EmLanProto mlan.cpp 342 It's all simple here. Since the pointer equals NULL, then let's dereference it and see if anything funny comes out of it. First using the pointer, then checking it There are numbers and numbers of errors of this kind in Miranda NG, just like in any other application. Such code usually works well because the function receives a non-null pointer. But if it is null, functions aren't ready to handle it. Here's a typical example: void TSAPI BB_InitDlgButtons(TWindowData *dat) { .... HWND hdlg = dat->hwnd; .... if (dat == 0 || hdlg == 0) { return; } .... } PVS-Studio's diagnostic message: V595 The 'dat' pointer was utilized before it was verified against nullptr. Check lines: 428, 430. TabSRMM buttonsbar.cpp 428 If you pass NULL into the BB_InitDlgButtons() function, the check will be done too late. The analyzer generated 164 more messages like this on Miranda NG's code. Citing them all in this article won't make any sense, so here they are all in a file: MirandaNG-595.txt. Potentially uninitialized pointer BSTR IEView::getHrefFromAnchor(IHTMLElement *element) { .... if (SUCCEEDED(....) { VARIANT variant; BSTR url; if (SUCCEEDED(element->getAttribute(L"href", 2, &variant) && variant.vt == VT_BSTR))
  • 3. { url = mir_tstrdup(variant.bstrVal); SysFreeString(variant.bstrVal); } pAnchor->Release(); return url; } .... } PVS-Studio's diagnostic message: V614 Potentially uninitialized pointer 'url' used. IEView ieview.cpp 1117 If the if (SUCCEEDED(....)) condition is wrong, the 'url' variable will remain uninitialized and the function will have to return god knows what. The situation is much trickier though. The code contains another error: a closing parenthesis is put in a wrong place. It will result in the SUCCEEDED macro being applied only to the expression of the 'bool' type, which doesn't make any sense. The second bug makes up for the first. Let's see what the SUCCEEDED macro really is in itself: #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) An expression of the 'bool' type evaluates to 0 or 1. In its turn, 0 or 1 are always >= 0. So it turns out that the SUCCEEDED macro will always return the truth value thus enabling the 'url' variable to be initialized all the time. So now we've just seen a very nice example of how one bug makes up for another. If we fix the condition, the bug with the uninitialized variable will show up. If we fix both, the code will look like this: BSTR url = NULL; if (SUCCEEDED(element->getAttribute(L"href", 2, &variant)) && variant.vt == VT_BSTR) The analyzer suspects something to be wrong in 20 more fragments. Here they are: MirandaNG-614.txt. Array size and item number mixed up The number of items in an array and the array size in bytes are two different entities. However, if you are not careful enough, you may easily mix them up. The Miranda NG project offers a handful of various ways to do that. Most harmful of all was the SIZEOF macro: #define SIZEOF(X) (sizeof(X)/sizeof(X[0])) This macro calculates the number of items in an array. But the programmer seems to treat it as a fellow of the sizeof() operator. I don't know, though, why use a macro instead of the standard sizeof() then, so I have another version - the programmer doesn't know how to use the memcpy() function. Here is a typical example:
  • 4. int CheckForDuplicate(MCONTACT contact_list[], MCONTACT lparam) { MCONTACT s_list[255] = { 0 }; memcpy(s_list, contact_list, SIZEOF(s_list)); for (int i = 0;; i++) { if (s_list[i] == lparam) return i; if (s_list[i] == 0) return -1; } return 0; } PVS-Studio's diagnostic message: V512 A call of the 'memcpy' function will lead to underflow of the buffer 's_list'. Sessions utils.cpp 288 The memcpy() function will copy only part of the array as the third argument specifies the array size in bytes. In the same incorrect way, the SIZEOF() macro is used in 8 more places: MirandaNG-512-1.txt. The next trouble. Programmers often forget to fix memset()/memcpy() calls when using Unicode in their code: void checkthread(void*) { .... WCHAR msgFrom[512]; WCHAR msgSubject[512]; ZeroMemory(msgFrom,512); ZeroMemory(msgSubject,512); .... } PVS-Studio's diagnostic messages: • V512 A call of the 'memset' function will lead to underflow of the buffer 'msgFrom'. LotusNotify lotusnotify.cpp 760 • V512 A call of the 'memset' function will lead to underflow of the buffer 'msgSubject'. LotusNotify lotusnotify.cpp 761 The ZeroMemoty() function will clear only half of the buffer as characters of the WCHAR type occupy 2 bytes. And here is an example of partial string copying:
  • 5. INT_PTR CALLBACK DlgProcMessage(....) { .... CopyMemory(tr.lpstrText, _T("mailto:"), 7); .... } PVS-Studio's diagnostic message: V512 A call of the 'memcpy' function will lead to underflow of the buffer 'L"mailto:"'. TabSRMM msgdialog.cpp 2085 Only part of the string will be copied. Each string character occupies 2 bytes, so 14 bytes instead of 7 should have been copied. Other similar issues: • userdetails.cpp 206 • weather_conv.cpp 476 • dirent.c 138 The next mistake was made due to mere inattentiveness: #define MSGDLGFONTCOUNT 22 LOGFONTA logfonts[MSGDLGFONTCOUNT + 2]; void TSAPI CacheLogFonts() { int i; HDC hdc = GetDC(NULL); logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); ReleaseDC(NULL, hdc); ZeroMemory(logfonts, sizeof(LOGFONTA) * MSGDLGFONTCOUNT + 2); .... } PVS-Studio's diagnostic message: V512 A call of the 'memset' function will lead to underflow of the buffer 'logfonts'. TabSRMM msglog.cpp 134 The programmer must have been in a hurry, for he mixed up the object size and number of objects. 2 should be added before the multiplication. Here's the fixed code: ZeroMemory(logfonts, sizeof(LOGFONTA) * (MSGDLGFONTCOUNT + 2)); In the next sample, the programmer tried his best to make it all work right using sizeof() but eventually ended up mixing sizes up again. The resulting value is larger than needed.
  • 6. BOOL HandleLinkClick(....) { .... MoveMemory(tr.lpstrText + sizeof(TCHAR)* 7, tr.lpstrText, sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 1)); .... } PVS-Studio's diagnostic message: V620 It's unusual that the expression of sizeof(T)*N kind is being summed with the pointer to T type. Scriver input.cpp 387 The 'tr.lpstrText' variable points to a string consisting of characters of the wchat_t type. If you want to skip 7 characters, you just need to add 7; no need to multiply it by sizeof(wchar_t). Another similar error: ctrl_edit.cpp 351 It's not over, I'm afraid. What about one more way of making a mistake: INT_PTR CALLBACK DlgProcThemeOptions(....) { .... str = (TCHAR *)malloc(MAX_PATH+1); .... } PVS-Studio's diagnostic message: V641 The size of the allocated memory buffer is not a multiple of the element size. KeyboardNotify options.cpp 718 Multiplication by sizeof(TCHAR) is missing. There are 2 more errors in the same file, lines 819 and 1076. And finally the last code fragment with an error related to the number of items: void createProcessList(void) { .... ProcessList.szFileName[i] = (TCHAR *)malloc(wcslen(dbv.ptszVal) + 1); if (ProcessList.szFileName[i]) wcscpy(ProcessList.szFileName[i], dbv.ptszVal); .... }
  • 7. PVS-Studio's diagnostic messages: V635 Consider inspecting the expression. The length should probably be multiplied by the sizeof(wchar_t). KeyboardNotify main.cpp 543 Missing multiplication by sizeof(TCHAR) can also be found in the following fragments: options.cpp 1177, options.cpp 1204. Now we're done with sizes, let's pass on to other methods of shooting yourself in the foot with a pointer. Array index out of bounds INT_PTR CALLBACK DlgProcFiles(....) { .... char fn[6], tmp[MAX_PATH]; .... SetDlgItemTextA(hwnd, IDC_WWW_TIMER, _itoa(db_get_w(NULL, MODNAME, strcat(fn, "_timer"), 60), tmp, 10)); .... } V512 A call of the 'strcat' function will lead to overflow of the buffer 'fn'. NimContact files.cpp 290 The "_timer" string doesn't fit into the 'fn' array. Although it consists of 6 characters only, mind the terminal null character (NUL). Theoretically, we've got undefined behavior here. In practice, it appears that the 'tmp' array will be affected: '0' will be written into the null element of the 'tmp' array. The next example is even worse. In the code below, HANDLE of some icon will be spoiled: typedef struct { int cbSize; char caps[0x10]; HANDLE hIcon; char name[MAX_CAPNAME]; } ICQ_CUSTOMCAP; void InitCheck() { .... strcpy(cap.caps, "GPG AutoExchange"); ....
  • 8. } PVS-Studio's diagnostic message: V512 A call of the 'strcpy' function will lead to overflow of the buffer 'cap.caps'. New_GPG main.cpp 2246 The end-of-string character is again not taken into account. I guess it would be better to use the memcpy() function here. Other similar issues: • main.cpp 2261 • messages.cpp 541 • messages.cpp 849 • utilities.cpp 547 The Great and Powerful strncat() function Many heard about the danger of using the strcat() function and therefore prefer to use a seemingly safer strncat() function instead. But few can really handle it right. This function is much more dangerous than you might think. You see, the third argument specifies the amount of free space left in the buffer, not the buffer's maximum length. The following code is totally incorrect: BOOL ExportSettings(....) { .... char header[512], buff[1024], abuff[1024]; .... strncat(buff, abuff, SIZEOF(buff)); .... } PVS-Studio's diagnostic message: V645 The 'strncat' function call could lead to the 'buff' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. Miranda fontoptions.cpp 162 If only half of 'buff' is occupied, the code will show no care about it and allow adding 1000 more characters thus causing an array overrun - and quite a large one indeed. After all, the programmer could simply use strcat() to get the same result. Well, to be exact, the statement strncat(...., ...., SIZEOF(X)) is fundamentally incorrect. It implies that the array ALWAYS has some free space left. There are 48 more fragments in Miranda NG where the strncat() function is misused. Here they are: MirandaNG-645-1.txt. By the way, such issues in the code can well be treated as potential vulnerabilities. In defense of Miranda NG programmers, I should note that some of them did read the description of the strncat() function. These guys write their code in the following way: void __cdecl GGPROTO::dccmainthread(void*)
  • 9. { .... strncat(filename, (char*)local_dcc->file_info.filename, sizeof(filename) - strlen(filename)); .... } PVS-Studio's diagnostic message: V645 The 'strncat' function call could lead to the 'filename' buffer overflow. The bounds should not contain the size of the buffer, but a number of characters it can hold. GG filetransfer.cpp 273 Unfortunately, it's wrong again. At least, there is a risk of spoiling 1 byte outside the array. And I think you have already guessed that the reason is that very ill-fated end-of-string character that wasn't taken into account. Let me explain this error by a simple example: char buf[5] = "ABCD"; strncat(buf, "E", 5 - strlen(buf)); The buffer doesn't have any more space left for new characters. It is keeping 4 characters and an end-of-string character. The "5 - strlen(buf)" expression evaluates to 1. The strncpy() function will copy the "E" character into the last item of the 'buf' array and the end-of-string character will be written outside the buffer bounds. Other 34 issues are collected in this file: MirandaNG-645-2.txt. Erotica with new[] and delete Someone of the Miranda NG team keeps constantly forgetting to write square brackets for the delete operator: extern "C" int __declspec(dllexport) Load(void) { int wdsize = GetCurrentDirectory(0, NULL); TCHAR *workingDir = new TCHAR[wdsize]; GetCurrentDirectory(wdsize, workingDir); Utils::convertPath(workingDir); workingDirUtf8 = mir_utf8encodeT(workingDir); delete workingDir; .... } PVS-Studio's diagnostic message: V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] workingDir;'. IEView ieview_main.cpp 68 Here are 20 more issues of the kind: MirandaNG-611-1.txt.
  • 10. Well, errors like that don't usually have any serious effects though. That's why I put them into the "erotica" category. More hard-core things are shown further. Perverted new, malloc, delete and free The programmer mixed up methods of memory allocation and freeing: void CLCDLabel::UpdateCutOffIndex() { .... int *piWidths = new int[(*--m_vLines.end()).length()]; .... free(piWidths); .... } PVS-Studio's diagnostic message: V611 The memory was allocated using 'new' operator but was released using the 'free' function. Consider inspecting operation logics behind the 'piWidths' variable. MirandaG15 clcdlabel.cpp 209 11 more Kama Sutra positions can be studied here: MirandaNG-611-2.txt. Meaningless checks In case of a memory shortage issue, the ordinary 'new' operator throws an exception. That's why it doesn't make sense checking a pointer returned by 'new' for being null. Such an excessive check is usually harmless. However, you may sometimes come across code fragments like the following one: int CIcqProto::GetAvatarData(....) { .... ar = new avatars_request(ART_GET); // get avatar if (!ar) { // out of memory, go away m_avatarsMutex->Leave(); return 0; } .... } PVS-Studio's diagnostic message: V668 There is no sense in testing the 'ar' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. ICQ icq_avatar.cpp 608 If the error occurs, the mutex should be freed. But it won't happen. If an object can't be created, things will go quite a different way than the programmer expects.
  • 11. I suggest checking the rest 83 analyzer's warnings of this kind: MirandaNG-668.txt. SIZEOF() and _tcslen() mixed up #define SIZEOF(X) (sizeof(X)/sizeof(X[0])) .... TCHAR *ptszVal; .... int OnButtonPressed(WPARAM wParam, LPARAM lParam) { .... int FinalLen = slen + SIZEOF(dbv.ptszVal) + 1; .... } PVS-Studio's diagnostic message: V514 Dividing sizeof a pointer 'sizeof (dbv.ptszVal)' by another value. There is a probability of logical error presence. TranslitSwitcher layoutproc.cpp 827 Something strange is written here. The SIZEOF() macro is applied to a pointer, which makes no sense at all. I suspect that the programmer really wanted to calculate the string length. Then he should have used the _tcslen() function. Other similar fragments: • layoutproc.cpp 876 • layoutproc.cpp 924 • main.cpp 1300 vptr spoiled class CBaseCtrl { .... virtual void Release() { } virtual BOOL OnInfoChanged(MCONTACT hContact, LPCSTR pszProto); .... }; CBaseCtrl::CBaseCtrl() { ZeroMemory(this, sizeof(*this)); _cbSize = sizeof(CBaseCtrl); }
  • 12. PVS-Studio's diagnostic message: V598 The 'memset' function is used to nullify the fields of 'CBaseCtrl' class. Virtual method table will be damaged by this. UInfoEx ctrl_base.cpp 77 The programmer was too lazy and settled for the ZeroMemory() function to zero the class fields. He didn't take into account, however, that the class contains a pointer to a virtual method table. In the base class, many methods are declared as virtual. Spoiling a pointer to a virtual method table will lead to undefined behavior when handling an object initialized in such a crude manner. Other similar issues: • ctrl_base.cpp 87 • ctrl_base.cpp 103. Object lifetime static INT_PTR CALLBACK DlgProcFindAdd(....) { .... case IDC_ADD: { ADDCONTACTSTRUCT acs = {0}; if (ListView_GetSelectedCount(hwndList) == 1) { .... } else { .... PROTOSEARCHRESULT psr = { 0 }; <<<--- psr.cbSize = sizeof(psr); psr.flags = PSR_TCHAR; psr.id = str; acs.psr = &psr; <<<--- acs.szProto = (char*)SendDlgItemMessage(....); } acs.handleType = HANDLE_SEARCHRESULT; CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs); } break;
  • 13. .... } PVS-Studio's diagnostic message: V506 Pointer to local variable 'psr' is stored outside the scope of this variable. Such a pointer will become invalid. Miranda findadd.cpp 777 The 'psr' object will cease to exist when the program leaves the else branch. However, the pointer to this object will have been already saved by the time and will be used further in the program. This is an example of a genuine "wild pointer". The results of handling it cannot be predicted. Another similar example: HMENU BuildRecursiveMenu(....) { .... if (GetKeyState(VK_CONTROL) & 0x8000) { TCHAR str[256]; mir_sntprintf(str, SIZEOF(str), _T("%s (%d, id %x)"), mi->pszName, mi->position, mii.dwItemData); mii.dwTypeData = str; } .... } PVS-Studio's diagnostic message: V507 Pointer to local array 'str' is stored outside the scope of this array. Such a pointer will become invalid. Miranda genmenu.cpp 973 The text is printed into a temporary array which is destroyed right after. But the pointer to this array will be used in some other part of the program. I wonder how programs like this work at all! Check other 9 fragments inhabited by wild pointers: MirandaNG-506-507.txt. Torments of 64-bit pointers I didn't examine the 64-bit diagnostics. I look only to V220 warnings. Almost each of them indicates a genuine bug. Here's an example of incorrect code from the viewpoint of the 64-bit mode: typedef LONG_PTR LPARAM; LRESULT WINAPI SendMessageA(
  • 14. __in HWND hWnd, __in UINT Msg, __in WPARAM wParam, __in LPARAM lParam); static INT_PTR CALLBACK DlgProcOpts(....) { .... SendMessageA(hwndCombo, CB_ADDSTRING, 0, (LONG)acc[i].name); .... } PVS-Studio's diagnostic message: V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize. The value being casted: 'acc[i].name'. GmailNotifier options.cpp 55 A 64-bit pointer is to be passed somewhere. To do this, it must be cast to the LPARAM type. But instead, this pointer is forced to turn into the 32-bit LONG type and only after that automatically expanded to LONG_PTR. This error dates back to the times of 32 bits when the LONG and LPARAM types' sizes coincided. Nowadays they no longer do. The most significant 32 bits will be spoiled in the 64-bit pointer. What is especially unpleasant about bugs like this is that they do not eagerly reveal themselves. You will be lucky while memory is allocated within the low addresses. Here are 20 more fragments where 64-bit pointers get spoiled: MirandaNG-220.txt. Non-erased private data void CAST256::Base::UncheckedSetKey(....) { AssertValidKeyLength(keylength); word32 kappa[8]; .... memset(kappa, 0, sizeof(kappa)); } PVS-Studio's diagnostic message: 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 compiler will delete the call of the memset() function in the release version. To find out why, see the diagnostic description. There are 6 more fragments where private data won't be erased: MirandaNG-597.txt. Miscellaneous There are another couple of analyzer's warnings which I'd like to discuss together.
  • 15. void LoadStationData(...., WIDATA *Data) { .... ZeroMemory(Data, sizeof(Data)); .... } PVS-Studio's diagnostic message: V512 A call of the 'memset' function will lead to underflow of the buffer 'Data'. Weather weather_ini.cpp 250 What the 'sizeof(Data)' expression returns is the size of the pointer, not WIDATA. Only part of the object will be zeroed. A correct way to write this code is as follows: sizeof(*Data). void CSametimeProto::CancelFileTransfer(HANDLE hFt) { .... FileTransferClientData* ftcd = ....; if (ftcd) { while (mwFileTransfer_isDone(ftcd->ft) && ftcd) ftcd = ftcd->next; .... } PVS-Studio's diagnostic message: V713 The pointer ftcd was utilized in the logical expression before it was verified against nullptr in the same logical expression. Sametime files.cpp 423 In the loop condition, the 'ftcd' pointer is first dereferenced and only then checked. I guess the expression should be rewritten in the following way: while (ftcd && mwFileTransfer_isDone(ftcd->ft)) Conclusion Handling pointers and memory is not the only aspect of C++ programs. In the next article, we'll discuss other types of bugs found in Miranda NG. There are not as many of them, but still quite a lot.