gh-134876: Add fallback for when process_vm_readv fails with ENOSYS (#134878)
This commit is contained in:
parent
24069fbca8
commit
ac9c3431cc
@ -658,6 +658,7 @@ Michael Goderbauer
|
|||||||
Karan Goel
|
Karan Goel
|
||||||
Jeroen Van Goey
|
Jeroen Van Goey
|
||||||
Christoph Gohlke
|
Christoph Gohlke
|
||||||
|
Daniel Golding
|
||||||
Tim Golden
|
Tim Golden
|
||||||
Yonatan Goldschmidt
|
Yonatan Goldschmidt
|
||||||
Mark Gollahon
|
Mark Gollahon
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
Add support to :pep:`768` remote debugging for Linux kernels which don't
|
||||||
|
have CONFIG_CROSS_MEMORY_ATTACH configured.
|
@ -116,6 +116,8 @@ typedef struct {
|
|||||||
mach_port_t task;
|
mach_port_t task;
|
||||||
#elif defined(MS_WINDOWS)
|
#elif defined(MS_WINDOWS)
|
||||||
HANDLE hProcess;
|
HANDLE hProcess;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
int memfd;
|
||||||
#endif
|
#endif
|
||||||
page_cache_entry_t pages[MAX_PAGES];
|
page_cache_entry_t pages[MAX_PAGES];
|
||||||
Py_ssize_t page_size;
|
Py_ssize_t page_size;
|
||||||
@ -162,6 +164,8 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
|
|||||||
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize Windows process handle");
|
_set_debug_exception_cause(PyExc_RuntimeError, "Failed to initialize Windows process handle");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
handle->memfd = -1;
|
||||||
#endif
|
#endif
|
||||||
handle->page_size = get_page_size();
|
handle->page_size = get_page_size();
|
||||||
for (int i = 0; i < MAX_PAGES; i++) {
|
for (int i = 0; i < MAX_PAGES; i++) {
|
||||||
@ -179,6 +183,11 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
|
|||||||
CloseHandle(handle->hProcess);
|
CloseHandle(handle->hProcess);
|
||||||
handle->hProcess = NULL;
|
handle->hProcess = NULL;
|
||||||
}
|
}
|
||||||
|
#elif defined(__linux__)
|
||||||
|
if (handle->memfd != -1) {
|
||||||
|
close(handle->memfd);
|
||||||
|
handle->memfd = -1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
handle->pid = 0;
|
handle->pid = 0;
|
||||||
_Py_RemoteDebug_FreePageCache(handle);
|
_Py_RemoteDebug_FreePageCache(handle);
|
||||||
@ -907,6 +916,61 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) && HAVE_PROCESS_VM_READV
|
||||||
|
|
||||||
|
static int
|
||||||
|
open_proc_mem_fd(proc_handle_t *handle)
|
||||||
|
{
|
||||||
|
char mem_file_path[64];
|
||||||
|
sprintf(mem_file_path, "/proc/%d/mem", handle->pid);
|
||||||
|
|
||||||
|
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));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Why is pwritev not guarded? Except on Android API level 23 (no longer
|
||||||
|
// supported), HAVE_PROCESS_VM_READV is sufficient.
|
||||||
|
static int
|
||||||
|
read_remote_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst)
|
||||||
|
{
|
||||||
|
if (handle->memfd == -1) {
|
||||||
|
if (open_proc_mem_fd(handle) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iovec local[1];
|
||||||
|
Py_ssize_t result = 0;
|
||||||
|
Py_ssize_t read_bytes = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
local[0].iov_base = (char*)dst + result;
|
||||||
|
local[0].iov_len = len - result;
|
||||||
|
off_t offset = remote_address + result;
|
||||||
|
|
||||||
|
read_bytes = preadv(handle->memfd, local, 1, offset);
|
||||||
|
if (read_bytes < 0) {
|
||||||
|
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));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += read_bytes;
|
||||||
|
} while ((size_t)read_bytes != local[0].iov_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
// Platform-independent memory read function
|
// Platform-independent memory read function
|
||||||
static int
|
static int
|
||||||
_Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst)
|
_Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address, size_t len, void* dst)
|
||||||
@ -928,6 +992,9 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||||||
} while (result < len);
|
} while (result < len);
|
||||||
return 0;
|
return 0;
|
||||||
#elif defined(__linux__) && HAVE_PROCESS_VM_READV
|
#elif defined(__linux__) && HAVE_PROCESS_VM_READV
|
||||||
|
if (handle->memfd != -1) {
|
||||||
|
return read_remote_memory_fallback(handle, remote_address, len, dst);
|
||||||
|
}
|
||||||
struct iovec local[1];
|
struct iovec local[1];
|
||||||
struct iovec remote[1];
|
struct iovec remote[1];
|
||||||
Py_ssize_t result = 0;
|
Py_ssize_t result = 0;
|
||||||
@ -941,6 +1008,9 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
|
|||||||
|
|
||||||
read_bytes = process_vm_readv(handle->pid, local, 1, remote, 1, 0);
|
read_bytes = process_vm_readv(handle->pid, local, 1, remote, 1, 0);
|
||||||
if (read_bytes < 0) {
|
if (read_bytes < 0) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
return read_remote_memory_fallback(handle, remote_address, len, dst);
|
||||||
|
}
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
_set_debug_exception_cause(PyExc_OSError,
|
_set_debug_exception_cause(PyExc_OSError,
|
||||||
"process_vm_readv failed for PID %d at address 0x%lx "
|
"process_vm_readv failed for PID %d at address 0x%lx "
|
||||||
|
@ -24,6 +24,39 @@ read_memory(proc_handle_t *handle, uint64_t remote_address, size_t len, void* ds
|
|||||||
return _Py_RemoteDebug_ReadRemoteMemory(handle, remote_address, len, dst);
|
return _Py_RemoteDebug_ReadRemoteMemory(handle, remote_address, len, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Why is pwritev not guarded? Except on Android API level 23 (no longer
|
||||||
|
// supported), HAVE_PROCESS_VM_READV is sufficient.
|
||||||
|
#if defined(__linux__) && HAVE_PROCESS_VM_READV
|
||||||
|
static int
|
||||||
|
write_memory_fallback(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src)
|
||||||
|
{
|
||||||
|
if (handle->memfd == -1) {
|
||||||
|
if (open_proc_mem_fd(handle) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iovec local[1];
|
||||||
|
Py_ssize_t result = 0;
|
||||||
|
Py_ssize_t written = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
local[0].iov_base = (char*)src + result;
|
||||||
|
local[0].iov_len = len - result;
|
||||||
|
off_t offset = remote_address + result;
|
||||||
|
|
||||||
|
written = pwritev(handle->memfd, local, 1, offset);
|
||||||
|
if (written < 0) {
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += written;
|
||||||
|
} while ((size_t)written != local[0].iov_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
static int
|
static int
|
||||||
write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src)
|
write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const void* src)
|
||||||
{
|
{
|
||||||
@ -39,6 +72,9 @@ write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const
|
|||||||
} while (result < len);
|
} while (result < len);
|
||||||
return 0;
|
return 0;
|
||||||
#elif defined(__linux__) && HAVE_PROCESS_VM_READV
|
#elif defined(__linux__) && HAVE_PROCESS_VM_READV
|
||||||
|
if (handle->memfd != -1) {
|
||||||
|
return write_memory_fallback(handle, remote_address, len, src);
|
||||||
|
}
|
||||||
struct iovec local[1];
|
struct iovec local[1];
|
||||||
struct iovec remote[1];
|
struct iovec remote[1];
|
||||||
Py_ssize_t result = 0;
|
Py_ssize_t result = 0;
|
||||||
@ -52,6 +88,9 @@ write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const
|
|||||||
|
|
||||||
written = process_vm_writev(handle->pid, local, 1, remote, 1, 0);
|
written = process_vm_writev(handle->pid, local, 1, remote, 1, 0);
|
||||||
if (written < 0) {
|
if (written < 0) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
return write_memory_fallback(handle, remote_address, len, src);
|
||||||
|
}
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user