I've recently started running cppcheck on an exising project, and I'm a little puzzled about why cppcheck is having issues with some of our VirtualAlloc calls. We're using VirtualAlloc to reserve a range of addresses (using MEM_RESERVE), and then repeatedly allocating the reserved pages using MEM_COMMIT. When we're all done with the buffer, we call VirtualFree on the address returned from the original MEM_RESERVE call.
It looks like when windows.cfg is used, cppcheck will report a memory leak error on every one of our MEM_COMMIT calls, even though the pages are all freed later with a single call to VirtualFree.
With the following code, the MEM_COMMIT line appears to generate a cppcheck error:
//Ashortprogramtodemonstratedynamicmemoryallocation//usingastructuredexceptionhandler.#include<windows.h>#include<tchar.h>#include<stdio.h>#include<stdlib.h>//Forexit#definePAGELIMIT80//NumberofpagestoaskforLPTSTRlpNxtPage;//AddressofthenextpagetoaskforDWORDdwPages=0;//CountofpagesgottensofarDWORDdwPageSize;//PagesizeonthiscomputerINTPageFaultExceptionFilter(DWORDdwCode){LPVOIDlpvResult;//Iftheexceptionisnotapagefault,exit.if(dwCode!=EXCEPTION_ACCESS_VIOLATION){_tprintf(TEXT("Exception code = %d.\n"),dwCode);returnEXCEPTION_EXECUTE_HANDLER;}_tprintf(TEXT("Exception is a page fault.\n"));//Ifthereservedpagesareusedup,exit.if(dwPages>=PAGELIMIT){_tprintf(TEXT("Exception: out of pages.\n"));returnEXCEPTION_EXECUTE_HANDLER;}//Otherwise,commitanotherpage.lpvResult=VirtualAlloc((LPVOID)lpNxtPage,//NextpagetocommitdwPageSize,//Pagesize,inbytesMEM_COMMIT,//AllocateacommittedpagePAGE_READWRITE);//Read/writeaccessif(lpvResult==NULL){_tprintf(TEXT("VirtualAlloc failed.\n"));returnEXCEPTION_EXECUTE_HANDLER;}else{_tprintf(TEXT("Allocating another page.\n"));}//Incrementthepagecount,andadvancelpNxtPagetothenextpage.dwPages++;lpNxtPage=(LPTSTR)((PCHAR)lpNxtPage+dwPageSize);//Continueexecutionwherethepagefaultoccurred.returnEXCEPTION_CONTINUE_EXECUTION;}VOIDErrorExit(LPTSTRlpMsg){_tprintf(TEXT("Error! %s with error code of %ld.\n"),lpMsg,GetLastError());exit(0);}VOID_tmain(VOID){LPVOIDlpvBase;//BaseaddressofthetestmemoryLPTSTRlpPtr;//GenericcharacterpointerBOOLbSuccess;//FlagDWORDi;//GenericcounterSYSTEM_INFOsSysInfo;//UsefulinformationaboutthesystemGetSystemInfo(&sSysInfo);//Initializethestructure._tprintf(TEXT("This computer has page size %d.\n"),sSysInfo.dwPageSize);dwPageSize=sSysInfo.dwPageSize;//Reservepagesinthevirtualaddressspaceoftheprocess.lpvBase=VirtualAlloc(NULL,//SystemselectsaddressPAGELIMIT*dwPageSize,//SizeofallocationMEM_RESERVE,//AllocatereservedpagesPAGE_NOACCESS);//Protection=noaccessif(lpvBase==NULL)ErrorExit(TEXT("VirtualAlloc reserve failed."));lpPtr=lpNxtPage=(LPTSTR)lpvBase;//Usestructuredexceptionhandlingwhenaccessingthepages.//Ifapagefaultoccurs,theexceptionfilterisexecutedto//commitanotherpagefromthereservedblockofpages.for(i=0;i<PAGELIMIT*dwPageSize;i++){__try{//Writetomemory.lpPtr[i]='a';}//Ifthere'sapagefault,commitanotherpageandtryagain.__except(PageFaultExceptionFilter(GetExceptionCode())){//Thiscodeisexecutedonlyifthefilterfunction//isunsuccessfulincommittingthenextpage._tprintf(TEXT("Exiting process.\n"));ExitProcess(GetLastError());}}//Releasetheblockofpageswhenyouarefinishedusingthem.bSuccess=VirtualFree(lpvBase,//Baseaddressofblock0,//BytesofcommittedpagesMEM_RELEASE);//Decommitthepages_tprintf(TEXT("Release %s.\n"),bSuccess?TEXT("succeeded"):TEXT("failed"));}
I'm curious, has anyone been able to work around this, or is this just another limitation in cppcheck's analysis of global variables? It also doesn't make a difference whether I use VirtualAlloc or VirtualAllocEx.
It'd be a shame to have to "--suppress=memleak" to get these errors to go away.
Thanks
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Since the return value was not stored, I've worked around this by now by enabling inline suppressions, and adding "// cppcheck-suppress leakReturnValNotUsed" before the VirtualAlloc(..., MEM_COMMIT) line.
I'm still somewhat new to cppcheck, so feel free let me know if there's a better way to deal with this sort of thing.
I'm not sure if it would be worth the effort to modify the checker to ignore cases where the flAllocationType equals MEM_COMMIT (since doing a dealloc on the return isn't required to avoid a leak in that case).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Means that each VirtualAlloc call allocates memory that must be freed with a VirtualFree.
If that is not entirely true then this should be removed.
Could you send us a fixed windows.cfg?
I'm not sure if it would be worth the effort to modify the checker to ignore cases where the flAllocationType equals MEM_COMMIT (since doing a dealloc on the return isn't required to avoid a leak in that case).
There is no way to configure this directly in the <alloc> currently.</alloc>
I do think this configuration is a hack. But it works. If we want to do it better then it takes some effort. Feel free to open a ticket in our issue tracker (http://trac.cppcheck.net) if you want it.
Last edit: Daniel Marjamäki 2016-07-25
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks for your input on this.
From what I understand, it's "normal" to call VirtualAlloc with MEM_RESERVE the first time. Then, you call VirtualAlloc repeatedly with MEM_COMMIT, to commit more pages as necessary. You can also use "MEM_COMMIT | MEM_RESERVE", if you want to reserve and commit all of the memory in one step. You only need to call VirtualFree (with MEM_RELEASE) on the address returned from the first MEM_RESERVE call.
Also, I tried to use 1.cfg from your example, and I think windows.cfg somehow automatically included first when cppcheck is run on windows, even when "--library=windows" is missing the command line. As a result, the memleak is still reported. Is there a way to include 1.cfg first, or would I have to modify windows.cfg to work around this?
I guess until the issue can be properly fixed, it's always possible to "// cppcheck-suppress leakReturnValNotUsed" before any false positives.
Last edit: cybersquash 2016-08-04
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello,
I've recently started running cppcheck on an exising project, and I'm a little puzzled about why cppcheck is having issues with some of our VirtualAlloc calls. We're using VirtualAlloc to reserve a range of addresses (using MEM_RESERVE), and then repeatedly allocating the reserved pages using MEM_COMMIT. When we're all done with the buffer, we call VirtualFree on the address returned from the original MEM_RESERVE call.
It looks like when windows.cfg is used, cppcheck will report a memory leak error on every one of our MEM_COMMIT calls, even though the pages are all freed later with a single call to VirtualFree.
This can be observed running cppcheck on the "Reserving and Committing Memory" example in the MSDN library.
With the following code, the MEM_COMMIT line appears to generate a cppcheck error:
Cppcheck version: 1.74
Command line:
"C:\Program Files\cppcheck\cppcheck.exe" --inline-suppr -v --template="{file}|{line}|{severity}|{id}|{message}" --library=windows --library=std --language=c++ .
Resulting error:
"VirtualAlloc.cpp|67|error|memleak|Memory leak: lpvResult"
I'm curious, has anyone been able to work around this, or is this just another limitation in cppcheck's analysis of global variables? It also doesn't make a difference whether I use VirtualAlloc or VirtualAllocEx.
It'd be a shame to have to "--suppress=memleak" to get these errors to go away.
Thanks
Since the return value was not stored, I've worked around this by now by enabling inline suppressions, and adding "// cppcheck-suppress leakReturnValNotUsed" before the VirtualAlloc(..., MEM_COMMIT) line.
I'm still somewhat new to cppcheck, so feel free let me know if there's a better way to deal with this sort of thing.
I'm not sure if it would be worth the effort to modify the checker to ignore cases where the flAllocationType equals MEM_COMMIT (since doing a dealloc on the return isn't required to avoid a leak in that case).
It sounds to me that the configuration for VirtualAlloc in windows.cfg should be changed.
This configuration:
Means that each VirtualAlloc call allocates memory that must be freed with a VirtualFree.
If that is not entirely true then this should be removed.
Could you send us a fixed windows.cfg?
There is no way to configure this directly in the <alloc> currently.</alloc>
But you can configure this indirectly.
For instance (1.cfg):
Example code (1.c):
Output from Cppcheck
I do think this configuration is a hack. But it works. If we want to do it better then it takes some effort. Feel free to open a ticket in our issue tracker (http://trac.cppcheck.net) if you want it.
Last edit: Daniel Marjamäki 2016-07-25
Or keep the issue until we can properly fix it. How often do such false positives occur?
I don't know. Is it normal that you always use MEM_COMMIT to VirtualAlloc? Or is the normal usage that you first use a MEM_RESERVE?
Thanks for your input on this.
From what I understand, it's "normal" to call VirtualAlloc with MEM_RESERVE the first time. Then, you call VirtualAlloc repeatedly with MEM_COMMIT, to commit more pages as necessary. You can also use "MEM_COMMIT | MEM_RESERVE", if you want to reserve and commit all of the memory in one step. You only need to call VirtualFree (with MEM_RELEASE) on the address returned from the first MEM_RESERVE call.
Also, I tried to use 1.cfg from your example, and I think windows.cfg somehow automatically included first when cppcheck is run on windows, even when "--library=windows" is missing the command line. As a result, the memleak is still reported. Is there a way to include 1.cfg first, or would I have to modify windows.cfg to work around this?
I guess until the issue can be properly fixed, it's always possible to "// cppcheck-suppress leakReturnValNotUsed" before any false positives.
Last edit: cybersquash 2016-08-04
Sorry Mr. X that I edited my post after your comment. Maybe you would have responded differently if you saw the workaround.