Solutions for Developers, Services for Companies, Software for Users
 



bottrap link

A blog is now available Here.

Interested in becoming an Affiliate and earning 25% of all generated sales?  Click Here.

Handmade Art and
Treasures!

Handmade Art and Treasures: Fannys Follies

25% off for first-time customers at VistaPrint!

 

  

Using Size-Plus-One Buffer Sizing, and Paranoid Termination of Buffers

By James R. Twine (Copyright 2003, James R. Twine)

[Previous Article In Segment] [Next Article In Segment]

As the last article demonstrated, correct use of fixed size buffers can help your application become less vulnerable to Buffer Attacks.  It also demonstrated that a misunderstanding on how to use them can make you more vulnerable to them too.

Manipulation of fixed buffers gets complicated due to the requirement that strings need to be terminated with a NUL character.  This changes the buffer size requirements.  For example, if you need to read a maximum 32 character string from an Edit control, you would do something like this (note that this uses MFC):

void CSomeDlg::OnClickGetString( void )
{
    const DWORD    STR_BUF_SIZE = 33;                   // 32 + 1 (NUL) == 33
    char           caBuffer[ STR_BUF_SIZE ];

    m_editName.GetWindowText( caBuffer, STR_BUF_SIZE ); // Get Name From Edit Control

    return;                                             // Done!
}

Simple enough.  The issue starts when you have for format the data further using additional buffers.  For example, the second version of the function shown in the Last Article used fixed buffers but did not account for chance that the terminating NUL might not get placed by the sntprintf(...) function.  One of the ways to fix it is shown here:

void CSomeDlg::OnClickShowHello( void )
{
    const DWORD    HELLO_BUF_SIZE = 129;               // 128 + 1 (NUL) == 129
    const DWORD    STR_BUF_SIZE = 33;                  // 32 + 1 (NUL) == 33  
    char           caBuffer[ HELLO_BUF_SIZE ];
    char           caName[ NAME_BUF_SIZE ];

    m_editName.GetWindowText( caName, NAME_BUF_SIZE ); // Get Name From Edit Control
    snprintf( caBuffer, HELLO_BUF_SIZE - 1,
            "Hello, %s!", caName );                    // Build "Hello" String
    caBuffer[ HELLO_BUF_SIZE - 1 ] = '\0';             // PTerminate Hello Message
    MessageBox( caBuffer );                            // Show Hello Message

    return;                                            // Done!
}

This version allocates additional an additional character for the terminating NUL to be placed at the end of the string.  Note the line right after the call to sntprintf(...) where I manually place a terminating NUL at the very end of the caBuffer buffer.  I call this "Paranoid Termination of a buffer, or simply "PTerminate"-ing a buffer.  By doing so, I can guarantee that a terminating NUL is always present in the buffer, regardless of which functions have manipulated it.

The only issue with this approach is that if you are going to be manipulating the buffer using several functions, you are going to be typing code similar to HELLO_BUF_SIZE - 1 quite a few times, and it increases the number of places where you may forget to adjust the buffer size correctly.  To address this, I use a technique called "Size+1" ("Size Plus One") to size buffers.  It works like this:

void CSomeDlg::OnClickShowHello( void )
{
    const DWORD    HELLO_BUF_SIZE = 128;              
    const DWORD    STR_BUF_SIZE = 32;                 
    char           caBuffer[ HELLO_BUF_SIZE + 1 ];
    char           caName[ NAME_BUF_SIZE + 1 ];

    m_editName.GetWindowText( caName, NAME_BUF_SIZE ); // Get Name From Edit Control
    snprintf( caBuffer, HELLO_BUF_SIZE, "Hello, %s!",
            caName );                                  // Build "Hello" String
    caBuffer[ HELLO_BUF_SIZE ] = '\0';                 // PTerminate Hello Message
    MessageBox( caBuffer );                            // Show Hello Message

    return;                                            // Done!
}

The function operates exactly the same, except for the fact that the constant size values are used directly throughout the code.  You only have to remember the "+ 1" when allocating the buffer.  Doing it this way means that you continue to use the buffer size value unmodified.  The benefit of doing this may not seem clear at first, so read on...

There are many different functions available that accept, process and manipulate strings.  However, they all do not behave the same with regard to NUL termination and buffer sizes.  For example:

  1. Functions like sntprintf(...) will only NUL terminate the buffer if the maximum length of the buffer has not been reached.
     
  2. Functions like CWnd::GetWindowText(...) treat the specified buffer size as the maximum size including a NUL terminator.  So given a buffer size greater than 1, you will always get a NUL terminator placed in the buffer.
     
  3. Some other functions treat the specified buffer size as the maximum size of characters, with no NUL terminator at all!

Add to that the fact that buffer size constants that are available in your environment, like the _MAX_PATH  value in Win32, may include space for a terminating NUL, while others may not!

Using my Size+1 approach handles all of these situations without any problems.  The worst thing that can happen is that you will allocate space for one more character than necessary (e.g. if you use _MAX_PATH + 1 as a buffer size).  And by using the Size+1 approach, you can be sure that all of your string buffers are always terminated correctly.

[Previous Article In Segment] [Next Article In Segment]

The contents of this web site are Copyright ©1998-2006 by JRTwine Software, LLC. All Rights Reserved. 
Legal and Copyright Information.
bottrap link