mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	[3.14] gh-134876: Add fallback for when process_vm_readv fails with ENOSYS (GH-134878) (#135240)
gh-134876: Add fallback for when process_vm_readv fails with ENOSYS (GH-134878)
(cherry picked from commit ac9c3431cc)
Co-authored-by: Daniel Golding <goldingd89@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									3faf00e003
								
							
						
					
					
						commit
						ef539654e6
					
				
					 4 changed files with 112 additions and 0 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)