Author: cfinck
Date: Thu Apr 13 16:48:40 2017
New Revision: 74297
URL: http://svn.reactos.org/svn/reactos?rev=74297&view=rev
Log:
[SPOOLSS]
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.
Added:
trunk/rostests/apitests/spoolss/AlignRpcPtr.c (with props)
Modified:
trunk/reactos/win32ss/printing/base/spoolss/memory.c
trunk/reactos/win32ss/printing/base/spoolss/spoolss.spec
trunk/reactos/win32ss/printing/base/spoolss/tools.c
trunk/reactos/win32ss/printing/include/spoolss.h
trunk/rostests/apitests/spoolss/CMakeLists.txt
trunk/rostests/apitests/spoolss/testlist.c
Modified: trunk/reactos/win32ss/printing/base/spoolss/memory.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/printing/base/spoo…
==============================================================================
--- trunk/reactos/win32ss/printing/base/spoolss/memory.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/printing/base/spoolss/memory.c [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -2,11 +2,41 @@
* 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(a)reactos.org>
+ * COPYRIGHT: Copyright 2015-2017 Colin Finck <colin(a)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 @@
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;
+}
Modified: trunk/reactos/win32ss/printing/base/spoolss/spoolss.spec
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/printing/base/spoo…
==============================================================================
--- trunk/reactos/win32ss/printing/base/spoolss/spoolss.spec [iso-8859-1] (original)
+++ trunk/reactos/win32ss/printing/base/spoolss/spoolss.spec [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -16,7 +16,7 @@
@ stub AdjustPointers
@ stub AdjustPointersInStructuresArray
@ stub AlignKMPtr
-@ stub AlignRpcPtr
+@ stdcall AlignRpcPtr(ptr ptr)
@ stdcall AllocSplStr(ptr)
@ stub AllowRemoteCalls
@ stub AppendPrinterNotifyInfoData
@@ -165,7 +165,7 @@
@ stdcall StartDocPrinterW(long long ptr)
@ stdcall StartPagePrinter(long)
@ stub UndoAlignKMPtr
-@ stub UndoAlignRpcPtr
+@ stdcall UndoAlignRpcPtr(ptr ptr long ptr)
@ stub UnloadDriver
@ stub UnloadDriverFile
@ stub UpdateBufferSize
Modified: trunk/reactos/win32ss/printing/base/spoolss/tools.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/printing/base/spoo…
==============================================================================
--- trunk/reactos/win32ss/printing/base/spoolss/tools.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/printing/base/spoolss/tools.c [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -86,7 +86,7 @@
* 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;
Modified: trunk/reactos/win32ss/printing/include/spoolss.h
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/printing/include/s…
==============================================================================
--- trunk/reactos/win32ss/printing/include/spoolss.h [iso-8859-1] (original)
+++ trunk/reactos/win32ss/printing/include/spoolss.h [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -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(a)reactos.org>
+ * COPYRIGHT: Copyright 2015-2017 Colin Finck <colin(a)reactos.org>
*/
#ifndef _REACTOS_SPOOLSS_H
@@ -19,14 +19,16 @@
}
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
Added: trunk/rostests/apitests/spoolss/AlignRpcPtr.c
URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/spoolss/AlignRpc…
==============================================================================
--- trunk/rostests/apitests/spoolss/AlignRpcPtr.c (added)
+++ trunk/rostests/apitests/spoolss/AlignRpcPtr.c [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -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(a)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);
+}
Propchange: trunk/rostests/apitests/spoolss/AlignRpcPtr.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: trunk/rostests/apitests/spoolss/CMakeLists.txt
URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/spoolss/CMakeLis…
==============================================================================
--- trunk/rostests/apitests/spoolss/CMakeLists.txt [iso-8859-1] (original)
+++ trunk/rostests/apitests/spoolss/CMakeLists.txt [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -2,6 +2,7 @@
include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
list(APPEND SOURCE
+ AlignRpcPtr.c
PackStrings.c
ReallocSplStr.c
SplInitializeWinSpoolDrv.c
Modified: trunk/rostests/apitests/spoolss/testlist.c
URL: http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/spoolss/testlist…
==============================================================================
--- trunk/rostests/apitests/spoolss/testlist.c [iso-8859-1] (original)
+++ trunk/rostests/apitests/spoolss/testlist.c [iso-8859-1] Thu Apr 13 16:48:40 2017
@@ -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(a)reactos.org>
+ * COPYRIGHT: Copyright 2015-2017 Colin Finck <colin(a)reactos.org>
*/
#define __ROS_LONG64__
@@ -10,12 +10,14 @@
#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 },