Skip to content

Commit bc8e27a

Browse files
encukoubefeleme
authored andcommitted
Raise an error when importing modules compiled for an older Python
This is a downstream workaround "implementing" python#137212 - the mechanism for the check exists in Python 3.15+, where it needs to be added to the standard library modules. In Fedora, we need it also in previous Python versions, as we experience segmentation fault when importing stdlib modules after update while Python is running. _tkinter, _tracemalloc and readline are not calling PyModuleDef_Init, which is modified with this patch, hence they need a direct call to the check function.
1 parent c85f8db commit bc8e27a

6 files changed

Lines changed: 59 additions & 0 deletions

File tree

Include/moduleobject.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,49 @@ struct PyModuleDef {
116116
freefunc m_free;
117117
};
118118

119+
#if defined(_PyHack_check_version_on_modinit) && defined(Py_BUILD_CORE)
120+
/* The mechanism for the check has been implemented on Python 3.15+:
121+
* https://github.com/python/cpython/pull/137212.
122+
* In Fedora, we need this in older Pythons too:
123+
* if somebody attempts to import a module compiled for a different Python version,
124+
* instead of segmentation fault a meaningful error is raised.
125+
*/
126+
PyAPI_DATA(const unsigned long) Py_Version;
127+
128+
static inline int
129+
_PyHack_CheckInternalAPIVersion(const char *mod_name)
130+
{
131+
if (PY_VERSION_HEX != Py_Version) {
132+
PyErr_Format(
133+
PyExc_ImportError,
134+
"internal Python C API version mismatch: "
135+
"module %s compiled with %lu.%lu.%lu; "
136+
"runtime version is %lu.%lu.%lu",
137+
mod_name,
138+
(const unsigned long)((PY_VERSION_HEX >> 24) & 0xFF),
139+
(const unsigned long)((PY_VERSION_HEX >> 16) & 0xFF),
140+
(const unsigned long)((PY_VERSION_HEX >> 8) & 0xFF),
141+
(const unsigned long)((Py_Version >> 24) & 0xFF),
142+
(const unsigned long)((Py_Version >> 16) & 0xFF),
143+
(const unsigned long)((Py_Version >> 8) & 0xFF)
144+
);
145+
return -1;
146+
}
147+
return 0;
148+
}
149+
150+
static inline PyObject *
151+
PyModuleDef_Init_with_check(PyModuleDef *def)
152+
{
153+
if (_PyHack_CheckInternalAPIVersion(def->m_name) < 0) {
154+
return NULL;
155+
}
156+
return PyModuleDef_Init(def);
157+
}
158+
159+
#define PyModuleDef_Init PyModuleDef_Init_with_check
160+
#endif
161+
119162
#ifdef __cplusplus
120163
}
121164
#endif

Makefile.pre.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3415,3 +3415,6 @@ MODULE__MULTIBYTECODEC_DEPS=$(srcdir)/Modules/cjkcodecs/multibytecodec.h
34153415
# Local Variables:
34163416
# mode: makefile
34173417
# End:
3418+
3419+
# Fedora-specific, downstream only
3420+
PY_STDMODULE_CFLAGS += -D_PyHack_check_version_on_modinit=1

Modules/_tkinter.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,6 +3489,10 @@ static struct PyModuleDef _tkintermodule = {
34893489
PyMODINIT_FUNC
34903490
PyInit__tkinter(void)
34913491
{
3492+
if (_PyHack_CheckInternalAPIVersion("_tkinter") < 0) {
3493+
return NULL;
3494+
}
3495+
34923496
PyObject *m, *uexe, *cexe;
34933497

34943498
tcl_lock = PyThread_allocate_lock();

Modules/_tracemalloc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ static struct PyModuleDef module_def = {
215215
PyMODINIT_FUNC
216216
PyInit__tracemalloc(void)
217217
{
218+
if (_PyHack_CheckInternalAPIVersion("_tracemalloc") < 0) {
219+
return NULL;
220+
}
221+
218222
PyObject *mod = PyModule_Create(&module_def);
219223
if (mod == NULL) {
220224
return NULL;

Modules/readline.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,10 @@ static struct PyModuleDef readlinemodule = {
16041604
PyMODINIT_FUNC
16051605
PyInit_readline(void)
16061606
{
1607+
if (_PyHack_CheckInternalAPIVersion("readline") < 0) {
1608+
return NULL;
1609+
}
1610+
16071611
const char *backend = "readline";
16081612
PyObject *m;
16091613
readlinestate *mod_state;

Objects/moduleobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ _PyModule_IsExtension(PyObject *obj)
5050
}
5151

5252

53+
#undef PyModuleDef_Init
5354
PyObject*
5455
PyModuleDef_Init(PyModuleDef* def)
5556
{

0 commit comments

Comments
 (0)