diff options
Diffstat (limited to 'src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3')
5 files changed, 564 insertions, 0 deletions
diff --git a/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/CMakeLists.txt b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/CMakeLists.txt new file mode 100644 index 0000000000..ecc0e06dac --- /dev/null +++ b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8.12.2) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(TESTSOURCES + test3.c +) + +add_executable(paltest_writeprocessmemory_test3 + ${TESTSOURCES} +) + +add_dependencies(paltest_writeprocessmemory_test3 coreclrpal) + +target_link_libraries(paltest_writeprocessmemory_test3 + pthread + m + coreclrpal +) + + +set(HELPERSOURCES + helper.c +) + +add_executable(paltest_writeprocessmemory_test3_helper + ${HELPERSOURCES} +) + +add_dependencies(paltest_writeprocessmemory_test3_helper coreclrpal) + +target_link_libraries(paltest_writeprocessmemory_test3_helper + pthread + m + coreclrpal +) diff --git a/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/commonconsts.h b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/commonconsts.h new file mode 100644 index 0000000000..c1cec18e2d --- /dev/null +++ b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/commonconsts.h @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================= +** +** Header: commonconsts.h +** +** +==============================================================*/ + +#ifndef _COMMONCONSTS_H_ +#define _COMMONCONSTS_H_ + +#include <pal.h> + +const int TIMEOUT = 40000; + +const WCHAR szcToHelperEvName[] = { 'T', 'o', '\0' }; +const WCHAR szcFromHelperEvName[] = { 'F', 'r', 'o', 'm', '\0' }; + +const char initialValue = '-'; +const char nextValue = '|'; +const char guardValue = '*'; +const char *commsFileName = "AddrNLen.dat"; + +/* PEDANTIC and PEDANTIC0 is a helper macro that just grumps about any + * zero return codes in a generic way. with little typing */ +#define PEDANTIC(function, parameters) \ +{ \ + unsigned int retval = (function parameters); \ + if ( !retval ) \ + { \ + Trace("%s: NonFatal failure of %s%s (returned %u) " \ + "for reasons %u and %u.\n", \ + __FILE__, #function, #parameters, retval, GetLastError(), errno); \ + } \ +} +#define PEDANTIC1(function, parameters) \ +{ \ + unsigned int retval = (function parameters); \ + if ( retval ) \ + { \ + Trace("%s: NonFatal failure of %s%s (returned %u) " \ + "for reasons %u and %u\n", \ + __FILE__, #function, #parameters, retval, GetLastError(), errno); \ + } \ +} + +#endif diff --git a/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/helper.c b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/helper.c new file mode 100644 index 0000000000..170e2064cb --- /dev/null +++ b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/helper.c @@ -0,0 +1,256 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================= +** +** Source: helper.c + +** +==============================================================*/ + + +/* +** +** Purpose: This helper process sets up a several blocks of memory +** that should be unwritable from the parent process, then uses a file +** to tell its parent process where that memory is so it can attempt a +** WriteProcessMemory on it. When the parent process is done we check +** here that it was (properly) unable to change the contents of the +** memory. +*/ + +#include "commonconsts.h" + +#include <palsuite.h> + +struct allhandles_t +{ + HANDLE hEvToHelper; + HANDLE hEvFromHelper; + char *valuesFileName; +}; + + +/* function: wpmVerifyCant + * + * This is a general WriteProcessMemory testing function that sets up + * the RAM pointed to and tells the companion process on the other end + * of the handles in 'Comms' to attempt to alter 'lenDest' bytes at + * '*pDest'. + * + * However, the memory at pDest[0..lenDest] is expected to be unwritable by + * the companion process. The companion is expects this. This function + * verifies that no bytes were affected + */ + +int wpmVerifyCant(struct allhandles_t Comms, + char * pDest, unsigned int lenDest, + unsigned int lenLegitDest, + DWORD dwExpectedErrorCode, + const char* storageDescription) +{ + char *pCurr; + FILE *commsFile; + DWORD dwRet; + + unsigned int lenSafe = min(lenDest, lenLegitDest); + + PAL_TRY + { + memset(pDest, initialValue, lenSafe); + } + PAL_EXCEPT_EX (setup, EXCEPTION_EXECUTE_HANDLER) + { + Trace("WriteProcessMemory: bug in test values for '%s' (%p, %u, %u), " + "the initial memset threw an exception.\n", + storageDescription, pDest, lenDest, lenSafe); + } + PAL_ENDTRY; + + /* tell the parent what RAM to attempt to adjust */ + if(!(commsFile = fopen(Comms.valuesFileName, "w"))) + { + Trace("WriteProcessMemory: fopen of '%S' failed (%u). \n", + Comms.valuesFileName, GetLastError()); + return FALSE; + } + if (!fprintf(commsFile, "%u %u %u '%s'\n", + pDest, lenDest, dwExpectedErrorCode, storageDescription)) + { + Trace("WriteProcessMemory: fprintf to '%S' failed (%u). \n", + Comms.valuesFileName, GetLastError()); + return FALSE; + } + PEDANTIC1(fclose, (commsFile)); + + /* Tell the parent the data is ready for it to adjust */ + PEDANTIC(ResetEvent, (Comms.hEvToHelper)); + PEDANTIC(SetEvent, (Comms.hEvFromHelper)); + + dwRet = WaitForSingleObject(Comms.hEvToHelper, TIMEOUT); + if (dwRet != WAIT_OBJECT_0) + { + Trace("helper WaitForSingleObjectTest: WaitForSingleObject " + "failed (%u)\n", GetLastError()); + return FALSE; + } + + PAL_TRY + { + /* check the stuff (as much as we can) that should NOT have changed */ + for (pCurr = pDest; pCurr < (pDest + lenSafe); pCurr++ ) + { + if ( *pCurr != initialValue) + { + Trace("When testing '%s': real memory values preservation failed " + "at %u offset %u. Found '%c' instead of '%c'\n.", + storageDescription, pDest, pCurr - pDest, + *pCurr, initialValue); + return FALSE; + } + } + } + PAL_EXCEPT_EX (testing, EXCEPTION_EXECUTE_HANDLER) + { + Trace("WriteProcessMemory: bug in test values for '%s' (%p, %u, %u), " + "the verification pass threw an exception.\n", + storageDescription, pDest, lenDest, lenSafe); + } + PAL_ENDTRY; + + return TRUE; +} + +int __cdecl main(int argc, char *argv[]) +{ + BOOL success = TRUE; /* assume success */ + struct allhandles_t Comms = {0,0,0} ; + + SYSTEM_INFO sysinfo; + + char* Memory; + + if(0 != (PAL_Initialize(argc, argv))) + { + return FAIL; + } + + /* hook up with the events created by the parent */ + Comms.hEvToHelper = OpenEventW(EVENT_ALL_ACCESS, 0, szcToHelperEvName); + if (!Comms.hEvToHelper) + { + Fail("WriteProcessMemory: OpenEvent of '%S' failed (%u). " + "(the event should already exist!)\n", + szcToHelperEvName, GetLastError()); + success = FALSE; + goto EXIT; + } + Comms.hEvFromHelper = OpenEventW(EVENT_ALL_ACCESS, 0, szcFromHelperEvName); + if (!Comms.hEvToHelper) + { + Trace("WriteProcessMemory: OpenEvent of '%S' failed (%u). " + "(the event should already exist!)\n", + szcFromHelperEvName, GetLastError()); + success = FALSE; + goto EXIT; + } + Comms.valuesFileName = argv[1]; + + /* test setup */ + GetSystemInfo(&sysinfo); + + { + unsigned int allocSize = sysinfo.dwPageSize * 2; + unsigned int writeLen = allocSize * 2; + + /* First test: overrun the allocated memory */ + Memory = (char*)VirtualAlloc(NULL, allocSize, + MEM_COMMIT, PAGE_READWRITE); + + if(Memory == NULL) + { + Fail("ERROR: Attempted to commit two pages, but the " + " VirtualAlloc call failed. " + "GetLastError() returned %u.\n",GetLastError()); + } + success &= wpmVerifyCant(Comms, Memory, writeLen, allocSize, + ERROR_INVALID_ADDRESS, + "should not write beyond committed allocation"); + + PEDANTIC1(VirtualFree, (Memory, allocSize, + MEM_DECOMMIT | MEM_RELEASE)); + } + + { + /* Allocate the memory as readonly */ + unsigned int allocSize = sysinfo.dwPageSize * 2; + unsigned int writeLen = allocSize; + + Memory = (char*)VirtualAlloc(NULL, allocSize, + MEM_COMMIT, PAGE_READONLY); + + if(Memory == NULL) + { + Fail("ERROR: Attempted to commit two pages readonly, but the " + " VirtualAlloc call failed. " + "GetLastError() returned %u.\n",GetLastError()); + } + success &= wpmVerifyCant(Comms, Memory, writeLen, 0, + ERROR_NOACCESS, + "should not write in READONLY allocation"); + + PEDANTIC1(VirtualFree, (Memory, allocSize, + MEM_DECOMMIT | MEM_RELEASE)); + } + + + { + /* attempt to write to memory that is not committed yet */ + unsigned int allocSize = sysinfo.dwPageSize * 2; + unsigned int writeLen = allocSize; + + Memory = (char*)VirtualAlloc(NULL, allocSize, + MEM_RESERVE, PAGE_NOACCESS); + + if(Memory == NULL) + { + Fail("ERROR: Attempted to reserve two pages, but the " + " VirtualAlloc call failed. " + "GetLastError() returned %u.\n",GetLastError()); + } + success &= wpmVerifyCant(Comms, Memory, writeLen, 0, + ERROR_INVALID_ADDRESS, + "should not write in memory that is" + " RESERVED but not COMMITTED"); + + PEDANTIC1(VirtualFree, (Memory, allocSize, MEM_RELEASE)); + } + + +EXIT: + /* Tell the parent that we are done */ + if (!DeleteFile(Comms.valuesFileName)) + { + Trace("helper: DeleteFile failed so parent (test1) is unlikely " + "to exit cleanly\n"); + } + PEDANTIC(ResetEvent, (Comms.hEvToHelper)); + if (!SetEvent(Comms.hEvFromHelper)) + { + Trace("helper: SetEvent failed so parent (test1) is unlikely " + "to exit cleanly\n"); + } + + PEDANTIC(CloseHandle, (Comms.hEvToHelper)); + PEDANTIC(CloseHandle, (Comms.hEvFromHelper)); + + if (!success) + { + Fail(""); + } + + PAL_Terminate(); + + return success ? PASS : FAIL; +} diff --git a/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.c b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.c new file mode 100644 index 0000000000..063cb4cbec --- /dev/null +++ b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.c @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================= +** +** Source: test3.c +** +** Purpose: Create a child process and debug it. When the child +** raises an exception, it sends back a memory location. Call +** WriteProcessMemory on the memory location, but attempt to write +** more than the memory allows. This should cause an error and the +** data should be unchanged. +** +** +==============================================================*/ + +#define UNICODE + +#include "commonconsts.h" + +#include <palsuite.h> + +int __cdecl main(int argc, char *argv[]) +{ + + PROCESS_INFORMATION pi; + STARTUPINFO si; + HANDLE hEvToHelper; + HANDLE hEvFromHelper; + DWORD dwExitCode; + + + DWORD dwRet; + BOOL success = TRUE; /* assume success */ + char cmdComposeBuf[MAX_PATH]; + PWCHAR uniString; + + if(0 != (PAL_Initialize(argc, argv))) + { + return FAIL; + } + + /* Create the signals we need for cross process communication */ + hEvToHelper = CreateEvent(NULL, TRUE, FALSE, szcToHelperEvName); + if (!hEvToHelper) + { + Fail("WriteProcessMemory: CreateEvent of '%S' failed. " + "GetLastError() returned %u.\n", szcToHelperEvName, + GetLastError()); + } + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + Fail("WriteProcessMemory: CreateEvent of '%S' failed. " + "(already exists!)\n", szcToHelperEvName); + } + hEvFromHelper = CreateEvent(NULL, TRUE, FALSE, szcFromHelperEvName); + if (!hEvToHelper) + { + Fail("WriteProcessMemory: CreateEvent of '%S' failed. " + "GetLastError() returned %u.\n", szcFromHelperEvName, + GetLastError()); + } + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + Fail("WriteProcessMemory: CreateEvent of '%S' failed. " + "(already exists!)\n", szcFromHelperEvName); + } + + if (!sprintf(cmdComposeBuf, "helper %s", commsFileName)) + { + Fail("Could not convert command line\n"); + } + uniString = convert(cmdComposeBuf); + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + /* Create a new process. This is the process that will ask for + * memory munging */ + if(!CreateProcess( NULL, uniString, NULL, NULL, + FALSE, 0, NULL, NULL, &si, &pi)) + { + Trace("ERROR: CreateProcess failed to load executable '%S'. " + "GetLastError() returned %u.\n", + uniString, GetLastError()); + free(uniString); + Fail(""); + } + free(uniString); + + while(1) + { + FILE *commsFile; + char* pSrcMemory; + char* pDestMemory; + int Count; + SIZE_T wpmCount; + DWORD dwExpectedErrorCode; + + char incomingCMDBuffer[MAX_PATH + 1]; + + /* wait until the helper tells us that it has given us + * something to do */ + dwRet = WaitForSingleObject(hEvFromHelper, TIMEOUT); + if (dwRet != WAIT_OBJECT_0) + { + Trace("test1 WaitForSingleObjectTest: WaitForSingleObject " + "failed (%u)\n", GetLastError()); + break; /* no more work incoming */ + } + + /* get the parameters to test WriteProcessMemory with */ + if (!(commsFile = fopen(commsFileName, "r"))) + { + /* no file means there is no more work */ + break; + } + if ( NULL == fgets(incomingCMDBuffer, MAX_PATH, commsFile)) + { + Trace ("unable to read from communication file %s " + "for reasons %u & %u\n", + errno, GetLastError()); + success = FALSE; + PEDANTIC1(fclose,(commsFile)); + /* it's not worth continuing this trial */ + goto doneIteration; + } + PEDANTIC1(fclose,(commsFile)); + sscanf(incomingCMDBuffer, "%u %u %u", + &pDestMemory, &Count, &dwExpectedErrorCode); + if (argc > 1) + { + Trace("Preparing to write to %u bytes @ %u ('%s')\n", + Count, pDestMemory, incomingCMDBuffer); + } + + /* compose some data to write to the client process */ + if (!(pSrcMemory = malloc(Count))) + { + Trace("could not dynamically allocate memory to copy from " + "for reasons %u & %u\n", + errno, GetLastError()); + success = FALSE; + goto doneIteration; + } + memset(pSrcMemory, nextValue, Count); + + /* do the work */ + dwRet = WriteProcessMemory(pi.hProcess, + pDestMemory, + pSrcMemory, + Count, + &wpmCount); + + if(dwRet != 0) + { + Trace("ERROR: Situation: '%s', return code: %u, bytes 'written': %u\n", + incomingCMDBuffer, dwRet, wpmCount); + Trace("ERROR: WriteProcessMemory did not fail as it should, as " + "it attempted to write to a range of memory which was " + "not completely accessible.\n"); + success = FALSE; + } + + if(GetLastError() != dwExpectedErrorCode) + { + Trace("ERROR: GetLastError() should have returned " + "%u , but instead it returned %u.\n", + dwExpectedErrorCode, GetLastError()); + success = FALSE; + } + free(pSrcMemory); + + doneIteration: + PEDANTIC(ResetEvent, (hEvFromHelper)); + PEDANTIC(SetEvent, (hEvToHelper)); + } + + + /* wait for the child process to complete */ + WaitForSingleObject ( pi.hProcess, TIMEOUT ); + /* this may return a failure code on a success path */ + + /* check the exit code from the process */ + if( ! GetExitCodeProcess( pi.hProcess, &dwExitCode ) ) + { + Trace( "GetExitCodeProcess call failed with error code %u\n", + GetLastError() ); + dwExitCode = FAIL; + } + if(!success) + { + dwExitCode = FAIL; + } + + PEDANTIC(CloseHandle, (hEvToHelper)); + PEDANTIC(CloseHandle, (hEvFromHelper)); + PEDANTIC(CloseHandle, (pi.hThread)); + PEDANTIC(CloseHandle, (pi.hProcess)); + + PAL_Terminate(dwExitCode); + return dwExitCode; +} diff --git a/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/testinfo.dat b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/testinfo.dat new file mode 100644 index 0000000000..23ad3ae567 --- /dev/null +++ b/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/testinfo.dat @@ -0,0 +1,17 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the MIT license. +# See the LICENSE file in the project root for more information. + +Version = 1.0 +Section = Debug +Function = WriteProcessMemory +Name = Check that you can't write from writable to protected memory. +TYPE = DEFAULT +EXE1 = test3 +EXE2 = helper +Description += Create a child process and debug it. When the child += raises an exception, it sends back a memory location. Call += WriteProcessMemory on the memory location, but attempt to write += more than the memory allows. This should cause an error and the += data should be unchanged. |