Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,28 @@ The available slot types are:

.. versionadded:: 3.13

.. c:macro:: Py_mod_abi

A pointer to a :c:struct:`PyABIInfo` structure that describes the ABI that
the extension is using.

When the module is loaded, the :c:struct:`!PyABIInfo` in this slot is checked
using :c:func:`PyABIInfo_Check`.

A suitable :c:struct:`!PyABIInfo` variable can be defined using the
:c:macro:`PyABIInfo_VAR` macro, as in:

.. code-block:: c

PyABIInfo_VAR(abi_info);

static PyModuleDef_Slot mymodule_slots[] = {
{Py_mod_abi, &abi_info},
...
};

.. versionadded:: 3.15


.. _moduledef-dynamic:

Expand Down
162 changes: 159 additions & 3 deletions Doc/c-api/stable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

.. _stable:

***************
C API Stability
***************
***********************
C API and ABI Stability
***********************

Unless documented otherwise, Python's C API is covered by the Backwards
Compatibility Policy, :pep:`387`.
Expand Down Expand Up @@ -199,6 +199,162 @@ This is the case with Windows and macOS releases from ``python.org`` and many
third-party distributors.


ABI Checking
============

.. versionadded:: next

Python includes a rudimentary check for ABI compatibility.

This check is not comprehensive.
It only guards against common cases of incompatible modules being
installed for the wrong interpreter.
It also does not take :ref:`platform incompatibilities <stable-abi-platform>`
into account.
It can only be done after an extension is successfully loaded.

Despite these limitations, it is recommended that extension modules use this
mechanism, so that detectable incompatibilities raise exceptions rather than
crash.

Most modules can use this check via the :c:data:`Py_mod_abi`
slot and the :c:macro:`PyABIInfo_VAR` macro, for example like this:

.. code-block:: c

PyABIInfo_VAR(abi_info);

static PyModuleDef_Slot mymodule_slots[] = {
{Py_mod_abi, &abi_info},
...
};


The full API is described below for advanced use cases.

.. c:function:: int PyABIInfo_Check(PyABIInfo *info, const char *module_name)

Verify that the given *info* is compatible with the currently running
interpreter.

Return 0 on success. On failure, raise an exception and return -1.

If the ABI is incompatible, the raised exception will be :py:exc:`ImportError`.

The *module_name* argument can be ``NULL``, or point to a NUL-terminated
UTF-8-encoded string used for error messages.

Note that if *info* describes the ABI that the current code uses (as defined
by :c:macro:`PyABIInfo_VAR`, for example), using any other Python C API
may lead to crashes.
In particular, it is not safe to examine the raised exception.

.. versionadded:: next

.. c:macro:: PyABIInfo_VAR(NAME)

Define a static :c:struct:`PyABIInfo` variable with the given *NAME* that
describes the ABI that the current code will use.
This macro expands to:

.. code-block:: c

static PyABIInfo NAME = {
1, 0,
PyABIInfo_DEFAULT_FLAGS,
PY_VERSION_HEX,
PyABIInfo_DEFAULT_ABI_VERSION
}

.. versionadded:: next

.. c:type:: PyABIInfo

.. c:member:: uint8_t abiinfo_major_version

The major version of :c:struct:`PyABIInfo`. Can be set to:

* ``0`` to skip all checking, or
* ``1`` to specify this version of :c:struct:`!PyABIInfo`.

.. c:member:: uint8_t abiinfo_minor_version

The major version of :c:struct:`PyABIInfo`.
Must be set to ``0``; larger values are reserved for backwards-compatible
future versions of :c:struct:`!PyABIInfo`.

.. c:member:: uint16_t flags

.. c:namespace:: NULL

This field is usually set to the following macro:

.. c:macro:: PyABIInfo_DEFAULT_FLAGS

Default flags, based on current values of macros such as
:c:macro:`Py_LIMITED_API` and :c:macro:`Py_GIL_DISABLED`.

Alternately, the field can be set to to the following flags, combined
by bitwise OR.
Unused bits must be set to zero.

ABI variant -- one of:

.. c:macro:: PyABIInfo_STABLE

Specifies that the stable ABI is used.

.. c:macro:: PyABIInfo_INTERNAL

Specifies ABI specific to a particular build of CPython.
Internal use only.

Free-threading compatibility -- one of:

.. c:macro:: PyABIInfo_FREETHREADED

Specifies ABI compatible with free-threading builds of CPython.
(That is, ones compiled with :option:`--disable-gil`; with ``t``
in :py:data:`sys.abiflags`)

.. c:macro:: PyABIInfo_GIL

Specifies ABI compatible with non-free-threading builds of CPython
(ones compiled *without* :option:`--disable-gil`).

.. c:member:: uint32_t build_version

The version of the Python headers used to build the code, in the format
used by :c:macro:`PY_VERSION_HEX`.

This can be set to ``0`` to skip any checks related to this field.
This option is meant mainly for projects that do not use the CPython
headers directly, and do not emulate a specific version of them.

.. c:member:: uint32_t abi_version

The ABI version.

For the Stable ABI, this field should be the value of
:c:macro:`Py_LIMITED_API`
(except if :c:macro:`Py_LIMITED_API` is ``3``; use
:c:expr:`Py_PACK_VERSION(3, 2)` in that case).

Otherwise, it should be set to :c:macro:`PY_VERSION_HEX`.

It can also be set to ``0`` to skip any checks related to this field.

.. c:namespace:: NULL

.. c:macro:: PyABIInfo_DEFAULT_ABI_VERSION

The value that should be used for this field, based on current
values of macros such as :c:macro:`Py_LIMITED_API`,
:c:macro:`PY_VERSION_HEX` and :c:macro:`Py_GIL_DISABLED`.

.. versionadded:: next


.. _limited-api-list:

Contents of Limited API
Expand Down
3 changes: 3 additions & 0 deletions Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions Doc/library/annotationlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ and :func:`call_annotate_function`, as well as the
:func:`call_evaluate_function` function for working with
:term:`evaluate functions <evaluate function>`.

.. caution::

Most functionality in this module can execute arbitrary code; see
:ref:`the security section <annotationlib-security>` for more information.

.. seealso::

Expand Down Expand Up @@ -604,3 +608,23 @@ Below are a few examples of the behavior with unsupported expressions:
>>> def ifexp(x: 1 if y else 0): ...
>>> get_annotations(ifexp, format=Format.STRING)
{'x': '1'}

.. _annotationlib-security:

Security implications of introspecting annotations
--------------------------------------------------

Much of the functionality in this module involves executing code related to annotations,
which can then do arbitrary things. For example,
:func:`get_annotations` may call an arbitrary :term:`annotate function`, and
:meth:`ForwardRef.evaluate` may call :func:`eval` on an arbitrary string. Code contained
in an annotation might make arbitrary system calls, enter an infinite loop, or perform any
other operation. This is also true for any access of the :attr:`~object.__annotations__` attribute,
and for various functions in the :mod:`typing` module that work with annotations, such as
:func:`typing.get_type_hints`.

Any security issue arising from this also applies immediately after importing
code that may contain untrusted annotations: importing code can always cause arbitrary operations
to be performed. However, it is unsafe to accept strings or other input from an untrusted source and
pass them to any of the APIs for introspecting annotations, for example by editing an
``__annotations__`` dictionary or directly creating a :class:`ForwardRef` object.
5 changes: 5 additions & 0 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,11 @@ Classes and functions
This is an alias for :func:`annotationlib.get_annotations`; see the documentation
of that function for more information.

.. caution::

This function may execute arbitrary code contained in annotations.
See :ref:`annotationlib-security` for more information.

.. versionadded:: 3.10

.. versionchanged:: 3.14
Expand Down
10 changes: 10 additions & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3367,6 +3367,11 @@ Introspection helpers
See also :func:`annotationlib.get_annotations`, a lower-level function that
returns annotations more directly.

.. caution::

This function may execute arbitrary code contained in annotations.
See :ref:`annotationlib-security` for more information.

.. note::

If any forward references in the annotations of *obj* are not resolvable
Expand Down Expand Up @@ -3513,6 +3518,11 @@ Introspection helpers
See the documentation for :meth:`annotationlib.ForwardRef.evaluate` for
the meaning of the *owner*, *globals*, *locals*, *type_params*, and *format* parameters.

.. caution::

This function may execute arbitrary code contained in annotations.
See :ref:`annotationlib-security` for more information.

.. versionadded:: 3.14

.. data:: NoDefault
Expand Down
7 changes: 7 additions & 0 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1638,6 +1638,7 @@ and are also passed to registered trace functions.
single: f_locals (frame attribute)
single: f_lasti (frame attribute)
single: f_builtins (frame attribute)
single: f_generator (frame attribute)

Special read-only attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1675,6 +1676,12 @@ Special read-only attributes
(this is an index into the :term:`bytecode` string of the
:ref:`code object <code-objects>`)

* - .. attribute:: frame.f_generator
- The :term:`generator` or :term:`coroutine` object that owns this frame,
or ``None`` if the frame is a normal function.

.. versionadded:: 3.14

.. index::
single: f_trace (frame attribute)
single: f_trace_lines (frame attribute)
Expand Down
3 changes: 3 additions & 0 deletions Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ General Options

.. option:: --disable-gil

.. c:macro:: Py_GIL_DISABLED
:no-typesetting:

Enables support for running Python without the :term:`global interpreter
lock` (GIL): free threading build.

Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ tarfile
:func:`~tarfile.TarFile.errorlevel` is zero.
(Contributed by Matt Prodani and Petr Viktorin in :gh:`112887`
and :cve:`2025-4435`.)
* :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall`
now replace slashes by backslashes in symlink targets on Windows to prevent
creation of corrupted links.
(Contributed by Christoph Walcher in :gh:`57911`.)


types
Expand Down Expand Up @@ -666,6 +670,11 @@ New features
a string. See the documentation for caveats.
(Contributed by Petr Viktorin in :gh:`131510`)

* Add API for checking an extension module's ABI compatibility:
:c:data:`Py_mod_abi`, :c:func:`PyABIInfo_Check`, :c:macro:`PyABIInfo_VAR`
and :c:data:`Py_mod_abi`.
(Contributed by Petr Viktorin in :gh:`137210`)


Porting to Python 3.15
----------------------
Expand Down
12 changes: 12 additions & 0 deletions Include/cpython/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,15 @@ typedef struct _PyArg_Parser {

PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,
struct _PyArg_Parser *, ...);

#ifdef Py_BUILD_CORE
// Internal; defined here to avoid explicitly including pycore_modsupport.h
#define _Py_INTERNAL_ABI_SLOT \
{Py_mod_abi, (void*) &(PyABIInfo) { \
.abiinfo_major_version = 1, \
.abiinfo_minor_version = 0, \
.flags = PyABIInfo_INTERNAL, \
.build_version = PY_VERSION_HEX, \
.abi_version = PY_VERSION_HEX }} \
///////////////////////////////////////////////////////
#endif
6 changes: 6 additions & 0 deletions Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ extern int _PyUnicode_FormatAdvancedWriter(
Py_ssize_t start,
Py_ssize_t end);

/* PyUnicodeWriter_Format, with va_list instead of `...` */
extern int _PyUnicodeWriter_FormatV(
PyUnicodeWriter *writer,
const char *format,
va_list vargs);

/* --- UTF-7 Codecs ------------------------------------------------------- */

extern PyObject* _PyUnicode_EncodeUTF7(
Expand Down
Loading
Loading