[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.

svn path=/trunk/; revision=74297
This commit is contained in:
Colin Finck 2017-04-13 16:48:40 +00:00
parent 6790e320bd
commit a737c007e8
7 changed files with 180 additions and 8 deletions

View 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;
}

View file

@ -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

View 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;

View 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

View file

@ -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);
}

View file

@ -2,6 +2,7 @@
include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
list(APPEND SOURCE
AlignRpcPtr.c
PackStrings.c
ReallocSplStr.c
SplInitializeWinSpoolDrv.c

View 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__
@ -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 },