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 | ||||
| Jeroen Van Goey | ||||
| Christoph Gohlke | ||||
| Daniel Golding | ||||
| Tim Golden | ||||
| Yonatan Goldschmidt | ||||
| 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; | ||||
| #elif defined(MS_WINDOWS) | ||||
|     HANDLE hProcess; | ||||
| #elif defined(__linux__) | ||||
|     int memfd; | ||||
| #endif | ||||
|     page_cache_entry_t pages[MAX_PAGES]; | ||||
|     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"); | ||||
|         return -1; | ||||
|     } | ||||
| #elif defined(__linux__) | ||||
|     handle->memfd = -1; | ||||
| #endif | ||||
|     handle->page_size = get_page_size(); | ||||
|     for (int i = 0; i < MAX_PAGES; i++) { | ||||
|  | @ -179,6 +183,11 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) { | |||
|         CloseHandle(handle->hProcess); | ||||
|         handle->hProcess = NULL; | ||||
|     } | ||||
| #elif defined(__linux__) | ||||
|     if (handle->memfd != -1) { | ||||
|         close(handle->memfd); | ||||
|         handle->memfd = -1; | ||||
|     } | ||||
| #endif | ||||
|     handle->pid = 0; | ||||
|     _Py_RemoteDebug_FreePageCache(handle); | ||||
|  | @ -907,6 +916,61 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle) | |||
|     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
 | ||||
| static int | ||||
| _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); | ||||
|     return 0; | ||||
| #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 remote[1]; | ||||
|     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); | ||||
|         if (read_bytes < 0) { | ||||
|             if (errno == ENOSYS) { | ||||
|                 return read_remote_memory_fallback(handle, remote_address, len, dst); | ||||
|             } | ||||
|             PyErr_SetFromErrno(PyExc_OSError); | ||||
|             _set_debug_exception_cause(PyExc_OSError, | ||||
|                 "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); | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| 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); | ||||
|     return 0; | ||||
| #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 remote[1]; | ||||
|     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); | ||||
|         if (written < 0) { | ||||
|             if (errno == ENOSYS) { | ||||
|                 return write_memory_fallback(handle, remote_address, len, src); | ||||
|             } | ||||
|             PyErr_SetFromErrno(PyExc_OSError); | ||||
|             return -1; | ||||
|         } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)