@@ -150,6 +150,31 @@ typedef struct {
150150 Py_ssize_t page_size ;
151151} proc_handle_t ;
152152
153+ // Forward declaration for use in validation function
154+ static int
155+ _Py_RemoteDebug_ReadRemoteMemory (proc_handle_t * handle , uintptr_t remote_address , size_t len , void * dst );
156+
157+ // Optional callback to validate a candidate section address found during
158+ // memory map searches. Returns 1 if the address is valid, 0 to skip it.
159+ // This allows callers to filter out duplicate/stale mappings (e.g. from
160+ // ctypes dlopen) whose sections were never initialized.
161+ typedef int (* section_validator_t )(proc_handle_t * handle , uintptr_t address );
162+
163+ // Validate that a candidate address starts with _Py_Debug_Cookie.
164+ static int
165+ _Py_RemoteDebug_ValidatePyRuntimeCookie (proc_handle_t * handle , uintptr_t address )
166+ {
167+ if (address == 0 ) {
168+ return 0 ;
169+ }
170+ char buf [sizeof (_Py_Debug_Cookie ) - 1 ];
171+ if (_Py_RemoteDebug_ReadRemoteMemory (handle , address , sizeof (buf ), buf ) != 0 ) {
172+ PyErr_Clear ();
173+ return 0 ;
174+ }
175+ return memcmp (buf , _Py_Debug_Cookie , sizeof (buf )) == 0 ;
176+ }
177+
153178static void
154179_Py_RemoteDebug_FreePageCache (proc_handle_t * handle )
155180{
@@ -509,7 +534,8 @@ pid_to_task(pid_t pid)
509534}
510535
511536static uintptr_t
512- search_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ) {
537+ search_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ,
538+ section_validator_t validator ) {
513539 mach_vm_address_t address = 0 ;
514540 mach_vm_size_t size = 0 ;
515541 mach_msg_type_number_t count = sizeof (vm_region_basic_info_data_64_t );
@@ -561,7 +587,9 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
561587 if (strncmp (filename , substr , strlen (substr )) == 0 ) {
562588 uintptr_t result = search_section_in_file (
563589 secname , map_filename , address , size , proc_ref );
564- if (result != 0 ) {
590+ if (result != 0
591+ && (validator == NULL || validator (handle , result )))
592+ {
565593 return result ;
566594 }
567595 }
@@ -678,7 +706,8 @@ search_elf_file_for_section(
678706}
679707
680708static uintptr_t
681- search_linux_map_for_section (proc_handle_t * handle , const char * secname , const char * substr )
709+ search_linux_map_for_section (proc_handle_t * handle , const char * secname , const char * substr ,
710+ section_validator_t validator )
682711{
683712 char maps_file_path [64 ];
684713 sprintf (maps_file_path , "/proc/%d/maps" , handle -> pid );
@@ -753,9 +782,12 @@ search_linux_map_for_section(proc_handle_t *handle, const char* secname, const c
753782
754783 if (strstr (filename , substr )) {
755784 retval = search_elf_file_for_section (handle , secname , start , path );
756- if (retval ) {
785+ if (retval
786+ && (validator == NULL || validator (handle , retval )))
787+ {
757788 break ;
758789 }
790+ retval = 0 ;
759791 }
760792 }
761793
@@ -859,7 +891,8 @@ static void* analyze_pe(const wchar_t* mod_path, BYTE* remote_base, const char*
859891
860892
861893static uintptr_t
862- search_windows_map_for_section (proc_handle_t * handle , const char * secname , const wchar_t * substr ) {
894+ search_windows_map_for_section (proc_handle_t * handle , const char * secname , const wchar_t * substr ,
895+ section_validator_t validator ) {
863896 HANDLE hProcSnap ;
864897 do {
865898 hProcSnap = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE , handle -> pid );
@@ -882,8 +915,11 @@ search_windows_map_for_section(proc_handle_t* handle, const char* secname, const
882915 for (BOOL hasModule = Module32FirstW (hProcSnap , & moduleEntry ); hasModule ; hasModule = Module32NextW (hProcSnap , & moduleEntry )) {
883916 // Look for either python executable or DLL
884917 if (wcsstr (moduleEntry .szModule , substr )) {
885- runtime_addr = analyze_pe (moduleEntry .szExePath , moduleEntry .modBaseAddr , secname );
886- if (runtime_addr != NULL ) {
918+ void * candidate = analyze_pe (moduleEntry .szExePath , moduleEntry .modBaseAddr , secname );
919+ if (candidate != NULL
920+ && (validator == NULL || validator (handle , (uintptr_t )candidate )))
921+ {
922+ runtime_addr = candidate ;
887923 break ;
888924 }
889925 }
@@ -904,7 +940,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
904940
905941#ifdef MS_WINDOWS
906942 // On Windows, search for 'python' in executable or DLL
907- address = search_windows_map_for_section (handle , "PyRuntime" , L"python" );
943+ address = search_windows_map_for_section (handle , "PyRuntime" , L"python" ,
944+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
908945 if (address == 0 ) {
909946 // Error out: 'python' substring covers both executable and DLL
910947 PyObject * exc = PyErr_GetRaisedException ();
@@ -915,7 +952,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
915952 }
916953#elif defined(__linux__ ) && HAVE_PROCESS_VM_READV
917954 // On Linux, search for 'python' in executable or DLL
918- address = search_linux_map_for_section (handle , "PyRuntime" , "python" );
955+ address = search_linux_map_for_section (handle , "PyRuntime" , "python" ,
956+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
919957 if (address == 0 ) {
920958 // Error out: 'python' substring covers both executable and DLL
921959 PyObject * exc = PyErr_GetRaisedException ();
@@ -929,7 +967,8 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
929967 const char * candidates [] = {"libpython" , "python" , "Python" , NULL };
930968 for (const char * * candidate = candidates ; * candidate ; candidate ++ ) {
931969 PyErr_Clear ();
932- address = search_map_for_section (handle , "PyRuntime" , * candidate );
970+ address = search_map_for_section (handle , "PyRuntime" , * candidate ,
971+ _Py_RemoteDebug_ValidatePyRuntimeCookie );
933972 if (address != 0 ) {
934973 break ;
935974 }
0 commit comments