diff --git a/Include/moduleobject.h b/Include/moduleobject.h index 2a17c891dda811..64017c666c5ca1 100644 --- a/Include/moduleobject.h +++ b/Include/moduleobject.h @@ -116,6 +116,49 @@ struct PyModuleDef { freefunc m_free; }; +#if defined(_PyHack_check_version_on_modinit) && defined(Py_BUILD_CORE) +/* The mechanism for the check has been implemented on Python 3.15+: + * https://github.com/python/cpython/pull/137212. + * In Fedora, we need this in older Pythons too: + * if somebody attempts to import a module compiled for a different Python version, + * instead of segmentation fault a meaningful error is raised. + */ +PyAPI_DATA(const unsigned long) Py_Version; + +static inline int +_PyHack_CheckInternalAPIVersion(const char *mod_name) +{ + if (PY_VERSION_HEX != Py_Version) { + PyErr_Format( + PyExc_ImportError, + "internal Python C API version mismatch: " + "module %s compiled with %lu.%lu.%lu; " + "runtime version is %lu.%lu.%lu", + mod_name, + (const unsigned long)((PY_VERSION_HEX >> 24) & 0xFF), + (const unsigned long)((PY_VERSION_HEX >> 16) & 0xFF), + (const unsigned long)((PY_VERSION_HEX >> 8) & 0xFF), + (const unsigned long)((Py_Version >> 24) & 0xFF), + (const unsigned long)((Py_Version >> 16) & 0xFF), + (const unsigned long)((Py_Version >> 8) & 0xFF) + ); + return -1; + } + return 0; +} + +static inline PyObject * +PyModuleDef_Init_with_check(PyModuleDef *def) +{ + if (_PyHack_CheckInternalAPIVersion(def->m_name) < 0) { + return NULL; + } + return PyModuleDef_Init(def); +} + +#define PyModuleDef_Init PyModuleDef_Init_with_check +#endif + #ifdef __cplusplus } #endif diff --git a/Makefile.pre.in b/Makefile.pre.in index ecf77bdc41cc67..91bfd06a783b66 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3153,3 +3153,6 @@ MODULE__MULTIBYTECODEC_DEPS=$(srcdir)/Modules/cjkcodecs/multibytecodec.h # Local Variables: # mode: makefile # End: + +# Fedora-specific, downstream only +PY_STDMODULE_CFLAGS += -D_PyHack_check_version_on_modinit=1 diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 0200f59020fb06..bb647555c07e0d 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4763,6 +4763,12 @@ curses_destructor(PyObject *op) PyMODINIT_FUNC PyInit__curses(void) { + #ifdef _PyHack_check_version_on_modinit + if (_PyHack_CheckInternalAPIVersion("_curses") < 0) { + return NULL; + } + #endif + PyObject *m, *d, *v, *c_api_object; /* Initialize object type */ diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 14efe18db5a634..70597c281504be 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -3431,6 +3431,12 @@ static struct PyModuleDef _tkintermodule = { PyMODINIT_FUNC PyInit__tkinter(void) { + #ifdef _PyHack_check_version_on_modinit + if (_PyHack_CheckInternalAPIVersion("_tkinter") < 0) { + return NULL; + } + #endif + PyObject *m, *uexe, *cexe; tcl_lock = PyThread_allocate_lock(); diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 0b85187e5fce07..87f358ed07df52 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -215,6 +215,12 @@ static struct PyModuleDef module_def = { PyMODINIT_FUNC PyInit__tracemalloc(void) { + #ifdef _PyHack_check_version_on_modinit + if (_PyHack_CheckInternalAPIVersion("_tracemalloc") < 0) { + return NULL; + } + #endif + PyObject *m; m = PyModule_Create(&module_def); if (m == NULL) diff --git a/Modules/readline.c b/Modules/readline.c index f9362c312de922..66f1ec65a60ca3 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1540,6 +1540,12 @@ static struct PyModuleDef readlinemodule = { PyMODINIT_FUNC PyInit_readline(void) { + #ifdef _PyHack_check_version_on_modinit + if (_PyHack_CheckInternalAPIVersion("readline") < 0) { + return NULL; + } + #endif + const char *backend = "readline"; PyObject *m; readlinestate *mod_state; diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index d787f29004550b..31175ceb3fcd4b 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -43,6 +43,7 @@ _PyModule_IsExtension(PyObject *obj) } +#undef PyModuleDef_Init PyObject* PyModuleDef_Init(PyModuleDef* def) {