Menu

nullPointer false positive when pointer is cast prior to assignment

MarkP
2020-12-15
2020-12-18
  • MarkP

    MarkP - 2020-12-15

    The codebase I am working has a vast amount of legacy code writting in a mizture of 'C' and 'C++' and includes its own memory allocation functions which hasve the following signature:

    STATUS osMemAlloc(void** ptr, size_t sz);
    

    This fuction is generally used throught the codebase as:

       DeviceInfo *device = NULL;
    
       if (osMemAlloc((void**)&device, sizeof(DeviceInfo)) == OS_OK)  
       {
            // code that de-references device  goes here
      }
    

    With an older vesion of CppCheck (Version 1.84) this function was defined as a macro in a cppcheck.cfg file as:

    <define name="osMemAlloc(pp,size)" value="((*(pp) = cppcheck_HeapAlloc(size)) != NULL ? OS_OK : OS_ERROR)"/>
    

    This now causes nullPointer derference errors in the code that uses a pointer allocated by osMemAlloc. I belieev this is because the cast to (void**) is causing CppCheck to miss the assignement of a value to teh pointer.

    Here is a full example

    #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 ouput from CppCheck (version 2.3) is as follows:

    /c/Program\ Files/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]
    ^

    When the same code is passed to the Older version of CppCheck (1.84) no errors are reported.

     
  • Daniel Marjamäki

     
  • MarkP

    MarkP - 2020-12-18

    Thank Daniel when its fixed I can verify it on a very large legacy codebase.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.