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
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,16 @@ jobs:
strategy:
fail-fast: false
matrix:
# macos-26 is Apple Silicon, macos-26-intel is Intel.
# macos-26-intel only runs tests against the GIL-enabled CPython.
# macos-26 is Apple Silicon, macos-15-intel is Intel.
# macos-15-intel only runs tests against the GIL-enabled CPython.
os:
- macos-26
- macos-26-intel
- macos-15-intel
free-threading:
- false
- true
exclude:
- os: macos-26-intel
- os: macos-15-intel
free-threading: true
uses: ./.github/workflows/reusable-macos.yml
with:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/reusable-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ jobs:
--prefix=/opt/python-dev \
--with-openssl="$(brew --prefix openssl@3.5)"
- name: Build CPython
if : ${{ inputs.free-threading || inputs.os != 'macos-26-intel' }}
if : ${{ inputs.free-threading || inputs.os != 'macos-15-intel' }}
run: gmake -j8
- name: Build CPython for compiler warning check
if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }}
if : ${{ !inputs.free-threading && inputs.os == 'macos-15-intel' }}
run: set -o pipefail; gmake -j8 --output-sync 2>&1 | tee compiler_output_macos.txt
- name: Display build info
run: make pythoninfo
- name: Check compiler warnings
if : ${{ !inputs.free-threading && inputs.os == 'macos-26-intel' }}
if : ${{ !inputs.free-threading && inputs.os == 'macos-15-intel' }}
run: >-
python3 Tools/build/check_warnings.py
--compiler-output-file-path=compiler_output_macos.txt
Expand Down
11 changes: 11 additions & 0 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,17 @@
stable_abi_file = 'data/stable_abi.dat'
threadsafety_file = 'data/threadsafety.dat'

# Options for notfound.extension
# -------------------------------

if not os.getenv("READTHEDOCS"):
if language_code:
notfound_urls_prefix = (
f'/{language_code.replace("_", "-").lower()}/{version}/'
)
else:
notfound_urls_prefix = f'/{version}/'

# Options for sphinxext-opengraph
# -------------------------------

Expand Down
55 changes: 55 additions & 0 deletions Doc/howto/remote_debugging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,58 @@ To inject and execute a Python script in a remote process:
6. Set ``_PY_EVAL_PLEASE_STOP_BIT`` in the ``eval_breaker`` field.
7. Resume the process (if suspended). The script will execute at the next safe
evaluation point.

.. _remote-debugging-threat-model:

Security and threat model
=========================

The remote debugging protocol relies on the same operating system primitives
used by native debuggers such as GDB and LLDB. Attaching to a process
requires the **same privileges** that those debuggers require, for example
``ptrace`` / Yama LSM on Linux, ``task_for_pid`` on macOS, and
``SeDebugPrivilege`` on Windows. Python does not introduce any new privilege
escalation path; if an attacker already possesses the permissions needed to
attach to a process, they could equally use GDB to read memory or inject
code.

The following principles define what is, and is not, considered a security
vulnerability in this feature:

Attaching requires OS-level privileges
On every supported platform the operating system gates cross-process
memory access behind privilege checks (``CAP_SYS_PTRACE``, root, or
administrator rights). A report that demonstrates an issue only after
these privileges have already been obtained is **not** a vulnerability in
CPython, since the OS security boundary was already crossed.

Crashes or memory errors when reading a compromised process are not vulnerabilities
A tool that reads internal interpreter state from a target process must
trust that memory to be well-formed. If the target process has been
corrupted or is controlled by an attacker, the debugger or profiler may
crash, produce garbage output, or behave unpredictably. This is the same
risk accepted by every ``ptrace``-based debugger. Bugs in this category
(buffer overflows, segmentation faults, or undefined behaviour triggered
by reading corrupted state) are **not** treated as security issues, though
fixes that improve robustness are welcome.

Vulnerabilities in the target process are not in scope
If the Python process being debugged has already been compromised, the
attacker already controls execution in that process. Demonstrating further
impact from that starting point does not constitute a vulnerability in the
remote debugging protocol.

When to use ``PYTHON_DISABLE_REMOTE_DEBUG``
-------------------------------------------

The environment variable :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` (and the
equivalent :option:`-X disable_remote_debug` flag) allows operators to disable
the in-process side of the protocol as a **defence-in-depth** measure. This
may be useful in hardened or sandboxed deployment environments where no
debugging or profiling of the process is expected and reducing attack surface
is a priority, even though the OS-level privilege checks already prevent
unprivileged access.

Setting this variable does **not** affect other OS-level debugging interfaces
(``ptrace``, ``/proc``, ``task_for_pid``, etc.), which remain available
according to their own permission models.
10 changes: 9 additions & 1 deletion Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,15 @@ User defined functions can be used as well:

The :func:`bool` function is not recommended as a type converter. All it does
is convert empty strings to ``False`` and non-empty strings to ``True``.
This is usually not what is desired.
This is usually not what is desired::

>>> parser = argparse.ArgumentParser()
>>> _ = parser.add_argument('--verbose', type=bool)
>>> parser.parse_args(['--verbose', 'False'])
Namespace(verbose=True)

See :class:`BooleanOptionalAction` or ``action='store_true'`` for common
alternatives.

In general, the ``type`` keyword is a convenience that should only be used for
simple conversions that can only raise one of the three supported exceptions.
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pystats.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ typedef struct _optimization_stats {
uint64_t unknown_callee;
uint64_t trace_immediately_deopts;
uint64_t executors_invalidated;
uint64_t fitness_terminated_traces;
UOpStats opcode[PYSTATS_MAX_UOP_ID + 1];
uint64_t unsupported_opcode[256];
uint64_t trace_length_hist[_Py_UOP_HIST_SIZE];
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ typedef struct _PyOptimizationConfig {
uint16_t side_exit_initial_value;
uint16_t side_exit_initial_backoff;

// Trace fitness thresholds
uint16_t fitness_initial;
uint16_t fitness_initial_side;

// Optimization flags
bool specialization_enabled;
bool uops_optimize_enabled;
Expand Down
20 changes: 19 additions & 1 deletion Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ extern "C" {
#include "pycore_optimizer_types.h"
#include <stdbool.h>

/* Default fitness configuration values for trace quality control.
* FITNESS_INITIAL and FITNESS_INITIAL_SIDE can be overridden via
* PYTHON_JIT_FITNESS_INITIAL and PYTHON_JIT_FITNESS_INITIAL_SIDE */
#define FITNESS_PER_INSTRUCTION 2
#define FITNESS_BRANCH_BASE 5
#define FITNESS_INITIAL (FITNESS_PER_INSTRUCTION * 1000)
#define FITNESS_INITIAL_SIDE (FITNESS_INITIAL / 2)
#define FITNESS_BACKWARD_EDGE (FITNESS_INITIAL / 10)

/* Exit quality constants for fitness-based trace termination.
* Higher values mean better places to stop the trace. */

#define EXIT_QUALITY_DEFAULT 200
#define EXIT_QUALITY_CLOSE_LOOP (4 * EXIT_QUALITY_DEFAULT)
#define EXIT_QUALITY_ENTER_EXECUTOR (2 * EXIT_QUALITY_DEFAULT + 100)
#define EXIT_QUALITY_SPECIALIZABLE (EXIT_QUALITY_DEFAULT / 4)


typedef struct _PyJitUopBuffer {
_PyUOpInstruction *start;
Expand Down Expand Up @@ -101,7 +118,8 @@ typedef struct _PyJitTracerPreviousState {
} _PyJitTracerPreviousState;

typedef struct _PyJitTracerTranslatorState {
int jump_backward_seen;
int32_t fitness; // Current trace fitness, starts high, decrements
int frame_depth; // Current inline depth (0 = root frame)
} _PyJitTracerTranslatorState;

typedef struct _PyJitTracerState {
Expand Down
Loading
Loading