[DDK] RtlUnicodeStringPrintf(Ex) implementations and ntstrsafe.h tests (#522, #523)
authorHernán Di Pietro <indiocolifa@hotmail.com>
Sat, 21 Apr 2018 06:56:12 +0000 (08:56 +0200)
committerColin Finck <colin@reactos.org>
Wed, 28 Aug 2019 20:28:13 +0000 (22:28 +0200)
[KMTESTS:RTL] (CORE-14565)

+ Added buffer overrun tests.
+ New style header.
+ ISO C90 compatibility

[DDK] (CORE-9819): RtlUnicodeStringPrintf(Ex) and RtlUnicodeStringValidate(Ex) fixes plus formatting.

[DDK]: Cosmetic changes  (NTSTATUS variable naming and tabs)

[DDK] First implementation of Unicode functions for NtStrSafe (CORE-9819)

+ This implementation does not modify the interface of previous functions.
+ Since we need length returned for the destination UNICODE_STRING.Length field:
  - an intermediate "Len" postfixed function was added with optional
    argument to return the _vsnwprintf character count.
  - Wide Printf(Ex) functions already existent work now call new Rtlp
    private worker functions.
+ For several UnicodeStringPrintf(Ex) tests, see work going on CORE-14565

NOTES:

+ MSDN says they won't insert NULL characters on formatted strings. But
  seems this is incorrect at MSDN (interpret this as DONT ASSUME NULL-termination).
  We return zero-terminated strings, and MS functions seems to do the same.
  (XP DDK 7.1)

[DDK] First implementation of Unicode functions for NtStrSafe (CORE-9819)

+ This implementation does not modify the interface of previous functions.
+ Since we need length returned for the destination UNICODE_STRING.Length field:
  - an intermediate "Len" postfixed function was added with optional
    argument to return the _vsnwprintf character count.
  - Wide Printf(Ex) functions already existent work now call new Rtlp
    private worker functions.
+ For several UnicodeStringPrintf(Ex) tests, see work going on CORE-14565

NOTES:

+ MSDN says they won't insert NULL characters on formatted strings. But
  seems this is incorrect at MSDN (interpret this as DONT ASSUME NULL-termination).
  We return zero-terminated strings, and MS functions seems to do the same.
  (XP DDK 7.1)

[DDK] Fixed bad NtStrSafe.h va_start call (CORE-9819)

Update on RtlUnicodeStringPrintfEx plus cosmetic changes.

[DDK] First implementation of Unicode functions for NtStrSafe (CORE-9819)

+ This implementation does not modify the interface of previous functions.
+ Since we need length returned for the destination UNICODE_STRING.Length field:
  - an intermediate "Len" postfixed function was added with optional
    argument to return the _vsnwprintf character count.
  - Wide Printf(Ex) functions already existent work now call new Rtlp
    private worker functions.
+ For several UnicodeStringPrintf(Ex) tests, see work going on CORE-14565

NOTES:

+ MSDN says they won't insert NULL characters on formatted strings. But
  seems this is incorrect at MSDN (interpret this as DONT ASSUME NULL-termination).
  We return zero-terminated strings, and MS functions seems to do the same.
  (XP DDK 7.1)

[KMTESTS:RTL] Tests for new RtlUnicodePrintf(Ex) functions (CORE-14565)

+ 45 Tests working.
+ Lacks remaining non-Unicode functions in NtStrSafe.h for now.
+ Used for first tests on CORE-9819

[KMTESTS/RTL] Fixed test not taking care of null (CORE-14565)

[DDK] Fixed bad NtStrSafe.h va_start call (CORE-9819)

[KMTESTS:RTL] First Test.

Update on RtlUnicodeStringPrintfEx plus cosmetic changes.

[KMTESTS] Added NtStrSafe to test lists (CORE-14565)

First commit: Skeleton for test implementation + Addition to COMMON SOURCES in kmtests Cmake lists.

WIP.
+Implementation of RtlStringVPrintfWorkerLenW, RtlStringVPrintfExWorkerLenW to avoid changing existing public functions. This is required as existent functions did not return vsnprintf result, so we didn't have any return info to update UNICODE_STRING.Length.
+Additional implementation of RtlUnicodeStringValidate for checking purposes.
+Former RtlStringVPrintfWorker(ex) refactored to RtlInternalStringVPrintf(Ex)WorkerW.
+No ANSI functions affected as we didn't need them for Unicode printf's.

WIP: RtlUnicodeStringPrintf

modules/rostests/kmtests/CMakeLists.txt
modules/rostests/kmtests/kmtest/testlist.c
modules/rostests/kmtests/kmtest_drv/testlist.c
modules/rostests/kmtests/rtl/RtlStrSafe.c [new file with mode: 0644]
sdk/include/ddk/ntstrsafe.h

index b7cf709..087bb31 100644 (file)
@@ -23,6 +23,7 @@ list(APPEND COMMON_SOURCE
     rtl/RtlRegistry.c
     rtl/RtlSplayTree.c
     rtl/RtlStack.c
+    rtl/RtlStrSafe.c
     rtl/RtlUnicodeString.c)
 
 #
index 5375d04..b71d1ba 100644 (file)
@@ -31,6 +31,7 @@ KMT_TESTFUNC Test_RtlMemory;
 KMT_TESTFUNC Test_RtlRegistry;
 KMT_TESTFUNC Test_RtlSplayTree;
 KMT_TESTFUNC Test_RtlStack;
+KMT_TESTFUNC Test_RtlStrSafe;
 KMT_TESTFUNC Test_RtlUnicodeString;
 KMT_TESTFUNC Test_TcpIpIoctl;
 KMT_TESTFUNC Test_TcpIpTdi;
@@ -64,6 +65,7 @@ const KMT_TEST TestList[] =
     { "RtlRegistry",                  Test_RtlRegistry },
     { "RtlSplayTree",                 Test_RtlSplayTree },
     { "RtlStack",                     Test_RtlStack },
+    { "RtlStrSafe",                   Test_RtlStrSafe },
     { "RtlUnicodeString",             Test_RtlUnicodeString },
     { "TcpIpTdi",                     Test_TcpIpTdi },
     { "TcpIpConnect",                 Test_TcpIpConnect },
index 3dc3a6d..8bdd245 100644 (file)
@@ -72,6 +72,7 @@ KMT_TESTFUNC Test_RtlMemory;
 KMT_TESTFUNC Test_RtlRegistry;
 KMT_TESTFUNC Test_RtlSplayTree;
 KMT_TESTFUNC Test_RtlStack;
+KMT_TESTFUNC Test_RtlStrSafe;
 KMT_TESTFUNC Test_RtlUnicodeString;
 KMT_TESTFUNC Test_ZwAllocateVirtualMemory;
 KMT_TESTFUNC Test_ZwCreateSection;
@@ -144,6 +145,7 @@ const KMT_TEST TestList[] =
     { "RtlRegistryKM",                      Test_RtlRegistry },
     { "RtlSplayTreeKM",                     Test_RtlSplayTree },
     { "RtlStackKM",                         Test_RtlStack },
+    { "RtlStrSafeKM",                       Test_RtlStrSafe },
     { "RtlUnicodeStringKM",                 Test_RtlUnicodeString },
     { "SeInheritance",                      Test_SeInheritance },
     { "SeQueryInfoToken",                   Test_SeQueryInfoToken },
diff --git a/modules/rostests/kmtests/rtl/RtlStrSafe.c b/modules/rostests/kmtests/rtl/RtlStrSafe.c
new file mode 100644 (file)
index 0000000..807a036
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * PROJECT:         ReactOS kernel-mode tests
+ * LICENSE:         GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE:         Test for ntstrsafe.h functions
+ * COPYRIGHT:       Copyright 2018 Hernán Di Pietro <hernan.di.pietro@gmail.com>
+ */
+
+#define KMT_EMULATE_KERNEL
+#include <kmt_test.h>
+#include <ntstrsafe.h>
+#include <ntdef.h>
+#include <ndk/rtlfuncs.h>
+
+#define TESTAPI static void
+
+TESTAPI
+Test_RtlUnicodeStringPrintf()
+{
+    WCHAR Buffer[1024];
+    WCHAR OvrBuffer[1024];
+    WCHAR BufferSmall[2];
+    WCHAR BufferSmall2[7];
+    UNICODE_STRING UsString;
+    const WCHAR FormatStringInts[] = L"%d %d %d";
+    const WCHAR FormatStringStrs[] = L"%s %s %s";
+    const WCHAR Result[] = L"1 2 3";
+    UNICODE_STRING UsStringNull;
+
+    /* No zeros (Don't assume UNICODE_STRINGS are NULL terminated) */
+
+    RtlFillMemory(Buffer, sizeof(Buffer), 0xAA);
+    RtlFillMemory(BufferSmall, sizeof(BufferSmall), 0xAA);
+    RtlFillMemory(BufferSmall2, sizeof(BufferSmall2), 0xAA);
+
+    /* STATUS_SUCCESS test */
+
+    UsString.Buffer = Buffer;
+    UsString.Length = 0;
+    UsString.MaximumLength = sizeof(Buffer);
+
+    ok_eq_hex(RtlUnicodeStringPrintf(&UsString, FormatStringInts, 1, 2, 3), STATUS_SUCCESS);
+    ok_eq_uint(UsString.Length, sizeof(Result) - sizeof(WCHAR));
+    ok_eq_uint(UsString.MaximumLength, sizeof(Buffer));
+    ok_eq_wchar(UsString.Buffer[0], L'1');
+    ok_eq_wchar(UsString.Buffer[1], L' ');
+    ok_eq_wchar(UsString.Buffer[2], L'2');
+    ok_eq_wchar(UsString.Buffer[3], L' ');
+    ok_eq_wchar(UsString.Buffer[4], L'3');
+    ok_eq_wchar(UsString.Buffer[5], (WCHAR) 0);
+    
+    /* STATUS_BUFFER_OVERFLOW tests */
+
+    UsString.Buffer = BufferSmall;
+    UsString.Length = 0;
+    UsString.MaximumLength = sizeof(BufferSmall);
+    
+    ok_eq_hex(RtlUnicodeStringPrintf(&UsString, FormatStringStrs, L"AAA", L"BBB", L"CCC"), STATUS_BUFFER_OVERFLOW);
+    ok_eq_uint(UsString.Length, UsString.MaximumLength);
+    ok_eq_char(UsString.Buffer[0], L'A');
+    ok_eq_char(UsString.Buffer[1], (WCHAR)0);
+
+    UsString.Buffer = BufferSmall2;
+    UsString.Length = 0;
+    UsString.MaximumLength = sizeof(BufferSmall2);
+
+    ok_eq_hex(RtlUnicodeStringPrintf(&UsString, FormatStringStrs, L"0123", L"4567", L"89AB"), STATUS_BUFFER_OVERFLOW);
+    ok_eq_uint(UsString.Length, UsString.MaximumLength);
+    ok_eq_char(UsString.Buffer[0], L'0');
+    ok_eq_char(UsString.Buffer[1], L'1');
+    ok_eq_char(UsString.Buffer[2], L'2');
+    ok_eq_char(UsString.Buffer[3], L'3');
+    ok_eq_char(UsString.Buffer[4], L' ');
+    ok_eq_char(UsString.Buffer[5], L'4');
+    ok_eq_char(UsString.Buffer[6], (WCHAR) 0);
+
+    ///* STATUS_INVALID_PARAMETER tests */
+
+    ok_eq_hex(RtlUnicodeStringPrintf(NULL, FormatStringStrs, L"AAA", L"BBB", L"CCC"), STATUS_INVALID_PARAMETER);
+    ok_eq_hex(RtlUnicodeStringPrintf(&UsString, NULL, L"AAA", L"BBB", L"CCC"), STATUS_INVALID_PARAMETER);
+
+    UsStringNull.Buffer = (PWCH)NULL;
+    UsStringNull.Length = 0;
+    UsStringNull.MaximumLength = 0;
+    ok_eq_bool(RtlUnicodeStringPrintf(&UsStringNull, FormatStringStrs, L"AAA", L"BBB", L"CCC"), STATUS_INVALID_PARAMETER);
+
+    /* Test  for buffer overruns */
+
+    RtlFillMemory(Buffer, sizeof(Buffer), 0xAA);
+    RtlFillMemory(OvrBuffer, sizeof(OvrBuffer), 0xAA);
+    UsString.Buffer = Buffer;
+    UsString.Length = 0;
+    UsString.MaximumLength = 16 * sizeof(WCHAR);
+
+    ok_eq_hex(RtlUnicodeStringPrintf(&UsString, FormatStringStrs, L"abc", L"def", L"ghi"), STATUS_SUCCESS);
+    ok_eq_uint(UsString.Length, sizeof(L"abc def ghi") -sizeof(WCHAR));
+    ok_eq_char(UsString.Buffer[11], (WCHAR)0);
+    ok_eq_uint(0, memcmp(OvrBuffer + 12, Buffer + 12, sizeof(Buffer) - (12 * sizeof(WCHAR))));
+}
+
+TESTAPI
+Test_RtlUnicodeStringPrintfEx()
+{
+    WCHAR Buffer[32];
+    WCHAR BufferSmall[8] = { 0 };
+    WCHAR OvrBuffer[1024];
+    UNICODE_STRING UsString, RemString;
+    const WCHAR FormatStringInts[] = L"%d %d %d";
+    const WCHAR FormatStringStrs[] = L"%s %s %s";
+    const WCHAR Result[] = L"1 2 3";
+
+    /* No zeros (Don't assume UNICODE_STRINGS are NULL terminated) */
+
+    RtlFillMemory(Buffer, sizeof(Buffer), 0xAA);
+
+    UsString.Buffer = Buffer;
+    UsString.Length = 0;
+    UsString.MaximumLength = sizeof(Buffer);
+
+    RemString.Buffer = (PWCH)NULL;
+    RemString.Length = 0;
+    RemString.MaximumLength = 0;
+
+    /* STATUS_SUCCESS test, fill behind flag: low-byte as fill character */
+
+    ok_eq_hex(RtlUnicodeStringPrintfEx(&UsString, &RemString, STRSAFE_FILL_BEHIND | 0xFF, FormatStringInts, 1, 2, 3), STATUS_SUCCESS);
+    
+    ok_eq_uint(UsString.Length, sizeof(Result) - sizeof(WCHAR));
+    ok_eq_uint(UsString.MaximumLength, sizeof(Buffer));
+    ok_eq_uint(memcmp(UsString.Buffer, Result, sizeof(Result) - sizeof(WCHAR)), 0);
+
+    ok_eq_pointer(RemString.Buffer, UsString.Buffer + (UsString.Length / sizeof(WCHAR)));
+    ok_eq_uint(RemString.Length, 0);
+    ok_eq_uint(RemString.MaximumLength, UsString.MaximumLength - UsString.Length);
+
+    /* STATUS_BUFFER_OVERFLOW test  */
+
+    UsString.Buffer = BufferSmall;
+    UsString.Length = 0;
+    UsString.MaximumLength = sizeof(BufferSmall);
+
+    RemString.Buffer = (PWCH)NULL;
+    RemString.Length = 0;
+    RemString.MaximumLength = 0;
+    
+    ok_eq_hex(RtlUnicodeStringPrintfEx(&UsString, &RemString, 0, FormatStringStrs, L"AAA", L"BBB", L"CCC"), STATUS_BUFFER_OVERFLOW);
+    ok_eq_uint(UsString.Length, UsString.MaximumLength);
+    ok_eq_char(UsString.Buffer[0], L'A');
+    ok_eq_char(UsString.Buffer[1], L'A');
+    ok_eq_char(UsString.Buffer[2], L'A');
+    ok_eq_char(UsString.Buffer[3], L' ');
+    ok_eq_char(UsString.Buffer[4], L'B');
+    ok_eq_char(UsString.Buffer[5], L'B');
+    ok_eq_char(UsString.Buffer[6], L'B');
+    ok_eq_char(UsString.Buffer[7], (WCHAR)0);
+
+    // Takes \0 into account
+    ok_eq_pointer(RemString.Buffer, UsString.Buffer + (UsString.Length - 1) / sizeof(WCHAR));
+    ok_eq_uint(RemString.Length, 0); 
+    ok_eq_uint(RemString.MaximumLength, 2);
+
+    /* Test  for buffer overruns */
+
+    RtlFillMemory(Buffer, sizeof(Buffer), 0xAA);
+    RtlFillMemory(OvrBuffer, sizeof(OvrBuffer), 0xAA);
+    UsString.Buffer = Buffer;
+    UsString.Length = 0;
+    UsString.MaximumLength = 16 * sizeof(WCHAR);
+
+    ok_eq_hex(RtlUnicodeStringPrintfEx(&UsString, &RemString, 0, FormatStringStrs, L"abc", L"def", L"ghi"), STATUS_SUCCESS);
+    ok_eq_uint(UsString.Length, sizeof(L"abc def ghi") - sizeof(WCHAR));
+    ok_eq_char(UsString.Buffer[11], (WCHAR)0);
+    ok_eq_uint(0, memcmp(OvrBuffer + 12, Buffer + 12, sizeof(Buffer) - (12 * sizeof(WCHAR))));
+}
+
+START_TEST(RtlStrSafe)
+{
+    Test_RtlUnicodeStringPrintf();
+    Test_RtlUnicodeStringPrintfEx();
+}
index 502096b..de2a492 100644 (file)
@@ -58,6 +58,9 @@
 #define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)((dwFlags) & 0x000000FF))
 #endif
 
+#define STRSAFE_FILL_BEHIND                 STRSAFE_FILL_BEHIND_NULL
+#define STRSAFE_ZERO_LENGTH_ON_FAILURE      STRSAFE_NULL_ON_FAILURE 
+
 typedef char *STRSAFE_LPSTR;
 typedef const char *STRSAFE_LPCSTR;
 typedef wchar_t *STRSAFE_LPWSTR;
@@ -88,8 +91,11 @@ NTSTRSAFEAPI RtlStringCatNExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t
 NTSTRSAFEAPI RtlStringCatNExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCWSTR pszSrc,size_t cchToAppend,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags);
 NTSTRSAFEAPI RtlStringVPrintfWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszFormat,va_list argList);
 NTSTRSAFEAPI RtlStringVPrintfWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszFormat,va_list argList);
+NTSTRSAFEAPI RtlStringVPrintfWorkerLenW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszFormat,size_t* pcchDestNewLen, va_list argList);
 NTSTRSAFEAPI RtlStringVPrintfExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags,STRSAFE_LPCSTR pszFormat,va_list argList);
 NTSTRSAFEAPI RtlStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags,STRSAFE_LPCWSTR pszFormat,va_list argList);
+NTSTRSAFEAPI RtlStringVPrintfExWorkerLenW(STRSAFE_LPWSTR pszDest, size_t cchDest, size_t cbDest, STRSAFE_LPWSTR *ppszDestEnd, size_t *pcchRemaining, STRSAFE_DWORD dwFlags, STRSAFE_LPCWSTR pszFormat, size_t* pcchDestNewLen, va_list argList);
+NTSTRSAFEAPI RtlUnicodeStringValidate(PCUNICODE_STRING SourceString);
 
 NTSTRSAFEAPI
 RtlStringLengthWorkerA(
@@ -1575,7 +1581,9 @@ RtlStringCbLengthW(
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszSrc)
+NTSTRSAFEAPI RtlStringCopyWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCSTR pszSrc)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     if (cchDest==0)
@@ -1597,7 +1605,9 @@ NTSTRSAFEAPI RtlStringCopyWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_L
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszSrc)
+NTSTRSAFEAPI RtlStringCopyWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCWSTR pszSrc)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     if (cchDest==0)
@@ -1619,7 +1629,13 @@ NTSTRSAFEAPI RtlStringCopyWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCSTR pszSrc,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCopyExWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCSTR pszSrc,
+    STRSAFE_LPSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPSTR pszDestEnd = pszDest;
@@ -1719,7 +1735,13 @@ NTSTRSAFEAPI RtlStringCopyExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCWSTR pszSrc,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCopyExWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCWSTR pszSrc,
+    STRSAFE_LPWSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPWSTR pszDestEnd = pszDest;
@@ -1819,7 +1841,9 @@ NTSTRSAFEAPI RtlStringCopyExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyNWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszSrc,size_t cchSrc)
+NTSTRSAFEAPI RtlStringCopyNWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCSTR pszSrc,size_t cchSrc)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     if (cchDest==0)
@@ -1842,7 +1866,10 @@ NTSTRSAFEAPI RtlStringCopyNWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyNWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszSrc,size_t cchToCopy)
+NTSTRSAFEAPI RtlStringCopyNWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCWSTR pszSrc,
+    size_t cchToCopy)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     if (cchDest==0)
@@ -1865,7 +1892,14 @@ NTSTRSAFEAPI RtlStringCopyNWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyNExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCSTR pszSrc,size_t cchToCopy,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCopyNExWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCSTR pszSrc,
+    size_t cchToCopy,
+    STRSAFE_LPSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPSTR pszDestEnd = pszDest;
@@ -1969,7 +2003,14 @@ NTSTRSAFEAPI RtlStringCopyNExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCopyNExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCWSTR pszSrc,size_t cchToCopy,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCopyNExWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCWSTR pszSrc,
+    size_t cchToCopy,
+    STRSAFE_LPWSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPWSTR pszDestEnd = pszDest;
@@ -2073,7 +2114,9 @@ NTSTRSAFEAPI RtlStringCopyNExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszSrc)
+NTSTRSAFEAPI RtlStringCatWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCSTR pszSrc)
 {
     NTSTATUS Status;
     size_t cchDestLength;
@@ -2083,7 +2126,9 @@ NTSTRSAFEAPI RtlStringCatWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LP
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszSrc)
+NTSTRSAFEAPI RtlStringCatWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCWSTR pszSrc)
 {
     NTSTATUS Status;
     size_t cchDestLength;
@@ -2093,7 +2138,13 @@ NTSTRSAFEAPI RtlStringCatWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_L
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCSTR pszSrc,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCatExWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCSTR pszSrc,
+    STRSAFE_LPSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPSTR pszDestEnd = pszDest;
@@ -2190,7 +2241,13 @@ NTSTRSAFEAPI RtlStringCatExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t c
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCWSTR pszSrc,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCatExWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCWSTR pszSrc,
+    STRSAFE_LPWSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPWSTR pszDestEnd = pszDest;
@@ -2287,7 +2344,10 @@ NTSTRSAFEAPI RtlStringCatExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatNWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszSrc,size_t cchToAppend)
+NTSTRSAFEAPI RtlStringCatNWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCSTR pszSrc,
+    size_t cchToAppend)
 {
     NTSTATUS Status;
     size_t cchDestLength;
@@ -2297,7 +2357,10 @@ NTSTRSAFEAPI RtlStringCatNWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_L
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatNWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszSrc,size_t cchToAppend)
+NTSTRSAFEAPI RtlStringCatNWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCWSTR pszSrc,
+    size_t cchToAppend)
 {
     NTSTATUS Status;
     size_t cchDestLength;
@@ -2307,7 +2370,14 @@ NTSTRSAFEAPI RtlStringCatNWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatNExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCSTR pszSrc,size_t cchToAppend,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCatNExWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCSTR pszSrc,
+    size_t cchToAppend,
+    STRSAFE_LPSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPSTR pszDestEnd = pszDest;
@@ -2407,7 +2477,14 @@ NTSTRSAFEAPI RtlStringCatNExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringCatNExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPCWSTR pszSrc,size_t cchToAppend,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags)
+NTSTRSAFEAPI RtlStringCatNExWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPCWSTR pszSrc,
+    size_t cchToAppend,
+    STRSAFE_LPWSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPWSTR pszDestEnd = pszDest;
@@ -2507,7 +2584,10 @@ NTSTRSAFEAPI RtlStringCatNExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringVPrintfWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAFE_LPCSTR pszFormat,va_list argList)
+NTSTRSAFEAPI RtlStringVPrintfWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCSTR pszFormat,
+    va_list argList)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     if (cchDest==0)
@@ -2534,34 +2614,67 @@ NTSTRSAFEAPI RtlStringVPrintfWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,STRSAF
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringVPrintfWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,STRSAFE_LPCWSTR pszFormat,va_list argList)
+NTSTRSAFEAPI RtlpStringVPrintfWorkerW(STRSAFE_LPWSTR pszDest, 
+    size_t cchDest, 
+    STRSAFE_LPCWSTR pszFormat, 
+    size_t* pcchDestNewLen, 
+    va_list argList)
 {
     NTSTATUS Status = STATUS_SUCCESS;
-    if (cchDest==0)
-        Status = STATUS_INVALID_PARAMETER;
-    else
+    int iRet;
+    size_t cchMax;
+    cchMax = cchDest - 1;
+    iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
+    if ((iRet < 0) || (((size_t) iRet) > cchMax))
     {
-        int iRet;
-        size_t cchMax;
-        cchMax = cchDest - 1;
-        iRet = _vsnwprintf(pszDest,cchMax,pszFormat,argList);
-        if ((iRet < 0) || (((size_t)iRet) > cchMax))
+        pszDest += cchMax;
+        *pszDest = L'\0';
+        Status = STATUS_BUFFER_OVERFLOW;
+    }
+    else
+        if (((size_t) iRet) == cchMax)
         {
             pszDest += cchMax;
             *pszDest = L'\0';
-            Status = STATUS_BUFFER_OVERFLOW;
         }
-        else
-            if (((size_t)iRet)==cchMax)
-            {
-                pszDest += cchMax;
-                *pszDest = L'\0';
-            }
-    }
+
+    if (pcchDestNewLen)
+        *pcchDestNewLen = (iRet == -1) ? cchDest : iRet;
+
     return Status;
 }
 
-NTSTRSAFEAPI RtlStringVPrintfExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags,STRSAFE_LPCSTR pszFormat,va_list argList)
+NTSTRSAFEAPI RtlStringVPrintfWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    STRSAFE_LPCWSTR pszFormat,
+    va_list argList)
+{
+    if (cchDest==0)
+        return STATUS_INVALID_PARAMETER;
+
+    return RtlpStringVPrintfWorkerW(pszDest, cchDest, pszFormat, NULL, argList);
+}
+
+NTSTRSAFEAPI RtlStringVPrintfWorkerLenW(STRSAFE_LPWSTR pszDest, 
+    size_t cchDest, 
+    STRSAFE_LPCWSTR pszFormat, 
+    size_t* pcchDestNewLen, 
+    va_list argList)
+{
+    if (cchDest == 0 || pcchDestNewLen == 0)
+        return STATUS_INVALID_PARAMETER;
+    
+    return RtlpStringVPrintfWorkerW(pszDest, cchDest, pszFormat, pcchDestNewLen, argList);
+}
+
+NTSTRSAFEAPI RtlStringVPrintfExWorkerA(STRSAFE_LPSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags,
+    STRSAFE_LPCSTR pszFormat,
+    va_list argList)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPSTR pszDestEnd = pszDest;
@@ -2667,8 +2780,15 @@ NTSTRSAFEAPI RtlStringVPrintfExWorkerA(STRSAFE_LPSTR pszDest,size_t cchDest,size
     }
     return Status;
 }
-
-NTSTRSAFEAPI RtlStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,size_t cbDest,STRSAFE_LPWSTR *ppszDestEnd,size_t *pcchRemaining,STRSAFE_DWORD dwFlags,STRSAFE_LPCWSTR pszFormat,va_list argList)
+NTSTRSAFEAPI RtlpStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest,
+    size_t cchDest,
+    size_t cbDest,
+    STRSAFE_LPWSTR *ppszDestEnd,
+    size_t *pcchRemaining,
+    STRSAFE_DWORD dwFlags,
+    STRSAFE_LPCWSTR pszFormat,
+    size_t* pcchDestNewLen, 
+    va_list argList)
 {
     NTSTATUS Status = STATUS_SUCCESS;
     STRSAFE_LPWSTR pszDestEnd = pszDest;
@@ -2731,6 +2851,9 @@ NTSTRSAFEAPI RtlStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,siz
                                 memset(pszDestEnd + 1,STRSAFE_GET_FILL_PATTERN(dwFlags),((cchRemaining - 1)*sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
                             }
                         }
+
+                if (pcchDestNewLen)
+                    *pcchDestNewLen = iRet == -1 ? cchDest : iRet;
             }
         }
     }
@@ -2775,6 +2898,35 @@ NTSTRSAFEAPI RtlStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest,size_t cchDest,siz
     return Status;
 }
 
+NTSTRSAFEAPI RtlStringVPrintfExWorkerW(STRSAFE_LPWSTR pszDest, 
+    size_t cchDest, 
+    size_t cbDest, 
+    STRSAFE_LPWSTR *ppszDestEnd, 
+    size_t *pcchRemaining, 
+    STRSAFE_DWORD dwFlags, 
+    STRSAFE_LPCWSTR pszFormat, 
+    va_list argList)
+{
+    return RtlpStringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, NULL, argList);
+}
+
+NTSTRSAFEAPI RtlStringVPrintfExWorkerLenW(STRSAFE_LPWSTR pszDest, 
+    size_t cchDest, 
+    size_t cbDest, 
+    STRSAFE_LPWSTR *ppszDestEnd, 
+    size_t *pcchRemaining, 
+    STRSAFE_DWORD dwFlags, 
+    STRSAFE_LPCWSTR pszFormat, 
+    size_t* pcchDestNewLen, 
+    va_list argList)
+{
+    if (pcchDestNewLen == 0)
+        return STATUS_INVALID_PARAMETER;
+
+    return RtlpStringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, pcchDestNewLen, argList);
+}
+
+
 NTSTRSAFEAPI
 RtlStringLengthWorkerA(
   _In_reads_or_z_(cchMax) STRSAFE_LPCSTR psz,
@@ -2825,6 +2977,126 @@ RtlStringLengthWorkerW(
     return Status;
 }
 
+NTSTRSAFEAPI
+RtlpUnicodeStringValidate(_In_opt_ PCUNICODE_STRING SourceString,
+    _In_ STRSAFE_DWORD dwFlags)
+{
+    if (SourceString)
+    {
+        if (SourceString->Length % sizeof(WCHAR) != 0 ||
+            SourceString->MaximumLength % sizeof(WCHAR) != 0 ||
+            SourceString->Length > SourceString->MaximumLength ||
+            SourceString->MaximumLength > NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR) ||
+            (SourceString->Buffer == NULL && (SourceString->Length != 0 || SourceString->MaximumLength != 0)))
+        {
+            return STATUS_INVALID_PARAMETER;
+        }
+    }
+    else
+    {
+        if (!(dwFlags & STRSAFE_IGNORE_NULLS))
+            return STATUS_INVALID_PARAMETER;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTRSAFEAPI
+RtlUnicodeStringValidate(_In_opt_ PCUNICODE_STRING SourceString)
+{
+    return RtlpUnicodeStringValidate(SourceString, 0);
+}
+
+NTSTRSAFEAPI
+RtlUnicodeStringValidateEx(_In_opt_ PCUNICODE_STRING SourceString,
+    _In_ STRSAFE_DWORD dwFlags)
+{
+    if (dwFlags & ~(STRSAFE_UNICODE_STRING_VALID_FLAGS))
+        return STATUS_INVALID_PARAMETER;
+
+    return RtlpUnicodeStringValidate(SourceString, dwFlags);
+}
+
+static 
+NTSTATUS
+__cdecl
+RtlUnicodeStringPrintf(_In_ PUNICODE_STRING DestinationString,
+    _In_ NTSTRSAFE_PCWSTR pszFormat, ...)
+{
+    NTSTATUS Status;
+    size_t   cchFinalLength;
+    va_list  argList;
+
+    if (DestinationString == NULL || pszFormat == NULL || DestinationString->Buffer == NULL)
+    {
+        Status = STATUS_INVALID_PARAMETER;
+    }
+    else
+    {
+        Status = RtlUnicodeStringValidate(DestinationString);
+        if (NT_SUCCESS(Status))
+        {
+            va_start(argList, pszFormat);
+
+            Status = RtlStringVPrintfWorkerLenW(DestinationString->Buffer,
+                DestinationString->MaximumLength / sizeof(WCHAR),
+                pszFormat,
+                &cchFinalLength,
+                argList);
+
+            if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
+            {
+                DestinationString->Length = (USHORT)(cchFinalLength * sizeof(WCHAR));
+            }
+
+            va_end(argList);
+        }
+    }
+
+    return Status;
+}
+
+static 
+NTSTATUS
+__cdecl
+RtlUnicodeStringPrintfEx(_In_opt_ PUNICODE_STRING DestinationString,
+    _In_opt_  PUNICODE_STRING RemainingString,
+    _In_ STRSAFE_DWORD dwFlags,
+    _In_ NTSTRSAFE_PCWSTR pszFormat, ...)
+{
+    NTSTATUS    Status;
+    size_t      cchFinalLength;
+    size_t      cchRemaining;
+    va_list     argList;
+
+    va_start(argList, pszFormat);
+
+    Status = RtlStringVPrintfExWorkerLenW(DestinationString->Buffer,
+        DestinationString->MaximumLength / sizeof(WCHAR),
+        DestinationString->MaximumLength,
+        &RemainingString->Buffer,
+        &cchRemaining,
+        dwFlags,
+        pszFormat,
+        &cchFinalLength,
+        argList);
+
+    va_end(argList);
+
+    if (Status == STATUS_BUFFER_OVERFLOW || NT_SUCCESS(Status))
+    {
+        DestinationString->Length = (USHORT)(cchFinalLength * sizeof(WCHAR));
+
+        if (RemainingString)
+        {
+            RemainingString->Length = 0;
+            RemainingString->MaximumLength = (USHORT)cchRemaining * sizeof(WCHAR);
+        }
+    }
+
+    return Status;
+}
+
 #define RtlStringCopyWorkerA RtlStringCopyWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
 #define RtlStringCopyWorkerW RtlStringCopyWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
 #define RtlStringCopyExWorkerA RtlStringCopyExWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;