https://github.com/python/cpython/commit/04735deeb2dbdc6404eb38544541abf387d71069
commit: 04735deeb2dbdc6404eb38544541abf387d71069
branch: 3.14
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-07-28T23:46:43+01:00
summary:
[3.14] gh-149619: Harden _remote_debugging error paths (GH-150349) (#154844)
(cherry picked from commit a5be25d3bdc1b3cbc9638a3249c0e3db5a97ebc6)
files:
M Modules/_remote_debugging_module.c
M Python/remote_debug.h
M Python/remote_debugging.c
diff --git a/Modules/_remote_debugging_module.c
b/Modules/_remote_debugging_module.c
index 0b6b9d7f301a98..4fe94d43924231 100644
--- a/Modules/_remote_debugging_module.c
+++ b/Modules/_remote_debugging_module.c
@@ -28,6 +28,7 @@
// issues.
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -807,7 +808,7 @@ static int append_awaited_by(RemoteUnwinderObject
*unwinder, unsigned long tid,
#define set_exception_cause(unwinder, exc_type, message)
\
do {
\
assert(PyErr_Occurred() && "function returned -1 without setting
exception"); \
- if (unwinder->debug) {
\
+ if (unwinder->debug && !_Py_RemoteDebug_HasPermissionError()) {
\
_set_debug_exception_cause(exc_type, message);
\
}
\
} while (0)
@@ -1246,26 +1247,21 @@ read_py_long(
return -1;
}
- // If the long object has inline digits, use them directly
- digit *digits;
- if (size <= _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS) {
- // For small integers, digits are inline in the long_value.ob_digit
array
- digits = (digit *)PyMem_RawMalloc(size * sizeof(digit));
- if (!digits) {
- PyErr_NoMemory();
- set_exception_cause(unwinder, PyExc_MemoryError, "Failed to
allocate digits for small PyLong");
- return -1;
- }
- memcpy(digits, long_obj +
unwinder->debug_offsets.long_object.ob_digit, size * sizeof(digit));
- } else {
- // For larger integers, we need to read the digits separately
- digits = (digit *)PyMem_RawMalloc(size * sizeof(digit));
- if (!digits) {
- PyErr_NoMemory();
- set_exception_cause(unwinder, PyExc_MemoryError, "Failed to
allocate digits for large PyLong");
- return -1;
- }
+ // Calculate how many digits fit inline in our local buffer
+ Py_ssize_t ob_digit_offset = unwinder->debug_offsets.long_object.ob_digit;
+ Py_ssize_t inline_digits_space = SIZEOF_LONG_OBJ - ob_digit_offset;
+ Py_ssize_t max_inline_digits = inline_digits_space /
(Py_ssize_t)sizeof(digit);
+ digit *digits = (digit *)PyMem_RawMalloc(size * sizeof(digit));
+ if (!digits) {
+ PyErr_NoMemory();
+ set_exception_cause(unwinder, PyExc_MemoryError, "Failed to allocate
digits for PyLong");
+ return -1;
+ }
+
+ if (size <= max_inline_digits && size <= _PY_NSMALLNEGINTS +
_PY_NSMALLPOSINTS) {
+ memcpy(digits, long_obj + ob_digit_offset, size * sizeof(digit));
+ } else {
bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory(
&unwinder->handle,
address + (uintptr_t)unwinder->debug_offsets.long_object.ob_digit,
@@ -1278,19 +1274,34 @@ read_py_long(
}
}
- long long value = 0;
+ unsigned long limit = negative
+ ? (unsigned long)LONG_MAX + 1UL
+ : (unsigned long)LONG_MAX;
+ unsigned long value = 0;
- // In theory this can overflow, but because of llvm/llvm-project#16778
- // we can't use __builtin_mul_overflow because it fails to link with
- // __muloti4 on aarch64. In practice this is fine because all we're
- // testing here are task numbers that would fit in a single byte.
- for (Py_ssize_t i = 0; i < size; ++i) {
- long long factor = digits[i] * (1UL << (Py_ssize_t)(shift * i));
- value += factor;
+ for (Py_ssize_t i = size; i-- > 0;) {
+ if (digits[i] >= PyLong_BASE) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Invalid PyLong digit: %u (base %u)", digits[i], PyLong_BASE);
+ set_exception_cause(unwinder, PyExc_RuntimeError,
+ "Invalid PyLong digit (corrupted remote memory)");
+ goto error;
+ }
+ if (value > ((limit - (unsigned long)digits[i]) >> shift)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Remote PyLong value does not fit in C long");
+ set_exception_cause(unwinder, PyExc_OverflowError,
+ "Remote PyLong value is too large");
+ goto error;
+ }
+ value = (value << shift) | (unsigned long)digits[i];
}
PyMem_RawFree(digits);
if (negative) {
- value *= -1;
+ if (value == (unsigned long)LONG_MAX + 1UL) {
+ return LONG_MIN;
+ }
+ return -(long)value;
}
return (long)value;
error:
@@ -1312,32 +1323,35 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t*
handle)
// On Windows, search for asyncio debug in executable or DLL
address = search_windows_map_for_section(handle, "AsyncioD", L"_asyncio",
NULL);
if (address == 0) {
- // Error out: 'python' substring covers both executable and DLL
- PyObject *exc = PyErr_GetRaisedException();
- PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug
section in the process.");
- _PyErr_ChainExceptions1(exc);
+ if (!_Py_RemoteDebug_HasPermissionError()) {
+ PyObject *exc = PyErr_GetRaisedException();
+ PyErr_SetString(PyExc_RuntimeError, "Failed to find the
AsyncioDebug section in the process.");
+ _PyErr_ChainExceptions1(exc);
+ }
}
#elif defined(__linux__)
// On Linux, search for asyncio debug in executable or DLL
address = search_linux_map_for_section(handle, "AsyncioDebug",
"_asyncio.cpython", NULL);
if (address == 0) {
- // Error out: 'python' substring covers both executable and DLL
- PyObject *exc = PyErr_GetRaisedException();
- PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug
section in the process.");
- _PyErr_ChainExceptions1(exc);
+ if (!_Py_RemoteDebug_HasPermissionError()) {
+ PyObject *exc = PyErr_GetRaisedException();
+ PyErr_SetString(PyExc_RuntimeError, "Failed to find the
AsyncioDebug section in the process.");
+ _PyErr_ChainExceptions1(exc);
+ }
}
#elif defined(__APPLE__) && TARGET_OS_OSX
// On macOS, try libpython first, then fall back to python
address = search_map_for_section(handle, "AsyncioDebug",
"_asyncio.cpython", NULL);
- if (address == 0) {
+ if (address == 0 && !_Py_RemoteDebug_HasPermissionError()) {
PyErr_Clear();
address = search_map_for_section(handle, "AsyncioDebug",
"_asyncio.cpython", NULL);
}
if (address == 0) {
- // Error out: 'python' substring covers both executable and DLL
- PyObject *exc = PyErr_GetRaisedException();
- PyErr_SetString(PyExc_RuntimeError, "Failed to find the AsyncioDebug
section in the process.");
- _PyErr_ChainExceptions1(exc);
+ if (!_Py_RemoteDebug_HasPermissionError()) {
+ PyObject *exc = PyErr_GetRaisedException();
+ PyErr_SetString(PyExc_RuntimeError, "Failed to find the
AsyncioDebug section in the process.");
+ _PyErr_ChainExceptions1(exc);
+ }
}
#else
Py_UNREACHABLE();
@@ -1418,7 +1432,7 @@ parse_task_name(
if ((GET_MEMBER(unsigned long, type_obj,
unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_LONG_SUBCLASS)) {
long res = read_py_long(unwinder, task_name_addr);
- if (res == -1) {
+ if (res == -1 && PyErr_Occurred()) {
set_exception_cause(unwinder, PyExc_RuntimeError, "Task name
PyLong parsing failed");
return NULL;
}
@@ -3124,6 +3138,9 @@
_remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self,
return -1;
}
if (async_debug_result < 0) {
+ if (_Py_RemoteDebug_HasPermissionError()) {
+ return -1;
+ }
PyErr_Clear();
memset(&self->async_debug_offsets, 0,
sizeof(self->async_debug_offsets));
self->async_debug_offsets_available = 0;
diff --git a/Python/remote_debug.h b/Python/remote_debug.h
index bacba2ed5f000f..ed0c4e2c85c37d 100644
--- a/Python/remote_debug.h
+++ b/Python/remote_debug.h
@@ -92,7 +92,7 @@ _Py_RemoteDebug_HasPermissionError(void)
#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__); \
@@ -102,6 +102,20 @@ _Py_RemoteDebug_HasPermissionError(void)
} \
} 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;
@@ -192,6 +206,20 @@ static mach_port_t pid_to_task(pid_t pid);
// Initialize the process handle
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();
+ 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);
@@ -204,18 +232,12 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle,
pid_t pid) {
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION |
PROCESS_QUERY_INFORMATION,
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();
- for (int i = 0; i < MAX_PAGES; i++) {
- handle->pages[i].data = NULL;
- handle->pages[i].valid = 0;
- }
return 0;
}
@@ -380,17 +402,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;
}
@@ -443,26 +467,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;
}
@@ -491,15 +518,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;
@@ -544,14 +577,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
@@ -575,18 +609,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;
}
@@ -609,25 +657,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;
}
@@ -684,12 +736,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;
@@ -804,9 +867,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;
}
@@ -908,11 +972,21 @@ search_linux_map_for_section(proc_handle_t *handle, const
char* secname, const c
}
}
+ 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;
}
@@ -924,12 +998,22 @@ search_linux_map_for_section(proc_handle_t *handle, const
char* secname, const c
#ifdef MS_WINDOWS
+static int
+is_process_alive(HANDLE hProcess)
+{
+ DWORD exitCode;
+ if (GetExitCodeProcess(hProcess, &exitCode)) {
+ return exitCode == STILL_ACTIVE;
+ }
+ return 0;
+}
+
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;
@@ -937,9 +1021,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);
@@ -948,9 +1032,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);
@@ -1008,9 +1092,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);
@@ -1021,17 +1105,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);
@@ -1052,12 +1165,14 @@ _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__)
// On Linux, search for 'python' in executable or DLL
@@ -1080,17 +1195,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
Py_UNREACHABLE();
@@ -1109,9 +1226,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;
@@ -1122,6 +1239,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;
@@ -1139,14 +1259,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;
@@ -1158,19 +1287,36 @@ 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)) {
- PyErr_SetFromWindowsErr(0);
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)) {
+ _set_errno(ESRCH);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ 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;
@@ -1191,28 +1337,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 (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 (kr) {
@@ -1236,6 +1394,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();
diff --git a/Python/remote_debugging.c b/Python/remote_debugging.c
index 71ffb17ed68b1d..82e58f03966de3 100644
--- a/Python/remote_debugging.c
+++ b/Python/remote_debugging.c
@@ -30,6 +30,9 @@ read_memory(proc_handle_t *handle, uintptr_t remote_address,
size_t len, void* d
static int
write_memory_fallback(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;
@@ -47,10 +50,19 @@ write_memory_fallback(proc_handle_t *handle, uintptr_t
remote_address, size_t le
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;
@@ -60,12 +72,27 @@ write_memory_fallback(proc_handle_t *handle, uintptr_t
remote_address, size_t le
static int
write_memory(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;
@@ -88,13 +115,26 @@ 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) {
+ int err = errno;
+ if (err == ENOSYS) {
return write_memory_fallback(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(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;
@@ -398,4 +438,3 @@ _PySysRemoteDebug_SendExec(int pid, int tid, const char
*debugger_script_path)
return rc;
#endif
}
-
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]