CppCheck with custom Malloc functions - cppcheck

I am working on a codebase that has a vast amount of legacy code and I am trying to update CppCheck to the latest version.
This code relies on a number of custom malloc style functions with the following signature:
Status osMemAlloc(void** ptr, size_t numBytes);
The signature of this function means the CppCheck does not deduce that the function osMemAlloc is allocating memory for ptr meaning that it flags {nullPointer} Null pointer dereference errors in code such as:
SomeStruct* myPtr = NULL;
Status status = osMemAlloc((void**)& myPtr, sizeof(SomeStruct));
A
assert(status == SUCCESS_E);

  pT
myPtr->param1 = val1;    // (error) {nullPointer} Null pointer dereference
myPtr-> param2 = val2;   // (error) {nullPointer} Null pointer dereference      
How do I tell CppCheck that the call to osMemAlloc((void**)& myPtr, sizeof(SomeStruct)) is allocating memory for myPtr.
Changing the signature of the function to something more natural is not an option as the code base is huge & very flakey with no proper test harnesses.
Further Info
I have tried playing with various macro definitions (expanded manually) which replace the call for osMemAlloc() as a way to resolve this and believe there is a bug in CppCHeck where a cast is causing CppCheck to miss the assignment of allocated memory to the pointer.
The original call site for the allocation has the form:
if (osMemAlloc((void **)&device,sizeof(DeviceInfo)) == OS_OK)
CPPCheck is happy with the osMemAlloc being replaced with a macro which would expand as:
if ((((*(&device)=cppcheck_HeapAlloc(sizeof(Device))) != NULL)
? SUCCESS_E : ERROR_E) == SUCCESS_E)
Unfortunately GCC is not happy with that call as it requires a cast to (void**) to match the original function. If that cast is added the code becomes:
if ((((*((void**)(&device))=cppcheck_HeapAlloc(sizeof(Device))) != NULL)
? OS_OK : OS_ERROR) == OS_OK)
Which causes CPPCheck to fail. I believe that the cast is enough to cause CppCheck to fail to call Token::addValue() in cppcheck/lib/token.cpp
Full Example code that fails in CppCheck:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
typedef struct Device
{
uint32_t param1;
uint32_t param2;
} Device;
enum Status
{
OS_OK = 0,
OS_ERROR = 1
};
// Macro used to hide/replace OS Abstraction of Malloc in legacy code.
// The real code forwards to a function withe following signature:
// Status osMemAlloc(void** ptr, size_t sx);
#define osMemAlloc(ptr, sz) ((*(ptr)=malloc(sz)) != NULL ? OS_OK : OS_ERROR)
int main()
{
Device* device1 = NULL;
Device* device2 = NULL;
/// This call / expansion of the macro without the casts is fine,
if ((((*(&device1)=malloc(sizeof(Device))) != NULL) ? OS_OK : OS_ERROR) == OS_OK)
{
device1->param1 = 10;
device1->param2 = 20;
}
/// Note the cast is ncessary when the real function is called for C++
//if ((((*((void**)&device2)=malloc(sizeof(Device))) != NULL) ? OS_OK : OS_ERROR) == OS_OK)
if (osMemAlloc((void**)&device2, sizeof(Device)) == OS_OK)
{
device2->param1 = 10; // error: Null pointer dereference: device2 [nullPointer]
device2->param2 = 20; // error: Null pointer dereference: device2 [nullPointer]
}
printf("Done\n");
free(device1);
free(device2);
}
The Output from CppCheck
cppcheck.exe cppcheck-error.c
Checking cppcheck-error.c ...
cppcheck-error.c:39:7: error: Null pointer dereference: device2 [nullPointer]
device2->param1 = 10; // error: Null pointer dereference: device2 [nullPointer]
^
cppcheck-error.c:26:22: note: Assignment 'device2=NULL', assigned value is 0
Device* device2 = NULL;
^
cppcheck-error.c:39:7: note: Null pointer dereference
device2->param1 = 10; // error: Null pointer dereference: device2 [nullPointer]
^
cppcheck-error.c:40:7: error: Null pointer dereference: device2 [nullPointer]
device2->param2 = 20; // error: Null pointer dereference: device2 [nullPointer]
^
cppcheck-error.c:26:22: note: Assignment 'device2=NULL', assigned value is 0
Device* device2 = NULL;
^
cppcheck-error.c:40:7: note: Null pointer dereference
device2->param2 = 20; // error: Null pointer dereference: device2 [nullPointer]
^

I am a Cppcheck developer.
I am not aware of a builtin configuration option or possible workaround. Not sure what to recommend.
a hack would be to copy the code to some temporary file and create a script (sed/python/..) that rewrites your code:
Status status = osMemAlloc((void**)& myPtr, sizeof(SomeStruct));
into:
Status status = success; myPtr = malloc(sizeof(SomeStruct));
A more proper solution would be to add an improvement in Cppcheck. If you would be interested to contribute a fix feel free to look at this. In my humble opinion the code is not super complex, there are no required dependencies so if you have a C++ compiler that can compile a hello world program you should be able to compile Cppcheck also. Source code: https://github.com/danmar/cppcheck

Related

Using std::make_unique with the GetProfileBinary function call

I have seen this answer (Advantages of using std::make_unique over new operator) where it states:
Don't use make_unique if you need a custom deleter or are adopting a raw pointer from elsewhere.
This is is my code:
void CAutomaticBackupSettingsPage::GetLastBackupDate(COleDateTime& rBackupDate)
{
DATE* pDatTime = nullptr;
UINT uSize;
theApp.GetProfileBinary(_T("Options"), _T("BackupLastBackupDate"), pointer_cast<LPBYTE*>(&pDatTime), &uSize);
if (uSize == sizeof(DATE))
rBackupDate = *pDatTime;
else
rBackupDate = COleDateTime::GetCurrentTime();
delete[] pDatTime;
pDatTime = nullptr;
}
Code analysis gives me two warnings:
and
The latter warning suggests I use std::make_unique but since my pointer data is returned from the GetProfileBinary call, and given the statement in the related question, does that mean I should not use std::make_unique? I admit it is something I have not done before.
The useage of GetProfileBinary clearly states:
GetProfileBinary allocates a buffer and returns its address in *ppData. The caller is responsible for freeing the buffer using delete[].
pDateTime is supposed to be nullptr, and GetProfileBinary handles the allocation. Code Analysis mistakenly thinks you forgot the allocation.
It does need to check for success before calling delete[]. We can't use delete[]pDatTime because pDatTime is not an array. But GetProfileBinary allocates using new BYTE[size], so we need to cast back to BYTE.
You can also add a NULL check before reading pDatTime, that might make Code Analysis happy.
if (pDatTime && uSize == sizeof(DATE))
rBackupDate = *pDatTime;
else
rBackupDate = COleDateTime::GetCurrentTime();
if(pDatTime) delete[](BYTE*)pDatTime;
You can use std::unique_ptr<BYTE[]> cleanup((BYTE*)pDatTime) for deletion, but this has to be after GetProfileBinary is called.
Example:
DATE* pDatTime = nullptr;
GetProfileBinary(_T("Options"), _T("BackupLastBackupDate"), (LPBYTE*)(&pDatTime), &uSize);
std::unique_ptr<BYTE[]> cleanup((BYTE*)pDatTime); //automatic delete
if (pDatTime && uSize == sizeof(DATE))
rBackupDate = *pDatTime;
else
rBackupDate = COleDateTime::GetCurrentTime();
//pDatTime = NULL; <- Error when used with unique_ptr
...
//pDatTime is deleted later, when `cleanup` goes out of scope

Staic analysis gives "Dereferencing <storage from new>, which is known to be nullptr"

I was running static analysis using Coverity on a project, while I ran into this error being reported by the tool:
Dereferencing <storage from new>, which is known to be nullptr
The code snippet is:
typedef struct envelope
{
char *message;
void *context;
bool response;
}envelopeRef;
int main()
{
/* here coverity tells that
1. returned_null: operator new returns nullptr (checked 46 out of 52 times)
2. var_assigned: Assigning: <storage from new> = nullptr return value from operator new
3. dereference: Dereferencing <storage from new>, which is known to be nullptr.
*/
envelopeRef *h = new (std::nothrow)envelopeRef();
if(nullptr != h)
{
//do something
delete h;
}
}
What am I doing wrong here?

GCC warning "dereferencing type-punned pointer will break strict-aliasing rules"

I use a QueryInterfacefunction that will return a pointer on a given interface based on an IID.
DecodingFramework::IVariableFramerate* pInt = NULL;
DecodingFramework::DecodeResult iVFR = pVideoDescription->QueryInterface(IID_IVariableFramerate, (void**)(&pInt));
if(pInt != NULL && iVFR == DF_SUCCESS)
{
//use the IVariableFramerate interface using pInt
}
But in that code (void**)(&pInt) produce an error with message dereferencing type-punned pointer will break strict-aliasing rules
I updated the code to the following :
void* pInt = NULL;
DecodingFramework::DecodeResult iVFR = pVideoDescription->QueryInterface(IID_IVariableFramerate, &pInt);
if(pInt != NULL && iVFR == DF_SUCCESS)
{
DecodingFramework::IVariableFramerate* pVideoVFR = reinterpret_cast<DecodingFramework::IVariableFramerate*>(pInt);
//use the IVariableFramerate interface using pVideoVFR
}
I found a lot of question related to that warning message but it was mainly when casting more complex data than just the address pointer on something to a void** ?
Is there a problem for real ? I don't understand the rational behind that warning.
Here's why lying to the compiler about the pointer type is bad:
struct SomeClass { int a; };
SomeClass* global_pointer;
void open_object( void** result, int x )
{
cout << global_pointer->a;
*result = new SomeClass{x};
cout << global_pointer->a;
}
The compiler is perfectly allowed to replace that by:
auto temp = global_pointer->a;
cout << temp;
*result = new SomeClass{x}; // according to the Standard, the compiler is allowed to assume this line CANNOT change global_pointer, because the type is wrong
cout << temp;
If you then call
open_object((void**)&global_pointer);
then you may be surprised by the result.

C++ check if pointer is valid?

I'm having problems with valid pointers in C++. I'm using one object in different threads, so I can't just set the pointer to NULL and return. Here's what I'm trying:
int main()
{
char *ptr = new char[1024]; //assume PTR = 0x12345678
changePtr(ptr); //after calling this,
//ptr is not NULL here.
return 0;
}
void changePtr(char *ptr)
{
delete [] ptr; //ptr = 0x12345678
ptr = NULL; //ptr = NULL
}
How can I change ptr to NULL for both functions?
change the signature of changePtr to:
void changePtr(char **ptr)
{
delete [] *ptr; //ptr = 0x12345678
*ptr = NULL; //ptr = NULL
}
And call it using:
changePtr(&ptr);
In C++, use reference parameter:
void changePtr(char *&ptr) {
delete [] ptr; //ptr = 0x12345678
ptr = NULL; //ptr = NULL
}
In C, you need to pass pointer to pointer, which is basically same thing with less pretty syntax.
You do not need to change the calling code. But you must give a modifiable variable as argument when calling, can't give for example NULL or nullptr, same as you can't do &NULL.
If you really want to manage memory in such a complex, error-prone way, then pass a reference to, rather than a copy of, the caller's pointer:
void changePtr(char *&ptr)
// ^
Much better would be to use a smart pointer; they are designed so that it's very difficult to leave them dangling when the target is deleted:
int main()
{
std::unique_ptr<char[]> ptr(new char[1024]); //assume PTR = 0x12345678
changePtr(ptr); //after calling this,
//ptr is empty here.
return 0;
}
void changePtr(std::unique_ptr<char[]> & ptr)
{
ptr.reset();
}
although if I wanted a dynamic array, I'd avoid new altogether and use std::vector.
I have seen a great many questions about how to check a pointer for validity. A large number of these questions have been about Windows. There may not be a general way to check in C++, but for a Windows specific solution the following seems to work on my system:
#include <windows.h>
#include <stdio.h>
int main(int argc, char **argv)
{
MEMORY_BASIC_INFORMATION lpBuffer;
int cActualBytes;
cActualBytes = VirtualQuery(&main, &lpBuffer, sizeof(lpBuffer)); // Can we get info about main?
if (!cActualBytes)
{
printf("Nope, you can't do that \n");
return 2;
}
if (cActualBytes != sizeof(lpBuffer))
{
printf("Surprise! Expected %d bytes, got %d\n", sizeof(lpBuffer), cActualBytes);
}
printf("Information for main\n");
printf("---------------------------\n");
printf("C reports pointer %p, Virtual Alloc sees it as %p\n",&main,lpBuffer.BaseAddress);
return 0;
}

Deleting array error

So I have the following class method (will probably add more if requested):
EDIT 1:
Here is where paObject gets initialized and how it remains until it reaches setCapacity(int c)
template <Object>
ArrayClass<Object>::ArrayClass()
{
_size = 0; //default in case allocation fails
paObject = new Object[ARRAY_CLASS_DEFAULT_SIZE];
if (paObject == NULL)
throw Exception();
_size = ARRAY_CLASS_DEFAULT_SIZE;
}
It may be important to note that my class Vector extends (or whatever it is in c++) ArrayClass: class MyVector: virtual public ArrayClass<Object>{} and then later on in
template <class Object>
MyVector<Object>::MyVector() : ArrayClass<Object>()
{
_currSize = 0;
_incFactor = 5;
}
it forces to call the ArrayClass constructor.
template <Object >
void MyVector<Object>::setCapacity(int c)
{
int len = _currSize;
if (len > c) len = c;
Object* paNew = new Object[c];
if (paNew == NULL) throw Exception();
for (int i = 0; i < len; i++)
paNew[i] = paObject[i];
if (paObject != NULL)
delete[] paObject;
paObject = paNew;
_size = c;
if (_currSize > len)
_currSize = len;
}
When the error happens, the value of c is 6 and the value of _currSize (a class private int variable) is 1. It is also remarkable to remark that paObject is a class pointer of type Object (which is a template... etc. etc.) initialized with an array of Objects of size 1 by the time the error happens (Object* paObject = new Object[1]).
Everything executes fine up until it reaches the line delete[] paObject; at which point it gives me a Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) error.
I seriously don't understand what is wrong.
Help would be appreciated.
This error occurs when you have a heap problem.
Something like:
int *a = new char[1];
a[500] = 0;
then later, on totally unrelated object you will get an error on delete or new.
You haven't shown us where paObject is declared ... or where it's (re)initialized ...
... but that's almost certainly the problem.
If you haven't initialized it to NULL, it might contain random garbage. In which case it won't evaluate to NULL, and Bad Things will happen.
Similarly, if don't explicitly reset it to NULL, then Bad Things will also happen.
Of course, we don't even know if paObject is an array (allocated with "new []") ... because you haven't shown us.
BOTTOM LINE:
1) Make sure paObject is initialized before you use it, and correctly initialized before you re-use it.
2) If you treat it as an array object, make sure it's initialized as an array object
3) You also need to be careful about the differences between "Object", "Object references" (&Object or Object *), arrays of Objects. In particular, you need to consider when you're passing a reference to an existing object, or when you're (perhaps unintentially) creating a new object.
These links should help:
http://cplus.about.com/od/learning1/ss/constructors.htm
http://www.tutorialspoint.com/cplusplus/cpp_copy_constructor.htm
==================================================================
ADDENDUM:
It sounds like the error Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) is occurring because you're overwriting your array.
Object* paObject = new Object[1] // This is really easy to overwrite!
I don't see where in the code you're writing to paObject (or writing to some alias of paObject), but I'm almost certain that's the problem.
IMHO...