@@ -668,6 +668,72 @@ write_memory(proc_handle_t *handle, uintptr_t remote_address, size_t len, const
668668#endif
669669}
670670
671+ static int
672+ is_prerelease_version (uint64_t version )
673+ {
674+ return (version & 0xF0 ) != 0xF0 ;
675+ }
676+
677+ static int
678+ ensure_debug_offset_compatibility (const _Py_DebugOffsets * debug_offsets )
679+ {
680+ if (memcmp (debug_offsets -> cookie , _Py_Debug_Cookie , sizeof (debug_offsets -> cookie )) != 0 ) {
681+ // The remote is probably running a Python version predating debug offsets.
682+ PyErr_SetString (
683+ PyExc_RuntimeError ,
684+ "Can't determine the Python version of the remote process" );
685+ return -1 ;
686+ }
687+
688+ // Assume debug offsets could change from one pre-release version to another,
689+ // or one minor version to another, but are stable across patch versions.
690+ if (is_prerelease_version (Py_Version ) && Py_Version != debug_offsets -> version ) {
691+ PyErr_SetString (
692+ PyExc_RuntimeError ,
693+ "Can't send commands from a pre-release Python interpreter"
694+ " to a process running a different Python version" );
695+ return -1 ;
696+ }
697+
698+ if (is_prerelease_version (debug_offsets -> version ) && Py_Version != debug_offsets -> version ) {
699+ PyErr_SetString (
700+ PyExc_RuntimeError ,
701+ "Can't send commands to a pre-release Python interpreter"
702+ " from a process running a different Python version" );
703+ return -1 ;
704+ }
705+
706+ unsigned int remote_major = (debug_offsets -> version >> 24 ) & 0xFF ;
707+ unsigned int remote_minor = (debug_offsets -> version >> 16 ) & 0xFF ;
708+
709+ if (PY_MAJOR_VERSION != remote_major || PY_MINOR_VERSION != remote_minor ) {
710+ PyErr_Format (
711+ PyExc_RuntimeError ,
712+ "Can't send commands from a Python %d.%d process to a Python %d.%d process" ,
713+ PY_MAJOR_VERSION , PY_MINOR_VERSION , remote_major , remote_minor );
714+ return -1 ;
715+ }
716+
717+ // The debug offsets differ between free threaded and non-free threaded builds.
718+ if (_Py_Debug_Free_Threaded && !debug_offsets -> free_threaded ) {
719+ PyErr_SetString (
720+ PyExc_RuntimeError ,
721+ "Cannot send commands from a free-threaded Python process"
722+ " to a process running a non-free-threaded version" );
723+ return -1 ;
724+ }
725+
726+ if (!_Py_Debug_Free_Threaded && debug_offsets -> free_threaded ) {
727+ PyErr_SetString (
728+ PyExc_RuntimeError ,
729+ "Cannot send commands to a free-threaded Python process"
730+ " from a process running a non-free-threaded version" );
731+ return -1 ;
732+ }
733+
734+ return 0 ;
735+ }
736+
671737static int
672738read_offsets (
673739 proc_handle_t * handle ,
@@ -685,7 +751,10 @@ read_offsets(
685751 size_t size = sizeof (struct _Py_DebugOffsets );
686752 Py_ssize_t bytes = read_memory (
687753 handle , * runtime_start_address , size , debug_offsets );
688- if (bytes == -1 ) {
754+ if (bytes < 0 || (size_t )bytes != size ) {
755+ return -1 ;
756+ }
757+ if (ensure_debug_offset_compatibility (debug_offsets )) {
689758 return -1 ;
690759 }
691760 return 0 ;
0 commit comments