backport upstream patches
This commit is contained in:
parent
d0bb0111a7
commit
c1943ea60d
@ -0,0 +1,89 @@
|
||||
From a6f73f61147048187908299ae911c5ad498d813a Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Wed, 17 Jan 2024 14:47:26 +0100
|
||||
Subject: [PATCH] [3.9] bpo-37013: Fix the error handling in
|
||||
socket.if_indextoname() (GH-13503) (GH-112600)
|
||||
|
||||
* Fix a crash when pass UINT_MAX.
|
||||
* Fix an integer overflow on 64-bit non-Windows platforms.
|
||||
(cherry picked from commit 0daf555c6fb3feba77989382135a58215e1d70a5)
|
||||
|
||||
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
|
||||
---
|
||||
Lib/test/test_socket.py | 13 +++++++++++++
|
||||
...2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst | 3 +++
|
||||
Modules/socketmodule.c | 16 +++++++++++-----
|
||||
3 files changed, 27 insertions(+), 5 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2023-12-01-16-09-59.gh-issue-81194.FFad1c.rst
|
||||
|
||||
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
|
||||
index 127d61cb6a..043e554388 100755
|
||||
--- a/Lib/test/test_socket.py
|
||||
+++ b/Lib/test/test_socket.py
|
||||
@@ -1070,7 +1070,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/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 0000000000..feb7a8643b
|
||||
--- /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/Modules/socketmodule.c b/Modules/socketmodule.c
|
||||
index 133470f9b8..9e0223b127 100644
|
||||
--- a/Modules/socketmodule.c
|
||||
+++ b/Modules/socketmodule.c
|
||||
@@ -6890,17 +6890,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;
|
||||
--
|
||||
2.34.1.windows.1
|
||||
|
||||
@ -0,0 +1,146 @@
|
||||
From a2c59992e9e8d35baba9695eb186ad6c6ff85c51 Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Wed, 17 Jan 2024 14:48:06 +0100
|
||||
Subject: [PATCH] [3.9] gh-109858: Protect zipfile from "quoted-overlap"
|
||||
zipbomb (GH-110016) (GH-113915)
|
||||
|
||||
Raise BadZipFile when try to read an entry that overlaps with other entry or
|
||||
central directory.
|
||||
(cherry picked from commit 66363b9a7b9fe7c99eba3a185b74c5fdbf842eba)
|
||||
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
---
|
||||
Lib/test/test_zipfile.py | 60 +++++++++++++++++++
|
||||
Lib/zipfile.py | 12 ++++
|
||||
...-09-28-13-15-51.gh-issue-109858.43e2dg.rst | 3 +
|
||||
3 files changed, 75 insertions(+)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2023-09-28-13-15-51.gh-issue-109858.43e2dg.rst
|
||||
|
||||
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
|
||||
index bd383d3f68..17e95eb862 100644
|
||||
--- a/Lib/test/test_zipfile.py
|
||||
+++ b/Lib/test/test_zipfile.py
|
||||
@@ -2045,6 +2045,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 1e942a503e..95f95ee112 100644
|
||||
--- a/Lib/zipfile.py
|
||||
+++ b/Lib/zipfile.py
|
||||
@@ -338,6 +338,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)):
|
||||
@@ -379,6 +380,7 @@ def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
|
||||
self.external_attr = 0 # External file attributes
|
||||
self.compress_size = 0 # Size of the compressed file
|
||||
self.file_size = 0 # Size of the uncompressed file
|
||||
+ 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
|
||||
@@ -1399,6 +1401,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."""
|
||||
@@ -1554,6 +1562,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/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 0000000000..be279caffc
|
||||
--- /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.
|
||||
--
|
||||
2.34.1.windows.1
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
From 8fc8c45b6717be58ad927def1bf3ea05c83cab8c Mon Sep 17 00:00:00 2001
|
||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||
Date: Wed, 17 Jan 2024 16:28:17 +0200
|
||||
Subject: [PATCH] [3.9] gh-113659: Skip hidden .pth files (GH-113660)
|
||||
(GH-114146)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
(cherry picked from commit 74208ed0c440244fb809d8acc97cb9ef51e888e3)
|
||||
|
||||
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
|
||||
---
|
||||
Lib/site.py | 11 +++++-
|
||||
Lib/test/test_site.py | 39 +++++++++++++++++++
|
||||
...-01-02-19-52-23.gh-issue-113659.DkmnQc.rst | 1 +
|
||||
3 files changed, 50 insertions(+), 1 deletion(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2024-01-02-19-52-23.gh-issue-113659.DkmnQc.rst
|
||||
|
||||
diff --git a/Lib/site.py b/Lib/site.py
|
||||
index 9e617afb00..54ffc4fdc0 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/test/test_site.py b/Lib/test/test_site.py
|
||||
index 3d25d7e473..e578cd7db3 100644
|
||||
--- a/Lib/test/test_site.py
|
||||
+++ b/Lib/test/test_site.py
|
||||
@@ -16,6 +16,7 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
+import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
@@ -185,6 +186,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/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 0000000000..744687e723
|
||||
--- /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.
|
||||
--
|
||||
2.34.1.windows.1
|
||||
|
||||
@ -0,0 +1,214 @@
|
||||
From d54e22a669ae6e987199bb5d2c69bb5a46b0083b Mon Sep 17 00:00:00 2001
|
||||
From: Serhiy Storchaka <storchaka@gmail.com>
|
||||
Date: Wed, 17 Jan 2024 15:47:47 +0200
|
||||
Subject: [PATCH] [3.9] gh-91133: tempfile.TemporaryDirectory: fix symlink bug
|
||||
in cleanup (GH-99930) (GH-112842)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
(cherry picked from commit 81c16cd94ec38d61aa478b9a452436dc3b1b524d)
|
||||
|
||||
Co-authored-by: Søren Løvborg <sorenl@unity3d.com>
|
||||
---
|
||||
Lib/tempfile.py | 27 ++--
|
||||
Lib/test/test_tempfile.py | 117 +++++++++++++++++-
|
||||
...2-12-01-16-57-44.gh-issue-91133.LKMVCV.rst | 2 +
|
||||
3 files changed, 136 insertions(+), 10 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Library/2022-12-01-16-57-44.gh-issue-91133.LKMVCV.rst
|
||||
|
||||
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
|
||||
index eafce6f25b..59a628a174 100644
|
||||
--- a/Lib/tempfile.py
|
||||
+++ b/Lib/tempfile.py
|
||||
@@ -268,6 +268,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.
|
||||
|
||||
@@ -789,17 +805,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_tempfile.py b/Lib/test/test_tempfile.py
|
||||
index 8ad1bb98e8..571263d9c9 100644
|
||||
--- a/Lib/test/test_tempfile.py
|
||||
+++ b/Lib/test/test_tempfile.py
|
||||
@@ -1394,6 +1394,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
|
||||
@@ -1506,9 +1603,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/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 0000000000..7991048fc4
|
||||
--- /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.
|
||||
--
|
||||
2.34.1.windows.1
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
From 95c9c2b9cb2d3c1d29c8ce77f154de8bd5313dae Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Tue, 24 May 2022 01:52:49 -0700
|
||||
Subject: [PATCH] gh-93065: Fix HAMT to iterate correctly over 7-level
|
||||
deep
|
||||
trees (GH-93066) (#93147)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Also while there, clarify a few things about why we reduce the hash to
|
||||
32 bits.
|
||||
|
||||
Co-authored-by: Eli Libman <eli@hyro.ai>
|
||||
Co-authored-by: Yury Selivanov <yury@edgedb.com>
|
||||
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
|
||||
|
||||
(cherry picked from commit c1f5c903a7e4ed27190488f4e33b00d3c3d952e5)
|
||||
|
||||
---
|
||||
Include/internal/pycore_hamt.h | 14 +++++++++++++-
|
||||
Lib/test/test_context.py | 35 ++++++++++++++++++++++++++++++++++
|
||||
Misc/ACKS | 1 +
|
||||
Python/hamt.c | 14 +++++++++++---
|
||||
4 files changed, 60 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/Include/internal/pycore_hamt.h b/Include/internal/pycore_hamt.h
|
||||
index aaf6559..357d966 100644
|
||||
--- a/Include/internal/pycore_hamt.h
|
||||
+++ b/Include/internal/pycore_hamt.h
|
||||
@@ -5,7 +5,19 @@
|
||||
# error "this header requires Py_BUILD_CORE define"
|
||||
#endif
|
||||
|
||||
-#define _Py_HAMT_MAX_TREE_DEPTH 7
|
||||
+
|
||||
+/*
|
||||
+HAMT tree is shaped by hashes of keys. Every group of 5 bits of a hash denotes
|
||||
+the exact position of the key in one level of the tree. Since we're using
|
||||
+32 bit hashes, we can have at most 7 such levels. Although if there are
|
||||
+two distinct keys with equal hashes, they will have to occupy the same
|
||||
+cell in the 7th level of the tree -- so we'd put them in a "collision" node.
|
||||
+Which brings the total possible tree depth to 8. Read more about the actual
|
||||
+layout of the HAMT tree in `hamt.c`.
|
||||
+
|
||||
+This constant is used to define a datastucture for storing iteration state.
|
||||
+*/
|
||||
+#define _Py_HAMT_MAX_TREE_DEPTH 8
|
||||
|
||||
|
||||
#define PyHamt_Check(o) Py_IS_TYPE(o, &_PyHamt_Type)
|
||||
diff --git a/Lib/test/test_context.py b/Lib/test/test_context.py
|
||||
index 2d8b63a..689e3d4 100644
|
||||
--- a/Lib/test/test_context.py
|
||||
+++ b/Lib/test/test_context.py
|
||||
@@ -533,6 +533,41 @@ class HamtTest(unittest.TestCase):
|
||||
self.assertEqual(len(h4), 2)
|
||||
self.assertEqual(len(h5), 3)
|
||||
|
||||
+ def test_hamt_collision_3(self):
|
||||
+ # Test that iteration works with the deepest tree possible.
|
||||
+ # https://github.com/python/cpython/issues/93065
|
||||
+
|
||||
+ C = HashKey(0b10000000_00000000_00000000_00000000, 'C')
|
||||
+ D = HashKey(0b10000000_00000000_00000000_00000000, 'D')
|
||||
+
|
||||
+ E = HashKey(0b00000000_00000000_00000000_00000000, 'E')
|
||||
+
|
||||
+ h = hamt()
|
||||
+ h = h.set(C, 'C')
|
||||
+ h = h.set(D, 'D')
|
||||
+ h = h.set(E, 'E')
|
||||
+
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=2 count=1 bitmap=0b1):
|
||||
+ # NULL:
|
||||
+ # BitmapNode(size=4 count=2 bitmap=0b101):
|
||||
+ # <Key name:E hash:0>: 'E'
|
||||
+ # NULL:
|
||||
+ # CollisionNode(size=4 id=0x107a24520):
|
||||
+ # <Key name:C hash:2147483648>: 'C'
|
||||
+ # <Key name:D hash:2147483648>: 'D'
|
||||
+
|
||||
+ self.assertEqual({k.name for k in h.keys()}, {'C', 'D', 'E'})
|
||||
+
|
||||
def test_hamt_stress(self):
|
||||
COLLECTION_SIZE = 7000
|
||||
TEST_ITERS_EVERY = 647
|
||||
diff --git a/Misc/ACKS b/Misc/ACKS
|
||||
index ac893ac..8699b98 100644
|
||||
--- a/Misc/ACKS
|
||||
+++ b/Misc/ACKS
|
||||
@@ -1031,6 +1031,7 @@ Robert Li
|
||||
Xuanji Li
|
||||
Zekun Li
|
||||
Zheao Li
|
||||
+Eli Libman
|
||||
Dan Lidral-Porter
|
||||
Robert van Liere
|
||||
Ross Light
|
||||
diff --git a/Python/hamt.c b/Python/hamt.c
|
||||
index 8801c5e..3296109 100644
|
||||
--- a/Python/hamt.c
|
||||
+++ b/Python/hamt.c
|
||||
@@ -407,14 +407,22 @@ hamt_hash(PyObject *o)
|
||||
return -1;
|
||||
}
|
||||
|
||||
- /* While it's suboptimal to reduce Python's 64 bit hash to
|
||||
+ /* While it's somewhat suboptimal to reduce Python's 64 bit hash to
|
||||
32 bits via XOR, it seems that the resulting hash function
|
||||
is good enough (this is also how Long type is hashed in Java.)
|
||||
Storing 10, 100, 1000 Python strings results in a relatively
|
||||
shallow and uniform tree structure.
|
||||
|
||||
- Please don't change this hashing algorithm, as there are many
|
||||
- tests that test some exact tree shape to cover all code paths.
|
||||
+ Also it's worth noting that it would be possible to adapt the tree
|
||||
+ structure to 64 bit hashes, but that would increase memory pressure
|
||||
+ and provide little to no performance benefits for collections with
|
||||
+ fewer than billions of key/value pairs.
|
||||
+
|
||||
+ Important: do not change this hash reducing function. There are many
|
||||
+ tests that need an exact tree shape to cover all code paths and
|
||||
+ we do that by specifying concrete values for test data's `__hash__`.
|
||||
+ If this function is changed most of the regression tests would
|
||||
+ become useless.
|
||||
*/
|
||||
int32_t xored = (int32_t)(hash & 0xffffffffl) ^ (int32_t)(hash >> 32);
|
||||
return xored == -1 ? -2 : xored;
|
||||
--
|
||||
2.33.0
|
||||
|
||||
23
python3.spec
23
python3.spec
@ -3,7 +3,7 @@ Summary: Interpreter of the Python3 programming language
|
||||
URL: https://www.python.org/
|
||||
|
||||
Version: 3.9.9
|
||||
Release: 29
|
||||
Release: 30
|
||||
License: Python-2.0
|
||||
|
||||
%global branchversion 3.9
|
||||
@ -112,6 +112,11 @@ Patch6018: backport-3.9-gh-99889-Fix-directory-traversal-security-flaw-i.patch
|
||||
Patch6019: backport-Fix-parsing-errors-in-email-_parseaddr.py.patch
|
||||
Patch6020: backport-Revert-fixes-for-CVE-2023-27043.patch
|
||||
Patch6021: backport-CVE-2023-27043.patch
|
||||
Patch6022: backport-gh-93065-Fix-HAMT-to-iterate-correctly-over-7-level-.patch
|
||||
Patch6023: backport-3.9-bpo-37013-Fix-the-error-handling-in-socket.if_in.patch
|
||||
Patch6024: backport-3.9-gh-91133-tempfile.TemporaryDirectory-fix-symlink.patch
|
||||
Patch6025: backport-3.9-gh-109858-Protect-zipfile-from-quoted-overlap-zi.patch
|
||||
Patch6026: backport-3.9-gh-113659-Skip-hidden-.pth-files-GH-113660-GH-11.patch
|
||||
|
||||
Patch9000: add-the-sm3-method-for-obtaining-the-salt-value.patch
|
||||
Patch9001: python3-Add-sw64-architecture.patch
|
||||
@ -222,6 +227,11 @@ rm -r Modules/expat
|
||||
%patch6019 -p1
|
||||
%patch6020 -p1
|
||||
%patch6021 -p1
|
||||
%patch6022 -p1
|
||||
%patch6023 -p1
|
||||
%patch6024 -p1
|
||||
%patch6025 -p1
|
||||
%patch6026 -p1
|
||||
|
||||
%patch9000 -p1
|
||||
%patch9001 -p1
|
||||
@ -849,6 +859,17 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP"
|
||||
%{_mandir}/*/*
|
||||
|
||||
%changelog
|
||||
* Web Mar 23 2024 xinsheng <xinsheng3@huawei.com> - 3.9.9-30
|
||||
- Type:bugfix
|
||||
- CVE:NA
|
||||
- SUG:NA
|
||||
- DESC:backport upstream patches
|
||||
- Fix HAMT to iterate correctly over 7 level
|
||||
- Fix the error handling in socket.if_in
|
||||
- tempfile.TemporaryDirectory fix symlink
|
||||
- Protect zipfile from quoted overlap zi
|
||||
- Skip hidden .pth files GH 113660 GH 11
|
||||
|
||||
* Wed Jan 10 2024 zhaoyu <zhaoyu64@huawei.com>- 3.9.9-29
|
||||
- Type:CVE
|
||||
- CVE:CVE-2023-27043
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user