Verifying C++ Buffers

December 27, 2009

no comments

This post is following this post: Verifying Pointers in C++ which demonstrated the problem in testing a pointer for NULL after allocation like this:

char* ptr=new char[1];
if (NULL == ptr) // fail

There is another issue with verifying pointers for NULL. The value NULL is #defined as 0 (zero) which is agreed to be an invalid memory address. Testing a pointer to see if it is NULL means testing to see if the pointer is pointing to address zero or not. As we all know, when we allocate a buffer and then delete it we must zero the pointer. Verifying a pointer is relying on the fact that every pointer that does not point to a legal buffer is manually reset. Like this:

main()
{
   char* ptr = NULL;
   MyFunc(ptr);
   ptr = new char[20];   MyFunc(ptr);
   delete[] ptr; ptr = NULL;
   MyFunc(ptr);
}

void MyFunc(char* ptr)
{

   if (NULL == ptr) return;
   // …do something…
}
 

You may assume that verifying the pointer in MyFunc is fine because the code is verified to make sure that every pointer is correctly initialized to zero and so if a NULL pointer is used the function is protected from it. This is not really the case. To demonstrate the problem we can look at the following scenario.


We have a class called Label. This class has several members including the text string to display. Here is the class:

class Label
{
protected:
   int X;
   int Y;
   char* text;
public:
   char* Text();
};


void UpdateDisplay()

{
   // … more
   Label* label1 = GetSomeLabel();

   char* ptr = label1->Text();
   if (NULL == ptr) // ERROR
   // … more
}


Now, here is the problem:


class Label
{
protected:
   int X;
   int Y;
   char text[256];
public:
   char* Text();
};


In the first version of the class the string was a pointer to a memory buffer. In this version the string is stored as part of the object. This means that in the original version the call to GetSomeLabel() in function UpdateDisplay() can return NULL if there is no label and calling the member label1->Text() will also return NULL. When we use the second version of the class we find a problem. If the call to GetSomeLabel() in function UpdateDisplay() returns NULL then the calling the member label1->Text() will return 8. Verifying the pointer for NULL will find no problem and using the macro ASSERT will also no find the bug!

void UpdateDisplay()
{
   // … more
   Label* label1 = GetSomeLabel(); // Got label1 = NULL // label1 = 0 

   char* ptr = label1->Text();     // ptr = label1 + 8  // ptr = 8
   if (NULL == ptr) // ERROR       // can’t see the problem, ignoring
   // … more
}


When label1 is NULL the data of label1 starts at address zero; X starts at address zero, Y at address 4 which is sizeof(X), after X, and text starts at address 8. Making sure that the string text is not zero is a bug.


When we try to access the buffer text the system will get an exception. This is a CPU exception for accessing the invalid page stating on address zero. If the page size is 4KB then accessing anywhere within the range is invalid and not just address zero, so address 8 is also invalid because it is in the same page. Actually the entire block of memory ranging from address 0 and up to 64KB is marked as inaccessible for this exact reason.


The way to do this correctly is not to verify that the address is under 64K. Instead you can use the Win32 API IsBadxxxPtr. Just like checking the pointer has to be thread-safe, so does calling these API functions. It is possible that between the verification and the usage some other thread deleted the buffer if you do not make sure that this will not happen. Also this does not replace the need to reset pointers to zero after deleting them. If there is another valid buffer on the same page then the entire page is valid.


* Note that this API is not recommended by Microsoft for a parallel environment.


For code example see the wrapper code as it is used internally by the cpp files in this sample code.


 

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*