pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/32b2c5de16183c74e43da01c5b33343e43c469a4

9af82350aeda.css" /> gh-137956: Guard against non-free-threaded extensions in free-threade… · python/cpython@32b2c5d · GitHub
Skip to content

Commit 32b2c5d

Browse files
authored
gh-137956: Guard against non-free-threaded extensions in free-threaded builds (GH-137957)
1 parent aa9ceb1 commit 32b2c5d

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

Include/object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ whose size is determined when the object is allocated.
7171
*
7272
* Statically allocated objects might be shared between
7373
* interpreters, so must be marked as immortal.
74+
*
75+
* Before changing this, see the check in PyModuleDef_Init().
7476
*/
7577
#if defined(Py_GIL_DISABLED)
7678
#define PyObject_HEAD_INIT(type) \
@@ -634,6 +636,7 @@ given type object has a specified feature.
634636

635637
// Flag values for ob_flags (16 bits available, if SIZEOF_VOID_P > 4).
636638
#define _Py_IMMORTAL_FLAGS (1 << 0)
639+
#define _Py_LEGACY_ABI_CHECK_FLAG (1 << 1) /* see PyModuleDef_Init() */
637640
#define _Py_STATICALLY_ALLOCATED_FLAG (1 << 2)
638641
#if defined(Py_GIL_DISABLED) && defined(Py_DEBUG)
639642
#define _Py_TYPE_REVEALED_FLAG (1 << 3)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Display and raise an exception if an extension compiled for
2+
non-free-threaded Python is loaded in a free-threaded interpreter.

Objects/moduleobject.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,59 @@ _PyModule_IsExtension(PyObject *obj)
5252
PyObject*
5353
PyModuleDef_Init(PyModuleDef* def)
5454
{
55+
#ifdef Py_GIL_DISABLED
56+
// Check that this def does not come from a non-free-threading ABI.
57+
//
58+
// This is meant as a "sanity check"; users should never rely on it.
59+
// In particular, if we run out of ob_flags bits, or otherwise need to
60+
// change some of the internals, this check can go away. Still, it
61+
// would be nice to keep it for the free-threading transition.
62+
//
63+
// A PyModuleDef must be initialized with PyModuleDef_HEAD_INIT,
64+
// which (via PyObject_HEAD_INIT) sets _Py_STATICALLY_ALLOCATED_FLAG
65+
// and not _Py_LEGACY_ABI_CHECK_FLAG. For PyModuleDef, these flags never
66+
// change.
67+
// This means that the lower nibble of a valid PyModuleDef's ob_flags is
68+
// always `_10_` (in binary; `_` is don't care).
69+
//
70+
// So, a check for these bits won't reject valid PyModuleDef.
71+
// Rejecting incompatible extensions is slightly less important; here's
72+
// how that works:
73+
//
74+
// In the pre-free-threading stable ABI, PyModuleDef_HEAD_INIT is big
75+
// enough to overlap with free-threading ABI's ob_flags, is all zeros
76+
// except for the refcount field.
77+
// The refcount field can be:
78+
// - 1 (3.11 and below)
79+
// - UINT_MAX >> 2 (32-bit 3.12 & 3.13)
80+
// - UINT_MAX (64-bit 3.12 & 3.13)
81+
// - 7L << 28 (3.14)
82+
//
83+
// This means that the lower nibble of *any byte* in PyModuleDef_HEAD_INIT
84+
// is not `_10_` -- it can be:
85+
// - 0b0000
86+
// - 0b0001
87+
// - 0b0011 (from UINT_MAX >> 2)
88+
// - 0b0111 (from 7L << 28)
89+
// - 0b1111 (e.g. from UINT_MAX)
90+
// (The values may change at runtime as the PyModuleDef is used, but
91+
// PyModuleDef_Init is required before using the def as a Python object,
92+
// so we check at least once with the initial values.
93+
uint16_t flags = ((PyObject*)def)->ob_flags;
94+
uint16_t bits = _Py_STATICALLY_ALLOCATED_FLAG | _Py_LEGACY_ABI_CHECK_FLAG;
95+
if ((flags & bits) != _Py_STATICALLY_ALLOCATED_FLAG) {
96+
const char *message = "invalid PyModuleDef, extension possibly "
97+
"compiled for non-free-threaded Python";
98+
// Write the error as unraisable: if the extension tries calling
99+
// any API, it's likely to segfault and lose the exception.
100+
PyErr_SetString(PyExc_SystemError, message);
101+
PyErr_WriteUnraisable(NULL);
102+
// But also raise the exception normally -- this is technically
103+
// a recoverable state.
104+
PyErr_SetString(PyExc_SystemError, message);
105+
return NULL;
106+
}
107+
#endif
55108
assert(PyModuleDef_Type.tp_flags & Py_TPFLAGS_READY);
56109
if (def->m_base.m_index == 0) {
57110
Py_SET_REFCNT(def, 1);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy