Archive for December, 2007

Checking for buffer overflows in unit tests

Functions that take a buffer and a corresponding buffer size as a parameter are ubiquitous. As an example, consider this function declaration:

/**
  Routine Description:
    ...queries some value...

  Parameters:
    BufferSize - size of buffer in WCHARs.
    Buffer     - buffer to store information in.

  Return Value:
    ERROR_SUCCESS on success.
    ERROR_INSUFFICIENT_BUFFER if the buffer is to small.
--*/
DWORD QueryInformation(
  __in UINT BufferSize,
  __out_ecount(BufferSize) PWSTR Buffer
  );

If we are to write a testcase for this function, it is important to check that the function properly handles buffers of insufficient size. Let us say that we expect this function to return a file path, so its is safe to assume that a buffer size of 2 is guaranteed to be too small and a buffer size of MAX_PATH should be large enough. A straightforward testcase could thus look like this:

  WCHAR SmallBuf[ 2 ];
  WCHAR ReasonableBuf[ MAX_PATH ];

  //
  // Use buffer that is guranteed to be too small.
  //
  TEST( ERROR_INSUFFICIENT_BUFFER == QueryInformation( 
    _countof( SmallBuf ), 
    SmallBuf ) );

  //
  // Use a reasonable buffer size.
  //
  TEST( ERROR_SUCCESS == QueryInformation( 
    _countof( ReasonableBuf ), 
    ReasonableBuf ) );

  ... test contents of ReasonableBuf ...

If the buffer size handling of QueryInformation is fundamentally flawed, this test case will catch the error. However, QueryInformation may contain a more subtle flaw such as an off-by-one error, that only manifests itself when the buffer is exactly one element too small. In this case odds are that this test case will fail to notice the mistake.

A handy idiom to overcome this is to use brute-force and just test all buffer sizes from 0 to a reasonable maximum size (MAX_PATH in this case). In order to catch buffer overflows, we use the same trick as employed by most debug heaps — we pad the end of the buffer with a guard value and make sure that the value is still intact after the function has used the buffer. An off-by-one error is now highly likely to be caught. The test case may look like this:

  WCHAR Buffer[ MAX_PATH ];
  UINT BufferSize;

  //
  // Try all buffer sizes.
  //
  BOOL WorkedAtLeastOnce = FALSE;
  for ( BufferSize = _countof( Buffer ); 
                                 BufferSize > 0; BufferSize-- )
  {
    DWORD Result;

    //
    // Write guard value to end of buffer..
    //
    Buffer[ BufferSize - 1 ] = 0xDEAD;

    //
    // Pass the buffer to the function under test.
    //
    Result = QueryInformation( 
      BufferSize - 1,    // account for guard value.
      Buffer );

    //
    // Check that guard value is intact - regardless of whether
    // function failed or not.
    //
    TEST( Buffer[ BufferSize - 1 ] == 0xDEAD );

    //
    // Expect either ERROR_INSUFFICIENT_BUFFER or 
    // ERROR_SUCCESS
    //
    TEST( Result == ERROR_INSUFFICIENT_BUFFER ||
        ( Result == ERROR_SUCCESS && 
                     ... check contents of Buffer ... ) );

    WorkedAtLeastOnce |= ( Result == ERROR_SUCCESS );
  }
  TEST( WorkedAtLeastOnce );

Of course, it is now important to make sure that the function under test succeeded at least once, hence the additional check.

Advertisements

Launch elevated processes from the command line

Every now and then you need to run an elevated command from the command line. If the application always requires elevation (i.e. the binary has been marked as requireAdministrator), the UAC prompt shows up — but in the case the application supports both non-elevated and elevated usage and you explicitly want it to run elevated, there is little support for you.

I would have expected start.com to have been augmented by something like a /elevate-switch, but unfortunately, this is not the case and you are left with having to open a new elevated command prompt to continue.

Fortunately, a tool to fill in this gap is straightforward and indeed J. Robbins has already created one. However, the tool is written in C# — it might be old-fashioned, but I still prefer those little tools to be unmanaged and free of the .Net-overhead. I thus created elevate.exe, which provides the same features as John’s tool but is written in C. In fact, it accepts exactly the same command line parameters, which are:

Execute a process on the command line with elevated rights on Vista

Usage: Elevate [-?|-wait|-k] prog [args]
-?    - Shows this help
-wait - Waits until prog terminates
-k    - Starts the the %COMSPEC% environment variable value and
                executes prog in it (CMD.EXE, 4NT.EXE, etc.)
prog  - The program to execute
args  - Optional command line arguments to prog

Examples:

elevate ipconfig -registerdns
elevate -wait procexp
elevate -k dir

Having this tool at hand, it is now easy to also extend the shell context menu for directories by an ‘Open Elevated Console here’ entry by adding the following registry entries:

[HKEY_CLASSES_ROOT\Directory\shell\Open Elevated Console here]
@="Open Ele&vated Console here"

[HKEY_CLASSES_ROOT\Directory\shell\
           Open Elevated Console here\command]
@="c:\\path\\to\\elevate.exe 
          /K \"title %1 && color 1a && cd /D %1\""

Download source code, x86 and x64 binary.

Update: You may use the source code under the terms of the MIT License.

Update 2: The source code is now available on Github


Categories




About me

Johannes Passing, M.Sc., living in Berlin, Germany.

Besides his consulting work, Johannes mainly focusses on Win32, COM, and NT kernel mode development, along with Java and .Net. He also is the author of cfix, a C/C++ unit testing framework for Win32 and NT kernel mode, Visual Assert, a Visual Studio Unit Testing-AddIn, and NTrace, a dynamic function boundary tracing toolkit for Windows NT/x86 kernel/user mode code.

Contact Johannes: jpassing (at) acm org

Johannes' GPG fingerprint is BBB1 1769 B82D CD07 D90A 57E8 9FE1 D441 F7A0 1BB1.

LinkedIn Profile
Xing Profile
Github Profile