2828// issues.
2929#include <errno.h>
3030#include <fcntl.h>
31+ #include <limits.h>
3132#include <stddef.h>
3233#include <stdint.h>
3334#include <stdio.h>
@@ -807,7 +808,7 @@ static int append_awaited_by(RemoteUnwinderObject *unwinder, unsigned long tid,
807808#define set_exception_cause (unwinder , exc_type , message ) \
808809 do { \
809810 assert(PyErr_Occurred() && "function returned -1 without setting exception"); \
810- if (unwinder->debug) { \
811+ if (unwinder->debug && !_Py_RemoteDebug_HasPermissionError()) { \
811812 _set_debug_exception_cause(exc_type, message); \
812813 } \
813814 } while (0)
@@ -1246,26 +1247,21 @@ read_py_long(
12461247 return -1 ;
12471248 }
12481249
1249- // If the long object has inline digits, use them directly
1250- digit * digits ;
1251- if (size <= _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS ) {
1252- // For small integers, digits are inline in the long_value.ob_digit array
1253- digits = (digit * )PyMem_RawMalloc (size * sizeof (digit ));
1254- if (!digits ) {
1255- PyErr_NoMemory ();
1256- set_exception_cause (unwinder , PyExc_MemoryError , "Failed to allocate digits for small PyLong" );
1257- return -1 ;
1258- }
1259- memcpy (digits , long_obj + unwinder -> debug_offsets .long_object .ob_digit , size * sizeof (digit ));
1260- } else {
1261- // For larger integers, we need to read the digits separately
1262- digits = (digit * )PyMem_RawMalloc (size * sizeof (digit ));
1263- if (!digits ) {
1264- PyErr_NoMemory ();
1265- set_exception_cause (unwinder , PyExc_MemoryError , "Failed to allocate digits for large PyLong" );
1266- return -1 ;
1267- }
1250+ // Calculate how many digits fit inline in our local buffer
1251+ Py_ssize_t ob_digit_offset = unwinder -> debug_offsets .long_object .ob_digit ;
1252+ Py_ssize_t inline_digits_space = SIZEOF_LONG_OBJ - ob_digit_offset ;
1253+ Py_ssize_t max_inline_digits = inline_digits_space / (Py_ssize_t )sizeof (digit );
12681254
1255+ digit * digits = (digit * )PyMem_RawMalloc (size * sizeof (digit ));
1256+ if (!digits ) {
1257+ PyErr_NoMemory ();
1258+ set_exception_cause (unwinder , PyExc_MemoryError , "Failed to allocate digits for PyLong" );
1259+ return -1 ;
1260+ }
1261+
1262+ if (size <= max_inline_digits && size <= _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS ) {
1263+ memcpy (digits , long_obj + ob_digit_offset , size * sizeof (digit ));
1264+ } else {
12691265 bytes_read = _Py_RemoteDebug_PagedReadRemoteMemory (
12701266 & unwinder -> handle ,
12711267 address + (uintptr_t )unwinder -> debug_offsets .long_object .ob_digit ,
@@ -1278,19 +1274,34 @@ read_py_long(
12781274 }
12791275 }
12801276
1281- long long value = 0 ;
1277+ unsigned long limit = negative
1278+ ? (unsigned long )LONG_MAX + 1UL
1279+ : (unsigned long )LONG_MAX ;
1280+ unsigned long value = 0 ;
12821281
1283- // In theory this can overflow, but because of llvm/llvm-project#16778
1284- // we can't use __builtin_mul_overflow because it fails to link with
1285- // __muloti4 on aarch64. In practice this is fine because all we're
1286- // testing here are task numbers that would fit in a single byte.
1287- for (Py_ssize_t i = 0 ; i < size ; ++ i ) {
1288- long long factor = digits [i ] * (1UL << (Py_ssize_t )(shift * i ));
1289- value += factor ;
1282+ for (Py_ssize_t i = size ; i -- > 0 ;) {
1283+ if (digits [i ] >= PyLong_BASE ) {
1284+ PyErr_Format (PyExc_RuntimeError ,
1285+ "Invalid PyLong digit: %u (base %u)" , digits [i ], PyLong_BASE );
1286+ set_exception_cause (unwinder , PyExc_RuntimeError ,
1287+ "Invalid PyLong digit (corrupted remote memory)" );
1288+ goto error ;
1289+ }
1290+ if (value > ((limit - (unsigned long )digits [i ]) >> shift )) {
1291+ PyErr_SetString (PyExc_OverflowError ,
1292+ "Remote PyLong value does not fit in C long" );
1293+ set_exception_cause (unwinder , PyExc_OverflowError ,
1294+ "Remote PyLong value is too large" );
1295+ goto error ;
1296+ }
1297+ value = (value << shift ) | (unsigned long )digits [i ];
12901298 }
12911299 PyMem_RawFree (digits );
12921300 if (negative ) {
1293- value *= -1 ;
1301+ if (value == (unsigned long )LONG_MAX + 1UL ) {
1302+ return LONG_MIN ;
1303+ }
1304+ return - (long )value ;
12941305 }
12951306 return (long )value ;
12961307error :
@@ -1312,32 +1323,35 @@ _Py_RemoteDebug_GetAsyncioDebugAddress(proc_handle_t* handle)
13121323 // On Windows, search for asyncio debug in executable or DLL
13131324 address = search_windows_map_for_section (handle , "AsyncioD" , L"_asyncio" , NULL );
13141325 if (address == 0 ) {
1315- // Error out: 'python' substring covers both executable and DLL
1316- PyObject * exc = PyErr_GetRaisedException ();
1317- PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1318- _PyErr_ChainExceptions1 (exc );
1326+ if (!_Py_RemoteDebug_HasPermissionError ()) {
1327+ PyObject * exc = PyErr_GetRaisedException ();
1328+ PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1329+ _PyErr_ChainExceptions1 (exc );
1330+ }
13191331 }
13201332#elif defined(__linux__ )
13211333 // On Linux, search for asyncio debug in executable or DLL
13221334 address = search_linux_map_for_section (handle , "AsyncioDebug" , "_asyncio.cpython" , NULL );
13231335 if (address == 0 ) {
1324- // Error out: 'python' substring covers both executable and DLL
1325- PyObject * exc = PyErr_GetRaisedException ();
1326- PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1327- _PyErr_ChainExceptions1 (exc );
1336+ if (!_Py_RemoteDebug_HasPermissionError ()) {
1337+ PyObject * exc = PyErr_GetRaisedException ();
1338+ PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1339+ _PyErr_ChainExceptions1 (exc );
1340+ }
13281341 }
13291342#elif defined(__APPLE__ ) && TARGET_OS_OSX
13301343 // On macOS, try libpython first, then fall back to python
13311344 address = search_map_for_section (handle , "AsyncioDebug" , "_asyncio.cpython" , NULL );
1332- if (address == 0 ) {
1345+ if (address == 0 && ! _Py_RemoteDebug_HasPermissionError () ) {
13331346 PyErr_Clear ();
13341347 address = search_map_for_section (handle , "AsyncioDebug" , "_asyncio.cpython" , NULL );
13351348 }
13361349 if (address == 0 ) {
1337- // Error out: 'python' substring covers both executable and DLL
1338- PyObject * exc = PyErr_GetRaisedException ();
1339- PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1340- _PyErr_ChainExceptions1 (exc );
1350+ if (!_Py_RemoteDebug_HasPermissionError ()) {
1351+ PyObject * exc = PyErr_GetRaisedException ();
1352+ PyErr_SetString (PyExc_RuntimeError , "Failed to find the AsyncioDebug section in the process." );
1353+ _PyErr_ChainExceptions1 (exc );
1354+ }
13411355 }
13421356#else
13431357 Py_UNREACHABLE ();
@@ -1418,7 +1432,7 @@ parse_task_name(
14181432
14191433 if ((GET_MEMBER (unsigned long , type_obj , unwinder -> debug_offsets .type_object .tp_flags ) & Py_TPFLAGS_LONG_SUBCLASS )) {
14201434 long res = read_py_long (unwinder , task_name_addr );
1421- if (res == -1 ) {
1435+ if (res == -1 && PyErr_Occurred () ) {
14221436 set_exception_cause (unwinder , PyExc_RuntimeError , "Task name PyLong parsing failed" );
14231437 return NULL ;
14241438 }
@@ -3124,6 +3138,9 @@ _remote_debugging_RemoteUnwinder___init___impl(RemoteUnwinderObject *self,
31243138 return -1 ;
31253139 }
31263140 if (async_debug_result < 0 ) {
3141+ if (_Py_RemoteDebug_HasPermissionError ()) {
3142+ return -1 ;
3143+ }
31273144 PyErr_Clear ();
31283145 memset (& self -> async_debug_offsets , 0 , sizeof (self -> async_debug_offsets ));
31293146 self -> async_debug_offsets_available = 0 ;
0 commit comments