From b3527f71641b28ffde73c5b20dfb37e394e4f4de Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jan 27 2021 00:18:04 +0000 Subject: import python3-3.6.8-36.el8 --- diff --git a/SOURCES/00356-k_and_a_options_for_pathfix.patch b/SOURCES/00356-k_and_a_options_for_pathfix.patch new file mode 100644 index 0000000..3782e6e --- /dev/null +++ b/SOURCES/00356-k_and_a_options_for_pathfix.patch @@ -0,0 +1,269 @@ +From 0cfd9a7f26488567b9a3e5ec192099a8b80ad9df Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Tue, 19 Jan 2021 07:55:37 +0100 +Subject: [PATCH] [PATCH] bpo-37064: Add -k and -a options to pathfix.py tool + (GH-16387) + +* bpo-37064: Add option -k to Tools/scripts/pathfix.py (GH-15548) + +Add flag -k to pathscript.py script: preserve shebang flags. + +(cherry picked from commit 50254ac4c179cb412e90682098c97db786143929) + +* bpo-37064: Add option -a to pathfix.py tool (GH-15717) + +Add option -a to Tools/Scripts/pathfix.py script: add flags. + +(cherry picked from commit 1dc1acbd73f05f14c974b7ce1041787d7abef31e) +--- + Lib/test/test_tools/test_pathfix.py | 104 ++++++++++++++++++++++++++++ + Tools/scripts/pathfix.py | 64 +++++++++++++++-- + 2 files changed, 163 insertions(+), 5 deletions(-) + create mode 100644 Lib/test/test_tools/test_pathfix.py + +diff --git a/Lib/test/test_tools/test_pathfix.py b/Lib/test/test_tools/test_pathfix.py +new file mode 100644 +index 0000000..1f0585e +--- /dev/null ++++ b/Lib/test/test_tools/test_pathfix.py +@@ -0,0 +1,104 @@ ++import os ++import subprocess ++import sys ++import unittest ++from test import support ++from test.test_tools import import_tool, scriptsdir ++ ++ ++class TestPathfixFunctional(unittest.TestCase): ++ script = os.path.join(scriptsdir, 'pathfix.py') ++ ++ def setUp(self): ++ self.temp_file = support.TESTFN ++ self.addCleanup(support.unlink, support.TESTFN) ++ ++ def pathfix(self, shebang, pathfix_flags, exitcode=0, stdout='', stderr=''): ++ with open(self.temp_file, 'w', encoding='utf8') as f: ++ f.write(f'{shebang}\n' + 'print("Hello world")\n') ++ ++ proc = subprocess.run( ++ [sys.executable, self.script, ++ *pathfix_flags, '-n', self.temp_file], ++ universal_newlines=True, stdout=subprocess.PIPE, ++ stderr=subprocess.PIPE) ++ ++ if stdout == '' and proc.returncode == 0: ++ stdout = f'{self.temp_file}: updating\n' ++ self.assertEqual(proc.returncode, exitcode, proc) ++ self.assertEqual(proc.stdout, stdout, proc) ++ self.assertEqual(proc.stderr, stderr, proc) ++ ++ with open(self.temp_file, 'r', encoding='utf8') as f: ++ output = f.read() ++ ++ lines = output.split('\n') ++ self.assertEqual(lines[1:], ['print("Hello world")', '']) ++ new_shebang = lines[0] ++ ++ if proc.returncode != 0: ++ self.assertEqual(shebang, new_shebang) ++ ++ return new_shebang ++ ++ def test_pathfix(self): ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python', ++ ['-i', '/usr/bin/python3']), ++ '#! /usr/bin/python3') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python -R', ++ ['-i', '/usr/bin/python3']), ++ '#! /usr/bin/python3') ++ ++ def test_pathfix_keeping_flags(self): ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python -R', ++ ['-i', '/usr/bin/python3', '-k']), ++ '#! /usr/bin/python3 -R') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python', ++ ['-i', '/usr/bin/python3', '-k']), ++ '#! /usr/bin/python3') ++ ++ def test_pathfix_adding_flag(self): ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python', ++ ['-i', '/usr/bin/python3', '-a', 's']), ++ '#! /usr/bin/python3 -s') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python -S', ++ ['-i', '/usr/bin/python3', '-a', 's']), ++ '#! /usr/bin/python3 -s') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python -V', ++ ['-i', '/usr/bin/python3', '-a', 'v', '-k']), ++ '#! /usr/bin/python3 -vV') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python', ++ ['-i', '/usr/bin/python3', '-a', 'Rs']), ++ '#! /usr/bin/python3 -Rs') ++ self.assertEqual( ++ self.pathfix( ++ '#! /usr/bin/env python -W default', ++ ['-i', '/usr/bin/python3', '-a', 's', '-k']), ++ '#! /usr/bin/python3 -sW default') ++ ++ def test_pathfix_adding_errors(self): ++ self.pathfix( ++ '#! /usr/bin/env python -E', ++ ['-i', '/usr/bin/python3', '-a', 'W default', '-k'], ++ exitcode=2, ++ stderr="-a option doesn't support whitespaces") ++ ++ ++if __name__ == '__main__': ++ unittest.main() +diff --git a/Tools/scripts/pathfix.py b/Tools/scripts/pathfix.py +index c5bf984..2dfa6e8 100755 +--- a/Tools/scripts/pathfix.py ++++ b/Tools/scripts/pathfix.py +@@ -1,6 +1,6 @@ + #!/usr/bin/env python3 + +-# Change the #! line occurring in Python scripts. The new interpreter ++# Change the #! line (shebang) occurring in Python scripts. The new interpreter + # pathname must be given with a -i option. + # + # Command line arguments are files or directories to be processed. +@@ -10,7 +10,13 @@ + # arguments). + # The original file is kept as a back-up (with a "~" attached to its name), + # -n flag can be used to disable this. +-# ++ ++# Sometimes you may find shebangs with flags such as `#! /usr/bin/env python -si`. ++# Normally, pathfix overwrites the entire line, including the flags. ++# To change interpreter and keep flags from the original shebang line, use -k. ++# If you want to keep flags and add to them one single literal flag, use option -a. ++ ++ + # Undoubtedly you can do this using find and sed or perl, but this is + # a nice example of Python code that recurses down a directory tree + # and uses regular expressions. Also note several subtleties like +@@ -33,16 +39,21 @@ rep = sys.stdout.write + new_interpreter = None + preserve_timestamps = False + create_backup = True ++keep_flags = False ++add_flags = b'' + + + def main(): + global new_interpreter + global preserve_timestamps + global create_backup +- usage = ('usage: %s -i /interpreter -p -n file-or-directory ...\n' % ++ global keep_flags ++ global add_flags ++ ++ usage = ('usage: %s -i /interpreter -p -n -k -a file-or-directory ...\n' % + sys.argv[0]) + try: +- opts, args = getopt.getopt(sys.argv[1:], 'i:pn') ++ opts, args = getopt.getopt(sys.argv[1:], 'i:a:kpn') + except getopt.error as msg: + err(str(msg) + '\n') + err(usage) +@@ -54,6 +65,13 @@ def main(): + preserve_timestamps = True + if o == '-n': + create_backup = False ++ if o == '-k': ++ keep_flags = True ++ if o == '-a': ++ add_flags = a.encode() ++ if b' ' in add_flags: ++ err("-a option doesn't support whitespaces") ++ sys.exit(2) + if not new_interpreter or not new_interpreter.startswith(b'/') or \ + not args: + err('-i option or file-or-directory missing\n') +@@ -70,10 +88,14 @@ def main(): + if fix(arg): bad = 1 + sys.exit(bad) + ++ + ispythonprog = re.compile(r'^[a-zA-Z0-9_]+\.py$') ++ ++ + def ispython(name): + return bool(ispythonprog.match(name)) + ++ + def recursedown(dirname): + dbg('recursedown(%r)\n' % (dirname,)) + bad = 0 +@@ -96,6 +118,7 @@ def recursedown(dirname): + if recursedown(fullname): bad = 1 + return bad + ++ + def fix(filename): + ## dbg('fix(%r)\n' % (filename,)) + try: +@@ -166,12 +189,43 @@ def fix(filename): + # Return success + return 0 + ++ ++def parse_shebang(shebangline): ++ shebangline = shebangline.rstrip(b'\n') ++ start = shebangline.find(b' -') ++ if start == -1: ++ return b'' ++ return shebangline[start:] ++ ++ ++def populate_flags(shebangline): ++ old_flags = b'' ++ if keep_flags: ++ old_flags = parse_shebang(shebangline) ++ if old_flags: ++ old_flags = old_flags[2:] ++ if not (old_flags or add_flags): ++ return b'' ++ # On Linux, the entire string following the interpreter name ++ # is passed as a single argument to the interpreter. ++ # e.g. "#! /usr/bin/python3 -W Error -s" runs "/usr/bin/python3 "-W Error -s" ++ # so shebang should have single '-' where flags are given and ++ # flag might need argument for that reasons adding new flags is ++ # between '-' and original flags ++ # e.g. #! /usr/bin/python3 -sW Error ++ return b' -' + add_flags + old_flags ++ ++ + def fixline(line): + if not line.startswith(b'#!'): + return line ++ + if b"python" not in line: + return line +- return b'#! ' + new_interpreter + b'\n' ++ ++ flags = populate_flags(line) ++ return b'#! ' + new_interpreter + flags + b'\n' ++ + + if __name__ == '__main__': + main() +-- +2.29.2 + diff --git a/SOURCES/00357-CVE-2021-3177.patch b/SOURCES/00357-CVE-2021-3177.patch new file mode 100644 index 0000000..339e1b5 --- /dev/null +++ b/SOURCES/00357-CVE-2021-3177.patch @@ -0,0 +1,184 @@ +From e92381a0a6a3e1f000956e1f1e70e543b9c2bcd5 Mon Sep 17 00:00:00 2001 +From: Benjamin Peterson +Date: Mon, 18 Jan 2021 14:47:05 -0600 +Subject: [PATCH] [3.6] closes bpo-42938: Replace snprintf with Python unicode + formatting in ctypes param reprs. (24239). (cherry picked from commit + 916610ef90a0d0761f08747f7b0905541f0977c7) + +Co-authored-by: Benjamin Peterson +--- + Lib/ctypes/test/test_parameters.py | 43 +++++++++++++++ + .../2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst | 2 + + Modules/_ctypes/callproc.c | 55 +++++++------------ + 3 files changed, 66 insertions(+), 34 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst + +diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py +index e4c25fd880cef..531894fdec838 100644 +--- a/Lib/ctypes/test/test_parameters.py ++++ b/Lib/ctypes/test/test_parameters.py +@@ -201,6 +201,49 @@ def __dict__(self): + with self.assertRaises(ZeroDivisionError): + WorseStruct().__setstate__({}, b'foo') + ++ def test_parameter_repr(self): ++ from ctypes import ( ++ c_bool, ++ c_char, ++ c_wchar, ++ c_byte, ++ c_ubyte, ++ c_short, ++ c_ushort, ++ c_int, ++ c_uint, ++ c_long, ++ c_ulong, ++ c_longlong, ++ c_ulonglong, ++ c_float, ++ c_double, ++ c_longdouble, ++ c_char_p, ++ c_wchar_p, ++ c_void_p, ++ ) ++ self.assertRegex(repr(c_bool.from_param(True)), r"^$") ++ self.assertEqual(repr(c_char.from_param(97)), "") ++ self.assertRegex(repr(c_wchar.from_param('a')), r"^$") ++ self.assertEqual(repr(c_byte.from_param(98)), "") ++ self.assertEqual(repr(c_ubyte.from_param(98)), "") ++ self.assertEqual(repr(c_short.from_param(511)), "") ++ self.assertEqual(repr(c_ushort.from_param(511)), "") ++ self.assertRegex(repr(c_int.from_param(20000)), r"^$") ++ self.assertRegex(repr(c_uint.from_param(20000)), r"^$") ++ self.assertRegex(repr(c_long.from_param(20000)), r"^$") ++ self.assertRegex(repr(c_ulong.from_param(20000)), r"^$") ++ self.assertRegex(repr(c_longlong.from_param(20000)), r"^$") ++ self.assertRegex(repr(c_ulonglong.from_param(20000)), r"^$") ++ self.assertEqual(repr(c_float.from_param(1.5)), "") ++ self.assertEqual(repr(c_double.from_param(1.5)), "") ++ self.assertEqual(repr(c_double.from_param(1e300)), "") ++ self.assertRegex(repr(c_longdouble.from_param(1.5)), r"^$") ++ self.assertRegex(repr(c_char_p.from_param(b'hihi')), "^$") ++ self.assertRegex(repr(c_wchar_p.from_param('hihi')), "^$") ++ self.assertRegex(repr(c_void_p.from_param(0x12)), r"^$") ++ + ################################################################ + + if __name__ == '__main__': +diff --git a/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst +new file mode 100644 +index 0000000000000..7df65a156feab +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst +@@ -0,0 +1,2 @@ ++Avoid static buffers when computing the repr of :class:`ctypes.c_double` and ++:class:`ctypes.c_longdouble` values. +diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c +index d1c190f359108..2bb289bce043f 100644 +--- a/Modules/_ctypes/callproc.c ++++ b/Modules/_ctypes/callproc.c +@@ -461,58 +461,47 @@ is_literal_char(unsigned char c) + static PyObject * + PyCArg_repr(PyCArgObject *self) + { +- char buffer[256]; + switch(self->tag) { + case 'b': + case 'B': +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.b); +- break; + case 'h': + case 'H': +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.h); +- break; + case 'i': + case 'I': +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.i); +- break; + case 'l': + case 'L': +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.l); +- break; + + case 'q': + case 'Q': +- sprintf(buffer, +-#ifdef MS_WIN32 +- "", +-#else +- "", +-#endif ++ return PyUnicode_FromFormat("", + self->tag, self->value.q); +- break; + case 'd': +- sprintf(buffer, "", +- self->tag, self->value.d); +- break; +- case 'f': +- sprintf(buffer, "", +- self->tag, self->value.f); +- break; +- ++ case 'f': { ++ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); ++ if (f == NULL) { ++ return NULL; ++ } ++ PyObject *result = PyUnicode_FromFormat("", self->tag, f); ++ Py_DECREF(f); ++ return result; ++ } + case 'c': + if (is_literal_char((unsigned char)self->value.c)) { +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.c); + } + else { +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, (unsigned char)self->value.c); + } +- break; + + /* Hm, are these 'z' and 'Z' codes useful at all? + Shouldn't they be replaced by the functionality of c_string +@@ -521,22 +510,20 @@ PyCArg_repr(PyCArgObject *self) + case 'z': + case 'Z': + case 'P': +- sprintf(buffer, "", ++ return PyUnicode_FromFormat("", + self->tag, self->value.p); + break; + + default: + if (is_literal_char((unsigned char)self->tag)) { +- sprintf(buffer, "", +- (unsigned char)self->tag, self); ++ return PyUnicode_FromFormat("", ++ (unsigned char)self->tag, (void *)self); + } + else { +- sprintf(buffer, "", +- (unsigned char)self->tag, self); ++ return PyUnicode_FromFormat("", ++ (unsigned char)self->tag, (void *)self); + } +- break; + } +- return PyUnicode_FromString(buffer); + } + + static PyMemberDef PyCArgType_members[] = { diff --git a/SPECS/python3.spec b/SPECS/python3.spec index f956b75..9d0b8b7 100644 --- a/SPECS/python3.spec +++ b/SPECS/python3.spec @@ -14,7 +14,7 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well Version: %{pybasever}.8 -Release: 34%{?dist} +Release: 36%{?dist} License: Python @@ -573,6 +573,17 @@ Patch354: 00354-cve-2020-26116-http-request-method-crlf-injection-in-httplib.pat # Fixed upstream: https://bugs.python.org/issue41944 Patch355: 00355-CVE-2020-27619.patch +# 00356 # +# options -a and -k for pathfix.py used in %%py3_shebang_fix +# Upstream: https://github.com/python/cpython/commit/c71c54c62600fd721baed3c96709e3d6e9c33817 +Patch356: 00356-k_and_a_options_for_pathfix.patch + +# 00357 # +# CVE-2021-3177 stack-based buffer overflow in PyCArg_repr in _ctypes/callproc.c +# Upstream: https://bugs.python.org/issue42938 +# Main BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1918168 +Patch357: 00357-CVE-2021-3177.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -897,6 +908,8 @@ git apply %{PATCH351} %patch353 -p1 %patch354 -p1 %patch355 -p1 +%patch356 -p1 +%patch357 -p1 # Remove files that should be generated by the build # (This is after patching, so that we can use patches directly from upstream) @@ -1822,6 +1835,14 @@ fi # ====================================================== %changelog +* Fri Jan 22 2021 Lumír Balhar - 3.6.8-36 +- Fix for CVE-2021-3177 +Resolves: rhbz#1918168 + +* Mon Jan 18 2021 Lumír Balhar - 3.6.8-35 +- New options -a and -k for pathfix.py script backported from upstream +Resolves: rhbz#1917691 + * Fri Dec 04 2020 Charalampos Stratakis - 3.6.8-34 - Security fix for CVE-2020-27619: eval() call on content received via HTTP in the CJK codec tests Resolves: rhbz#1890237