mirror of
https://github.com/reactos/reactos.git
synced 2024-07-02 10:45:24 +00:00
[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:
parent
6790e320bd
commit
a737c007e8
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
84
rostests/apitests/spoolss/AlignRpcPtr.c
Normal file
84
rostests/apitests/spoolss/AlignRpcPtr.c
Normal 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);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
include_directories(${REACTOS_SOURCE_DIR}/win32ss/printing/include)
|
||||
|
||||
list(APPEND SOURCE
|
||||
AlignRpcPtr.c
|
||||
PackStrings.c
|
||||
ReallocSplStr.c
|
||||
SplInitializeWinSpoolDrv.c
|
||||
|
|
|
@ -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 },
|
||||
|
|
Loading…
Reference in a new issue