Help prevent buffer overruns in your driver! Use safe string functions
Buffer overruns in kernel-mode drivers increase the risk of faults and memory pool corruption that can crash the system, and they create security holes that an attacker could exploit. String manipulation operations are a common source of buffer overruns, largely because the standard string manipulation functions supplied by the C/C++ language runtime libraries (strcat, strcpy, sprintf, and so on) do not prevent writing beyond the end of the buffer.
You can eliminate many potential buffer overruns in your driver by using the kernel-mode safe string functions instead of the standard string manipulation functions. The kernel-mode safe string functions are prototyped in Ntstrsafe.h; this header file and the associated library, Ntstrsafe.lib, are provided in the Windows DDK for Windows XP SP1 and later versions of Windows. (A corresponding set of safe string functions for user-mode applications is prototyped in Strsafe.h. For more information about these functions, see the Platform SDK.)
The kernel-mode safe string functions take the size of the destination buffer as an input parameter, to ensure that the function does not write past the end of the buffer. These functions also null-terminate all output strings, to avoid problems caused by a missing null terminator. To make checking return values simpler and more consistent, all safe string functions return an NTSTATUS value with only one possible success code, STATUS_SUCCESS.
Each kernel-mode safe string function is available in both byte-counted and character-counted versions (RtlStringCbXxx and RtlStringCchXxx) as well as versions for double-byte Unicode characters and single-byte ANSI characters (RtlStringXxxW and RtlStringXxxA), to make handling mixed Unicode and ANSI strings easier. Most functions are also available in an extended version (RtlStringXxxEx) that provides advanced functionality, such as filling the destination buffer after the null terminator.
What should you do?
| • | If your driver will run only on Windows XP and later versions of Windows, just include Ntstrsafe.h in your code. Ntstrsafe.h as a standalone header relies on an export from Ntoskrnl.exe, which is available only on Windows XP and later versions of Windows. |
| • | If your driver must run on Windows 2000 and earlier, you must explicitly link to Ntstrsafe.lib, which contains the implementation of the needed export: 1. | Define NTSTRSAFE_LIB before including ntstrsafe.h, as shown. #define NTSTRSAFE_LIB
#include <ntstrsafe.h> | 2. | In your project's sources file, add a TARGETLIBS entry for #(DDK_LIB_PATH)\ntstrsafe.lib |
|
| • | Place the include directive for Ntstrsafe.h after the include directives for other headers. The functions in Ntstrsafe.h replace the C/C++ language runtime library functions, so calling any of those functions will cause a compiler error. If other header files supply code that references the runtime library functions, compiler errors will also occur. |
| • | If you must use both the C/C++ language runtime library functions and the safe string functions in your code, include the following line before the include directive for Ntstrsafe.h: #define NTSTRSAFE_NO_DEPRECATE |
| • | If you prefer, you can make available only the byte-counted or only the character-counted versions of safe string functions by defining one (but not both) of the following: #define NTSTRSAFE_NO_CCH_FUNCTIONS
#define NTSTRSAFE_NO_CB_FUNCTIONS |
| • | You must explicitly call the Unicode (W) or ANSI (A) versions of the kernel-mode safe string functions (as shown in the following example). Unlike the user-mode safe string functions defined in Strsafe.h, it is not possible to hide the Unicode or ANSI versions of the kernel-mode safe string functions. |
Example: Using RtlStringCbVPrintfA instead of _vsnprintf
The Toaster sample in the Windows DDK uses the safe string function RtlStringCbVPrintfA instead of _vsnprintf in its ToasterDebugPrint function. RtlStringCbVPrintfA creates a byte-counted ANSI text string and null-terminates the message if it is longer than the buffer. Equally safe use of _vsnprintf would require a great deal more error handling.
//%winddk%\src\general\toaster\func\featured1\toaster.c
//See sample code for complete comments
VOID
ToasterDebugPrint (
IN ULONG DebugPrintLevel,
IN PCCHAR DebugMessage,
...
)
{
#define TEMP_BUFFER_SIZE 1024
va_list list;
UCHAR debugMessageBuffer[TEMP_BUFFER_SIZE];
NTSTATUS status;
va_start(list, DebugMessage);
if (DebugMessage) {
//Use safe string function instead of _vsnprintf
status = RtlStringCbVPrintfA(debugMessageBuffer, sizeof(debugMessageBuffer),
DebugMessage, list);
if(!NT_SUCCESS(status)) {
KdPrint ((_DRIVER_NAME_": RtlStringCbVPrintfA failed %x\n", status));
return;
}
if (DebugPrintLevel <= DebugLevel) {
KdPrint ((_DRIVER_NAME_": %s", debugMessageBuffer));
}
}
va_end(list);
return;
}
For more information:
Windows Driver Kit
Using Safe String Functions
MSDN Library
Using the Strsafe.h Functions
Writing Secure Code, 2nd edition, by Michael Howard and David LeBlanc (ISBN 0-7356-1722-8)
Chapter 5, Public Enemy #1: The Buffer Overrun