diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b8c9483a90fb7b..700fe10e034fec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: run_tests: ${{ steps.check.outputs.run_tests }} run_ssl_tests: ${{ steps.check.outputs.run_ssl_tests }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Check for source changes id: check run: | @@ -50,8 +50,8 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 - name: Install Dependencies run: | sudo ./.github/workflows/posix-deps-apt.sh @@ -72,10 +72,23 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh + - name: Check Autoconf version 2.69 and aclocal 1.16.3 + run: | + grep "Generated by GNU Autoconf 2.69" configure + grep "aclocal 1.16.3" aclocal.m4 + grep -q "runstatedir" configure + grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4 + - name: Configure CPython + run: | + # Build Python with the libpython dynamic library + ./configure --config-cache --with-pydebug --enable-shared + - name: Regenerate autoconf files + run: | + make regen-configure - name: Build CPython run: | ./configure --with-pydebug @@ -99,7 +112,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build CPython run: .\PCbuild\build.bat -e -p Win32 - name: Display build info @@ -113,7 +126,7 @@ jobs: needs: check_source if: needs.check_source.outputs.run_tests == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build CPython run: .\PCbuild\build.bat -e -p x64 - name: Display build info @@ -131,7 +144,7 @@ jobs: HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Homebrew dependencies run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk - name: Configure CPython @@ -160,7 +173,7 @@ jobs: env: OPENSSL_VER: 1.1.1v steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Configure OpenSSL env vars @@ -206,7 +219,7 @@ jobs: OPENSSL_DIR: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }} LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }}/lib steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Configure OpenSSL env vars diff --git a/.github/workflows/build_msi.yml b/.github/workflows/build_msi.yml index e35ebe42565224..f9400b9ea8ac26 100644 --- a/.github/workflows/build_msi.yml +++ b/.github/workflows/build_msi.yml @@ -23,7 +23,7 @@ jobs: name: 'Windows (x86) Installer' runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build CPython installer run: .\Tools\msi\build.bat -x86 @@ -31,6 +31,6 @@ jobs: name: 'Windows (x64) Installer' runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build CPython installer run: .\Tools\msi\build.bat -x64 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 762cdb5ab2188a..4ed1e7d4ebab8d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -25,7 +25,7 @@ jobs: env: OPENSSL_VER: 1.1.1n steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: 'Restore OpenSSL build' @@ -73,7 +73,7 @@ jobs: name: 'Ubuntu (C Coverage)' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Dependencies run: sudo ./.github/workflows/posix-deps-apt.sh - name: Configure CPython diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 2b3dc878949dfa..51612a748634e3 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -22,7 +22,7 @@ jobs: name: 'Docs' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: 'Install Dependencies' run: sudo ./.github/workflows/posix-deps-apt.sh && sudo apt-get install wamerican - name: 'Configure CPython' diff --git a/.github/workflows/verify-ensurepip-wheels.yml b/.github/workflows/verify-ensurepip-wheels.yml index 61e3d1adf534d5..9a36240146964c 100644 --- a/.github/workflows/verify-ensurepip-wheels.yml +++ b/.github/workflows/verify-ensurepip-wheels.yml @@ -20,7 +20,7 @@ jobs: verify: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3' diff --git a/.gitignore b/.gitignore index 960bc7ca59e314..24d5a244e0e0bb 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,11 @@ Tools/ssl/win32 # Ignore ./python binary on Unix but still look into ./Python/ directory. /python !/Python/ + +# Artifacts generated by 3.11 lying around when switching branches: +/_bootstrap_python +/Programs/_freeze_module +/Modules/Setup.bootstrap +/Modules/Setup.stdlib +/Python/deepfreeze/ +/Python/frozen_modules/ \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000000000..6a9db718698269 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,12 @@ +# This is a dummy config file so that readthedocs.org doesn't fail on security branches. +# Note that this won't result in docs actually getting built; +# clicking on the docs preview link on a PR will result in a 404. +version: 2 +formats: [] +build: + os: "ubuntu-22.04" + tools: + python: "3.11" + jobs: + post_checkout: + - exit 183 diff --git a/Doc/constraints.txt b/Doc/constraints.txt new file mode 100644 index 00000000000000..16b735ea07a72a --- /dev/null +++ b/Doc/constraints.txt @@ -0,0 +1,24 @@ +# We have upper bounds on our transitive dependencies here +# To avoid new releases unexpectedly breaking our build. +# This file can be updated on an ad-hoc basis, +# though it will probably have to be updated +# whenever Doc/requirements.txt is updated. + +# Direct dependencies of Sphinx +babel<3 +colorama<0.5 +imagesize<1.5 +Jinja2<3.2 +packaging<24 +Pygments>=2.16.1,<3 +requests<3 +snowballstemmer<3 +sphinxcontrib-applehelp<1.0.5 +sphinxcontrib-devhelp<1.0.6 +sphinxcontrib-htmlhelp<2.0.5 +sphinxcontrib-jsmath<1.1 +sphinxcontrib-qthelp<1.0.7 +sphinxcontrib-serializinghtml<1.1.10 + +# Direct dependencies of Jinja2 (Jinja is a dependency of Sphinx, see above) +MarkupSafe<2.2 diff --git a/Doc/requirements.txt b/Doc/requirements.txt index aa615e6bf3141f..0e36985fd2a31a 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -10,9 +10,13 @@ sphinx==2.4.4 docutils==0.17.1 # Jinja version is pinned to a version compatible with Sphinx version 2.4.4. jinja2==3.0.3 +# Alabaster version is pinned to a version compatible with Sphinx version 2.4.4. +alabaster==0.7.13 blurb # The theme used by the documentation is stored separately, so we need # to install that as well. python-docs-theme + +-c constraints.txt diff --git a/Include/patchlevel.h b/Include/patchlevel.h index 531cc8f5e1a008..d52a7329516be7 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 8 -#define PY_MICRO_VERSION 17 +#define PY_MICRO_VERSION 18 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.8.17+" +#define PY_VERSION "3.8.18+" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff --git a/Lib/ensurepip/_bundled/pip-22.0.4-py3-none-any.whl b/Lib/ensurepip/_bundled/pip-22.0.4-py3-none-any.whl deleted file mode 100644 index 7ba048e245227d..00000000000000 Binary files a/Lib/ensurepip/_bundled/pip-22.0.4-py3-none-any.whl and /dev/null differ diff --git a/Lib/site.py b/Lib/site.py index 9fa21cca386674..9b7314e8213a3e 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -74,6 +74,7 @@ import builtins import _sitebuiltins import io +import stat # Prefixes for site-packages; add additional prefixes like /usr/local here PREFIXES = [sys.prefix, sys.exec_prefix] @@ -156,6 +157,13 @@ def addpackage(sitedir, name, known_paths): else: reset = False fullname = os.path.join(sitedir, name) + try: + st = os.lstat(fullname) + except OSError: + return + if ((getattr(st, 'st_flags', 0) & stat.UF_HIDDEN) or + (getattr(st, 'st_file_attributes', 0) & stat.FILE_ATTRIBUTE_HIDDEN)): + return try: f = io.TextIOWrapper(io.open_code(fullname)) except OSError: @@ -203,7 +211,8 @@ def addsitedir(sitedir, known_paths=None): names = os.listdir(sitedir) except OSError: return - names = [name for name in names if name.endswith(".pth")] + names = [name for name in names + if name.endswith(".pth") and not name.startswith(".")] for name in sorted(names): addpackage(sitedir, name, known_paths) if reset: diff --git a/Lib/ssl.py b/Lib/ssl.py index bd9ba346dae8cc..4ccccdeba2a55b 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1048,7 +1048,11 @@ def _create(cls, sock, server_side=False, do_handshake_on_connect=True, self.close() except OSError: pass - raise notconn_pre_handshake_data_error + try: + raise notconn_pre_handshake_data_error + finally: + # Explicitly break the reference cycle. + notconn_pre_handshake_data_error = None else: connected = True diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 8f9cb6c3ca3500..20ecd427c03fb6 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -263,6 +263,22 @@ def _mkstemp_inner(dir, pre, suf, flags, output_type): raise FileExistsError(_errno.EEXIST, "No usable temporary file name found") +def _dont_follow_symlinks(func, path, *args): + # Pass follow_symlinks=False, unless not supported on this platform. + if func in _os.supports_follow_symlinks: + func(path, *args, follow_symlinks=False) + elif _os.name == 'nt' or not _os.path.islink(path): + func(path, *args) + +def _resetperms(path): + try: + chflags = _os.chflags + except AttributeError: + pass + else: + _dont_follow_symlinks(chflags, path, 0) + _dont_follow_symlinks(_os.chmod, path, 0o700) + # User visible interfaces. @@ -786,17 +802,10 @@ def __init__(self, suffix=None, prefix=None, dir=None): def _rmtree(cls, name): def onerror(func, path, exc_info): if issubclass(exc_info[0], PermissionError): - def resetperms(path): - try: - _os.chflags(path, 0) - except AttributeError: - pass - _os.chmod(path, 0o700) - try: if path != name: - resetperms(_os.path.dirname(path)) - resetperms(path) + _resetperms(_os.path.dirname(path)) + _resetperms(path) try: _os.unlink(path) diff --git a/Lib/test/test_codecencodings_iso2022.py b/Lib/test/test_codecencodings_iso2022.py index 00ea1c39dd6fb6..027dbecc6134df 100644 --- a/Lib/test/test_codecencodings_iso2022.py +++ b/Lib/test/test_codecencodings_iso2022.py @@ -24,6 +24,52 @@ class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase): (b'ab\x1BNdef', 'replace', 'abdef'), ) +class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_3' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(O\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(O\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(O\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(O\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(O\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(O\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(O\x2E\x21\x1B(Bdef', 'replace', 'ab\uFFFDdef'), + ('ab\u4FF1def', 'replace', b'ab?def'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(O\x29\x28\x1B(Bℜ\x1B$(O\x29\x32\x1B(B = ⟨ሴ⟩' + ) + +class Test_ISO2022_JP2004(multibytecodec_support.TestBase, unittest.TestCase): + encoding = 'iso2022_jp_2004' + tstring = multibytecodec_support.load_teststring('iso2022_jp') + codectests = COMMON_CODEC_TESTS + ( + (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'), + (b'\x1B$(Q\x2E\x23\x1B(B', 'strict', '\u3402' ), + (b'\x1B$(Q\x2E\x22\x1B(B', 'strict', '\U0002000B' ), + (b'\x1B$(Q\x24\x77\x1B(B', 'strict', '\u304B\u309A'), + (b'\x1B$(P\x21\x22\x1B(B', 'strict', '\u4E02' ), + (b'\x1B$(P\x7E\x76\x1B(B', 'strict', '\U0002A6B2' ), + ('\u3402', 'strict', b'\x1B$(Q\x2E\x23\x1B(B'), + ('\U0002000B', 'strict', b'\x1B$(Q\x2E\x22\x1B(B'), + ('\u304B\u309A', 'strict', b'\x1B$(Q\x24\x77\x1B(B'), + ('\u4E02', 'strict', b'\x1B$(P\x21\x22\x1B(B'), + ('\U0002A6B2', 'strict', b'\x1B$(P\x7E\x76\x1B(B'), + (b'ab\x1B$(Q\x2E\x21\x1B(Bdef', 'replace', 'ab\u4FF1def'), + ('ab\u4FF1def', 'replace', b'ab\x1B$(Q\x2E\x21\x1B(Bdef'), + ) + xmlcharnametest = ( + '\xAB\u211C\xBB = \u2329\u1234\u232A', + b'\x1B$(Q\x29\x28\x1B(Bℜ\x1B$(Q\x29\x32\x1B(B = ⟨ሴ⟩' + ) + class Test_ISO2022_KR(multibytecodec_support.TestBase, unittest.TestCase): encoding = 'iso2022_kr' tstring = multibytecodec_support.load_teststring('iso2022_kr') diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 3806f7ac27a85e..6b7c35f54b4e42 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -417,10 +417,10 @@ def test_undecodable_filename(self): def test_undecodable_parameter(self): # sanity check using a valid parameter response = self.request(self.base_url + '/?x=123').read() - self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1')) + self.assertRegex(response, f'listing for {self.base_url}/\\?x=123'.encode('latin1')) # now the bogus encoding response = self.request(self.base_url + '/?x=%bb').read() - self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) + self.assertRegex(response, f'listing for {self.base_url}/\\?x=\xef\xbf\xbd'.encode('latin1')) def test_get_dir_redirect_location_domain_injection_bug(self): """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 88c501d81a07f9..358a01d7185232 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -240,10 +240,15 @@ def test_access_parameter(self): # Try writing with PROT_EXEC and without PROT_WRITE prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0) with open(TESTFN, "r+b") as f: - m = mmap.mmap(f.fileno(), mapsize, prot=prot) - self.assertRaises(TypeError, m.write, b"abcdef") - self.assertRaises(TypeError, m.write_byte, 0) - m.close() + try: + m = mmap.mmap(f.fileno(), mapsize, prot=prot) + except PermissionError: + # on macOS 14, PROT_READ | PROT_WRITE is not allowed + pass + else: + self.assertRaises(TypeError, m.write, b"abcdef") + self.assertRaises(TypeError, m.write_byte, 0) + m.close() def test_bad_file_desc(self): # Try opening a bad file descriptor... diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 9b2df6bfc39ee8..43233ab9b4ae83 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -18,6 +18,7 @@ import urllib.request import urllib.error import shutil +import stat import subprocess import sysconfig import tempfile @@ -182,6 +183,44 @@ def test_addsitedir(self): finally: pth_file.cleanup() + def test_addsitedir_dotfile(self): + pth_file = PthFile('.dotfile') + pth_file.cleanup(prep=True) + try: + pth_file.create() + site.addsitedir(pth_file.base_dir, set()) + self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) + self.assertIn(pth_file.base_dir, sys.path) + finally: + pth_file.cleanup() + + @unittest.skipUnless(hasattr(os, 'chflags'), 'test needs os.chflags()') + def test_addsitedir_hidden_flags(self): + pth_file = PthFile() + pth_file.cleanup(prep=True) + try: + pth_file.create() + st = os.stat(pth_file.file_path) + os.chflags(pth_file.file_path, st.st_flags | stat.UF_HIDDEN) + site.addsitedir(pth_file.base_dir, set()) + self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) + self.assertIn(pth_file.base_dir, sys.path) + finally: + pth_file.cleanup() + + @unittest.skipUnless(sys.platform == 'win32', 'test needs Windows') + def test_addsitedir_hidden_file_attribute(self): + pth_file = PthFile() + pth_file.cleanup(prep=True) + try: + pth_file.create() + subprocess.check_call(['attrib', '+H', pth_file.file_path]) + site.addsitedir(pth_file.base_dir, set()) + self.assertNotIn(site.makepath(pth_file.good_dir_path)[0], sys.path) + self.assertIn(pth_file.base_dir, sys.path) + finally: + pth_file.cleanup() + # This tests _getuserbase, hence the double underline # to distinguish from a test for getuserbase def test__getuserbase(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index eada6e929fe159..ed724ae03e1e73 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -979,7 +979,20 @@ def testInterfaceNameIndex(self): 'socket.if_indextoname() not available.') def testInvalidInterfaceIndexToName(self): self.assertRaises(OSError, socket.if_indextoname, 0) + self.assertRaises(OverflowError, socket.if_indextoname, -1) + self.assertRaises(OverflowError, socket.if_indextoname, 2**1000) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') + if hasattr(socket, 'if_nameindex'): + indices = dict(socket.if_nameindex()) + for index in indices: + index2 = index + 2**32 + if index2 not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index2) + for index in 2**32-1, 2**64-1: + if index not in indices: + with self.assertRaises((OverflowError, OSError)): + socket.if_indextoname(index) @unittest.skipUnless(hasattr(socket, 'if_nametoindex'), 'socket.if_nametoindex() not available.') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 8de491566b8395..e729c627064287 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -150,6 +150,9 @@ def data_file(*name): OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0) OP_IGNORE_UNEXPECTED_EOF = getattr(ssl, "OP_IGNORE_UNEXPECTED_EOF", 0) +# *_TIMEOUT constants are available in test.support in 3.9+ +SHORT_TIMEOUT = 30.0 + # Ubuntu has patched OpenSSL and changed behavior of security level 2 # see https://bugs.python.org/issue41561#msg389003 def is_ubuntu(): @@ -4828,12 +4831,16 @@ class TestPreHandshakeClose(unittest.TestCase): class SingleConnectionTestServerThread(threading.Thread): - def __init__(self, *, name, call_after_accept): + def __init__(self, *, name, call_after_accept, timeout=None): self.call_after_accept = call_after_accept self.received_data = b'' # set by .run() self.wrap_error = None # set by .run() self.listener = None # set by .start() self.port = None # set by .start() + if timeout is None: + self.timeout = SHORT_TIMEOUT + else: + self.timeout = timeout super().__init__(name=name) def __enter__(self): @@ -4856,13 +4863,19 @@ def start(self): self.ssl_ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY) self.listener = socket.socket() self.port = support.bind_port(self.listener) - self.listener.settimeout(2.0) + self.listener.settimeout(self.timeout) self.listener.listen(1) super().start() def run(self): - conn, address = self.listener.accept() - self.listener.close() + try: + conn, address = self.listener.accept() + except TimeoutError: + # on timeout, just close the listener + return + finally: + self.listener.close() + with conn: if self.call_after_accept(conn): return @@ -4890,8 +4903,13 @@ def non_linux_skip_if_other_okay_error(self, err): # we're specifically trying to test. The way this test is written # is known to work on Linux. We'll skip it anywhere else that it # does not present as doing so. - self.skipTest(f"Could not recreate conditions on {sys.platform}:" - f" {err=}") + try: + self.skipTest(f"Could not recreate conditions on {sys.platform}:" + f" {err=}") + finally: + # gh-108342: Explicitly break the reference cycle + err = None + # If maintaining this conditional winds up being a problem. # just turn this into an unconditional skip anything but Linux. # The important thing is that our CI has the logic covered. @@ -4902,7 +4920,7 @@ def test_preauth_data_to_tls_server(self): def call_after_accept(unused): server_accept_called.set() - if not ready_for_server_wrap_socket.wait(2.0): + if not ready_for_server_wrap_socket.wait(SHORT_TIMEOUT): raise RuntimeError("wrap_socket event never set, test may fail.") return False # Tell the server thread to continue. @@ -4924,20 +4942,31 @@ def call_after_accept(unused): ready_for_server_wrap_socket.set() server.join() + wrap_error = server.wrap_error - self.assertEqual(b"", server.received_data) - self.assertIsInstance(wrap_error, OSError) # All platforms. - self.non_linux_skip_if_other_okay_error(wrap_error) - self.assertIsInstance(wrap_error, ssl.SSLError) - self.assertIn("before TLS handshake with data", wrap_error.args[1]) - self.assertIn("before TLS handshake with data", wrap_error.reason) - self.assertNotEqual(0, wrap_error.args[0]) - self.assertIsNone(wrap_error.library, msg="attr must exist") + server.wrap_error = None + try: + self.assertEqual(b"", server.received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + finally: + # gh-108342: Explicitly break the reference cycle + wrap_error = None + server = None def test_preauth_data_to_tls_client(self): + server_can_continue_with_wrap_socket = threading.Event() client_can_continue_with_wrap_socket = threading.Event() def call_after_accept(conn_to_client): + if not server_can_continue_with_wrap_socket.wait(SHORT_TIMEOUT): + print("ERROR: test client took too long") + # This forces an immediate connection close via RST on .close(). set_socket_so_linger_on_with_zero_timeout(conn_to_client) conn_to_client.send( @@ -4959,8 +4988,10 @@ def call_after_accept(conn_to_client): with socket.socket() as client: client.connect(server.listener.getsockname()) - if not client_can_continue_with_wrap_socket.wait(2.0): - self.fail("test server took too long.") + server_can_continue_with_wrap_socket.set() + + if not client_can_continue_with_wrap_socket.wait(SHORT_TIMEOUT): + self.fail("test server took too long") ssl_ctx = ssl.create_default_context() try: tls_client = ssl_ctx.wrap_socket( @@ -4974,24 +5005,31 @@ def call_after_accept(conn_to_client): tls_client.close() server.join() - self.assertEqual(b"", received_data) - self.assertIsInstance(wrap_error, OSError) # All platforms. - self.non_linux_skip_if_other_okay_error(wrap_error) - self.assertIsInstance(wrap_error, ssl.SSLError) - self.assertIn("before TLS handshake with data", wrap_error.args[1]) - self.assertIn("before TLS handshake with data", wrap_error.reason) - self.assertNotEqual(0, wrap_error.args[0]) - self.assertIsNone(wrap_error.library, msg="attr must exist") + try: + self.assertEqual(b"", received_data) + self.assertIsInstance(wrap_error, OSError) # All platforms. + self.non_linux_skip_if_other_okay_error(wrap_error) + self.assertIsInstance(wrap_error, ssl.SSLError) + self.assertIn("before TLS handshake with data", wrap_error.args[1]) + self.assertIn("before TLS handshake with data", wrap_error.reason) + self.assertNotEqual(0, wrap_error.args[0]) + self.assertIsNone(wrap_error.library, msg="attr must exist") + finally: + # gh-108342: Explicitly break the reference cycle + wrap_error = None + server = None def test_https_client_non_tls_response_ignored(self): - server_responding = threading.Event() class SynchronizedHTTPSConnection(http.client.HTTPSConnection): def connect(self): + # Call clear text HTTP connect(), not the encrypted HTTPS (TLS) + # connect(): wrap_socket() is called manually below. http.client.HTTPConnection.connect(self) + # Wait for our fault injection server to have done its thing. - if not server_responding.wait(1.0) and support.verbose: + if not server_responding.wait(SHORT_TIMEOUT) and support.verbose: sys.stdout.write("server_responding event never set.") self.sock = self._context.wrap_socket( self.sock, server_hostname=self.host) @@ -5006,29 +5044,34 @@ def call_after_accept(conn_to_client): server_responding.set() return True # Tell the server to stop. + timeout = 2.0 server = self.SingleConnectionTestServerThread( call_after_accept=call_after_accept, - name="non_tls_http_RST_responder") + name="non_tls_http_RST_responder", + timeout=timeout) server.__enter__() # starts it self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it. # Redundant; call_after_accept sets SO_LINGER on the accepted conn. set_socket_so_linger_on_with_zero_timeout(server.listener) connection = SynchronizedHTTPSConnection( - f"localhost", + server.listener.getsockname()[0], port=server.port, context=ssl.create_default_context(), - timeout=2.0, + timeout=timeout, ) + # There are lots of reasons this raises as desired, long before this # test was added. Sending the request requires a successful TLS wrapped # socket; that fails if the connection is broken. It may seem pointless # to test this. It serves as an illustration of something that we never # want to happen... properly not happening. - with self.assertRaises(OSError) as err_ctx: + with self.assertRaises(OSError): connection.request("HEAD", "/test", headers={"Host": "localhost"}) response = connection.getresponse() + server.join() + def test_main(verbose=False): if support.verbose: @@ -5064,7 +5107,7 @@ def test_main(verbose=False): tests = [ ContextTests, BasicSocketTests, SSLErrorTests, MemoryBIOTests, SSLObjectTests, SimpleBackgroundTests, ThreadedTests, - TestPostHandshakeAuth, TestSSLDebug + TestPostHandshakeAuth, TestSSLDebug, TestPreHandshakeClose ] if support.is_resource_enabled('network'): diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py index f129454f4c33ee..8cb36f38a2a35c 100644 --- a/Lib/test/test_tempfile.py +++ b/Lib/test/test_tempfile.py @@ -1377,6 +1377,103 @@ def test_cleanup_with_symlink_to_a_directory(self): "were deleted") d2.cleanup() + @support.skip_unless_symlink + def test_cleanup_with_symlink_modes(self): + # cleanup() should not follow symlinks when fixing mode bits (#91133) + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + for mode in range(8): + mode <<= 6 + with self.subTest(mode=format(mode, '03o')): + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chmod(symlink, mode, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chmod(symlink, mode) + except FileNotFoundError: + pass + os.chmod(d1.name, mode) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chmod(file1, mode) + old_mode = os.stat(file1).st_mode + test(file1, target_is_directory=False) + new_mode = os.stat(file1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + with self.subTest('existing dir'): + os.chmod(dir1, mode) + old_mode = os.stat(dir1).st_mode + test(dir1, target_is_directory=True) + new_mode = os.stat(dir1).st_mode + self.assertEqual(new_mode, old_mode, + '%03o != %03o' % (new_mode, old_mode)) + + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') + @support.skip_unless_symlink + def test_cleanup_with_symlink_flags(self): + # cleanup() should not follow symlinks when fixing flags (#91133) + flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + + with self.do_create(recurse=0) as d2: + file1 = os.path.join(d2, 'file1') + open(file1, 'wb').close() + dir1 = os.path.join(d2, 'dir1') + os.mkdir(dir1) + def test(target, target_is_directory): + d1 = self.do_create(recurse=0) + symlink = os.path.join(d1.name, 'symlink') + os.symlink(target, symlink, + target_is_directory=target_is_directory) + try: + os.chflags(symlink, flags, follow_symlinks=False) + except NotImplementedError: + pass + try: + os.chflags(symlink, flags) + except FileNotFoundError: + pass + os.chflags(d1.name, flags) + d1.cleanup() + self.assertFalse(os.path.exists(d1.name)) + + with self.subTest('nonexisting file'): + test('nonexisting', target_is_directory=False) + with self.subTest('nonexisting dir'): + test('nonexisting', target_is_directory=True) + + with self.subTest('existing file'): + os.chflags(file1, flags) + old_flags = os.stat(file1).st_flags + test(file1, target_is_directory=False) + new_flags = os.stat(file1).st_flags + self.assertEqual(new_flags, old_flags) + + with self.subTest('existing dir'): + os.chflags(dir1, flags) + old_flags = os.stat(dir1).st_flags + test(dir1, target_is_directory=True) + new_flags = os.stat(dir1).st_flags + self.assertEqual(new_flags, old_flags) + @support.cpython_only def test_del_on_collection(self): # A TemporaryDirectory is deleted when garbage collected @@ -1489,9 +1586,27 @@ def test_modes(self): d.cleanup() self.assertFalse(os.path.exists(d.name)) - @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.lchflags') + def check_flags(self, flags): + # skip the test if these flags are not supported (ex: FreeBSD 13) + filename = support.TESTFN + try: + open(filename, "w").close() + try: + os.chflags(filename, flags) + except OSError as exc: + # "OSError: [Errno 45] Operation not supported" + self.skipTest(f"chflags() doesn't support flags " + f"{flags:#b}: {exc}") + else: + os.chflags(filename, 0) + finally: + support.unlink(filename) + + @unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.chflags') def test_flags(self): flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK + self.check_flags(flags) + d = self.do_create(recurse=3, dirs=2, files=2) with d: # Change files and directories flags recursively. diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 31153445697fdd..05cb479857c2a3 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -1998,6 +1998,66 @@ def test_decompress_without_3rd_party_library(self): with zipfile.ZipFile(zip_file) as zf: self.assertRaises(RuntimeError, zf.extract, 'a.txt') + @requires_zlib + def test_full_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00a\xed' + b'\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\d\x0b`P' + b'K\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2' + b'\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00aPK' + b'\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0lH\x05\xe2\x1e' + b'8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00bPK\x05' + b'\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00\x00/\x00\x00' + b'\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + self.assertEqual(len(zipf.read('a')), 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'File name.*differ'): + zipf.read('b') + + @requires_zlib + def test_quoted_overlap(self): + data = ( + b'PK\x03\x04\x14\x00\x00\x00\x08\x00\xa0lH\x05Y\xfc' + b'8\x044\x00\x00\x00(\x04\x00\x00\x01\x00\x00\x00a\x00' + b'\x1f\x00\xe0\xffPK\x03\x04\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00b\xed\xc0\x81\x08\x00\x00\x00\xc00\xd6\xfbK\\' + b'd\x0b`PK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0' + b'lH\x05Y\xfc8\x044\x00\x00\x00(\x04\x00\x00\x01' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00aPK\x01\x02\x14\x00\x14\x00\x00\x00\x08\x00\xa0l' + b'H\x05\xe2\x1e8\xbb\x10\x00\x00\x00\t\x04\x00\x00\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x00\x00' + b'bPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00^\x00\x00' + b'\x00S\x00\x00\x00\x00\x00' + ) + with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf: + self.assertEqual(zipf.namelist(), ['a', 'b']) + zi = zipf.getinfo('a') + self.assertEqual(zi.header_offset, 0) + self.assertEqual(zi.compress_size, 52) + self.assertEqual(zi.file_size, 1064) + zi = zipf.getinfo('b') + self.assertEqual(zi.header_offset, 36) + self.assertEqual(zi.compress_size, 16) + self.assertEqual(zi.file_size, 1033) + with self.assertRaisesRegex(zipfile.BadZipFile, 'Overlapped entries'): + zipf.read('a') + self.assertEqual(len(zipf.read('b')), 1033) + def tearDown(self): unlink(TESTFN) unlink(TESTFN2) diff --git a/Lib/zipfile.py b/Lib/zipfile.py index e95e2b2d452df4..669f1a42309fd1 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -339,6 +339,7 @@ class ZipInfo (object): 'compress_size', 'file_size', '_raw_time', + '_end_offset', ) def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): @@ -378,6 +379,7 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): self.volume = 0 # Volume number of file header self.internal_attr = 0 # Internal attributes self.external_attr = 0 # External file attributes + self._end_offset = None # Start of the next local header or central directory # Other attributes are set by class ZipFile: # header_offset Byte offset to the file header # CRC CRC-32 of the uncompressed file @@ -1402,6 +1404,12 @@ def _RealGetContents(self): if self.debug > 2: print("total", total) + end_offset = self.start_dir + for zinfo in sorted(self.filelist, + key=lambda zinfo: zinfo.header_offset, + reverse=True): + zinfo._end_offset = end_offset + end_offset = zinfo.header_offset def namelist(self): """Return a list of file names in the archive.""" @@ -1557,6 +1565,10 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False): 'File name in directory %r and header %r differ.' % (zinfo.orig_filename, fname)) + if (zinfo._end_offset is not None and + zef_file.tell() + zinfo.compress_size > zinfo._end_offset): + raise BadZipFile(f"Overlapped entries: {zinfo.orig_filename!r} (possible zip bomb)") + # check for encrypted flag & handle password is_encrypted = zinfo.flag_bits & 0x1 if is_encrypted: diff --git a/Makefile.pre.in b/Makefile.pre.in index 7e381283fee886..67a99bdbea38b4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1742,6 +1742,18 @@ autoconf: # Regenerate pyconfig.h.in from configure.ac using autoheader (cd $(srcdir); autoheader -Wall) +# See https://github.com/tiran/cpython_autoconf container +.PHONY: regen-configure +regen-configure: + @if command -v podman >/dev/null; then RUNTIME="podman"; else RUNTIME="docker"; fi; \ + if ! command -v $$RUNTIME; then echo "$@ needs either Podman or Docker container runtime." >&2; exit 1; fi; \ + if command -v selinuxenabled >/dev/null && selinuxenabled; then OPT=":Z"; fi; \ + # Manifest corresponds with tag '269' \ + CPYTHON_AUTOCONF_MANIFEST="sha256:f370fee95eefa3d57b00488bce4911635411fa83e2d293ced8cf8a3674ead939" \ + CMD="$$RUNTIME run --rm --pull=missing -v $(abs_srcdir):/src$$OPT quay.io/tiran/cpython_autoconf@$$CPYTHON_AUTOCONF_MANIFEST"; \ + echo $$CMD; \ + $$CMD || exit $? + # Create a tags file for vi tags:: ctags -w $(srcdir)/Include/*.h $(srcdir)/Include/cpython/*.h $(srcdir)/Include/internal/*.h diff --git a/Misc/NEWS.d/3.8.18.rst b/Misc/NEWS.d/3.8.18.rst new file mode 100644 index 00000000000000..8b201ab0d5637a --- /dev/null +++ b/Misc/NEWS.d/3.8.18.rst @@ -0,0 +1,34 @@ +.. date: 2023-08-22-17-39-12 +.. gh-issue: 108310 +.. nonce: fVM3sg +.. release date: 2023-08-24 +.. section: Security + +Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to +a bypass of the TLS handshake and included protections (like certificate +verification) and treating sent unencrypted data as if it were +post-handshake TLS encrypted data. Security issue reported as +`CVE-2023-40217 +`_ by Aapo +Oksman. Patch by Gregory P. Smith. + +.. + +.. date: 2023-08-10-17-36-22 +.. gh-issue: 107845 +.. nonce: dABiMJ +.. section: Library + +:func:`tarfile.data_filter` now takes the location of symlinks into account +when determining their target, so it will no longer reject some valid +tarballs with ``LinkOutsideDestinationError``. + +.. + +.. date: 2023-08-12-13-18-15 +.. gh-issue: 107565 +.. nonce: Tv22Ne +.. section: Tools/Demos + +Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, +and 3.1.2. diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst new file mode 100644 index 00000000000000..268a3d310f2b49 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-27-19-38-33.gh-issue-102388.vd5YUZ.rst @@ -0,0 +1 @@ +Fix a bug where ``iso2022_jp_3`` and ``iso2022_jp_2004`` codecs read out of bounds diff --git a/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst new file mode 100644 index 00000000000000..7991048fc48e03 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst @@ -0,0 +1,2 @@ +Fix a bug in :class:`tempfile.TemporaryDirectory` cleanup, which now no longer +dereferences symlinks when working around file system permission errors. diff --git a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst b/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst deleted file mode 100644 index 32c1fb93f4ab2c..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-08-10-17-36-22.gh-issue-107845.dABiMJ.rst +++ /dev/null @@ -1,3 +0,0 @@ -:func:`tarfile.data_filter` now takes the location of symlinks into account -when determining their target, so it will no longer reject some valid -tarballs with ``LinkOutsideDestinationError``. diff --git a/Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst b/Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst new file mode 100644 index 00000000000000..be279caffc46ee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst @@ -0,0 +1,3 @@ +Protect :mod:`zipfile` from "quoted-overlap" zipbomb. It now raises +BadZipFile when try to read an entry that overlaps with other entry or +central directory. diff --git a/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst new file mode 100644 index 00000000000000..feb7a8643b97f6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst @@ -0,0 +1,3 @@ +Fix a crash in :func:`socket.if_indextoname` with specific value (UINT_MAX). +Fix an integer overflow in :func:`socket.if_indextoname` on 64-bit +non-Windows platforms. diff --git a/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst b/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst deleted file mode 100644 index 403c77a9d480ee..00000000000000 --- a/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst +++ /dev/null @@ -1,7 +0,0 @@ -Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to -a bypass of the TLS handshake and included protections (like certificate -verification) and treating sent unencrypted data as if it were -post-handshake TLS encrypted data. Security issue reported as -`CVE-2023-40217 -`_ by -Aapo Oksman. Patch by Gregory P. Smith. diff --git a/Misc/NEWS.d/next/Security/2024-01-02-19-52-23.gh-issue-113659.DkmnQc.rst b/Misc/NEWS.d/next/Security/2024-01-02-19-52-23.gh-issue-113659.DkmnQc.rst new file mode 100644 index 00000000000000..744687e72324d1 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2024-01-02-19-52-23.gh-issue-113659.DkmnQc.rst @@ -0,0 +1 @@ +Skip ``.pth`` files with names starting with a dot or hidden file attribute. diff --git a/Misc/NEWS.d/next/Tests/2023-10-11-16-02-55.gh-issue-108310.URRe8Y.rst b/Misc/NEWS.d/next/Tests/2023-10-11-16-02-55.gh-issue-108310.URRe8Y.rst new file mode 100644 index 00000000000000..87f0a3b0ddfd30 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2023-10-11-16-02-55.gh-issue-108310.URRe8Y.rst @@ -0,0 +1,2 @@ +SSL tests for pre-handshake close were previously not enabled on Python 3.8 +due to an incorrect backport. This is now fixed. Patch by Lumír Balhar. diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst b/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst deleted file mode 100644 index c43ee680e8158e..00000000000000 --- a/Misc/NEWS.d/next/Tools-Demos/2023-08-12-13-18-15.gh-issue-107565.Tv22Ne.rst +++ /dev/null @@ -1,2 +0,0 @@ -Update multissltests and GitHub CI workflows to use OpenSSL 1.1.1v, 3.0.10, -and 3.1.2. diff --git a/Misc/NEWS.d/next/Windows/2023-09-29-10-35-29.gh-issue-109991.GmuzGZ.rst b/Misc/NEWS.d/next/Windows/2023-09-29-10-35-29.gh-issue-109991.GmuzGZ.rst new file mode 100644 index 00000000000000..e5b4b46630effa --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2023-09-29-10-35-29.gh-issue-109991.GmuzGZ.rst @@ -0,0 +1,4 @@ +Windows builds now use OpenSSL 1.1.1w. Note that OpenSSL 1.1 has reached its +end of life and no future fixes will be made, and this version of Python is +no longer receiving maintenance fixes and will not be updated to OpenSSL +3.0. diff --git a/Modules/cjkcodecs/_codecs_iso2022.c b/Modules/cjkcodecs/_codecs_iso2022.c index 7394cf67e0e7dd..6d906ecdd396c2 100644 --- a/Modules/cjkcodecs/_codecs_iso2022.c +++ b/Modules/cjkcodecs/_codecs_iso2022.c @@ -181,8 +181,9 @@ ENCODER(iso2022) encoded = MAP_UNMAPPABLE; for (dsg = CONFIG_DESIGNATIONS; dsg->mark; dsg++) { + Py_UCS4 buf[2] = {c, 0}; Py_ssize_t length = 1; - encoded = dsg->encoder(&c, &length); + encoded = dsg->encoder(buf, &length); if (encoded == MAP_MULTIPLE_AVAIL) { /* this implementation won't work for pair * of non-bmp characters. */ @@ -191,9 +192,11 @@ ENCODER(iso2022) return MBERR_TOOFEW; length = -1; } - else + else { + buf[1] = INCHAR2; length = 2; - encoded = dsg->encoder(&c, &length); + } + encoded = dsg->encoder(buf, &length); if (encoded != MAP_UNMAPPABLE) { insize = length; break; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 5406f8b46f243f..c085b77bcd3348 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -6803,17 +6803,23 @@ Returns the interface index corresponding to the interface name if_name."); static PyObject * socket_if_indextoname(PyObject *self, PyObject *arg) { + unsigned long index_long = PyLong_AsUnsignedLong(arg); + if (index_long == (unsigned long) -1 && PyErr_Occurred()) { + return NULL; + } + #ifdef MS_WINDOWS - NET_IFINDEX index; + NET_IFINDEX index = (NET_IFINDEX)index_long; #else - unsigned long index; + unsigned int index = (unsigned int)index_long; #endif - char name[IF_NAMESIZE + 1]; - index = PyLong_AsUnsignedLong(arg); - if (index == (unsigned long) -1) + if ((unsigned long)index != index_long) { + PyErr_SetString(PyExc_OverflowError, "index is too large"); return NULL; + } + char name[IF_NAMESIZE + 1]; if (if_indextoname(index, name) == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat index 16646bf551fa34..2322763d0d356f 100644 --- a/PCbuild/get_externals.bat +++ b/PCbuild/get_externals.bat @@ -53,7 +53,7 @@ echo.Fetching external libraries... set libraries= set libraries=%libraries% bzip2-1.0.8 if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0 -if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1u +if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1w set libraries=%libraries% sqlite-3.35.5.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 @@ -77,7 +77,7 @@ echo.Fetching external binaries... set binaries= if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.3.0 -if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1u +if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1w if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.9.0 if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 diff --git a/PCbuild/python.props b/PCbuild/python.props index 3d7c8df65c6c19..b5dc2a981b64ad 100644 --- a/PCbuild/python.props +++ b/PCbuild/python.props @@ -67,8 +67,8 @@ $(ExternalsDir)libffi-3.3.0\ $(libffiDir)$(ArchName)\ $(libffiOutDir)include - $(ExternalsDir)openssl-1.1.1u\ - $(ExternalsDir)openssl-bin-1.1.1u\$(ArchName)\ + $(ExternalsDir)openssl-1.1.1w\ + $(ExternalsDir)openssl-bin-1.1.1w\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ $(ExternalsDir)\zlib-1.2.12\ diff --git a/README.rst b/README.rst index 539b3f8e42c125..ecd047cf5e187f 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -This is Python version 3.8.17 +This is Python version 3.8.18 ============================= .. image:: https://travis-ci.org/python/cpython.svg?branch=3.8 diff --git a/Tools/scripts/verify_ensurepip_wheels.py b/Tools/scripts/verify_ensurepip_wheels.py index 044d1fd6b3cf2d..434a0b4c5387d4 100755 --- a/Tools/scripts/verify_ensurepip_wheels.py +++ b/Tools/scripts/verify_ensurepip_wheels.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 """ Compare checksums for wheels in :mod:`ensurepip` against the Cheeseshop. @@ -35,11 +35,17 @@ def print_error(file_path: str, message: str) -> None: def verify_wheel(package_name: str) -> bool: # Find the package on disk - package_path = next(WHEEL_DIR.glob(f"{package_name}*.whl"), None) - if not package_path: - print_error("", f"Could not find a {package_name} wheel on disk.") + package_paths = list(WHEEL_DIR.glob(f"{package_name}*.whl")) + if len(package_paths) != 1: + if package_paths: + for p in package_paths: + print_error(p, f"Found more than one wheel for package {package_name}.") + else: + print_error("", f"Could not find a {package_name} wheel on disk.") return False + package_path = package_paths[0] + print(f"Verifying checksum for {package_path}.") # Find the version of the package used by ensurepip diff --git a/aclocal.m4 b/aclocal.m4 index 04342a4982e2dd..e5e804276f501c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.16.2 -*- Autoconf -*- +# generated automatically by aclocal 1.16.3 -*- Autoconf -*- # Copyright (C) 1996-2020 Free Software Foundation, Inc. diff --git a/configure b/configure index c091865aff7502..7004660d2f01eb 100755 --- a/configure +++ b/configure @@ -783,6 +783,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -897,6 +898,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1149,6 +1151,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1286,7 +1297,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1439,6 +1450,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] diff --git a/pyconfig.h.in b/pyconfig.h.in index 41cfe07902794b..75cd6905a084cc 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -281,6 +281,9 @@ /* Define to 1 if you have the `dup3' function. */ #undef HAVE_DUP3 +/* Define if you have the '_dyld_shared_cache_contains_path' function. */ +#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH + /* Defined when any dynamic module loading is enabled. */ #undef HAVE_DYNAMIC_LOADING @@ -772,9 +775,6 @@ /* Define if you have the 'prlimit' functions. */ #undef HAVE_PRLIMIT -/* Define if you have the '_dyld_shared_cache_contains_path' function. */ -#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH - /* Define to 1 if you have the header file. */ #undef HAVE_PROCESS_H