diff options
author | Aditya Mandaleeka <adityam@microsoft.com> | 2016-01-14 12:38:45 -0800 |
---|---|---|
committer | Aditya Mandaleeka <adityam@microsoft.com> | 2016-01-14 12:44:21 -0800 |
commit | a1cbc293fe66e33fbb815b018c5f3e5da76a7dfc (patch) | |
tree | 919ab3cd2954808e5f547171a3703d34b7626771 /src | |
parent | db743bc4103e78484bd0dac54a1b887da63a7059 (diff) | |
download | coreclr-a1cbc293fe66e33fbb815b018c5f3e5da76a7dfc.tar.gz coreclr-a1cbc293fe66e33fbb815b018c5f3e5da76a7dfc.tar.bz2 coreclr-a1cbc293fe66e33fbb815b018c5f3e5da76a7dfc.zip |
Add hardware exception handling back to Read/WriteProcessMemory
Diffstat (limited to 'src')
-rw-r--r-- | src/pal/src/debug/debug.cpp | 1197 |
1 files changed, 602 insertions, 595 deletions
diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index fa8aa3848f..9a9fefcdb0 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -547,600 +547,6 @@ SetThreadContext( return ret; } -/*++ -Function: - ReadProcessMemory - -See MSDN doc. ---*/ -BOOL -PALAPI -ReadProcessMemory( - IN HANDLE hProcess, - IN LPCVOID lpBaseAddress, - IN LPVOID lpBuffer, - IN SIZE_T nSize, - OUT SIZE_T * lpNumberOfBytesRead - ) -{ - CPalThread *pThread; - DWORD processId; - Volatile<BOOL> ret = FALSE; - Volatile<SIZE_T> numberOfBytesRead = 0; -#if HAVE_VM_READ - kern_return_t result; - vm_map_t task; - LONG_PTR bytesToRead; -#elif HAVE_PROCFS_CTL - int fd; - char memPath[64]; - off_t offset; -#elif !HAVE_TTRACE - SIZE_T nbInts; - int* ptrInt; - int* lpTmpBuffer; -#endif -#if !HAVE_PROCFS_CTL && !HAVE_TTRACE - int* lpBaseAddressAligned; - SIZE_T offset; -#endif // !HAVE_PROCFS_CTL && !HAVE_TTRACE - - PERF_ENTRY(ReadProcessMemory); - ENTRY("ReadProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, " - "nSize=%u, lpNumberOfBytesRead=%p)\n",hProcess,lpBaseAddress, - lpBuffer, (unsigned int)nSize, lpNumberOfBytesRead); - - pThread = InternalGetCurrentThread(); - - if (!(processId = PROCGetProcessIDFromHandle(hProcess))) - { - ERROR("Invalid process handler hProcess:%p.",hProcess); - SetLastError(ERROR_INVALID_HANDLE); - goto EXIT; - } - - // Check if the read request is for the current process. - // We don't need ptrace in that case. - if (GetCurrentProcessId() == processId) - { - TRACE("We are in the same process, so ptrace is not needed\n"); - - struct Param - { - LPCVOID lpBaseAddress; - LPVOID lpBuffer; - SIZE_T nSize; - SIZE_T numberOfBytesRead; - BOOL ret; - } param; - param.lpBaseAddress = lpBaseAddress; - param.lpBuffer = lpBuffer; - param.nSize = nSize; - param.numberOfBytesRead = numberOfBytesRead; - param.ret = ret; - - PAL_TRY(Param *, pParam, ¶m) - { - SIZE_T i; - - // Seg fault in memcpy can't be caught - // so we simulate the memcpy here - - for (i = 0; i<pParam->nSize; i++) - { - *((char*)(pParam->lpBuffer)+i) = *((char*)(pParam->lpBaseAddress)+i); - } - - pParam->numberOfBytesRead = pParam->nSize; - pParam->ret = TRUE; - } - PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - SetLastError(ERROR_ACCESS_DENIED); - } - PAL_ENDTRY - - numberOfBytesRead = param.numberOfBytesRead; - ret = param.ret; - goto EXIT; - } - -#if HAVE_VM_READ - result = task_for_pid(mach_task_self(), processId, &task); - if (result != KERN_SUCCESS) - { - ERROR("No Mach task for pid %d: %d\n", processId, ret.Load()); - SetLastError(ERROR_INVALID_HANDLE); - goto EXIT; - } - // vm_read_overwrite usually requires that the address be page-aligned - // and the size be a multiple of the page size. We can't differentiate - // between the cases in which that's required and those in which it - // isn't, so we do it all the time. - lpBaseAddressAligned = (int*)((SIZE_T) lpBaseAddress & ~VIRTUAL_PAGE_MASK); - offset = ((SIZE_T) lpBaseAddress & VIRTUAL_PAGE_MASK); - char *data; - data = (char*)alloca(VIRTUAL_PAGE_SIZE); - while (nSize > 0) - { - vm_size_t bytesRead; - - bytesToRead = VIRTUAL_PAGE_SIZE - offset; - if (bytesToRead > (LONG_PTR)nSize) - { - bytesToRead = nSize; - } - bytesRead = VIRTUAL_PAGE_SIZE; - result = vm_read_overwrite(task, (vm_address_t) lpBaseAddressAligned, - VIRTUAL_PAGE_SIZE, (vm_address_t) data, &bytesRead); - if (result != KERN_SUCCESS || bytesRead != VIRTUAL_PAGE_SIZE) - { - ERROR("vm_read_overwrite failed for %d bytes from %p in %d: %d\n", - VIRTUAL_PAGE_SIZE, (char *) lpBaseAddressAligned, task, result); - if (result <= KERN_RETURN_MAX) - { - SetLastError(ERROR_INVALID_ACCESS); - } - else - { - SetLastError(ERROR_INTERNAL_ERROR); - } - goto EXIT; - } - memcpy((LPSTR)lpBuffer + numberOfBytesRead, data + offset, bytesToRead); - numberOfBytesRead.Store(numberOfBytesRead.Load() + bytesToRead); - lpBaseAddressAligned = (int*)((char*)lpBaseAddressAligned + VIRTUAL_PAGE_SIZE); - nSize -= bytesToRead; - offset = 0; - } - ret = TRUE; -#else // HAVE_VM_READ -#if HAVE_PROCFS_CTL - snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME); - fd = InternalOpen(memPath, O_RDONLY); - if (fd == -1) - { - ERROR("Failed to open %s\n", memPath); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; - } - - // - // off_t may be greater in size than void*, so first cast to - // an unsigned type to ensure that no sign extension takes place - // - - offset = (off_t) (UINT_PTR) lpBaseAddress; - - if (lseek(fd, offset, SEEK_SET) == -1) - { - ERROR("Failed to seek to base address\n"); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; - } - - numberOfBytesRead = read(fd, lpBuffer, nSize); - ret = TRUE; - -#else // HAVE_PROCFS_CTL - // Attach the process before calling ttrace/ptrace otherwise it fails. - if (DBGAttachProcess(pThread, hProcess, processId)) - { -#if HAVE_TTRACE - if (ttrace(TT_PROC_RDDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1) - { - if (errno == EFAULT) - { - ERROR("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed" - " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer, - errno, strerror(errno)); - - SetLastError(ERROR_ACCESS_DENIED); - } - else - { - ASSERT("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed" - " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer, - errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - - goto CLEANUP1; - } - - numberOfBytesRead = nSize; - ret = TRUE; - -#else // HAVE_TTRACE - - offset = (SIZE_T)lpBaseAddress % sizeof(int); - lpBaseAddressAligned = (int*)((char*)lpBaseAddress - offset); - nbInts = (nSize + offset)/sizeof(int) + - ((nSize + offset)%sizeof(int) ? 1:0); - - /* before transferring any data to lpBuffer we should make sure that all - data is accessible for read. so we need to use a temp buffer for that.*/ - if (!(lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int))))) - { - ERROR("Insufficient memory available !\n"); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto CLEANUP1; - } - - for (ptrInt = lpTmpBuffer; nbInts; ptrInt++, - lpBaseAddressAligned++, nbInts--) - { - errno = 0; - *ptrInt = - PAL_PTRACE(PAL_PT_READ_D, processId, lpBaseAddressAligned, 0); - if (*ptrInt == -1 && errno) - { - if (errno == EFAULT) - { - ERROR("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed" - " errno=%d (%s)\n", processId, lpBaseAddressAligned, - errno, strerror(errno)); - - SetLastError(ptrInt == lpTmpBuffer ? ERROR_ACCESS_DENIED : - ERROR_PARTIAL_COPY); - } - else - { - ASSERT("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed" - " errno=%d (%s)\n", processId, lpBaseAddressAligned, - errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - - goto CLEANUP2; - } - } - - /* transfer data from temp buffer to lpBuffer */ - memcpy( (char *)lpBuffer, ((char*)lpTmpBuffer) + offset, nSize); - numberOfBytesRead = nSize; - ret = TRUE; -#endif // HAVE_TTRACE - } - else - { - /* Failed to attach processId */ - goto EXIT; - } -#endif // HAVE_PROCFS_CTL - -#if HAVE_PROCFS_CTL -PROCFSCLEANUP: - if (fd != -1) - { - close(fd); - } -#elif !HAVE_TTRACE -CLEANUP2: - if (lpTmpBuffer) - { - InternalFree(lpTmpBuffer); - } -#endif // !HAVE_TTRACE - -#if !HAVE_PROCFS_CTL -CLEANUP1: - if (!DBGDetachProcess(pThread, hProcess, processId)) - { - /* Failed to detach processId */ - ret = FALSE; - } -#endif // HAVE_PROCFS_CTL -#endif // HAVE_VM_READ - -EXIT: - if (lpNumberOfBytesRead) - { - *lpNumberOfBytesRead = numberOfBytesRead; - } - LOGEXIT("ReadProcessMemory returns BOOL %d\n", ret.Load()); - PERF_EXIT(ReadProcessMemory); - return ret; -} - -/*++ -Function: - WriteProcessMemory - -See MSDN doc. ---*/ -BOOL -PALAPI -WriteProcessMemory( - IN HANDLE hProcess, - IN LPVOID lpBaseAddress, - IN LPCVOID lpBuffer, - IN SIZE_T nSize, - OUT SIZE_T * lpNumberOfBytesWritten - ) - -{ - CPalThread *pThread; - DWORD processId; - Volatile<BOOL> ret = FALSE; - Volatile<SIZE_T> numberOfBytesWritten = 0; -#if HAVE_VM_READ - kern_return_t result; - vm_map_t task; -#elif HAVE_PROCFS_CTL - int fd; - char memPath[64]; - LONG_PTR bytesWritten; - off_t offset; -#elif !HAVE_TTRACE - SIZE_T FirstIntOffset; - SIZE_T LastIntOffset; - unsigned int FirstIntMask; - unsigned int LastIntMask; - SIZE_T nbInts; - int *lpTmpBuffer = 0, *lpInt; - int* lpBaseAddressAligned; -#endif - - PERF_ENTRY(WriteProcessMemory); - ENTRY("WriteProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, " - "nSize=%u, lpNumberOfBytesWritten=%p)\n", - hProcess,lpBaseAddress, lpBuffer, (unsigned int)nSize, lpNumberOfBytesWritten); - - pThread = InternalGetCurrentThread(); - - if (!(nSize && (processId = PROCGetProcessIDFromHandle(hProcess)))) - { - ERROR("Invalid nSize:%u number or invalid process handler " - "hProcess:%p\n", (unsigned int)nSize, hProcess); - SetLastError(ERROR_INVALID_PARAMETER); - goto EXIT; - } - - // Check if the write request is for the current process. - // In that case we don't need ptrace. - if (GetCurrentProcessId() == processId) - { - TRACE("We are in the same process so we don't need ptrace\n"); - - struct Param - { - LPVOID lpBaseAddress; - LPCVOID lpBuffer; - SIZE_T nSize; - SIZE_T numberOfBytesWritten; - BOOL ret; - } param; - param.lpBaseAddress = lpBaseAddress; - param.lpBuffer = lpBuffer; - param.nSize = nSize; - param.numberOfBytesWritten = numberOfBytesWritten; - param.ret = ret; - - PAL_TRY(Param *, pParam, ¶m) - { - SIZE_T i; - - // Seg fault in memcpy can't be caught - // so we simulate the memcpy here - - for (i = 0; i<pParam->nSize; i++) - { - *((char*)(pParam->lpBaseAddress)+i) = *((char*)(pParam->lpBuffer)+i); - } - - pParam->numberOfBytesWritten = pParam->nSize; - pParam->ret = TRUE; - } - PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - SetLastError(ERROR_ACCESS_DENIED); - } - PAL_ENDTRY - - numberOfBytesWritten = param.numberOfBytesWritten; - ret = param.ret; - goto EXIT; - } - -#if HAVE_VM_READ - result = task_for_pid(mach_task_self(), processId, &task); - if (result != KERN_SUCCESS) - { - ERROR("No Mach task for pid %d: %d\n", processId, ret.Load()); - SetLastError(ERROR_INVALID_HANDLE); - goto EXIT; - } - result = vm_write(task, (vm_address_t) lpBaseAddress, - (vm_address_t) lpBuffer, nSize); - if (result != KERN_SUCCESS) - { - ERROR("vm_write failed for %d bytes from %p in %d: %d\n", - (int)nSize, lpBaseAddress, task, result); - if (result <= KERN_RETURN_MAX) - { - SetLastError(ERROR_ACCESS_DENIED); - } - else - { - SetLastError(ERROR_INTERNAL_ERROR); - } - goto EXIT; - } - numberOfBytesWritten = nSize; - ret = TRUE; -#else // HAVE_VM_READ -#if HAVE_PROCFS_CTL - snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME); - fd = InternalOpen(memPath, O_WRONLY); - if (fd == -1) - { - ERROR("Failed to open %s\n", memPath); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; - } - - // - // off_t may be greater in size than void*, so first cast to - // an unsigned type to ensure that no sign extension takes place - // - - offset = (off_t) (UINT_PTR) lpBaseAddress; - - if (lseek(fd, offset, SEEK_SET) == -1) - { - ERROR("Failed to seek to base address\n"); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; - } - - bytesWritten = write(fd, lpBuffer, nSize); - if (bytesWritten < 0) - { - ERROR("Failed to write to %s\n", memPath); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; - } - - numberOfBytesWritten = bytesWritten; - ret = TRUE; - -#else // HAVE_PROCFS_CTL - /* Attach the process before calling ptrace otherwise it fails */ - if (DBGAttachProcess(pThread, hProcess, processId)) - { -#if HAVE_TTRACE - if (ttrace(TT_PROC_WRDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1) - { - if (errno == EFAULT) - { - ERROR("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed" - " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer, - errno, strerror(errno)); - - SetLastError(ERROR_ACCESS_DENIED); - } - else - { - ASSERT("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed" - " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer, - errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - - goto CLEANUP1; - } - - numberOfBytesWritten = nSize; - ret = TRUE; - -#else // HAVE_TTRACE - - FirstIntOffset = (SIZE_T)lpBaseAddress % sizeof(int); - FirstIntMask = -1; - FirstIntMask <<= (FirstIntOffset * 8); - - nbInts = (nSize + FirstIntOffset) / sizeof(int) + - (((nSize + FirstIntOffset)%sizeof(int)) ? 1:0); - lpBaseAddressAligned = (int*)((char*)lpBaseAddress - FirstIntOffset); - - if ((lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int)))) == NULL) - { - ERROR("Insufficient memory available !\n"); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto CLEANUP1; - } - - memcpy((char *)lpTmpBuffer + FirstIntOffset, (char *)lpBuffer, nSize); - lpInt = lpTmpBuffer; - - LastIntOffset = (nSize + FirstIntOffset) % sizeof(int); - LastIntMask = -1; - LastIntMask >>= ((sizeof(int) - LastIntOffset) * 8); - - if (nbInts == 1) - { - if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned, - *lpInt, - LastIntMask & FirstIntMask) - == 0) - { - goto CLEANUP2; - } - numberOfBytesWritten = nSize; - ret = TRUE; - goto CLEANUP2; - } - - if (DBGWriteProcMem_IntWithMask(processId, - lpBaseAddressAligned++, - *lpInt++, FirstIntMask) - == 0) - { - goto CLEANUP2; - } - - while (--nbInts > 1) - { - if (DBGWriteProcMem_Int(processId, lpBaseAddressAligned++, - *lpInt++) == 0) - { - goto CLEANUP2; - } - } - - if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned, - *lpInt, LastIntMask ) == 0) - { - goto CLEANUP2; - } - - numberOfBytesWritten = nSize; - ret = TRUE; -#endif // HAVE_TTRACE - } - else - { - /* Failed to attach processId */ - goto EXIT; - } -#endif // HAVE_PROCFS_CTL - -#if HAVE_PROCFS_CTL -PROCFSCLEANUP: - if (fd != -1) - { - close(fd); - } -#elif !HAVE_TTRACE -CLEANUP2: - if (lpTmpBuffer) - { - InternalFree(lpTmpBuffer); - } -#endif // !HAVE_TTRACE - -#if !HAVE_PROCFS_CTL -CLEANUP1: - if (!DBGDetachProcess(pThread, hProcess, processId)) - { - /* Failed to detach processId */ - ret = FALSE; - } -#endif // !HAVE_PROCFS_CTL -#endif // HAVE_VM_READ - -EXIT: - if (lpNumberOfBytesWritten) - { - *lpNumberOfBytesWritten = numberOfBytesWritten; - } - - LOGEXIT("WriteProcessMemory returns BOOL %d\n", ret.Load()); - PERF_EXIT(WriteProcessMemory); - return ret; -} - #if !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE /*++ Function: @@ -1815,4 +1221,605 @@ PAL_DeleteExecWatchpointExit: return dwError; } -} // extern "C" +// We want to enable hardware exception handling for ReadProcessMemory +// and WriteProcessMemory in all cases since it is acceptable if they +// hit AVs, so redefine HardwareExceptionHolder for these two functions +// (here to the end of the file). +#undef HardwareExceptionHolder +#define HardwareExceptionHolder CatchHardwareExceptionHolder __catchHardwareException; + +/*++ +Function: + ReadProcessMemory + +See MSDN doc. +--*/ +BOOL +PALAPI +ReadProcessMemory( + IN HANDLE hProcess, + IN LPCVOID lpBaseAddress, + IN LPVOID lpBuffer, + IN SIZE_T nSize, + OUT SIZE_T * lpNumberOfBytesRead + ) +{ + CPalThread *pThread; + DWORD processId; + Volatile<BOOL> ret = FALSE; + Volatile<SIZE_T> numberOfBytesRead = 0; +#if HAVE_VM_READ + kern_return_t result; + vm_map_t task; + LONG_PTR bytesToRead; +#elif HAVE_PROCFS_CTL + int fd; + char memPath[64]; + off_t offset; +#elif !HAVE_TTRACE + SIZE_T nbInts; + int* ptrInt; + int* lpTmpBuffer; +#endif +#if !HAVE_PROCFS_CTL && !HAVE_TTRACE + int* lpBaseAddressAligned; + SIZE_T offset; +#endif // !HAVE_PROCFS_CTL && !HAVE_TTRACE + + PERF_ENTRY(ReadProcessMemory); + ENTRY("ReadProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, " + "nSize=%u, lpNumberOfBytesRead=%p)\n",hProcess,lpBaseAddress, + lpBuffer, (unsigned int)nSize, lpNumberOfBytesRead); + + pThread = InternalGetCurrentThread(); + + if (!(processId = PROCGetProcessIDFromHandle(hProcess))) + { + ERROR("Invalid process handler hProcess:%p.",hProcess); + SetLastError(ERROR_INVALID_HANDLE); + goto EXIT; + } + + // Check if the read request is for the current process. + // We don't need ptrace in that case. + if (GetCurrentProcessId() == processId) + { + TRACE("We are in the same process, so ptrace is not needed\n"); + + struct Param + { + LPCVOID lpBaseAddress; + LPVOID lpBuffer; + SIZE_T nSize; + SIZE_T numberOfBytesRead; + BOOL ret; + } param; + param.lpBaseAddress = lpBaseAddress; + param.lpBuffer = lpBuffer; + param.nSize = nSize; + param.numberOfBytesRead = numberOfBytesRead; + param.ret = ret; + + PAL_TRY(Param *, pParam, ¶m) + { + SIZE_T i; + + // Seg fault in memcpy can't be caught + // so we simulate the memcpy here + + for (i = 0; i<pParam->nSize; i++) + { + *((char*)(pParam->lpBuffer)+i) = *((char*)(pParam->lpBaseAddress)+i); + } + + pParam->numberOfBytesRead = pParam->nSize; + pParam->ret = TRUE; + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastError(ERROR_ACCESS_DENIED); + } + PAL_ENDTRY + + numberOfBytesRead = param.numberOfBytesRead; + ret = param.ret; + goto EXIT; + } + +#if HAVE_VM_READ + result = task_for_pid(mach_task_self(), processId, &task); + if (result != KERN_SUCCESS) + { + ERROR("No Mach task for pid %d: %d\n", processId, ret.Load()); + SetLastError(ERROR_INVALID_HANDLE); + goto EXIT; + } + // vm_read_overwrite usually requires that the address be page-aligned + // and the size be a multiple of the page size. We can't differentiate + // between the cases in which that's required and those in which it + // isn't, so we do it all the time. + lpBaseAddressAligned = (int*)((SIZE_T) lpBaseAddress & ~VIRTUAL_PAGE_MASK); + offset = ((SIZE_T) lpBaseAddress & VIRTUAL_PAGE_MASK); + char *data; + data = (char*)alloca(VIRTUAL_PAGE_SIZE); + while (nSize > 0) + { + vm_size_t bytesRead; + + bytesToRead = VIRTUAL_PAGE_SIZE - offset; + if (bytesToRead > (LONG_PTR)nSize) + { + bytesToRead = nSize; + } + bytesRead = VIRTUAL_PAGE_SIZE; + result = vm_read_overwrite(task, (vm_address_t) lpBaseAddressAligned, + VIRTUAL_PAGE_SIZE, (vm_address_t) data, &bytesRead); + if (result != KERN_SUCCESS || bytesRead != VIRTUAL_PAGE_SIZE) + { + ERROR("vm_read_overwrite failed for %d bytes from %p in %d: %d\n", + VIRTUAL_PAGE_SIZE, (char *) lpBaseAddressAligned, task, result); + if (result <= KERN_RETURN_MAX) + { + SetLastError(ERROR_INVALID_ACCESS); + } + else + { + SetLastError(ERROR_INTERNAL_ERROR); + } + goto EXIT; + } + memcpy((LPSTR)lpBuffer + numberOfBytesRead, data + offset, bytesToRead); + numberOfBytesRead.Store(numberOfBytesRead.Load() + bytesToRead); + lpBaseAddressAligned = (int*)((char*)lpBaseAddressAligned + VIRTUAL_PAGE_SIZE); + nSize -= bytesToRead; + offset = 0; + } + ret = TRUE; +#else // HAVE_VM_READ +#if HAVE_PROCFS_CTL + snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME); + fd = InternalOpen(memPath, O_RDONLY); + if (fd == -1) + { + ERROR("Failed to open %s\n", memPath); + SetLastError(ERROR_INVALID_ACCESS); + goto PROCFSCLEANUP; + } + + // + // off_t may be greater in size than void*, so first cast to + // an unsigned type to ensure that no sign extension takes place + // + + offset = (off_t) (UINT_PTR) lpBaseAddress; + + if (lseek(fd, offset, SEEK_SET) == -1) + { + ERROR("Failed to seek to base address\n"); + SetLastError(ERROR_INVALID_ACCESS); + goto PROCFSCLEANUP; + } + + numberOfBytesRead = read(fd, lpBuffer, nSize); + ret = TRUE; + +#else // HAVE_PROCFS_CTL + // Attach the process before calling ttrace/ptrace otherwise it fails. + if (DBGAttachProcess(pThread, hProcess, processId)) + { +#if HAVE_TTRACE + if (ttrace(TT_PROC_RDDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1) + { + if (errno == EFAULT) + { + ERROR("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed" + " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer, + errno, strerror(errno)); + + SetLastError(ERROR_ACCESS_DENIED); + } + else + { + ASSERT("ttrace(TT_PROC_RDDATA, pid:%d, 0, addr:%p, data:%d, addr2:%d) failed" + " errno=%d (%s)\n", processId, lpBaseAddress, (int)nSize, lpBuffer, + errno, strerror(errno)); + SetLastError(ERROR_INTERNAL_ERROR); + } + + goto CLEANUP1; + } + + numberOfBytesRead = nSize; + ret = TRUE; + +#else // HAVE_TTRACE + + offset = (SIZE_T)lpBaseAddress % sizeof(int); + lpBaseAddressAligned = (int*)((char*)lpBaseAddress - offset); + nbInts = (nSize + offset)/sizeof(int) + + ((nSize + offset)%sizeof(int) ? 1:0); + + /* before transferring any data to lpBuffer we should make sure that all + data is accessible for read. so we need to use a temp buffer for that.*/ + if (!(lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int))))) + { + ERROR("Insufficient memory available !\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto CLEANUP1; + } + + for (ptrInt = lpTmpBuffer; nbInts; ptrInt++, + lpBaseAddressAligned++, nbInts--) + { + errno = 0; + *ptrInt = + PAL_PTRACE(PAL_PT_READ_D, processId, lpBaseAddressAligned, 0); + if (*ptrInt == -1 && errno) + { + if (errno == EFAULT) + { + ERROR("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed" + " errno=%d (%s)\n", processId, lpBaseAddressAligned, + errno, strerror(errno)); + + SetLastError(ptrInt == lpTmpBuffer ? ERROR_ACCESS_DENIED : + ERROR_PARTIAL_COPY); + } + else + { + ASSERT("ptrace(PT_READ_D, pid:%d, addr:%p, data:0) failed" + " errno=%d (%s)\n", processId, lpBaseAddressAligned, + errno, strerror(errno)); + SetLastError(ERROR_INTERNAL_ERROR); + } + + goto CLEANUP2; + } + } + + /* transfer data from temp buffer to lpBuffer */ + memcpy( (char *)lpBuffer, ((char*)lpTmpBuffer) + offset, nSize); + numberOfBytesRead = nSize; + ret = TRUE; +#endif // HAVE_TTRACE + } + else + { + /* Failed to attach processId */ + goto EXIT; + } +#endif // HAVE_PROCFS_CTL + +#if HAVE_PROCFS_CTL +PROCFSCLEANUP: + if (fd != -1) + { + close(fd); + } +#elif !HAVE_TTRACE +CLEANUP2: + if (lpTmpBuffer) + { + InternalFree(lpTmpBuffer); + } +#endif // !HAVE_TTRACE + +#if !HAVE_PROCFS_CTL +CLEANUP1: + if (!DBGDetachProcess(pThread, hProcess, processId)) + { + /* Failed to detach processId */ + ret = FALSE; + } +#endif // HAVE_PROCFS_CTL +#endif // HAVE_VM_READ + +EXIT: + if (lpNumberOfBytesRead) + { + *lpNumberOfBytesRead = numberOfBytesRead; + } + LOGEXIT("ReadProcessMemory returns BOOL %d\n", ret.Load()); + PERF_EXIT(ReadProcessMemory); + return ret; +} + +/*++ +Function: + WriteProcessMemory + +See MSDN doc. +--*/ +BOOL +PALAPI +WriteProcessMemory( + IN HANDLE hProcess, + IN LPVOID lpBaseAddress, + IN LPCVOID lpBuffer, + IN SIZE_T nSize, + OUT SIZE_T * lpNumberOfBytesWritten + ) + +{ + CPalThread *pThread; + DWORD processId; + Volatile<BOOL> ret = FALSE; + Volatile<SIZE_T> numberOfBytesWritten = 0; +#if HAVE_VM_READ + kern_return_t result; + vm_map_t task; +#elif HAVE_PROCFS_CTL + int fd; + char memPath[64]; + LONG_PTR bytesWritten; + off_t offset; +#elif !HAVE_TTRACE + SIZE_T FirstIntOffset; + SIZE_T LastIntOffset; + unsigned int FirstIntMask; + unsigned int LastIntMask; + SIZE_T nbInts; + int *lpTmpBuffer = 0, *lpInt; + int* lpBaseAddressAligned; +#endif + + PERF_ENTRY(WriteProcessMemory); + ENTRY("WriteProcessMemory (hProcess=%p,lpBaseAddress=%p, lpBuffer=%p, " + "nSize=%u, lpNumberOfBytesWritten=%p)\n", + hProcess,lpBaseAddress, lpBuffer, (unsigned int)nSize, lpNumberOfBytesWritten); + + pThread = InternalGetCurrentThread(); + + if (!(nSize && (processId = PROCGetProcessIDFromHandle(hProcess)))) + { + ERROR("Invalid nSize:%u number or invalid process handler " + "hProcess:%p\n", (unsigned int)nSize, hProcess); + SetLastError(ERROR_INVALID_PARAMETER); + goto EXIT; + } + + // Check if the write request is for the current process. + // In that case we don't need ptrace. + if (GetCurrentProcessId() == processId) + { + TRACE("We are in the same process so we don't need ptrace\n"); + + struct Param + { + LPVOID lpBaseAddress; + LPCVOID lpBuffer; + SIZE_T nSize; + SIZE_T numberOfBytesWritten; + BOOL ret; + } param; + param.lpBaseAddress = lpBaseAddress; + param.lpBuffer = lpBuffer; + param.nSize = nSize; + param.numberOfBytesWritten = numberOfBytesWritten; + param.ret = ret; + + PAL_TRY(Param *, pParam, ¶m) + { + SIZE_T i; + + // Seg fault in memcpy can't be caught + // so we simulate the memcpy here + + for (i = 0; i<pParam->nSize; i++) + { + *((char*)(pParam->lpBaseAddress)+i) = *((char*)(pParam->lpBuffer)+i); + } + + pParam->numberOfBytesWritten = pParam->nSize; + pParam->ret = TRUE; + } + PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastError(ERROR_ACCESS_DENIED); + } + PAL_ENDTRY + + numberOfBytesWritten = param.numberOfBytesWritten; + ret = param.ret; + goto EXIT; + } + +#if HAVE_VM_READ + result = task_for_pid(mach_task_self(), processId, &task); + if (result != KERN_SUCCESS) + { + ERROR("No Mach task for pid %d: %d\n", processId, ret.Load()); + SetLastError(ERROR_INVALID_HANDLE); + goto EXIT; + } + result = vm_write(task, (vm_address_t) lpBaseAddress, + (vm_address_t) lpBuffer, nSize); + if (result != KERN_SUCCESS) + { + ERROR("vm_write failed for %d bytes from %p in %d: %d\n", + (int)nSize, lpBaseAddress, task, result); + if (result <= KERN_RETURN_MAX) + { + SetLastError(ERROR_ACCESS_DENIED); + } + else + { + SetLastError(ERROR_INTERNAL_ERROR); + } + goto EXIT; + } + numberOfBytesWritten = nSize; + ret = TRUE; +#else // HAVE_VM_READ +#if HAVE_PROCFS_CTL + snprintf(memPath, sizeof(memPath), "/proc/%u/%s", processId, PROCFS_MEM_NAME); + fd = InternalOpen(memPath, O_WRONLY); + if (fd == -1) + { + ERROR("Failed to open %s\n", memPath); + SetLastError(ERROR_INVALID_ACCESS); + goto PROCFSCLEANUP; + } + + // + // off_t may be greater in size than void*, so first cast to + // an unsigned type to ensure that no sign extension takes place + // + + offset = (off_t) (UINT_PTR) lpBaseAddress; + + if (lseek(fd, offset, SEEK_SET) == -1) + { + ERROR("Failed to seek to base address\n"); + SetLastError(ERROR_INVALID_ACCESS); + goto PROCFSCLEANUP; + } + + bytesWritten = write(fd, lpBuffer, nSize); + if (bytesWritten < 0) + { + ERROR("Failed to write to %s\n", memPath); + SetLastError(ERROR_INVALID_ACCESS); + goto PROCFSCLEANUP; + } + + numberOfBytesWritten = bytesWritten; + ret = TRUE; + +#else // HAVE_PROCFS_CTL + /* Attach the process before calling ptrace otherwise it fails */ + if (DBGAttachProcess(pThread, hProcess, processId)) + { +#if HAVE_TTRACE + if (ttrace(TT_PROC_WRDATA, processId, 0, (__uint64_t)lpBaseAddress, (__uint64_t)nSize, (__uint64_t)lpBuffer) == -1) + { + if (errno == EFAULT) + { + ERROR("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed" + " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer, + errno, strerror(errno)); + + SetLastError(ERROR_ACCESS_DENIED); + } + else + { + ASSERT("ttrace(TT_PROC_WRDATA, pid:%d, addr:%p, data:%d, addr2:%d) failed" + " errno=%d (%s)\n", processId, lpBaseAddress, nSize, lpBuffer, + errno, strerror(errno)); + SetLastError(ERROR_INTERNAL_ERROR); + } + + goto CLEANUP1; + } + + numberOfBytesWritten = nSize; + ret = TRUE; + +#else // HAVE_TTRACE + + FirstIntOffset = (SIZE_T)lpBaseAddress % sizeof(int); + FirstIntMask = -1; + FirstIntMask <<= (FirstIntOffset * 8); + + nbInts = (nSize + FirstIntOffset) / sizeof(int) + + (((nSize + FirstIntOffset)%sizeof(int)) ? 1:0); + lpBaseAddressAligned = (int*)((char*)lpBaseAddress - FirstIntOffset); + + if ((lpTmpBuffer = (int*)InternalMalloc((nbInts * sizeof(int)))) == NULL) + { + ERROR("Insufficient memory available !\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto CLEANUP1; + } + + memcpy((char *)lpTmpBuffer + FirstIntOffset, (char *)lpBuffer, nSize); + lpInt = lpTmpBuffer; + + LastIntOffset = (nSize + FirstIntOffset) % sizeof(int); + LastIntMask = -1; + LastIntMask >>= ((sizeof(int) - LastIntOffset) * 8); + + if (nbInts == 1) + { + if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned, + *lpInt, + LastIntMask & FirstIntMask) + == 0) + { + goto CLEANUP2; + } + numberOfBytesWritten = nSize; + ret = TRUE; + goto CLEANUP2; + } + + if (DBGWriteProcMem_IntWithMask(processId, + lpBaseAddressAligned++, + *lpInt++, FirstIntMask) + == 0) + { + goto CLEANUP2; + } + + while (--nbInts > 1) + { + if (DBGWriteProcMem_Int(processId, lpBaseAddressAligned++, + *lpInt++) == 0) + { + goto CLEANUP2; + } + } + + if (DBGWriteProcMem_IntWithMask(processId, lpBaseAddressAligned, + *lpInt, LastIntMask ) == 0) + { + goto CLEANUP2; + } + + numberOfBytesWritten = nSize; + ret = TRUE; +#endif // HAVE_TTRACE + } + else + { + /* Failed to attach processId */ + goto EXIT; + } +#endif // HAVE_PROCFS_CTL + +#if HAVE_PROCFS_CTL +PROCFSCLEANUP: + if (fd != -1) + { + close(fd); + } +#elif !HAVE_TTRACE +CLEANUP2: + if (lpTmpBuffer) + { + InternalFree(lpTmpBuffer); + } +#endif // !HAVE_TTRACE + +#if !HAVE_PROCFS_CTL +CLEANUP1: + if (!DBGDetachProcess(pThread, hProcess, processId)) + { + /* Failed to detach processId */ + ret = FALSE; + } +#endif // !HAVE_PROCFS_CTL +#endif // HAVE_VM_READ + +EXIT: + if (lpNumberOfBytesWritten) + { + *lpNumberOfBytesWritten = numberOfBytesWritten; + } + + LOGEXIT("WriteProcessMemory returns BOOL %d\n", ret.Load()); + PERF_EXIT(WriteProcessMemory); + return ret; +} + +} // extern "C"
\ No newline at end of file |