diff --git a/.gitignore b/.gitignore index fb34299..2576193 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/Python-2.7.16-noexe.tar.xz +SOURCES/Python-2.7.17-noexe.tar.xz diff --git a/.python2.metadata b/.python2.metadata index 1ad3965..dd6dfd3 100644 --- a/.python2.metadata +++ b/.python2.metadata @@ -1 +1 @@ -c3f14ebccf0b8848a154eb510c7fcf6a8bb038f4 SOURCES/Python-2.7.16-noexe.tar.xz +e63124a9a86b4b52c09384915a0842adf00b9d45 SOURCES/Python-2.7.17-noexe.tar.xz diff --git a/SOURCES/00153-fix-test_gdb-noise.patch b/SOURCES/00153-fix-test_gdb-noise.patch deleted file mode 100644 index 8884d4c..0000000 --- a/SOURCES/00153-fix-test_gdb-noise.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- Lib/test/test_gdb.py.old 2012-04-11 21:04:01.367073855 -0400 -+++ Lib/test/test_gdb.py 2012-04-12 08:52:58.320288761 -0400 -@@ -211,6 +211,10 @@ - # ignore all warnings - 'warning: ', - ) -+ ignore_patterns += ('warning: Unable to open', -+ 'Missing separate debuginfo for', -+ 'Try: yum --disablerepo=', -+ 'Undefined set print command') - for line in errlines: - if not line: - continue diff --git a/SOURCES/00157-uid-gid-overflows.patch b/SOURCES/00157-uid-gid-overflows.patch deleted file mode 100644 index a31c98a..0000000 --- a/SOURCES/00157-uid-gid-overflows.patch +++ /dev/null @@ -1,49 +0,0 @@ -diff -up Python-2.7.3/Lib/test/test_os.py.uid-gid-overflows Python-2.7.3/Lib/test/test_os.py ---- Python-2.7.3/Lib/test/test_os.py.uid-gid-overflows 2012-04-09 19:07:32.000000000 -0400 -+++ Python-2.7.3/Lib/test/test_os.py 2012-06-26 14:51:36.000817929 -0400 -@@ -677,30 +677,36 @@ if sys.platform != 'win32': - def test_setuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setuid, 0) -+ self.assertRaises(TypeError, os.setuid, 'not an int') - self.assertRaises(OverflowError, os.setuid, 1<<32) - - @unittest.skipUnless(hasattr(os, 'setgid'), 'test needs os.setgid()') - def test_setgid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setgid, 0) -+ self.assertRaises(TypeError, os.setgid, 'not an int') - self.assertRaises(OverflowError, os.setgid, 1<<32) - - @unittest.skipUnless(hasattr(os, 'seteuid'), 'test needs os.seteuid()') - def test_seteuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.seteuid, 0) -+ self.assertRaises(TypeError, os.seteuid, 'not an int') - self.assertRaises(OverflowError, os.seteuid, 1<<32) - - @unittest.skipUnless(hasattr(os, 'setegid'), 'test needs os.setegid()') - def test_setegid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setegid, 0) -+ self.assertRaises(TypeError, os.setegid, 'not an int') - self.assertRaises(OverflowError, os.setegid, 1<<32) - - @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') - def test_setreuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setreuid, 0, 0) -+ self.assertRaises(TypeError, os.setreuid, 'not an int', 0) -+ self.assertRaises(TypeError, os.setreuid, 0, 'not an int') - self.assertRaises(OverflowError, os.setreuid, 1<<32, 0) - self.assertRaises(OverflowError, os.setreuid, 0, 1<<32) - -@@ -715,6 +721,8 @@ if sys.platform != 'win32': - def test_setregid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setregid, 0, 0) -+ self.assertRaises(TypeError, os.setregid, 'not an int', 0) -+ self.assertRaises(TypeError, os.setregid, 0, 'not an int') - self.assertRaises(OverflowError, os.setregid, 1<<32, 0) - self.assertRaises(OverflowError, os.setregid, 0, 1<<32) - diff --git a/SOURCES/00168-distutils-cflags.patch b/SOURCES/00168-distutils-cflags.patch deleted file mode 100644 index 0c4a8df..0000000 --- a/SOURCES/00168-distutils-cflags.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -up Python-2.6.6/Lib/distutils/sysconfig.py.distutils-cflags Python-2.6.6/Lib/distutils/sysconfig.py ---- Python-2.6.6/Lib/distutils/sysconfig.py.distutils-cflags 2011-08-12 17:18:17.833091153 -0400 -+++ Python-2.6.6/Lib/distutils/sysconfig.py 2011-08-12 17:18:27.449106938 -0400 -@@ -187,7 +187,7 @@ def customize_compiler(compiler): - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: -- cflags = opt + ' ' + os.environ['CFLAGS'] -+ cflags = cflags + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] diff --git a/SOURCES/00189-use-rpm-wheels.patch b/SOURCES/00189-use-rpm-wheels.patch index dce22df..a9a9a7e 100644 --- a/SOURCES/00189-use-rpm-wheels.patch +++ b/SOURCES/00189-use-rpm-wheels.patch @@ -18,10 +18,10 @@ index 5021ebf..29a7d1b 100644 __all__ = ["version", "bootstrap"] --_SETUPTOOLS_VERSION = "40.6.2" +-_SETUPTOOLS_VERSION = "41.2.0" +_WHEEL_DIR = "/usr/share/python{}-wheels/".format(sys.version_info[0]) --_PIP_VERSION = "18.1" +-_PIP_VERSION = "19.2.3" +def _get_most_recent_wheel_version(pkg): + prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg)) + suffix = "-py2.py3-none-any.whl" diff --git a/SOURCES/00190-gdb-py-bt-dont-raise-exception-from-eval.patch b/SOURCES/00190-gdb-py-bt-dont-raise-exception-from-eval.patch deleted file mode 100644 index df1f4e7..0000000 --- a/SOURCES/00190-gdb-py-bt-dont-raise-exception-from-eval.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py -index 9def56e..c0df208 100755 ---- a/Tools/gdb/libpython.py -+++ b/Tools/gdb/libpython.py -@@ -939,6 +939,9 @@ class PyFrameObjectPtr(PyObjectPtr): - if self.is_optimized_out(): - return '(frame information optimized out)' - -+ if self.filename() == '': -+ return '(in an eval block)' -+ - lineno = self.current_line_num() - if lineno is None: - return '(failed to get frame line number)' diff --git a/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch b/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch deleted file mode 100644 index 670f3e4..0000000 --- a/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch +++ /dev/null @@ -1,152 +0,0 @@ -diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst -index 22249da..0989c88 100644 ---- a/Doc/library/urlparse.rst -+++ b/Doc/library/urlparse.rst -@@ -119,12 +119,22 @@ The :mod:`urlparse` module defines the following functions: - See section :ref:`urlparse-result-object` for more information on the result - object. - -+ Characters in the :attr:`netloc` attribute that decompose under NFKC -+ normalization (as used by the IDNA encoding) into any of ``/``, ``?``, -+ ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is -+ decomposed before parsing, or is not a Unicode string, no error will be -+ raised. -+ - .. versionchanged:: 2.5 - Added attributes to return value. - - .. versionchanged:: 2.7 - Added IPv6 URL parsing capabilities. - -+ .. versionchanged:: 2.7.17 -+ Characters that affect netloc parsing under NFKC normalization will -+ now raise :exc:`ValueError`. -+ - - .. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]]) - -@@ -232,11 +242,21 @@ The :mod:`urlparse` module defines the following functions: - See section :ref:`urlparse-result-object` for more information on the result - object. - -+ Characters in the :attr:`netloc` attribute that decompose under NFKC -+ normalization (as used by the IDNA encoding) into any of ``/``, ``?``, -+ ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is -+ decomposed before parsing, or is not a Unicode string, no error will be -+ raised. -+ - .. versionadded:: 2.2 - - .. versionchanged:: 2.5 - Added attributes to return value. - -+ .. versionchanged:: 2.7.17 -+ Characters that affect netloc parsing under NFKC normalization will -+ now raise :exc:`ValueError`. -+ - - .. function:: urlunsplit(parts) - -diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -index 4e1ded7..86c4a05 100644 ---- a/Lib/test/test_urlparse.py -+++ b/Lib/test/test_urlparse.py -@@ -1,4 +1,6 @@ - from test import test_support -+import sys -+import unicodedata - import unittest - import urlparse - -@@ -624,6 +626,45 @@ class UrlParseTestCase(unittest.TestCase): - self.assertEqual(urlparse.urlparse("http://www.python.org:80"), - ('http','www.python.org:80','','','','')) - -+ def test_urlsplit_normalization(self): -+ # Certain characters should never occur in the netloc, -+ # including under normalization. -+ # Ensure that ALL of them are detected and cause an error -+ illegal_chars = u'/:#?@' -+ hex_chars = {'{:04X}'.format(ord(c)) for c in illegal_chars} -+ denorm_chars = [ -+ c for c in map(unichr, range(128, sys.maxunicode)) -+ if (hex_chars & set(unicodedata.decomposition(c).split())) -+ and c not in illegal_chars -+ ] -+ # Sanity check that we found at least one such character -+ self.assertIn(u'\u2100', denorm_chars) -+ self.assertIn(u'\uFF03', denorm_chars) -+ -+ # bpo-36742: Verify port separators are ignored when they -+ # existed prior to decomposition -+ urlparse.urlsplit(u'http://\u30d5\u309a:80') -+ with self.assertRaises(ValueError): -+ urlparse.urlsplit(u'http://\u30d5\u309a\ufe1380') -+ -+ for scheme in [u"http", u"https", u"ftp"]: -+ for netloc in [u"netloc{}false.netloc", u"n{}user@netloc"]: -+ for c in denorm_chars: -+ url = u"{}://{}/path".format(scheme, netloc.format(c)) -+ if test_support.verbose: -+ print "Checking %r" % url -+ with self.assertRaises(ValueError): -+ urlparse.urlsplit(url) -+ -+ # check error message: invalid netloc must be formated with repr() -+ # to get an ASCII error message -+ with self.assertRaises(ValueError) as cm: -+ urlparse.urlsplit(u'http://example.com\uFF03@bing.com') -+ self.assertEqual(str(cm.exception), -+ "netloc u'example.com\\uff03@bing.com' contains invalid characters " -+ "under NFKC normalization") -+ self.assertIsInstance(cm.exception.args[0], str) -+ - def test_main(): - test_support.run_unittest(UrlParseTestCase) - -diff --git a/Lib/urlparse.py b/Lib/urlparse.py -index f7c2b03..798b467 100644 ---- a/Lib/urlparse.py -+++ b/Lib/urlparse.py -@@ -165,6 +165,25 @@ def _splitnetloc(url, start=0): - delim = min(delim, wdelim) # use earliest delim position - return url[start:delim], url[delim:] # return (domain, rest) - -+def _checknetloc(netloc): -+ if not netloc or not isinstance(netloc, unicode): -+ return -+ # looking for characters like \u2100 that expand to 'a/c' -+ # IDNA uses NFKC equivalence, so normalize for this check -+ import unicodedata -+ n = netloc.replace(u'@', u'') # ignore characters already included -+ n = n.replace(u':', u'') # but not the surrounding text -+ n = n.replace(u'#', u'') -+ n = n.replace(u'?', u'') -+ netloc2 = unicodedata.normalize('NFKC', n) -+ if n == netloc2: -+ return -+ for c in '/?#@:': -+ if c in netloc2: -+ raise ValueError("netloc %r contains invalid characters " -+ "under NFKC normalization" -+ % netloc) -+ - def urlsplit(url, scheme='', allow_fragments=True): - """Parse a URL into 5 components: - :///?# -@@ -193,6 +212,7 @@ def urlsplit(url, scheme='', allow_fragments=True): - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) -+ _checknetloc(netloc) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return v -@@ -216,6 +236,7 @@ def urlsplit(url, scheme='', allow_fragments=True): - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) -+ _checknetloc(netloc) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return v diff --git a/SOURCES/00323-coverity-scan-fixes.patch b/SOURCES/00323-coverity-scan-fixes.patch deleted file mode 100644 index 4b12000..0000000 --- a/SOURCES/00323-coverity-scan-fixes.patch +++ /dev/null @@ -1,423 +0,0 @@ -diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c -index 2097342..defcde1 100644 ---- a/Modules/_ctypes/callproc.c -+++ b/Modules/_ctypes/callproc.c -@@ -1831,6 +1831,7 @@ POINTER(PyObject *self, PyObject *cls) - "s(O){}", - buf, - &PyCPointer_Type); -+ PyMem_Free(buf); - if (result == NULL) - return result; - key = PyLong_FromVoidPtr(result); -diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c -index 46f041b..1b495fc 100644 ---- a/Modules/_ctypes/cfield.c -+++ b/Modules/_ctypes/cfield.c -@@ -1291,24 +1291,16 @@ U_set(void *ptr, PyObject *value, Py_ssize_t length) - static PyObject * - s_get(void *ptr, Py_ssize_t size) - { -- PyObject *result; -- size_t slen; -+ Py_ssize_t i; -+ char *p; - -- result = PyString_FromString((char *)ptr); -- if (!result) -- return NULL; -- /* chop off at the first NUL character, if any. -- * On error, result will be deallocated and set to NULL. -- */ -- slen = strlen(PyString_AS_STRING(result)); -- size = min(size, (Py_ssize_t)slen); -- if (result->ob_refcnt == 1) { -- /* shorten the result */ -- _PyString_Resize(&result, size); -- return result; -- } else -- /* cannot shorten the result */ -- return PyString_FromStringAndSize(ptr, size); -+ p = (char *)ptr; -+ for (i = 0; i < size; ++i) { -+ if (*p++ == '\0') -+ break; -+ } -+ -+ return PyBytes_FromStringAndSize((char *)ptr, (Py_ssize_t)i); - } - - static PyObject * -diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index de69f6f..78445eb 100644 ---- a/Modules/_hashopenssl.c -+++ b/Modules/_hashopenssl.c -@@ -133,12 +133,6 @@ newEVPobject(PyObject *name) - if (retval == NULL) - return NULL; - -- retval->ctx = EVP_MD_CTX_new(); -- if (retval->ctx == NULL) { -- PyErr_NoMemory(); -- return NULL; -- } -- - /* save the name for .name to return */ - Py_INCREF(name); - retval->name = name; -@@ -146,6 +140,13 @@ newEVPobject(PyObject *name) - retval->lock = NULL; - #endif - -+ retval->ctx = EVP_MD_CTX_new(); -+ if (retval->ctx == NULL) { -+ Py_DECREF(retval); -+ PyErr_NoMemory(); -+ return NULL; -+ } -+ - return retval; - } - -@@ -205,6 +206,7 @@ EVP_copy(EVPobject *self, PyObject *unused) - return NULL; - - if (!locked_EVP_MD_CTX_copy(newobj->ctx, self)) { -+ Py_DECREF(newobj); - return _setException(PyExc_ValueError); - } - return (PyObject *)newobj; -diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c -index 33cd38d..4fc5cee 100644 ---- a/Modules/_hotshot.c -+++ b/Modules/_hotshot.c -@@ -482,8 +482,11 @@ restart: - } - else if (!err) { - result = PyTuple_New(4); -- if (result == NULL) -+ if (result == NULL) { -+ Py_XDECREF(s1); -+ Py_XDECREF(s2); - return NULL; -+ } - PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what)); - PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno)); - if (s1 == NULL) -diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c -index b8c98a4..d68f7d8 100644 ---- a/Modules/_io/bufferedio.c -+++ b/Modules/_io/bufferedio.c -@@ -1363,6 +1363,7 @@ _bufferedreader_read_all(buffered *self) - res = buffered_flush_and_rewind_unlocked(self); - if (res == NULL) { - Py_DECREF(chunks); -+ Py_XDECREF(data); - return NULL; - } - Py_CLEAR(res); -diff --git a/Modules/_json.c b/Modules/_json.c -index 3a88882..050d37d 100644 ---- a/Modules/_json.c -+++ b/Modules/_json.c -@@ -1375,8 +1375,10 @@ _match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssiz - else { - double d = PyOS_string_to_double(PyString_AS_STRING(numstr), - NULL, NULL); -- if (d == -1.0 && PyErr_Occurred()) -+ if (d == -1.0 && PyErr_Occurred()) { -+ Py_DECREF(numstr); - return NULL; -+ } - rval = PyFloat_FromDouble(d); - } - } -diff --git a/Modules/linuxaudiodev.c b/Modules/linuxaudiodev.c -index 7fe20ae..f5135d9 100644 ---- a/Modules/linuxaudiodev.c -+++ b/Modules/linuxaudiodev.c -@@ -126,10 +126,12 @@ newladobject(PyObject *arg) - } - if (imode == O_WRONLY && ioctl(fd, SNDCTL_DSP_NONBLOCK, NULL) == -1) { - PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev); -+ close(fd); - return NULL; - } - if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) { - PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev); -+ close(fd); - return NULL; - } - /* Create and initialize the object */ -diff --git a/Parser/myreadline.c b/Parser/myreadline.c -index 59db41a..5376214 100644 ---- a/Parser/myreadline.c -+++ b/Parser/myreadline.c -@@ -108,7 +108,7 @@ char * - PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) - { - size_t n; -- char *p; -+ char *p, *pr; - n = 100; - if ((p = (char *)PyMem_MALLOC(n)) == NULL) - return NULL; -@@ -140,17 +140,29 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) - n = strlen(p); - while (n > 0 && p[n-1] != '\n') { - size_t incr = n+2; -- p = (char *)PyMem_REALLOC(p, n + incr); -- if (p == NULL) -- return NULL; - if (incr > INT_MAX) { -+ PyMem_FREE(p); - PyErr_SetString(PyExc_OverflowError, "input line too long"); -+ return NULL; -+ } -+ pr = (char *)PyMem_REALLOC(p, n + incr); -+ if (pr == NULL) { -+ PyMem_FREE(p); -+ PyErr_NoMemory(); -+ return NULL; - } -+ p = pr; - if (my_fgets(p+n, (int)incr, sys_stdin) != 0) - break; - n += strlen(p+n); - } -- return (char *)PyMem_REALLOC(p, n+1); -+ pr = (char *)PyMem_REALLOC(p, n+1); -+ if (pr == NULL) { -+ PyMem_FREE(p); -+ PyErr_NoMemory(); -+ return NULL; -+ } -+ return pr; - } - - -diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c -index c6e61df..8966661 100644 ---- a/Parser/tokenizer.c -+++ b/Parser/tokenizer.c -@@ -656,9 +656,14 @@ translate_newlines(const char *s, int exec_input, struct tok_state *tok) { - } - *current = '\0'; - final_length = current - buf + 1; -- if (final_length < needed_length && final_length) -+ if (final_length < needed_length && final_length) { - /* should never fail */ -- buf = PyMem_REALLOC(buf, final_length); -+ char* result = PyMem_REALLOC(buf, final_length); -+ if (result == NULL) { -+ PyMem_FREE(buf); -+ } -+ buf = result; -+ } - return buf; - } - -diff --git a/Python/dtoa.c b/Python/dtoa.c -index 73e23af..25eb9a7 100644 ---- a/Python/dtoa.c -+++ b/Python/dtoa.c -@@ -1514,8 +1514,9 @@ _Py_dg_strtod(const char *s00, char **se) - ULong y, z, abs_exp; - Long L; - BCinfo bc; -- Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; -+ Bigint *bb = NULL, *bd = NULL, *bd0 = NULL, *bs = NULL, *delta = NULL; - size_t ndigits, fraclen; -+ double result; - - dval(&rv) = 0.; - -@@ -1707,7 +1708,6 @@ _Py_dg_strtod(const char *s00, char **se) - if (k > 9) { - dval(&rv) = tens[k - 9] * dval(&rv) + z; - } -- bd0 = 0; - if (nd <= DBL_DIG - && Flt_Rounds == 1 - ) { -@@ -1877,14 +1877,11 @@ _Py_dg_strtod(const char *s00, char **se) - - bd = Balloc(bd0->k); - if (bd == NULL) { -- Bfree(bd0); - goto failed_malloc; - } - Bcopy(bd, bd0); - bb = sd2b(&rv, bc.scale, &bbe); /* srv = bb * 2^bbe */ - if (bb == NULL) { -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - /* Record whether lsb of bb is odd, in case we need this -@@ -1894,9 +1891,6 @@ _Py_dg_strtod(const char *s00, char **se) - /* tdv = bd * 10**e; srv = bb * 2**bbe */ - bs = i2b(1); - if (bs == NULL) { -- Bfree(bb); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - -@@ -1945,56 +1939,39 @@ _Py_dg_strtod(const char *s00, char **se) - - /* Scale bb, bd, bs by the appropriate powers of 2 and 5. */ - if (bb5 > 0) { -+ Bigint *bb1; - bs = pow5mult(bs, bb5); - if (bs == NULL) { -- Bfree(bb); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - if (bb == NULL) { -- Bfree(bs); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - } - if (bb2 > 0) { - bb = lshift(bb, bb2); - if (bb == NULL) { -- Bfree(bs); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - } - if (bd5 > 0) { - bd = pow5mult(bd, bd5); - if (bd == NULL) { -- Bfree(bb); -- Bfree(bs); -- Bfree(bd0); - goto failed_malloc; - } - } - if (bd2 > 0) { - bd = lshift(bd, bd2); - if (bd == NULL) { -- Bfree(bb); -- Bfree(bs); -- Bfree(bd0); - goto failed_malloc; - } - } - if (bs2 > 0) { - bs = lshift(bs, bs2); - if (bs == NULL) { -- Bfree(bb); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - } -@@ -2005,10 +1982,6 @@ _Py_dg_strtod(const char *s00, char **se) - - delta = diff(bb, bd); - if (delta == NULL) { -- Bfree(bb); -- Bfree(bs); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - dsign = delta->sign; -@@ -2062,10 +2035,6 @@ _Py_dg_strtod(const char *s00, char **se) - } - delta = lshift(delta,Log2P); - if (delta == NULL) { -- Bfree(bb); -- Bfree(bs); -- Bfree(bd); -- Bfree(bd0); - goto failed_malloc; - } - if (cmp(delta, bs) > 0) -@@ -2167,11 +2136,6 @@ _Py_dg_strtod(const char *s00, char **se) - if ((word0(&rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(&rv0) == Big0 && word1(&rv0) == Big1) { -- Bfree(bb); -- Bfree(bd); -- Bfree(bs); -- Bfree(bd0); -- Bfree(delta); - goto ovfl; - } - word0(&rv) = Big0; -@@ -2213,16 +2177,11 @@ _Py_dg_strtod(const char *s00, char **se) - } - } - cont: -- Bfree(bb); -- Bfree(bd); -- Bfree(bs); -- Bfree(delta); -+ Bfree(bb); bb = NULL; -+ Bfree(bd); bd = NULL; -+ Bfree(bs); bs = NULL; -+ Bfree(delta); delta = NULL; - } -- Bfree(bb); -- Bfree(bd); -- Bfree(bs); -- Bfree(bd0); -- Bfree(delta); - if (bc.nd > nd) { - error = bigcomp(&rv, s0, &bc); - if (error) -@@ -2236,24 +2195,37 @@ _Py_dg_strtod(const char *s00, char **se) - } - - ret: -- return sign ? -dval(&rv) : dval(&rv); -+ result = sign ? -dval(&rv) : dval(&rv); -+ goto done; - - parse_error: -- return 0.0; -+ result = 0.0; -+ goto done; - - failed_malloc: - errno = ENOMEM; -- return -1.0; -+ result = -1.0; -+ goto done; - - undfl: -- return sign ? -0.0 : 0.0; -+ result = sign ? -0.0 : 0.0; -+ goto done; - - ovfl: - errno = ERANGE; - /* Can't trust HUGE_VAL */ - word0(&rv) = Exp_mask; - word1(&rv) = 0; -- return sign ? -dval(&rv) : dval(&rv); -+ result = sign ? -dval(&rv) : dval(&rv); -+ goto done; -+ -+ done: -+ Bfree(bb); -+ Bfree(bd); -+ Bfree(bs); -+ Bfree(bd0); -+ Bfree(delta); -+ return result; - - } - diff --git a/SOURCES/00324-disallow-control-chars-in-http-urls.patch b/SOURCES/00324-disallow-control-chars-in-http-urls.patch deleted file mode 100644 index 84e788f..0000000 --- a/SOURCES/00324-disallow-control-chars-in-http-urls.patch +++ /dev/null @@ -1,172 +0,0 @@ -diff --git a/Lib/httplib.py b/Lib/httplib.py -index 60a8fb4e355f..1b41c346e090 100644 ---- a/Lib/httplib.py -+++ b/Lib/httplib.py -@@ -247,6 +247,16 @@ - _is_legal_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match - _is_illegal_header_value = re.compile(r'\n(?![ \t])|\r(?![ \t\n])').search - -+# These characters are not allowed within HTTP URL paths. -+# See https://tools.ietf.org/html/rfc3986#section-3.3 and the -+# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition. -+# Prevents CVE-2019-9740. Includes control characters such as \r\n. -+# Restrict non-ASCII characters above \x7f (0x80-0xff). -+_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]') -+# Arguably only these _should_ allowed: -+# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") -+# We are more lenient for assumed real world compatibility purposes. -+ - # We always set the Content-Length header for these methods because some - # servers will otherwise respond with a 411 - _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} -@@ -927,6 +937,12 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): - self._method = method - if not url: - url = '/' -+ # Prevent CVE-2019-9740. -+ match = _contains_disallowed_url_pchar_re.search(url) -+ if match: -+ raise InvalidURL("URL can't contain control characters. %r " -+ "(found at least %r)" -+ % (url, match.group())) - hdr = '%s %s %s' % (method, url, self._http_vsn_str) - - self._output(hdr) -diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py -index 1ce9201c0693..d7778d4194f3 100644 ---- a/Lib/test/test_urllib.py -+++ b/Lib/test/test_urllib.py -@@ -257,6 +257,31 @@ def test_url_fragment(self): - finally: - self.unfakehttp() - -+ def test_url_with_control_char_rejected(self): -+ for char_no in range(0, 0x21) + range(0x7f, 0x100): -+ char = chr(char_no) -+ schemeless_url = "//localhost:7777/test%s/" % char -+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") -+ try: -+ # urllib quotes the URL so there is no injection. -+ resp = urllib.urlopen("http:" + schemeless_url) -+ self.assertNotIn(char, resp.geturl()) -+ finally: -+ self.unfakehttp() -+ -+ def test_url_with_newline_header_injection_rejected(self): -+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") -+ host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123" -+ schemeless_url = "//" + host + ":8080/test/?test=a" -+ try: -+ # urllib quotes the URL so there is no injection. -+ resp = urllib.urlopen("http:" + schemeless_url) -+ self.assertNotIn(' ', resp.geturl()) -+ self.assertNotIn('\r', resp.geturl()) -+ self.assertNotIn('\n', resp.geturl()) -+ finally: -+ self.unfakehttp() -+ - def test_read_bogus(self): - # urlopen() should raise IOError for many error codes. - self.fakehttp('''HTTP/1.1 401 Authentication Required -diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py -index 6d24d5ddf83c..9531818e16b2 100644 ---- a/Lib/test/test_urllib2.py -+++ b/Lib/test/test_urllib2.py -@@ -15,6 +15,9 @@ - except ImportError: - ssl = None - -+from test.test_urllib import FakeHTTPMixin -+ -+ - # XXX - # Request - # CacheFTPHandler (hard to write) -@@ -1262,7 +1265,7 @@ def _test_basic_auth(self, opener, auth_handler, auth_header, - self.assertEqual(len(http_handler.requests), 1) - self.assertFalse(http_handler.requests[0].has_header(auth_header)) - --class MiscTests(unittest.TestCase): -+class MiscTests(unittest.TestCase, FakeHTTPMixin): - - def test_build_opener(self): - class MyHTTPHandler(urllib2.HTTPHandler): pass -@@ -1317,6 +1320,52 @@ def test_unsupported_algorithm(self): - "Unsupported digest authentication algorithm 'invalid'" - ) - -+ @unittest.skipUnless(ssl, "ssl module required") -+ def test_url_with_control_char_rejected(self): -+ for char_no in range(0, 0x21) + range(0x7f, 0x100): -+ char = chr(char_no) -+ schemeless_url = "//localhost:7777/test%s/" % char -+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") -+ try: -+ # We explicitly test urllib.request.urlopen() instead of the top -+ # level 'def urlopen()' function defined in this... (quite ugly) -+ # test suite. They use different url opening codepaths. Plain -+ # urlopen uses FancyURLOpener which goes via a codepath that -+ # calls urllib.parse.quote() on the URL which makes all of the -+ # above attempts at injection within the url _path_ safe. -+ escaped_char_repr = repr(char).replace('\\', r'\\') -+ InvalidURL = httplib.InvalidURL -+ with self.assertRaisesRegexp( -+ InvalidURL, "contain control.*" + escaped_char_repr): -+ urllib2.urlopen("http:" + schemeless_url) -+ with self.assertRaisesRegexp( -+ InvalidURL, "contain control.*" + escaped_char_repr): -+ urllib2.urlopen("https:" + schemeless_url) -+ finally: -+ self.unfakehttp() -+ -+ @unittest.skipUnless(ssl, "ssl module required") -+ def test_url_with_newline_header_injection_rejected(self): -+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.") -+ host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123" -+ schemeless_url = "//" + host + ":8080/test/?test=a" -+ try: -+ # We explicitly test urllib2.urlopen() instead of the top -+ # level 'def urlopen()' function defined in this... (quite ugly) -+ # test suite. They use different url opening codepaths. Plain -+ # urlopen uses FancyURLOpener which goes via a codepath that -+ # calls urllib.parse.quote() on the URL which makes all of the -+ # above attempts at injection within the url _path_ safe. -+ InvalidURL = httplib.InvalidURL -+ with self.assertRaisesRegexp( -+ InvalidURL, r"contain control.*\\r.*(found at least . .)"): -+ urllib2.urlopen("http:" + schemeless_url) -+ with self.assertRaisesRegexp(InvalidURL, r"contain control.*\\n"): -+ urllib2.urlopen("https:" + schemeless_url) -+ finally: -+ self.unfakehttp() -+ -+ - - class RequestTests(unittest.TestCase): - -diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py -index 36b3be67fd6b..90ccb30716ff 100644 ---- a/Lib/test/test_xmlrpc.py -+++ b/Lib/test/test_xmlrpc.py -@@ -659,7 +659,13 @@ def test_dotted_attribute(self): - def test_partial_post(self): - # Check that a partial POST doesn't make the server loop: issue #14001. - conn = httplib.HTTPConnection(ADDR, PORT) -- conn.request('POST', '/RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nbye') -+ conn.send('POST /RPC2 HTTP/1.0\r\n' -+ 'Content-Length: 100\r\n\r\n' -+ 'bye HTTP/1.1\r\n' -+ 'Host: %s:%s\r\n' -+ 'Accept-Encoding: identity\r\n' -+ 'Content-Length: 0\r\n\r\n' -+ % (ADDR, PORT)) - conn.close() - - class SimpleServerEncodingTestCase(BaseServerTestCase): -diff --git a/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst -new file mode 100644 -index 000000000000..47cb899df1af ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst -@@ -0,0 +1 @@ -+Address CVE-2019-9740 by disallowing URL paths with embedded whitespace or control characters through into the underlying http client request. Such potentially malicious header injection URLs now cause an httplib.InvalidURL exception to be raised. diff --git a/SOURCES/00325-CVE-2019-9948.patch b/SOURCES/00325-CVE-2019-9948.patch deleted file mode 100644 index 890bf71..0000000 --- a/SOURCES/00325-CVE-2019-9948.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py -index d2da0f8..7813b9f 100644 ---- a/Lib/test/test_urllib.py -+++ b/Lib/test/test_urllib.py -@@ -872,6 +872,17 @@ class URLopener_Tests(unittest.TestCase): - "spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"), - "//c:|windows%/:=&?~#+!$,;'@()*[]|/path/") - -+ def test_local_file_open(self): -+ # bpo-35907, CVE-2019-9948: urllib must reject local_file:// scheme -+ class DummyURLopener(urllib.URLopener): -+ def open_local_file(self, url): -+ return url -+ for url in ('local_file://example', 'local-file://example'): -+ self.assertRaises(IOError, urllib.urlopen, url) -+ self.assertRaises(IOError, urllib.URLopener().open, url) -+ self.assertRaises(IOError, urllib.URLopener().retrieve, url) -+ self.assertRaises(IOError, DummyURLopener().open, url) -+ self.assertRaises(IOError, DummyURLopener().retrieve, url) - - # Just commented them out. - # Can't really tell why keep failing in windows and sparc. -diff --git a/Lib/urllib.py b/Lib/urllib.py -index 2201e3e..71e3637 100644 ---- a/Lib/urllib.py -+++ b/Lib/urllib.py -@@ -198,7 +198,9 @@ class URLopener: - name = 'open_' + urltype - self.type = urltype - name = name.replace('-', '_') -- if not hasattr(self, name): -+ -+ # bpo-35907: disallow the file reading with the type not allowed -+ if not hasattr(self, name) or name == 'open_local_file': - if proxy: - return self.open_unknown_proxy(proxy, fullurl, data) - else: diff --git a/SPECS/python2.spec b/SPECS/python2.spec index 1c3cc9a..00e89ee 100644 --- a/SPECS/python2.spec +++ b/SPECS/python2.spec @@ -103,8 +103,8 @@ Summary: An interpreted, interactive, object-oriented programming language Name: %{python} # Remember to also rebase python2-docs when changing this: -Version: 2.7.16 -Release: 12%{?dist} +Version: 2.7.17 +Release: 1%{?dist} License: Python Group: Development/Languages Requires: %{python}-libs%{?_isa} = %{version}-%{release} @@ -583,13 +583,6 @@ Patch146: 00146-hashlib-fips.patch # Sent upstream as http://bugs.python.org/issue14785 Patch147: 00147-add-debug-malloc-stats.patch -# 00153 # -# Strip out lines of the form "warning: Unable to open ..." from gdb's stderr -# when running test_gdb.py; also cope with change to gdb in F17 onwards in -# which values are printed as "v@entry" rather than just "v": -# Not yet sent upstream -Patch153: 00153-fix-test_gdb-noise.patch - # 00155 # # Avoid allocating thunks in ctypes unless absolutely necessary, to avoid # generating SELinux denials on "import ctypes" and "import uuid" when @@ -603,22 +596,6 @@ Patch155: 00155-avoid-ctypes-thunks.patch # Not yet sent upstream Patch156: 00156-gdb-autoload-safepath.patch -# 00157 # -# Update uid/gid handling throughout the standard library: uid_t and gid_t are -# unsigned 32-bit values, but existing code often passed them through C long -# values, which are signed 32-bit values on 32-bit architectures, leading to -# negative int objects for uid/gid values >= 2^31 on 32-bit architectures. -# -# Introduce _PyObject_FromUid/Gid to convert uid_t/gid_t values to python -# objects, using int objects where the value will fit (long objects otherwise), -# and _PyArg_ParseUid/Gid to convert int/long to uid_t/gid_t, with -1 allowed -# as a special case (since this is given special meaning by the chown syscall) -# -# Update standard library to use this throughout for uid/gid values, so that -# very large uid/gid values are round-trippable, and -1 remains usable. -# (rhbz#697470) -Patch157: 00157-uid-gid-overflows.patch - # 00165 # # Backport to Python 2 from Python 3.3 of improvements to the "crypt" module # adding precanned ways of salting a password (rhbz#835021) @@ -637,18 +614,6 @@ Patch165: 00165-crypt-module-salt-backport.patch # Not yet sent upstream Patch167: 00167-disable-stack-navigation-tests-when-optimized-in-test_gdb.patch -# 00168 # -# Update distutils.sysconfig so that if CFLAGS is defined in the environment, -# when building extension modules, it is appended to the full compilation -# flags from Python's Makefile, rather than instead reducing the compilation -# flags to the subset within OPT and adding it to those. -# -# In particular, this should ensure that "-fno-strict-aliasing" is used by -# "python setup.py build" even when CFLAGS is defined in the environment. -# -# (rhbz#849994) -Patch168: 00168-distutils-cflags.patch - # 00170 # # In debug builds, try to print repr() when a C-level assert fails in the # garbage collector (typically indicating a reference-counting error @@ -700,12 +665,6 @@ Patch187: 00187-add-RPATH-to-pyexpat.patch # /usr/share/python2-wheels Patch189: 00189-use-rpm-wheels.patch -# 00190 # -# Fixes gdb py-bt command not to raise exception while processing -# statements from eval -# rhbz#1008154 (patch by Attila Fazekas) -Patch190: 00190-gdb-py-bt-dont-raise-exception-from-eval.patch - # 00191 # # Disabling NOOP test as it fails without internet connection Patch191: 00191-disable-NOOP.patch @@ -736,44 +695,6 @@ Patch288: 00288-ambiguous-python-version-rpmbuild-warn.patch # (we handle it it in Setup.dist, see Patch0) Patch289: 00289-disable-nis-detection.patch -# 00320 # -# Security fix for CVE-2019-9636 and CVE-2019-10160: Information Disclosure due to urlsplit improper NFKC normalization -# Fixed upstream: https://bugs.python.org/issue36216 and https://bugs.python.org/issue36742 -# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1689327 -Patch320: 00320-CVE-2019-9636-and-CVE-2019-10160.patch - -# 00323 # -# Coverity scan fixes -# Fixed upstream: -# https://bugs.python.org/issue13096 -# https://bugs.python.org/issue36147 -# https://bugs.python.org/issue36179 -# https://bugs.python.org/issue36212 -# https://bugs.python.org/issue36289 -# https://bugs.python.org/issue36291 -# https://bugs.python.org/issue36186 -# https://bugs.python.org/issue18368 -# https://bugs.python.org/issue36367 -# https://bugs.python.org/issue36262 -# https://bugs.python.org/issue36459 -# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1690919 -Patch323: 00323-coverity-scan-fixes.patch - -# 00324 # -# Disallow control chars in http URLs -# Security fix for CVE-2019-9740 and CVE-2019-9947 -# Fixed upstream: https://bugs.python.org/issue30458 -# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1703539 -# and https://bugzilla.redhat.com/show_bug.cgi?id=1704367 -Patch324: 00324-disallow-control-chars-in-http-urls.patch - -# 00325 # -# Unnecessary URL scheme exists to allow local_file:// reading file in urllib -# Security fix for CVE-2019-9948 -# Fixed upstream: https://bugs.python.org/issue35907 -# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1704176 -Patch325: 00325-CVE-2019-9948.patch - # (New patches go here ^^^) # # When adding new patches to "python2" and "python3" in Fedora, EL, etc., @@ -1062,14 +983,11 @@ rm -r Modules/zlib || exit 1 %endif %patch146 -p1 %patch147 -p1 -%patch153 -p0 %patch155 -p1 %patch156 -p1 -%patch157 -p1 %patch165 -p1 mv Modules/cryptmodule.c Modules/_cryptmodule.c %patch167 -p1 -%patch168 -p1 %patch170 -p1 %patch174 -p1 -b .fix-for-usr-move %patch180 -p1 @@ -1082,16 +1000,11 @@ mv Modules/cryptmodule.c Modules/_cryptmodule.c rm Lib/ensurepip/_bundled/*.whl %endif -%patch190 -p1 %patch191 -p1 %patch193 -p1 %patch257 -p1 %patch288 -p1 %patch289 -p1 -%patch320 -p1 -%patch323 -p1 -%patch324 -p1 -%patch325 -p1 # This shouldn't be necesarry, but is right now (2.2a3) find -name "*~" |xargs rm -f @@ -2030,6 +1943,10 @@ fi # ====================================================== %changelog +* Wed Oct 23 2019 Charalampos Stratakis - 2.7.17-1 +- Update to 2.7.17 +Resolves: rhbz#1759944 + * Tue Sep 03 2019 Tomas Orsava - 2.7.16-12 - Adding FIPS compliance to Python 2 in RHEL8: - Updated patch 146 with a new version of the FIPS patch