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/spool... ============================================================================== --- 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@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 @@ 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/spool... ============================================================================== --- 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/spool... ============================================================================== --- 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/sp... ============================================================================== --- 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@reactos.org + * COPYRIGHT: Copyright 2015-2017 Colin Finck colin@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/AlignRpcP... ============================================================================== --- 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@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/CMakeList... ============================================================================== --- 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@reactos.org + * COPYRIGHT: Copyright 2015-2017 Colin Finck colin@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 },