diff options
Diffstat (limited to 'src/pal/src/debug/debug.cpp')
-rw-r--r-- | src/pal/src/debug/debug.cpp | 1087 |
1 files changed, 36 insertions, 1051 deletions
diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index 5461ac6265..2f7d17cabe 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -92,29 +92,6 @@ static const char PAL_OUTPUTDEBUGSTRING[] = "PAL_OUTPUTDEBUGSTRING"; static const char PAL_RUN_ON_DEBUG_BREAK[] = "PAL_RUN_ON_DEBUG_BREAK"; #endif // ENABLE_RUN_ON_DEBUG_BREAK -/* ------------------- Static function prototypes ----------------------------*/ - -#if !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE -static int -DBGWriteProcMem_Int(DWORD processId, int *addr, int data); -static int -DBGWriteProcMem_IntWithMask(DWORD processId, int *addr, int data, - unsigned int mask); -#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE - -#if !HAVE_VM_READ && !HAVE_PROCFS_CTL - -static BOOL -DBGAttachProcess(CPalThread *pThread, HANDLE hProcess, DWORD dwProcessId); - -static BOOL -DBGDetachProcess(CPalThread *pThread, HANDLE hProcess, DWORD dwProcessId); - -static int -DBGSetProcessAttached(CPalThread *pThread, HANDLE hProcess, BOOL bAttach); - -#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL - extern "C" { /*++ @@ -566,457 +543,6 @@ SetThreadContext( return ret; } -#if !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE -/*++ -Function: - DBGWriteProcMem_Int - -Abstract - write one int to a process memory address - -Parameter - processId : process handle - addr : memory address where the int should be written - data : int to be written in addr - -Return - Return 1 if it succeeds, or 0 if it's fails ---*/ -static -int -DBGWriteProcMem_Int(IN DWORD processId, - IN int *addr, - IN int data) -{ - if (PAL_PTRACE( PAL_PT_WRITE_D, processId, addr, data ) == -1) - { - if (errno == EFAULT) - { - ERROR("ptrace(PT_WRITE_D, pid:%d caddr_t:%p data:%x) failed " - "errno:%d (%s)\n", processId, addr, data, errno, strerror(errno)); - SetLastError(ERROR_INVALID_ADDRESS); - } - else - { - ASSERT("ptrace(PT_WRITE_D, pid:%d caddr_t:%p data:%x) failed " - "errno:%d (%s)\n", processId, addr, data, errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - return 0; - } - - return 1; -} - -/*++ -Function: - DBGWriteProcMem_IntWithMask - -Abstract - write one int to a process memory address space using mask - -Parameter - processId : process ID - addr : memory address where the int should be written - data : int to be written in addr - mask : the mask used to write only a parts of data - -Return - Return 1 if it succeeds, or 0 if it's fails ---*/ -static -int -DBGWriteProcMem_IntWithMask(IN DWORD processId, - IN int *addr, - IN int data, - IN unsigned int mask ) -{ - int readInt; - - if (mask != ~0) - { - errno = 0; - if (((readInt = PAL_PTRACE( PAL_PT_READ_D, processId, addr, 0 )) == -1) - && errno) - { - if (errno == EFAULT) - { - ERROR("ptrace(PT_READ_D, pid:%d, caddr_t:%p, 0) failed " - "errno:%d (%s)\n", processId, addr, errno, strerror(errno)); - SetLastError(ERROR_INVALID_ADDRESS); - } - else - { - ASSERT("ptrace(PT_READ_D, pid:%d, caddr_t:%p, 0) failed " - "errno:%d (%s)\n", processId, addr, errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - - return 0; - } - data = (data & mask) | (readInt & ~mask); - } - return DBGWriteProcMem_Int(processId, addr, data); -} -#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL && !HAVE_TTRACE - -#if !HAVE_VM_READ && !HAVE_PROCFS_CTL - -/*++ -Function: - DBGAttachProcess - -Abstract - - Attach the indicated process to the current process. - - if the indicated process is already attached by the current process, then - increment the number of attachment pending. if ot, attach it to the current - process (with PT_ATTACH). - -Parameter - hProcess : handle to process to attach to - processId : process ID to attach -Return - Return true if it succeeds, or false if it's fails ---*/ -static -BOOL -DBGAttachProcess( - CPalThread *pThread, - HANDLE hProcess, - DWORD processId - ) -{ - int attchmentCount; - int savedErrno; -#if HAVE_PROCFS_CTL - int fd = -1; - char ctlPath[1024]; -#endif // HAVE_PROCFS_CTL - - attchmentCount = - DBGSetProcessAttached(pThread, hProcess, DBG_ATTACH); - - if (attchmentCount == -1) - { - /* Failed to set the process as attached */ - goto EXIT; - } - - if (attchmentCount == 1) - { -#if HAVE_PROCFS_CTL - struct timespec waitTime; - - // FreeBSD has some trouble when a series of attach/detach sequences - // occurs too close together. When this happens, we'll be able to - // attach to the process, but waiting for the process to stop - // (either via writing "wait" to /proc/<pid>/ctl or via waitpid) - // will hang. If we pause for a very short amount of time before - // trying to attach, we don't run into this situation. - waitTime.tv_sec = 0; - waitTime.tv_nsec = 50000000; - nanosleep(&waitTime, NULL); - - sprintf_s(ctlPath, sizeof(ctlPath), "/proc/%d/ctl", processId); - fd = InternalOpen(ctlPath, O_WRONLY); - if (fd == -1) - { - ERROR("Failed to open %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - goto DETACH1; - } - - if (write(fd, CTL_ATTACH, sizeof(CTL_ATTACH)) < (int)sizeof(CTL_ATTACH)) - { - ERROR("Failed to attach to %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - close(fd); - goto DETACH1; - } - - if (write(fd, CTL_WAIT, sizeof(CTL_WAIT)) < (int)sizeof(CTL_WAIT)) - { - ERROR("Failed to wait for %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - goto DETACH2; - } - - close(fd); -#elif HAVE_TTRACE - if (ttrace(TT_PROC_ATTACH, processId, 0, TT_DETACH_ON_EXIT, TT_VERSION, 0) == -1) - { - if (errno != ESRCH) - { - ASSERT("ttrace(TT_PROC_ATTACH, pid:%d) failed errno:%d (%s)\n", - processId, errno, strerror(errno)); - } - goto DETACH1; - } -#else // HAVE_TTRACE - if (PAL_PTRACE( PAL_PT_ATTACH, processId, 0, 0 ) == -1) - { - if (errno != ESRCH) - { - ASSERT("ptrace(PT_ATTACH, pid:%d) failed errno:%d (%s)\n", - processId, errno, strerror(errno)); - } - goto DETACH1; - } - - if (waitpid(processId, NULL, WUNTRACED) == -1) - { - if (errno != ESRCH) - { - ASSERT("waitpid(pid:%d, NULL, WUNTRACED) failed.errno:%d" - " (%s)\n", processId, errno, strerror(errno)); - } - goto DETACH2; - } -#endif // HAVE_PROCFS_CTL - } - - return TRUE; - -#if HAVE_PROCFS_CTL -DETACH2: - if (write(fd, CTL_DETACH, sizeof(CTL_DETACH)) < (int)sizeof(CTL_DETACH)) - { - ASSERT("Failed to detach from %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - } - close(fd); -#elif !HAVE_TTRACE -DETACH2: - if (PAL_PTRACE(PAL_PT_DETACH, processId, 0, 0) == -1) - { - ASSERT("ptrace(PT_DETACH, pid:%d) failed. errno:%d (%s)\n", processId, - errno, strerror(errno)); - } -#endif // HAVE_PROCFS_CTL - -DETACH1: - savedErrno = errno; - DBGSetProcessAttached(pThread, hProcess, DBG_DETACH); - errno = savedErrno; -EXIT: - if (errno == ESRCH || errno == ENOENT || errno == EBADF) - { - ERROR("Invalid process ID:%d\n", processId); - SetLastError(ERROR_INVALID_PARAMETER); - } - else - { - SetLastError(ERROR_INTERNAL_ERROR); - } - return FALSE; -} - -/*++ -Function: - DBGDetachProcess - -Abstract - Detach the indicated process from the current process. - - if the indicated process is already attached by the current process, then - decrement the number of attachment pending and detach it from the current - process (with PT_DETACH) if there's no more attachment left. - -Parameter - hProcess : process handle - processId : process ID - -Return - Return true if it succeeds, or true if it's fails ---*/ -static -BOOL -DBGDetachProcess( - CPalThread *pThread, - HANDLE hProcess, - DWORD processId - ) -{ - int nbAttachLeft; -#if HAVE_PROCFS_CTL - int fd = -1; - char ctlPath[1024]; -#endif // HAVE_PROCFS_CTL - - nbAttachLeft = DBGSetProcessAttached(pThread, hProcess, DBG_DETACH); - - if (nbAttachLeft == -1) - { - /* Failed to set the process as detached */ - return FALSE; - } - - /* check if there's no more attachment left on processId */ - if (nbAttachLeft == 0) - { -#if HAVE_PROCFS_CTL - sprintf(ctlPath, sizeof(ctlPath), "/proc/%d/ctl", processId); - fd = InternalOpen(pThread, ctlPath, O_WRONLY); - if (fd == -1) - { - if (errno == ENOENT) - { - ERROR("Invalid process ID: %d\n", processId); - SetLastError(ERROR_INVALID_PARAMETER); - } - else - { - ERROR("Failed to open %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - return FALSE; - } - - if (write(fd, CTL_DETACH, sizeof(CTL_DETACH)) < (int)sizeof(CTL_DETACH)) - { - ERROR("Failed to detach from %s: errno is %d (%s)\n", ctlPath, - errno, strerror(errno)); - close(fd); - return FALSE; - } - close(fd); - -#elif HAVE_TTRACE - if (ttrace(TT_PROC_DETACH, processId, 0, 0, 0, 0) == -1) - { - if (errno == ESRCH) - { - ERROR("Invalid process ID: %d\n", processId); - SetLastError(ERROR_INVALID_PARAMETER); - } - else - { - ASSERT("ttrace(TT_PROC_DETACH, pid:%d) failed. errno:%d (%s)\n", - processId, errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - return FALSE; - } -#else // HAVE_TTRACE - if (PAL_PTRACE(PAL_PT_DETACH, processId, 1, 0) == -1) - { - if (errno == ESRCH) - { - ERROR("Invalid process ID: %d\n", processId); - SetLastError(ERROR_INVALID_PARAMETER); - } - else - { - ASSERT("ptrace(PT_DETACH, pid:%d) failed. errno:%d (%s)\n", - processId, errno, strerror(errno)); - SetLastError(ERROR_INTERNAL_ERROR); - } - return FALSE; - } -#endif // HAVE_PROCFS_CTL - -#if !HAVE_TTRACE - if (kill(processId, SIGCONT) == -1) - { - ERROR("Failed to continue the detached process:%d errno:%d (%s)\n", - processId, errno, strerror(errno)); - return FALSE; - } -#endif // !HAVE_TTRACE - } - return TRUE; -} - -/*++ -Function: - DBGSetProcessAttached - -Abstract - saves the current process Id in the attached process structure - -Parameter - hProcess : process handle - bAttach : true (false) to set the process as attached (as detached) -Return - returns the number of attachment left on attachedProcId, or -1 if it fails ---*/ -static int -DBGSetProcessAttached( - CPalThread *pThread, - HANDLE hProcess, - BOOL bAttach - ) -{ - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjProcess = NULL; - IDataLock *pDataLock = NULL; - CProcProcessLocalData *pLocalData = NULL; - int ret = -1; - CAllowedObjectTypes aotProcess(otiProcess); - - palError = g_pObjectManager->ReferenceObjectByHandle( - pThread, - hProcess, - &aotProcess, - 0, - &pobjProcess - ); - - if (NO_ERROR != palError) - { - goto DBGSetProcessAttachedExit; - } - - palError = pobjProcess->GetProcessLocalData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast<void **>(&pLocalData) - ); - - if (NO_ERROR != palError) - { - goto DBGSetProcessAttachedExit; - } - - if (bAttach) - { - pLocalData->lAttachCount += 1; - } - else - { - pLocalData->lAttachCount -= 1; - - if (pLocalData->lAttachCount < 0) - { - ASSERT("pLocalData->lAttachCount < 0 check for extra DBGDetachProcess calls\n"); - palError = ERROR_INTERNAL_ERROR; - goto DBGSetProcessAttachedExit; - } - } - - ret = pLocalData->lAttachCount; - -DBGSetProcessAttachedExit: - - if (NULL != pDataLock) - { - pDataLock->ReleaseLock(pThread, TRUE); - } - - if (NULL != pobjProcess) - { - pobjProcess->ReleaseReference(pThread); - } - - return ret; -} - -#endif // !HAVE_VM_READ && !HAVE_PROCFS_CTL - /*++ Function: PAL_CreateExecWatchpoint @@ -1240,605 +766,64 @@ PAL_DeleteExecWatchpointExit: return dwError; } -// 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 - ) +__attribute__((noinline)) +__attribute__((optnone)) +void +ProbeMemory(volatile PBYTE pbBuffer, DWORD cbBuffer, bool fWriteAccess) { - 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 = -1; - 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) + // Need an throw in this function to fool the C++ runtime into handling the + // possible h/w exception below. + if (pbBuffer == NULL) { - 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; + throw PAL_SEHException(); } -#if HAVE_VM_READ - result = task_for_pid(mach_task_self(), processId, &task); - if (result != KERN_SUCCESS) + // Simple one byte at a time probing + while (cbBuffer > 0) { - 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) + volatile BYTE read = *pbBuffer; + if (fWriteAccess) { - 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; + *pbBuffer = read; } - memcpy((LPSTR)lpBuffer + numberOfBytesRead, data + offset, bytesToRead); - numberOfBytesRead.Store(numberOfBytesRead.Load() + bytesToRead); - lpBaseAddressAligned = (int*)((char*)lpBaseAddressAligned + VIRTUAL_PAGE_SIZE); - nSize -= bytesToRead; - offset = 0; + ++pbBuffer; + --cbBuffer; } - 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) - { - free(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 + PAL_ProbeMemory -See MSDN doc. +Abstract + +Parameter + pBuffer : address of memory to validate + cbBuffer : size of memory region to validate + fWriteAccess : if true, validate writable access, else just readable. + +Return + true if memory is valid, false if not. --*/ BOOL PALAPI -WriteProcessMemory( - IN HANDLE hProcess, - IN LPVOID lpBaseAddress, - IN LPCVOID lpBuffer, - IN SIZE_T nSize, - OUT SIZE_T * lpNumberOfBytesWritten - ) - +PAL_ProbeMemory( + PVOID pBuffer, + DWORD cbBuffer, + BOOL fWriteAccess) { - 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 = -1; - 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) + try { - 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; + // Need to explicit h/w exception holder so to catch them in ProbeMemory + CatchHardwareExceptionHolder __catchHardwareException; - if (lseek(fd, offset, SEEK_SET) == -1) - { - ERROR("Failed to seek to base address\n"); - SetLastError(ERROR_INVALID_ACCESS); - goto PROCFSCLEANUP; + ProbeMemory((PBYTE)pBuffer, cbBuffer, fWriteAccess); } - - 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)) + catch(...) { -#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) - { - free(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; + return FALSE; } - - LOGEXIT("WriteProcessMemory returns BOOL %d\n", ret.Load()); - PERF_EXIT(WriteProcessMemory); - return ret; + return TRUE; } } // extern "C" |