Ticket #5961: Python-3.14.6-security_fixes-1.patch

File Python-3.14.6-security_fixes-1.patch, 14.5 KB (added by Joe Locash, 3 hours ago)
  • Makefile.pre.in

    Submitted By:            Joe Locash <jlocash at gmail dot com>
    Date:                    2026-06-23
    Initial Package Version: 3.14.6
    Upstream Status:         Applied
    Origin:                  Upstream:
                               https://github.com/python/cpython/pull/151565
                               https://github.com/python/cpython/pull/151998
                               https://github.com/python/cpython/pull/151565
    Description:             Upsream fixes for CVE-2026-12003, CVE-2026-11940, and
                             CVE-2026-0864.
    
    From 4d228c411bf23ff71e36db7d285d7e23e875b926 Mon Sep 17 00:00:00 2001
    From: Steve Dower <steve.dower@python.org>
    Date: Wed, 17 Jun 2026 00:16:06 +0100
    Subject: [PATCH 1/4] gh-151544: Fixes CVE-2026-12003 by removing the fallback
     to %VPATH%/Modules/Setup.local for discovering sources in getpath.py
     (GH-151545) (cherry picked from commit
     9e863fab283eddca9c2a8f9d1ee30f4dc243e314)
    
    Co-authored-by: Steve Dower <steve.dower@python.org>
    ---
     Makefile.pre.in                                   |  2 ++
     ...2026-06-16-14-58-02.gh-issue-151544._bexVy.rst |  4 ++++
     Modules/getpath.py                                | 15 ++++-----------
     3 files changed, 10 insertions(+), 11 deletions(-)
     create mode 100644 Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst
    
    diff --git a/Makefile.pre.in b/Makefile.pre.in
    index f86d7363e0900f..75a892e94b0965 100644
    a b Programs/_bootstrap_python.o: Programs/_bootstrap_python.c $(BOOTSTRAP_HEADERS)  
    16791679_bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local
    16801680        $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \
    16811681                Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(MODLIBS) $(SYSLIBS)
     1682        # Dummy pybuilddir.txt  is needed for _bootstrap_python to be runnable
     1683        @echo "none" > ./pybuilddir.txt
    16821684
    16831685
    16841686############################################################################
  • new file Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst

    diff --git a/Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst b/Misc/NEWS.d/next/Security/2026-06-16-14-58-02.gh-issue-151544._bexVy.rst
    new file mode 100644
    index 00000000000000..418e3b4b967794
    - +  
     1:file:`Modules/Setup.local` is no longer used as a landmark to discover
     2whether Python is running in a source tree, as it could potentially affect
     3actual installs. The :file:`pybuilddir.txt` file is now the sole indicator
     4of running in a source tree.
  • Modules/getpath.py

    diff --git a/Modules/getpath.py b/Modules/getpath.py
    index b89d7427e3febd..0e4f1e87e7342a 100644
    a b  
    129129# checked by looking for the BUILDDIR_TXT file, which contains the
    130130# relative path to the platlib dir. The executable_dir value is
    131131# derived from joining the VPATH preprocessor variable to the
    132 # directory containing pybuilddir.txt. If it is not found, the
    133 # BUILD_LANDMARK file is found, which is part of the source tree.
     132# directory containing pybuilddir.txt.
    134133# prefix is then found by searching up for a file that should only
    135134# exist in the source tree, and the stdlib dir is set to prefix/Lib.
    136135
     
    177176
    178177if os_name == 'posix' or os_name == 'darwin':
    179178    BUILDDIR_TXT = 'pybuilddir.txt'
    180     BUILD_LANDMARK = 'Modules/Setup.local'
    181179    DEFAULT_PROGRAM_NAME = f'python{VERSION_MAJOR}'
    182180    STDLIB_SUBDIR = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}{ABI_THREAD}'
    183181    STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}/os.py', f'{STDLIB_SUBDIR}/os.pyc']
     
    190188
    191189elif os_name == 'nt':
    192190    BUILDDIR_TXT = 'pybuilddir.txt'
    193     BUILD_LANDMARK = f'{VPATH}\\Modules\\Setup.local'
    194191    DEFAULT_PROGRAM_NAME = f'python'
    195192    STDLIB_SUBDIR = 'Lib'
    196193    STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}\\os.py', f'{STDLIB_SUBDIR}\\os.pyc']
    def search_up(prefix, *landmarks, test=isfile):  
    512509        platstdlib_dir = real_executable_dir
    513510        build_prefix = joinpath(real_executable_dir, VPATH)
    514511    except (FileNotFoundError, PermissionError):
    515         if isfile(joinpath(real_executable_dir, BUILD_LANDMARK)):
    516             build_prefix = joinpath(real_executable_dir, VPATH)
    517             if os_name == 'nt':
    518                 # QUIRK: Windows builds need platstdlib_dir to be the executable
    519                 # dir. Normally the builddir marker handles this, but in this
    520                 # case we need to correct manually.
    521                 platstdlib_dir = real_executable_dir
     512        # We used to check for an alternate landmark here, but now we require
     513        # BUILDDIR_TXT to exist. (gh-151544; CVE-2026-12003)
     514        pass
    522515
    523516    if build_prefix:
    524517        if os_name == 'nt':
  • Tools/wasm/wasi/__main__.py

    From b76f1f9ba175c511063c086c083614b8bd5cd4bc Mon Sep 17 00:00:00 2001
    From: Steve Dower <steve.dower@python.org>
    Date: Wed, 17 Jun 2026 19:36:56 +0100
    Subject: [PATCH 2/4] Add argv0 setting for Python process in WASM
    
    ---
     Tools/wasm/wasi/__main__.py | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py
    index b57bcaca924380..25c1829e1295e9 100644
    a b def main():  
    421421        "--wasm max-wasm-stack=16777216 "
    422422        # Enable thread support; causes use of preview1.
    423423        # "--wasm threads=y --wasi threads=y "
     424        # Set argv0 to the Python process
     425        "--argv0 {PYTHON_WASM} "
    424426        # Map the checkout to / to load the stdlib from /Lib.
    425427        "--dir {HOST_DIR}::{GUEST_DIR} "
    426428        # Set PYTHONPATH to the sysconfig data.
  • Tools/wasm/wasi/__main__.py

    From b002dba22ac4bdec24e7b89cad3e4cc50dbec081 Mon Sep 17 00:00:00 2001
    From: Steve Dower <steve.dower@python.org>
    Date: Wed, 17 Jun 2026 20:46:53 +0100
    Subject: [PATCH 3/4] Add symlink creation for pybuilddir.txt
    
    ---
     Tools/wasm/wasi/__main__.py | 11 +++++++++--
     1 file changed, 9 insertions(+), 2 deletions(-)
    
    diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py
    index 25c1829e1295e9..3d15b9f62df000 100644
    a b def configure_wasi_python(context, working_dir):  
    368368        file.write(f'#!/bin/sh\nexec {host_runner} {python_wasm} "$@"\n')
    369369    exec_script.chmod(0o755)
    370370    log("๐Ÿƒ", f"Created {exec_script} (--host-runner)... ")
     371    pybuilddir_txt = working_dir / "pybuilddir.txt"
     372    if not pybuilddir_txt.exists():
     373        os.symlink(CHECKOUT / "pybuilddir.txt", pybuilddir_txt)
     374        log("๐Ÿ“", f"Symlinked {pybuilddir_txt} to normal location")
    371375    sys.stdout.flush()
    372376
    373377
    def clean_contents(context):  
    399403        if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER:
    400404            log("๐Ÿงน", f"Deleting generated {LOCAL_SETUP} ...")
    401405
     406    pybuilddir_txt = working_dir / "pybuilddir.txt"
     407    if pybuilddir_txt.exists():
     408        log("๐Ÿงน", f"Deleting {pybuilddir_txt} ...")
     409        pybuilddir_txt.unlink()
     410
    402411
    403412def build_steps(*steps):
    404413    """Construct a command from other steps."""
    def main():  
    421430        "--wasm max-wasm-stack=16777216 "
    422431        # Enable thread support; causes use of preview1.
    423432        # "--wasm threads=y --wasi threads=y "
    424         # Set argv0 to the Python process
    425         "--argv0 {PYTHON_WASM} "
    426433        # Map the checkout to / to load the stdlib from /Lib.
    427434        "--dir {HOST_DIR}::{GUEST_DIR} "
    428435        # Set PYTHONPATH to the sysconfig data.
  • Tools/wasm/wasi/__main__.py

    From c5125ff1e63a50694f17b712a600b4f971e8d1ff Mon Sep 17 00:00:00 2001
    From: Steve Dower <steve.dower@python.org>
    Date: Wed, 17 Jun 2026 21:02:34 +0100
    Subject: [PATCH 4/4] Remove unnecessary deletion of pybuilddir.txt
    
    ---
     Tools/wasm/wasi/__main__.py | 5 -----
     1 file changed, 5 deletions(-)
    
    diff --git a/Tools/wasm/wasi/__main__.py b/Tools/wasm/wasi/__main__.py
    index 3d15b9f62df000..abe30fe0b4c77f 100644
    a b def clean_contents(context):  
    403403        if LOCAL_SETUP.read_bytes() == LOCAL_SETUP_MARKER:
    404404            log("๐Ÿงน", f"Deleting generated {LOCAL_SETUP} ...")
    405405
    406     pybuilddir_txt = working_dir / "pybuilddir.txt"
    407     if pybuilddir_txt.exists():
    408         log("๐Ÿงน", f"Deleting {pybuilddir_txt} ...")
    409         pybuilddir_txt.unlink()
    410 
    411406
    412407def build_steps(*steps):
    413408    """Construct a command from other steps."""
  • Lib/tarfile.py

    From 6b66c843887161f1e82c61c9e9489ba7cf5d36cf Mon Sep 17 00:00:00 2001
    From: Stan Ulbrych <stan@python.org>
    Date: Tue, 23 Jun 2026 14:31:38 +0100
    Subject: [PATCH] gh-151558: Fix symlink escape via `tarfile`
     hardlink-extraction fallback (GH-151559) (cherry picked from commit
     27dd970bf6b17ebca7c8ed486a40ab043ed7af8f)
    
    Co-authored-by: Stan Ulbrych <stan@python.org>
    ---
     Lib/tarfile.py                                |  3 +++
     Lib/test/test_tarfile.py                      | 24 +++++++++++++++++++
     ...-06-10-13-08-19.gh-issue-151558.mL74i2.rst |  3 +++
     3 files changed, 30 insertions(+)
     create mode 100644 Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
    
    diff --git a/Lib/tarfile.py b/Lib/tarfile.py
    index e6734db24f642e8..63f23490e8a149e 100644
    a b def makelink_with_filter(self, tarinfo, targetpath,  
    27822782                    "makelink_with_filter: if filter_function is not None, "
    27832783                    + "extraction_root must also not be None")
    27842784            try:
     2785                filter_function(
     2786                    unfiltered.replace(name=tarinfo.name, deep=False),
     2787                    extraction_root)
    27852788                filtered = filter_function(unfiltered, extraction_root)
    27862789            except _FILTER_ERRORS as cause:
    27872790                raise LinkFallbackError(tarinfo, unfiltered.name) from cause
  • Lib/test/test_tarfile.py

    diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
    index d974c7d46ec18c0..0fc7413be8db28c 100644
    a b def test_sneaky_hardlink_fallback(self):  
    43444344                    self.expect_file("boom", symlink_to='../../link_here')
    43454345                    self.expect_file("c", symlink_to='b')
    43464346
     4347    @symlink_test
     4348    def test_sneaky_hardlink_fallback_deep(self):
     4349        # (CVE-2026-11940)
     4350        with ArchiveMaker() as arc:
     4351            arc.add("a/b/s", symlink_to=os.path.join("..", "escape"))
     4352            arc.add("s", hardlink_to=os.path.join("a", "b", "s"))
     4353
     4354        with self.check_context(arc.open(), 'data'):
     4355            e = self.expect_exception(
     4356                tarfile.LinkFallbackError,
     4357                "link 's' would be extracted as a copy of "
     4358                + "'a/b/s', which was rejected")
     4359            self.assertIsInstance(e.__cause__,
     4360                                  tarfile.LinkOutsideDestinationError)
     4361
     4362        for filter in 'tar', 'fully_trusted':
     4363            with self.subTest(filter), self.check_context(arc.open(), filter):
     4364                if not os_helper.can_symlink():
     4365                    self.expect_file("a/")
     4366                    self.expect_file("a/b/")
     4367                else:
     4368                    self.expect_file("a/b/s", symlink_to=os.path.join('..', 'escape'))
     4369                    self.expect_file("s", symlink_to=os.path.join('..', 'escape'))
     4370
    43474371    @symlink_test
    43484372    def test_exfiltration_via_symlink(self):
    43494373        # (CVE-2025-4138)
  • new file Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst

    diff --git a/Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst b/Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
    new file mode 100644
    index 000000000000000..74459d5680e21a3
    - +  
     1Fixed an vulnerability in the :mod:`tarfile` ``data`` and ``tar`` extraction
     2filters where crafted archives could create a symlink pointing outside the
     3destination directory. This was a bypass of :cve:`2025-4330`.
  • Lib/configparser.py

    From 314f46b6c51d366460b66292b1aecfe9fb71de94 Mon Sep 17 00:00:00 2001
    From: Seth Larson <seth@python.org>
    Date: Tue, 23 Jun 2026 08:33:51 -0500
    Subject: [PATCH] gh-143927: Normalize all line endings (CR, CRLF, and LF) in
     configparser (GH-143929) (cherry picked from commit
     5858e42c539dac8394636a6e9b30472b8994851f)
    
    Co-authored-by: Seth Larson <seth@python.org>
    ---
     Lib/configparser.py                                   |  4 +++-
     Lib/test/test_configparser.py                         | 11 +++++++++++
     .../2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst    |  2 ++
     3 files changed, 16 insertions(+), 1 deletion(-)
     create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst
    
    diff --git a/Lib/configparser.py b/Lib/configparser.py
    index a53ac87276445ad..3c452afe8ade485 100644
    a b def _write_section(self, fp, section_name, section_items, delimiter, unnamed=Fal  
    992992            value = self._interpolation.before_write(self, section_name, key,
    993993                                                     value)
    994994            if value is not None or not self._allow_no_value:
    995                 value = delimiter + str(value).replace('\n', '\n\t')
     995                # Convert all possible line-endings into '\n\t'
     996                value = (delimiter + str(value).replace('\r\n', '\n')
     997                         .replace('\r', '\n').replace('\n', '\n\t'))
    996998            else:
    997999                value = ""
    9981000            fp.write("{}{}\n".format(key, value))
  • Lib/test/test_configparser.py

    diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py
    index 8d8dd2a2bf27fbf..4783943f71a1092 100644
    a b def test_default_case_sensitivity(self):  
    526526            cf.get(self.default_section, "Foo"), "Bar",
    527527            "could not locate option, expecting case-insensitive defaults")
    528528
     529    def test_crlf_normalization(self):
     530        cf = self.newconfig({"key1": "a\nb","key2": "a\rb", "key3": "a\r\nb", "key4": "a\r\nb"})
     531        buf = io.StringIO()
     532        cf.write(buf)
     533        cf_str = buf.getvalue()
     534        self.assertNotIn("\r", cf_str)
     535        self.assertNotIn("\r\n", cf_str)
     536        self.assertEqual(cf_str.count("\n"), 10)
     537        self.assertEqual(cf_str.count("\n\t"), 4)
     538        self.assertTrue(cf_str.endswith("\n\n"))
     539
    529540    def test_parse_errors(self):
    530541        cf = self.newconfig()
    531542        self.parse_error(cf, configparser.ParsingError,
  • new file Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst

    diff --git a/Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst b/Misc/NEWS.d/next/Security/2026-01-16-11-58-19.gh-issue-143927.aviFeG.rst
    new file mode 100644
    index 000000000000000..ca554997e5c3963
    - +  
     1Normalize all line endings (CR, CRLF, and LF) to LF+TAB when writing
     2multi-line configparser values.