Another Step to Kernel String Safety

Over roughly the last decade Microsoft has made a lot of effort on providing Safe String functions and requiring their use.  Unfortunately, there is at least one hole in this effort that some of us pointed out years ago, that is still there, namely the functions RtlInitAnsiString, RtlInitString and RtlInitUnicodeString.  The functions take a NULL terminated string, and setup the appropriate counted string structure.  These three functions are all unsafe, but still remain widely in use.  For example, the Windows 8.1 WDK has 342 calls to these functions.
The problem with these calls is that they essentially use strlen and its derivatives to get the length of the string.  Now strlen has no check for the end of the input string’s buffer, which is why calling the function is an error that recommends that you call RtlStringCbLength  instead, but RtlInitAnsiString doesn’t have the extra parameter the new call requires.
So how did this slip by?  The simple answer is the common use of these functions is:

RtlInitUnicodeString( &UniString, L”This is the string” );

Such a usage is perfectly safe, since the compiler guarantees the string constant as the second argument has a NULL terminator.  But what happens if the second argument is a variable?  If there is no NULL termination the function will keep going until it page faults.  Even for the case of a constant string, why are you doing this?  The compiler knows the length of the string, so why are you using time in your driver to calculate a constant?
Fortunately, there are a number of simple ways to replace the function.  The simplest for UNICODE_STRINGS is to use:

DECLARE_CONST_UNICODE_STRING( UniString, L”This is the string” );

This is almost identical to RtlInitUnicodeString but it declares the variable.  So if your original code used UniString multiple times in a function as the first argument to RtlInitUnicodeString you will have to make them unique.  While DECLARE_CONST_UNICODE_STRING does not have a documentation page, it was well covered in Doron Holon’s excellent blog post.
The second approach is to:

UNICODE_STRING UniString = RTL_CONSTANT_STRING(L”This is the string”);

This version works with all the string types.  See the MSDN documentation for RTL_CONSTANT_STRING.

Finally, what are you doing with the string you just initialized?  Many old drivers use one of the RtlInitXXXString functions, then immediately copied the string to another string and appended some value.  With the safe string functions the better approach is to use RtlUnicodeStringPrintf.  In most cases a number of functions can be collapsed into a single call with this approach.

Once you have done the work of getting rid of these unsafe functions consider adding the following three lines to a common include file for your driver project so the functions are flagged in the future.

#pragma deprecated(RtlInitAnsiString)
#pragma deprecated(RtlInitString)
#pragma deprecated(RtlInitUnicodeString)

Hopefully, someday soon Microsoft will put these three lines into their include files where they really belong.