mirror of
https://github.com/python/cpython.git
synced 2026-06-28 03:41:13 +00:00
[3.15] gh-149619: Harden _remote_debugging error paths (GH-150349) (#150435)
(cherry picked from commit a5be25d3bd)
This commit is contained in:
parent
d23b06b2a8
commit
d5381e18b8
11 changed files with 597 additions and 196 deletions
|
|
@ -100,9 +100,16 @@ extern "C" {
|
|||
# define HAVE_PROCESS_VM_READV 0
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
_Py_RemoteDebug_HasPermissionError(void)
|
||||
{
|
||||
return PyErr_Occurred()
|
||||
&& PyErr_ExceptionMatches(PyExc_PermissionError);
|
||||
}
|
||||
|
||||
#define _set_debug_exception_cause(exception, format, ...) \
|
||||
do { \
|
||||
if (!PyErr_ExceptionMatches(PyExc_PermissionError)) { \
|
||||
if (!_Py_RemoteDebug_HasPermissionError()) { \
|
||||
PyThreadState *tstate = _PyThreadState_GET(); \
|
||||
if (!_PyErr_Occurred(tstate)) { \
|
||||
_PyErr_Format(tstate, exception, format, ##__VA_ARGS__); \
|
||||
|
|
@ -112,6 +119,20 @@ extern "C" {
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define _set_debug_oserror_from_errno(err, format, ...) \
|
||||
do { \
|
||||
errno = (err); \
|
||||
PyErr_SetFromErrno(PyExc_OSError); \
|
||||
_set_debug_exception_cause(PyExc_OSError, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define _set_debug_oserror_from_errno_with_filename(err, filename, format, ...) \
|
||||
do { \
|
||||
errno = (err); \
|
||||
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename); \
|
||||
_set_debug_exception_cause(PyExc_OSError, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static inline size_t
|
||||
get_page_size(void) {
|
||||
size_t page_size = 0;
|
||||
|
|
@ -170,7 +191,7 @@ _Py_RemoteDebug_ValidatePyRuntimeCookie(proc_handle_t *handle, uintptr_t address
|
|||
}
|
||||
char buf[sizeof(_Py_Debug_Cookie) - 1];
|
||||
if (_Py_RemoteDebug_ReadRemoteMemory(handle, address, sizeof(buf), buf) != 0) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_PermissionError)) {
|
||||
if (!_Py_RemoteDebug_HasPermissionError()) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -207,6 +228,21 @@ static mach_port_t pid_to_task(pid_t pid);
|
|||
// Initialize the process handle
|
||||
UNUSED static int
|
||||
_Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
|
||||
handle->pid = 0;
|
||||
#if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
|
||||
handle->task = 0;
|
||||
#elif defined(MS_WINDOWS)
|
||||
handle->hProcess = NULL;
|
||||
#elif defined(__linux__)
|
||||
handle->memfd = -1;
|
||||
#endif
|
||||
handle->page_size = get_page_size();
|
||||
handle->page_cache_count = 0;
|
||||
for (int i = 0; i < MAX_PAGES; i++) {
|
||||
handle->pages[i].data = NULL;
|
||||
handle->pages[i].valid = 0;
|
||||
}
|
||||
|
||||
handle->pid = pid;
|
||||
#if defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
|
||||
handle->task = pid_to_task(handle->pid);
|
||||
|
|
@ -219,19 +255,12 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
|
|||
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME,
|
||||
FALSE, pid);
|
||||
if (handle->hProcess == NULL) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize Windows process handle");
|
||||
return -1;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
handle->memfd = -1;
|
||||
#endif
|
||||
handle->page_size = get_page_size();
|
||||
handle->page_cache_count = 0;
|
||||
for (int i = 0; i < MAX_PAGES; i++) {
|
||||
handle->pages[i].data = NULL;
|
||||
handle->pages[i].valid = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -396,17 +425,19 @@ return_section_address_fat(
|
|||
size_t cpu_size = sizeof(cpu), abi64_size = sizeof(is_abi64);
|
||||
|
||||
if (sysctlbyname("hw.cputype", &cpu, &cpu_size, NULL, 0) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno(err,
|
||||
"Failed to determine CPU type via sysctlbyname "
|
||||
"for fat binary analysis at 0x%lx: %s",
|
||||
base, strerror(errno));
|
||||
base, strerror(err));
|
||||
return 0;
|
||||
}
|
||||
if (sysctlbyname("hw.cpu64bit_capable", &is_abi64, &abi64_size, NULL, 0) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno(err,
|
||||
"Failed to determine CPU ABI capability via sysctlbyname "
|
||||
"for fat binary analysis at 0x%lx: %s",
|
||||
base, strerror(errno));
|
||||
base, strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -459,26 +490,29 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
|
|||
{
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, path,
|
||||
"Cannot open binary file '%s' for section '%s' search: %s",
|
||||
path, secname, strerror(errno));
|
||||
path, secname, strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stat fs;
|
||||
if (fstat(fd, &fs) == -1) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, path,
|
||||
"Cannot get file size for binary '%s' during section '%s' search: %s",
|
||||
path, secname, strerror(errno));
|
||||
path, secname, strerror(err));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* map = mmap(0, fs.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, path,
|
||||
"Cannot memory map binary file '%s' (size: %lld bytes) for section '%s' search: %s",
|
||||
path, (long long)fs.st_size, secname, strerror(errno));
|
||||
path, (long long)fs.st_size, secname, strerror(err));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -507,15 +541,21 @@ search_section_in_file(const char* secname, char* path, uintptr_t base, mach_vm_
|
|||
}
|
||||
|
||||
if (munmap(map, fs.st_size) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"Failed to unmap binary file '%s' (size: %lld bytes): %s",
|
||||
path, (long long)fs.st_size, strerror(errno));
|
||||
if (!PyErr_Occurred()) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, path,
|
||||
"Failed to unmap binary file '%s' (size: %lld bytes): %s",
|
||||
path, (long long)fs.st_size, strerror(err));
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"Failed to close binary file '%s': %s",
|
||||
path, strerror(errno));
|
||||
if (!PyErr_Occurred()) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, path,
|
||||
"Failed to close binary file '%s': %s",
|
||||
path, strerror(err));
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
|
|
@ -560,14 +600,15 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
|
|||
|
||||
char map_filename[MAXPATHLEN + 1];
|
||||
|
||||
while (mach_vm_region(
|
||||
proc_ref,
|
||||
&address,
|
||||
&size,
|
||||
VM_REGION_BASIC_INFO_64,
|
||||
(vm_region_info_t)®ion_info,
|
||||
&count,
|
||||
&object_name) == KERN_SUCCESS)
|
||||
kern_return_t kr;
|
||||
while ((kr = mach_vm_region(
|
||||
proc_ref,
|
||||
&address,
|
||||
&size,
|
||||
VM_REGION_BASIC_INFO_64,
|
||||
(vm_region_info_t)®ion_info,
|
||||
&count,
|
||||
&object_name)) == KERN_SUCCESS)
|
||||
{
|
||||
|
||||
if ((region_info.protection & VM_PROT_READ) == 0
|
||||
|
|
@ -591,18 +632,32 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
|
|||
}
|
||||
|
||||
if (strncmp(filename, substr, strlen(substr)) == 0) {
|
||||
PyErr_Clear();
|
||||
uintptr_t result = search_section_in_file(
|
||||
secname, map_filename, address, size, proc_ref);
|
||||
if (result != 0
|
||||
&& (validator == NULL || validator(handle, result)))
|
||||
{
|
||||
return result;
|
||||
if (result != 0) {
|
||||
if (validator == NULL || validator(handle, result)) {
|
||||
return result;
|
||||
}
|
||||
if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
address += size;
|
||||
}
|
||||
|
||||
if (kr != KERN_INVALID_ADDRESS && !PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"mach_vm_region failed while searching PID %d for section '%s' "
|
||||
"(kern_return_t: %d)",
|
||||
handle->pid, secname, kr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -625,25 +680,29 @@ search_elf_file_for_section(
|
|||
|
||||
int fd = open(elf_file, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, elf_file,
|
||||
"Cannot open ELF file '%s' for section '%s' search: %s",
|
||||
elf_file, secname, strerror(errno));
|
||||
elf_file, secname, strerror(err));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
struct stat file_stats;
|
||||
if (fstat(fd, &file_stats) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, elf_file,
|
||||
"Cannot get file size for ELF file '%s' during section '%s' search: %s",
|
||||
elf_file, secname, strerror(errno));
|
||||
elf_file, secname, strerror(err));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
file_memory = mmap(NULL, file_stats.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (file_memory == MAP_FAILED) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, elf_file,
|
||||
"Cannot memory map ELF file '%s' (size: %lld bytes) for section '%s' search: %s",
|
||||
elf_file, (long long)file_stats.st_size, secname, strerror(errno));
|
||||
elf_file, (long long)file_stats.st_size, secname, strerror(err));
|
||||
file_memory = NULL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
|
@ -700,12 +759,23 @@ search_elf_file_for_section(
|
|||
|
||||
exit:
|
||||
if (file_memory != NULL) {
|
||||
munmap(file_memory, file_stats.st_size);
|
||||
if (munmap(file_memory, file_stats.st_size) != 0) {
|
||||
if (!PyErr_Occurred()) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, elf_file,
|
||||
"Failed to unmap ELF file '%s' (size: %lld bytes): %s",
|
||||
elf_file, (long long)file_stats.st_size, strerror(err));
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
if (fd >= 0 && close(fd) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"Failed to close ELF file '%s': %s",
|
||||
elf_file, strerror(errno));
|
||||
if (!PyErr_Occurred()) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, elf_file,
|
||||
"Failed to close ELF file '%s': %s",
|
||||
elf_file, strerror(err));
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
|
|
@ -720,9 +790,10 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
|
|||
|
||||
FILE* maps_file = fopen(maps_file_path, "r");
|
||||
if (maps_file == NULL) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, maps_file_path,
|
||||
"Cannot open process memory map file '%s' for PID %d section search: %s",
|
||||
maps_file_path, handle->pid, strerror(errno));
|
||||
maps_file_path, handle->pid, strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -787,26 +858,39 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
|
|||
}
|
||||
|
||||
if (strstr(filename, substr)) {
|
||||
if (PyErr_ExceptionMatches(PyExc_PermissionError)) {
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
PyErr_Clear();
|
||||
retval = search_elf_file_for_section(handle, secname, start, path);
|
||||
if (retval
|
||||
&& (validator == NULL || validator(handle, retval)))
|
||||
{
|
||||
if (retval) {
|
||||
if (validator == NULL || validator(handle, retval)) {
|
||||
break;
|
||||
}
|
||||
if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == 0 && !PyErr_Occurred() && ferror(maps_file)) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, maps_file_path,
|
||||
"Failed to read process map file '%s' for PID %d section search: %s",
|
||||
maps_file_path, handle->pid, strerror(err));
|
||||
}
|
||||
|
||||
PyMem_Free(line);
|
||||
if (fclose(maps_file) != 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"Failed to close process map file '%s': %s",
|
||||
maps_file_path, strerror(errno));
|
||||
if (!PyErr_Occurred()) {
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, maps_file_path,
|
||||
"Failed to close process map file '%s': %s",
|
||||
maps_file_path, strerror(err));
|
||||
}
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
|
|
@ -829,9 +913,9 @@ static int is_process_alive(HANDLE hProcess) {
|
|||
static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char* secname) {
|
||||
HANDLE hFile = CreateFileW(mod_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_Format(PyExc_OSError,
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Cannot open PE file for section '%s' analysis (error %lu)",
|
||||
secname, error);
|
||||
return NULL;
|
||||
|
|
@ -839,9 +923,9 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
|
|||
|
||||
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0);
|
||||
if (!hMap) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_Format(PyExc_OSError,
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Cannot create file mapping for PE file section '%s' analysis (error %lu)",
|
||||
secname, error);
|
||||
CloseHandle(hFile);
|
||||
|
|
@ -850,9 +934,9 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
|
|||
|
||||
BYTE* mapView = (BYTE*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!mapView) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_Format(PyExc_OSError,
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Cannot map view of PE file for section '%s' analysis (error %lu)",
|
||||
secname, error);
|
||||
CloseHandle(hMap);
|
||||
|
|
@ -910,9 +994,9 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
|
|||
} while (hProcSnap == INVALID_HANDLE_VALUE && GetLastError() == ERROR_BAD_LENGTH);
|
||||
|
||||
if (hProcSnap == INVALID_HANDLE_VALUE) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_Format(PyExc_PermissionError,
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Unable to create module snapshot for PID %d section '%s' "
|
||||
"search (error %lu). Check permissions or PID validity",
|
||||
handle->pid, secname, error);
|
||||
|
|
@ -923,17 +1007,46 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
|
|||
moduleEntry.dwSize = sizeof(moduleEntry);
|
||||
void* runtime_addr = NULL;
|
||||
|
||||
for (BOOL hasModule = Module32FirstW(hProcSnap, &moduleEntry); hasModule; hasModule = Module32NextW(hProcSnap, &moduleEntry)) {
|
||||
if (!Module32FirstW(hProcSnap, &moduleEntry)) {
|
||||
DWORD error = GetLastError();
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Unable to enumerate modules for PID %d section '%s' "
|
||||
"search (error %lu)",
|
||||
handle->pid, secname, error);
|
||||
CloseHandle(hProcSnap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
// Look for either python executable or DLL
|
||||
if (wcsstr(moduleEntry.szModule, substr)) {
|
||||
PyErr_Clear();
|
||||
void *candidate = analyze_pe(moduleEntry.szExePath, moduleEntry.modBaseAddr, secname);
|
||||
if (candidate != NULL
|
||||
&& (validator == NULL || validator(handle, (uintptr_t)candidate)))
|
||||
{
|
||||
runtime_addr = candidate;
|
||||
if (candidate != NULL) {
|
||||
if (validator == NULL || validator(handle, (uintptr_t)candidate)) {
|
||||
runtime_addr = candidate;
|
||||
break;
|
||||
}
|
||||
if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (_Py_RemoteDebug_HasPermissionError()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (Module32NextW(hProcSnap, &moduleEntry));
|
||||
|
||||
if (runtime_addr == NULL && !PyErr_Occurred()) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_NO_MORE_FILES) {
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"Module enumeration failed for PID %d section '%s' "
|
||||
"search (error %lu)",
|
||||
handle->pid, secname, error);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProcSnap);
|
||||
|
|
@ -954,19 +1067,21 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
|
|||
address = search_windows_map_for_section(handle, "PyRuntime", L"python",
|
||||
_Py_RemoteDebug_ValidatePyRuntimeCookie);
|
||||
if (address == 0) {
|
||||
// Error out: 'python' substring covers both executable and DLL
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Failed to find the PyRuntime section in process %d on Windows platform",
|
||||
handle->pid);
|
||||
_PyErr_ChainExceptions1(exc);
|
||||
if (!_Py_RemoteDebug_HasPermissionError()) {
|
||||
// Error out: 'python' substring covers both executable and DLL
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Failed to find the PyRuntime section in process %d on Windows platform",
|
||||
handle->pid);
|
||||
_PyErr_ChainExceptions1(exc);
|
||||
}
|
||||
}
|
||||
#elif defined(__linux__) && HAVE_PROCESS_VM_READV
|
||||
// On Linux, search for 'python' in executable or DLL
|
||||
address = search_linux_map_for_section(handle, "PyRuntime", "python",
|
||||
_Py_RemoteDebug_ValidatePyRuntimeCookie);
|
||||
if (address == 0) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_PermissionError)) {
|
||||
if (!_Py_RemoteDebug_HasPermissionError()) {
|
||||
// Error out: 'python' substring covers both executable and DLL
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
|
|
@ -982,17 +1097,19 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
|
|||
PyErr_Clear();
|
||||
address = search_map_for_section(handle, "PyRuntime", *candidate,
|
||||
_Py_RemoteDebug_ValidatePyRuntimeCookie);
|
||||
if (address != 0) {
|
||||
if (address != 0 || _Py_RemoteDebug_HasPermissionError()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (address == 0) {
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Failed to find the PyRuntime section in process %d "
|
||||
"on macOS platform (tried both libpython and python)",
|
||||
handle->pid);
|
||||
_PyErr_ChainExceptions1(exc);
|
||||
if (!_Py_RemoteDebug_HasPermissionError()) {
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"Failed to find the PyRuntime section in process %d "
|
||||
"on macOS platform (tried both libpython and python)",
|
||||
handle->pid);
|
||||
_PyErr_ChainExceptions1(exc);
|
||||
}
|
||||
}
|
||||
#else
|
||||
_set_debug_exception_cause(PyExc_RuntimeError,
|
||||
|
|
@ -1013,9 +1130,9 @@ open_proc_mem_fd(proc_handle_t *handle)
|
|||
|
||||
handle->memfd = open(mem_file_path, O_RDWR);
|
||||
if (handle->memfd == -1) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"failed to open file %s: %s", mem_file_path, strerror(errno));
|
||||
int err = errno;
|
||||
_set_debug_oserror_from_errno_with_filename(err, mem_file_path,
|
||||
"failed to open file %s: %s", mem_file_path, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -1026,6 +1143,9 @@ open_proc_mem_fd(proc_handle_t *handle)
|
|||
static int
|
||||
read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (handle->memfd == -1) {
|
||||
if (open_proc_mem_fd(handle) < 0) {
|
||||
return -1;
|
||||
|
|
@ -1043,14 +1163,23 @@ read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, siz
|
|||
|
||||
read_bytes = preadv(handle->memfd, local, 1, offset);
|
||||
if (read_bytes < 0) {
|
||||
int err = errno;
|
||||
errno = err;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"preadv failed for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zd bytes): %s",
|
||||
handle->pid, remote_address + result, len - result, result, strerror(errno));
|
||||
handle->pid, remote_address + result, len - result, result, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_bytes == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"preadv returned 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zd bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += read_bytes;
|
||||
} while ((size_t)read_bytes != local[0].iov_len);
|
||||
return 0;
|
||||
|
|
@ -1062,11 +1191,15 @@ read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, siz
|
|||
static int
|
||||
_Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
SIZE_T read_bytes = 0;
|
||||
SIZE_T result = 0;
|
||||
do {
|
||||
if (!ReadProcessMemory(handle->hProcess, (LPCVOID)(remote_address + result), (char*)dst + result, len - result, &read_bytes)) {
|
||||
DWORD error = GetLastError();
|
||||
// Check if the process is still alive: we need to be able to tell our caller
|
||||
// that the process is dead and not just that the read failed.
|
||||
if (!is_process_alive(handle->hProcess)) {
|
||||
|
|
@ -1074,14 +1207,20 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return -1;
|
||||
}
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"ReadProcessMemory failed for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zu bytes): Windows error %lu",
|
||||
handle->pid, remote_address + result, len - result, result, error);
|
||||
return -1;
|
||||
}
|
||||
if (read_bytes == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"ReadProcessMemory returned 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zu bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += read_bytes;
|
||||
} while (result < len);
|
||||
return 0;
|
||||
|
|
@ -1102,31 +1241,40 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||
|
||||
read_bytes = process_vm_readv(handle->pid, local, 1, remote, 1, 0);
|
||||
if (read_bytes < 0) {
|
||||
if (errno == ENOSYS) {
|
||||
int err = errno;
|
||||
if (err == ENOSYS) {
|
||||
return read_remote_memory_fallback(handle, remote_address, len, dst);
|
||||
}
|
||||
errno = err;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
if (errno == ESRCH) {
|
||||
if (err == ESRCH) {
|
||||
return -1;
|
||||
}
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"process_vm_readv failed for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zd bytes): %s",
|
||||
handle->pid, remote_address + result, len - result, result, strerror(errno));
|
||||
handle->pid, remote_address + result, len - result, result, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_bytes == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"process_vm_readv returned 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial read %zd bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += read_bytes;
|
||||
} while ((size_t)read_bytes != local[0].iov_len);
|
||||
return 0;
|
||||
#elif defined(__APPLE__) && defined(TARGET_OS_OSX) && TARGET_OS_OSX
|
||||
Py_ssize_t result = -1;
|
||||
mach_vm_size_t bytes_read = 0;
|
||||
kern_return_t kr = mach_vm_read_overwrite(
|
||||
handle->task,
|
||||
(mach_vm_address_t)remote_address,
|
||||
len,
|
||||
(mach_vm_address_t)dst,
|
||||
(mach_vm_size_t*)&result);
|
||||
&bytes_read);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
switch (err_get_code(kr)) {
|
||||
|
|
@ -1170,6 +1318,13 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
if (bytes_read != (mach_vm_size_t)len) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"mach_vm_read_overwrite read %llu of %zu bytes for PID %d at "
|
||||
"address 0x%lx",
|
||||
(unsigned long long)bytes_read, len, handle->pid, remote_address);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
Py_UNREACHABLE();
|
||||
|
|
@ -1181,6 +1336,9 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||
static int
|
||||
_Py_RemoteDebug_WriteRemoteMemoryFallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (handle->memfd == -1) {
|
||||
if (open_proc_mem_fd(handle) < 0) {
|
||||
return -1;
|
||||
|
|
@ -1198,10 +1356,19 @@ _Py_RemoteDebug_WriteRemoteMemoryFallback(proc_handle_t *handle, uintptr_t remot
|
|||
|
||||
written = pwritev(handle->memfd, local, 1, offset);
|
||||
if (written < 0) {
|
||||
int err = errno;
|
||||
errno = err;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (written == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"pwritev wrote 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial write %zd bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += written;
|
||||
} while ((size_t)written != local[0].iov_len);
|
||||
return 0;
|
||||
|
|
@ -1212,19 +1379,29 @@ _Py_RemoteDebug_WriteRemoteMemoryFallback(proc_handle_t *handle, uintptr_t remot
|
|||
UNUSED static int
|
||||
_Py_RemoteDebug_WriteRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
#ifdef MS_WINDOWS
|
||||
SIZE_T written = 0;
|
||||
SIZE_T result = 0;
|
||||
do {
|
||||
if (!WriteProcessMemory(handle->hProcess, (LPVOID)(remote_address + result), (const char*)src + result, len - result, &written)) {
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
DWORD error = GetLastError();
|
||||
PyErr_SetFromWindowsErr(error);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"WriteProcessMemory failed for PID %d at address 0x%lx "
|
||||
"(size %zu, partial write %zu bytes): Windows error %lu",
|
||||
handle->pid, remote_address + result, len - result, result, error);
|
||||
return -1;
|
||||
}
|
||||
if (written == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"WriteProcessMemory wrote 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial write %zu bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += written;
|
||||
} while (result < len);
|
||||
return 0;
|
||||
|
|
@ -1245,17 +1422,26 @@ _Py_RemoteDebug_WriteRemoteMemory(proc_handle_t *handle, uintptr_t remote_addres
|
|||
|
||||
written = process_vm_writev(handle->pid, local, 1, remote, 1, 0);
|
||||
if (written < 0) {
|
||||
if (errno == ENOSYS) {
|
||||
int err = errno;
|
||||
if (err == ENOSYS) {
|
||||
return _Py_RemoteDebug_WriteRemoteMemoryFallback(handle, remote_address, len, src);
|
||||
}
|
||||
errno = err;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
_set_debug_exception_cause(PyExc_OSError,
|
||||
"process_vm_writev failed for PID %d at address 0x%lx "
|
||||
"(size %zu, partial write %zd bytes): %s",
|
||||
handle->pid, remote_address + result, len - result, result, strerror(errno));
|
||||
handle->pid, remote_address + result, len - result, result, strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (written == 0) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"process_vm_writev wrote 0 bytes for PID %d at address 0x%lx "
|
||||
"(size %zu, partial write %zd bytes)",
|
||||
handle->pid, remote_address + result, len - result, result);
|
||||
return -1;
|
||||
}
|
||||
result += written;
|
||||
} while ((size_t)written != local[0].iov_len);
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue