[SPOOLSS]
authorColin Finck <colin@reactos.org>
Thu, 13 Apr 2017 16:48:40 +0000 (16:48 +0000)
committerColin Finck <colin@reactos.org>
Thu, 13 Apr 2017 16:48:40 +0000 (16:48 +0000)
Implement the undocumented AlignRpcPtr and UndoAlignRpcPtr functions used by many Rpc* functions in spoolsv according to traced callchains.
I could reverse engineer them entirely using rohitab.com's API Monitor and black-box testing.
I also add documented tests covering all cases I found out. We now pass 17/17 tests on Windows Server 2003 and ReactOS.

Also const-ify a parameter in PackStrings.

svn path=/trunk/; revision=74297

reactos/win32ss/printing/base/spoolss/memory.c
reactos/win32ss/printing/base/spoolss/spoolss.spec
reactos/win32ss/printing/base/spoolss/tools.c
reactos/win32ss/printing/include/spoolss.h
rostests/apitests/spoolss/AlignRpcPtr.c [new file with mode: 0644]
rostests/apitests/spoolss/CMakeLists.txt
rostests/apitests/spoolss/testlist.c

index 96a7fd1..07be553 100644 (file)
@@ -2,12 +2,42 @@
  * PROJECT:     ReactOS Spooler Router
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions for allocating and freeing memory
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
 
 
+/**
+ * @name AlignRpcPtr
+ *
+ * Checks if the input buffer and buffer size are 4-byte aligned.
+ * If the buffer size is not 4-byte aligned, it is aligned down.
+ * If the input buffer is not 4-byte aligned, a 4-byte aligned buffer of the aligned down buffer size is allocated and returned.
+ *
+ * @param pBuffer
+ * The buffer to check.
+ *
+ * @param pcbBuffer
+ * Pointer to the buffer size to check. Its value is aligned down if needed.
+ *
+ * @return
+ * pBuffer if pBuffer is already 4-byte aligned, or a newly allocated 4-byte aligned buffer of the aligned down buffer size otherwise.
+ * If a buffer was allocated, you have to free it using UndoAlignRpcPtr.
+ */
+PVOID WINAPI
+AlignRpcPtr(PVOID pBuffer, PDWORD pcbBuffer)
+{
+    // Align down the buffer size in pcbBuffer to a 4-byte boundary.
+    *pcbBuffer -= *pcbBuffer % sizeof(DWORD);
+
+    // Check if pBuffer is 4-byte aligned. If not, allocate a 4-byte aligned buffer.
+    if ((ULONG_PTR)pBuffer % sizeof(DWORD))
+        pBuffer = DllAllocSplMem(*pcbBuffer);
+
+    return pBuffer;
+}
+
 /**
  * @name AllocSplStr
  *
@@ -163,6 +193,59 @@ ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput)
         DllFreeSplStr(*ppwszString);
 
     *ppwszString = AllocSplStr(pwszInput);
-    
+
     return TRUE;
 }
+
+/**
+ * @name UndoAlignRpcPtr
+ *
+ * Copies the data from the aligned buffer previously allocated by AlignRpcPtr back to the original unaligned buffer.
+ * The aligned buffer is freed.
+ *
+ * Also aligns up the returned required buffer size of a function to a 4-byte boundary.
+ *
+ * @param pDestinationBuffer
+ * The original unaligned buffer, which you input as pBuffer to AlignRpcPtr.
+ * The data from pSourceBuffer is copied into this buffer before pSourceBuffer is freed.
+ * If AlignRpcPtr did not allocate a buffer, pDestinationBuffer equals pSourceBuffer and no memory is copied or freed.
+ * This parameter may be NULL if pSourceBuffer is NULL.
+ *
+ * @param pSourceBuffer
+ * The aligned buffer, which is returned by AlignRpcPtr.
+ * Its data is copied into pDestinationBuffer and then pSourceBuffer is freed.
+ * If AlignRpcPtr did not allocate a buffer, pDestinationBuffer equals pSourceBuffer and no memory is copied or freed.
+ * This parameter may be NULL.
+ *
+ * @param cbBuffer
+ * Number of bytes to copy.
+ * Set this to the size returned by AlignRpcPtr's pcbBuffer or less.
+ *
+ * @param pcbNeeded
+ * Let this parameter point to your variable calculating the needed bytes for a buffer and returning this value to the user.
+ * It is then aligned up to a 4-byte boundary, so that the user supplies a large enough buffer in the next call.
+ * Otherwise, AlignRpcPtr would align down the buffer size in the next call and your buffer would be smaller than intended.
+ * This parameter may be NULL.
+ *
+ * @return
+ * pcbNeeded
+ */
+PDWORD WINAPI
+UndoAlignRpcPtr(PVOID pDestinationBuffer, PVOID pSourceBuffer, DWORD cbBuffer, PDWORD pcbNeeded)
+{
+    // If pSourceBuffer is given, and source and destination pointers don't match,
+    // we assume that pSourceBuffer is the buffer allocated by AlignRpcPtr.
+    if (pSourceBuffer && pSourceBuffer != pDestinationBuffer)
+    {
+        // Copy back the buffer data to the (usually unaligned) destination buffer
+        // and free the buffer allocated by AlignRpcPtr.
+        CopyMemory(pDestinationBuffer, pSourceBuffer, cbBuffer);
+        DllFreeSplMem(pSourceBuffer);
+    }
+
+    // If pcbNeeded is given, align it up to a 4-byte boundary.
+    if (pcbNeeded && *pcbNeeded % sizeof(DWORD))
+        *pcbNeeded += sizeof(DWORD) - *pcbNeeded % sizeof(DWORD);
+
+    return pcbNeeded;
+}
index 49dbc0d..5c0892c 100644 (file)
@@ -16,7 +16,7 @@
 @ stub AdjustPointers
 @ stub AdjustPointersInStructuresArray
 @ stub AlignKMPtr
-@ stub AlignRpcPtr
+@ stdcall AlignRpcPtr(ptr ptr)
 @ stdcall AllocSplStr(ptr)
 @ stub AllowRemoteCalls
 @ stub AppendPrinterNotifyInfoData
 @ stdcall StartDocPrinterW(long long ptr)
 @ stdcall StartPagePrinter(long)
 @ stub UndoAlignKMPtr
-@ stub UndoAlignRpcPtr
+@ stdcall UndoAlignRpcPtr(ptr ptr long ptr)
 @ stub UnloadDriver
 @ stub UnloadDriverFile
 @ stub UpdateBufferSize
index 5113600..c287080 100644 (file)
@@ -86,7 +86,7 @@ MarshallDownStructure(PVOID pStructure, PMARSHALL_DOWN_INFO pParameters, DWORD c
  * The strings are copied in reverse order, so this pointer will point to the last copied string of pSource.
  */
 PBYTE WINAPI
-PackStrings(PCWSTR* pSource, PBYTE pDest, PDWORD DestOffsets, PBYTE pEnd)
+PackStrings(PCWSTR* pSource, PBYTE pDest, const DWORD* DestOffsets, PBYTE pEnd)
 {
     DWORD cbString;
     ULONG_PTR StringAddress;
index d1677ae..e43c346 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Printing Include files
  * LICENSE:     GNU LGPLv2 or any later version as published by the Free Software Foundation
  * PURPOSE:     Undocumented APIs of the Spooler Router "spoolss.dll"
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #ifndef _REACTOS_SPOOLSS_H
@@ -19,14 +19,16 @@ typedef struct _MARSHALL_DOWN_INFO
 }
 MARSHALL_DOWN_INFO, *PMARSHALL_DOWN_INFO;
 
+PVOID WINAPI AlignRpcPtr(PVOID pBuffer, PDWORD pcbBuffer);
 PWSTR WINAPI AllocSplStr(PCWSTR pwszInput);
 PVOID WINAPI DllAllocSplMem(DWORD dwBytes);
 BOOL WINAPI DllFreeSplMem(PVOID pMem);
 BOOL WINAPI DllFreeSplStr(PWSTR pwszString);
 BOOL WINAPI MarshallDownStructure(PVOID pStructure, PMARSHALL_DOWN_INFO pParameters, DWORD cbStructureSize, BOOL bSomeBoolean);
-PBYTE WINAPI PackStrings(PCWSTR* pSource, PBYTE pDest, PDWORD DestOffsets, PBYTE pEnd);
+PBYTE WINAPI PackStrings(PCWSTR* pSource, PBYTE pDest, const DWORD* DestOffsets, PBYTE pEnd);
 PVOID WINAPI ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew);
 BOOL WINAPI ReallocSplStr(PWSTR* ppwszString, PCWSTR pwszInput);
 BOOL WINAPI SplInitializeWinSpoolDrv(PVOID* pTable);
+PDWORD WINAPI UndoAlignRpcPtr(PVOID pDestinationBuffer, PVOID pSourceBuffer, DWORD cbBuffer, PDWORD pcbNeeded);
 
 #endif
diff --git a/rostests/apitests/spoolss/AlignRpcPtr.c b/rostests/apitests/spoolss/AlignRpcPtr.c
new file mode 100644 (file)
index 0000000..54aee0a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * PROJECT:     ReactOS Spooler Router API Tests
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Tests for AlignRpcPtr/UndoAlignRpcPtr
+ * COPYRIGHT:   Copyright 2017 Colin Finck <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <spoolss.h>
+
+START_TEST(AlignRpcPtr)
+{
+    char* pMemory;
+    char* pInputBuffer;
+    char* pOutputBuffer;
+    DWORD cbBuffer;
+    PDWORD pcbBuffer;
+
+    // Allocate memory with GlobalAlloc. It is guaranteed to be aligned to a 8-byte boundary.
+    pMemory = (char*)GlobalAlloc(GMEM_FIXED, 16);
+
+    // First try AlignRpcPtr with already aligned memory and buffer size. It should leave everything unchanged.
+    pInputBuffer = pMemory;
+    cbBuffer = 8;
+    pOutputBuffer = (char*)AlignRpcPtr(pInputBuffer, &cbBuffer);
+    ok(pOutputBuffer == pInputBuffer, "pOutputBuffer != pInputBuffer\n");
+    ok(cbBuffer == 8, "cbBuffer is %lu\n", cbBuffer);
+
+    // Now try it with unaligned buffer size. The size should be aligned down while the buffer stays the same.
+    pInputBuffer = pMemory;
+    cbBuffer = 7;
+    pOutputBuffer = (char*)AlignRpcPtr(pInputBuffer, &cbBuffer);
+    ok(pOutputBuffer == pInputBuffer, "pOutputBuffer != pInputBuffer\n");
+    ok(cbBuffer == 4, "cbBuffer is %lu\n", cbBuffer);
+
+    // Now try with unaligned memory, but aligned buffer size. A new buffer is allocated while the size stays the same.
+    // The allocated buffer is then freed with UndoAlignRpcPtr. It is important to specify 0 as the size here, otherwise
+    // the NULL pointer for pDestinationBuffer is accessed.
+    pInputBuffer = pMemory + 1;
+    cbBuffer = 8;
+    pOutputBuffer = (char*)AlignRpcPtr(pInputBuffer, &cbBuffer);
+    ok(pOutputBuffer != pInputBuffer, "pOutputBuffer == pInputBuffer\n");
+    ok(cbBuffer == 8, "cbBuffer is %lu\n", cbBuffer);
+    ok(!UndoAlignRpcPtr(NULL, pOutputBuffer, 0, NULL), "UndoAlignRpcPtr returns something\n");
+
+    // Now try with memory and buffer size unaligned. A new buffer of the aligned down size is allocated.
+    pInputBuffer = pMemory + 1;
+    cbBuffer = 7;
+    pOutputBuffer = (char*)AlignRpcPtr(pInputBuffer, &cbBuffer);
+    ok(pOutputBuffer != pInputBuffer, "pOutputBuffer == pInputBuffer\n");
+    ok(cbBuffer == 4, "cbBuffer is %lu\n", cbBuffer);
+
+    // We can also test all parameters of UndoAlignRpcPtr here.
+    // Because pOutputBuffer != pInputBuffer, it copies the given 4 bytes from (aligned) pOutputBuffer to (unaligned) pInputBuffer
+    // while aligning up the given 7 bytes in our passed &cbBuffer.
+    // &cbBuffer is also returned.
+    strcpy(pOutputBuffer, "abc");
+    strcpy(pInputBuffer, "XXXXXXXXX");
+    cbBuffer = 7;
+    pcbBuffer = UndoAlignRpcPtr(pInputBuffer, pOutputBuffer, 4, &cbBuffer);
+    ok(strcmp(pInputBuffer, "abc") == 0, "pInputBuffer is %s\n", pInputBuffer);
+    ok(pcbBuffer == &cbBuffer, "pcbBuffer != &cbBuffer\n");
+    ok(cbBuffer == 8, "cbBuffer is %lu\n", cbBuffer);
+
+    // Prove that UndoAlignRpcPtr works without any parameters and doesn't try to copy data from NULL pointers.
+    ok(!UndoAlignRpcPtr(NULL, NULL, 0, NULL), "UndoAlignRpcPtr returns something\n");
+    ok(!UndoAlignRpcPtr(NULL, NULL, 4, NULL), "UndoAlignRpcPtr returns something\n");
+
+    // Prove that UndoAlignRpcPtr doesn't access source and destination memory at all when they are equal.
+    // If it did, it should crash here, because I'm giving invalid memory addresses.
+    ok(!UndoAlignRpcPtr((PVOID)1, (PVOID)1, 4, NULL), "UndoAlignRpcPtr returns something\n");
+
+    // Prove that the pcbNeeded parameter of UndoAlignRpcPtr works independently and aligns up everything up to a DWORD.
+    cbBuffer = 0xFFFFFFFF;
+    pcbBuffer = UndoAlignRpcPtr(NULL, NULL, 0, &cbBuffer);
+    ok(pcbBuffer == &cbBuffer, "pcbBuffer != &cbBuffer\n");
+    ok(cbBuffer == 0, "cbBuffer is %lu\n", cbBuffer);
+
+    GlobalFree(pMemory);
+}
index 438e188..f838489 100644 (file)
@@ -2,6 +2,7 @@
 include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
 
 list(APPEND SOURCE
+    AlignRpcPtr.c
     PackStrings.c
     ReallocSplStr.c
     SplInitializeWinSpoolDrv.c
index 82f57b5..7557296 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Print Spooler Router API Tests
  * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
  * PURPOSE:     Test list
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #define __ROS_LONG64__
 #define STANDALONE
 #include <apitest.h>
 
+extern void func_AlignRpcPtr(void);
 extern void func_PackStrings(void);
 extern void func_ReallocSplStr(void);
 extern void func_SplInitializeWinSpoolDrv(void);
 
 const struct test winetest_testlist[] =
 {
+    { "AlignRpcPtr", func_AlignRpcPtr },
     { "PackStrings", func_PackStrings },
     { "ReallocSplStr", func_ReallocSplStr },
     { "SplInitializeWinSpoolDrv", func_SplInitializeWinSpoolDrv },