@@ -133,6 +133,31 @@ typedef struct {
133133 Py_ssize_t page_size ;
134134} proc_handle_t ;
135135
136+ // Forward declaration for use in validation function
137+ static int
138+ _Py_RemoteDebug_ReadRemoteMemory (proc_handle_t * handle , uintptr_t remote_address , size_t len , void * dst );
139+
140+ // Optional callback to validate a candidate section address found during
141+ // memory map searches. Returns 1 if the address is valid, 0 to skip it.
142+ // This allows callers to filter out duplicate/stale mappings (e.g. from
143+ // ctypes dlopen) whose sections were never initialized.
144+ typedef int (* section_validator_t )(proc_handle_t * handle , uintptr_t address );
145+
146+ // Validate that a candidate address starts with _Py_Debug_Cookie.
147+ static int
148+ _Py_RemoteDebug_ValidatePyRuntimeCookie (proc_handle_t * handle , uintptr_t address )
149+ {
150+ if (address == 0 ) {
151+ return 0 ;
152+ }
153+ char buf [sizeof (_Py_Debug_Cookie ) - 1 ];
154+ if (_Py_RemoteDebug_ReadRemoteMemory (handle , address , sizeof (buf ), buf ) != 0 ) {
155+ PyErr_Clear ();
156+ return 0 ;
157+ }
158+ return memcmp (buf , _Py_Debug_Cookie , sizeof (buf )) == 0 ;
159+ }
160+
136161static void
137162_Py_RemoteDebug_FreePageCache (proc_handle_t * handle )
138163{
@@ -490,7 +515,8 @@ pid_to_task(pid_t pid)
490515}
491516
492517static uintptr_t
493- search_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ) {
518+ search_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ,
519+ section_validator_t validator ) {
494520 mach_vm_address_t address = 0 ;
495521 mach_vm_size_t size = 0 ;
496522 mach_msg_type_number_t count = sizeof (vm_region_basic_info_data_64_t );
@@ -542,7 +568,9 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
542568 if (strncmp (filename , substr , strlen (substr )) == 0 ) {
543569 uintptr_t result = search_section_in_file (
544570 secname , map_filename , address , size , proc_ref );
545- if (result != 0 ) {
571+ if (result != 0
572+ && (validator == NULL || validator (handle , result )))
573+ {
546574 return result ;
547575 }
548576 }
@@ -659,7 +687,8 @@ search_elf_file_for_section(
659687}
660688
661689static uintptr_t
662- search_linux_map_for_section (proc_handle_t * handle , const char * secname , const char * substr )
690+ search_linux_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ,
691+ section_validator_t validator )
663692{
664693 char maps_file_path [64 ];
665694 sprintf (maps_file_path , "/proc/%d/maps" , handle -> pid );
@@ -734,9 +763,12 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
734763
735764 if (strstr (filename , substr )) {
736765 retval = search_elf_file_for_section (handle , secname , start , path );
737- if (retval ) {
766+ if (retval
767+ && (validator == NULL || validator (handle , retval )))
768+ {
738769 break ;
739770 }
771+ retval = 0 ;
740772 }
741773 }
742774
@@ -832,7 +864,8 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
832864
833865
834866static uintptr_t
835- search_windows_map_for_section (proc_handle_t * handle , const char * secname , const wchar_t * substr ) {
867+ search_windows_map_for_section (proc_handle_t * handle , const char * secname , const wchar_t * substr ,
868+ section_validator_t validator ) {
836869 HANDLE hProcSnap ;
837870 do {
838871 hProcSnap = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE , handle -> pid );
@@ -855,8 +888,11 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
855888 for (BOOL hasModule = Module32FirstW (hProcSnap , & moduleEntry ); hasModule ; hasModule = Module32NextW (hProcSnap , & moduleEntry )) {
856889 // Look for either python executable or DLL
857890 if (wcsstr (moduleEntry .szModule , substr )) {
858- runtime_addr = analyze_pe (moduleEntry .szExePath , moduleEntry .modBaseAddr , secname );
859- if (runtime_addr != NULL ) {
891+ void * candidate = analyze_pe (moduleEntry .szExePath , moduleEntry .modBaseAddr , secname );
892+ if (candidate != NULL
893+ && (validator == NULL || validator (handle , (uintptr_t )candidate )))
894+ {
895+ runtime_addr = candidate ;
860896 break ;
861897 }
862898 }
@@ -877,7 +913,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
877913
878914#ifdef MS_WINDOWS
879915 // On Windows, search for 'python' in executable or DLL
880- address = search_windows_map_for_section (handle , "PyRuntime" , L"python" );
916+ address = search_windows_map_for_section (handle , "PyRuntime" , L"python" ,
917+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
881918 if (address == 0 ) {
882919 // Error out: 'python' substring covers both executable and DLL
883920 PyObject * exc = PyErr_GetRaisedException ();
@@ -888,7 +925,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
888925 }
889926#elif defined(__linux__ )
890927 // On Linux, search for 'python' in executable or DLL
891- address = search_linux_map_for_section (handle , "PyRuntime" , "python" );
928+ address = search_linux_map_for_section (handle , "PyRuntime" , "python" ,
929+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
892930 if (address == 0 ) {
893931 // Error out: 'python' substring covers both executable and DLL
894932 PyObject * exc = PyErr_GetRaisedException ();
@@ -902,7 +940,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
902940 const char * candidates [] = {"libpython" , "python" , "Python" , NULL };
903941 for (const char * * candidate = candidates ; * candidate ; candidate ++ ) {
904942 PyErr_Clear ();
905- address = search_map_for_section (handle , "PyRuntime" , * candidate );
943+ address = search_map_for_section (handle , "PyRuntime" , * candidate ,
944+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
906945 if (address != 0 ) {
907946 break ;
908947 }
0 commit comments