summaryrefslogtreecommitdiff
path: root/src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3
diff options
context:
space:
mode:
Diffstat (limited to 'src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3')
-rw-r--r--src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/CMakeLists.txt36
-rw-r--r--src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/commonconsts.h50
-rw-r--r--src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/helper.c256
-rw-r--r--src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/test3.c205
-rw-r--r--src/pal/tests/palsuite/debug_api/WriteProcessMemory/test3/testinfo.dat17
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.