From 254af2a371ac6ea298ed4edef83216508cdc3e0c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 05 2019 19:36:30 +0000 Subject: import python2-2.7.16-12.module+el8.1.0+4148+33a50073 --- diff --git a/.gitignore b/.gitignore index f0ec3cd..fb34299 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/Python-2.7.15-noexe.tar.xz +SOURCES/Python-2.7.16-noexe.tar.xz diff --git a/.python2.metadata b/.python2.metadata index 42bf267..1ad3965 100644 --- a/.python2.metadata +++ b/.python2.metadata @@ -1 +1 @@ -621c26410e4fbfdcb3683c34365f1b579b21b99d SOURCES/Python-2.7.15-noexe.tar.xz +c3f14ebccf0b8848a154eb510c7fcf6a8bb038f4 SOURCES/Python-2.7.16-noexe.tar.xz diff --git a/SOURCES/00102-2.7.13-lib64.patch b/SOURCES/00102-2.7.13-lib64.patch index 5a87d03..02d3f6c 100644 --- a/SOURCES/00102-2.7.13-lib64.patch +++ b/SOURCES/00102-2.7.13-lib64.patch @@ -20,10 +20,10 @@ index b9f1c6c..7b23714 100644 'scripts': '$base/bin', 'data' : '$base', diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py -index 068d1ba..3e7f077 100644 +index 031f809..ec5d584 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py -@@ -119,8 +119,12 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): +@@ -120,8 +120,12 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": @@ -59,10 +59,10 @@ index c360802..868b7cb 100644 return sitepackages diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py -index d9a9324..e411e5c 100644 +index b4384ee..349f688 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py -@@ -235,17 +235,20 @@ class HelperFunctionsTests(unittest.TestCase): +@@ -254,17 +254,20 @@ class HelperFunctionsTests(unittest.TestCase): self.assertEqual(dirs[0], wanted) elif os.sep == '/': # OS X, Linux, FreeBSD, etc @@ -86,12 +86,12 @@ index d9a9324..e411e5c 100644 + wanted = os.path.join('xoxo', 'lib64', 'site-packages') self.assertEqual(dirs[1], wanted) - class PthFile(object): + def test_no_home_directory(self): diff --git a/Makefile.pre.in b/Makefile.pre.in -index adae76b..ecb27f3 100644 +index 4f59dd3..877698c 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -111,7 +111,7 @@ LIBDIR= @libdir@ +@@ -110,7 +110,7 @@ LIBDIR= @libdir@ MANDIR= @mandir@ INCLUDEDIR= @includedir@ CONFINCLUDEDIR= $(exec_prefix)/include @@ -101,10 +101,10 @@ index adae76b..ecb27f3 100644 # Detailed destination directories BINLIBDEST= $(LIBDIR)/python$(VERSION) diff --git a/Modules/Setup.dist b/Modules/Setup.dist -index fbfa1c1..138fb33 100644 +index 2cf35a9..c4c88cb 100644 --- a/Modules/Setup.dist +++ b/Modules/Setup.dist -@@ -231,7 +231,7 @@ +@@ -231,7 +231,7 @@ crypt cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems # Some more UNIX dependent modules -- off by default, since these # are not supported by all UNIX systems: @@ -130,7 +130,7 @@ index fbfa1c1..138fb33 100644 +zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib64 -lz # Interface to the Expat XML parser - # + # More information on Expat can be found at www.libexpat.org. diff --git a/Modules/getpath.c b/Modules/getpath.c index fd33a01..c5c86fd 100644 --- a/Modules/getpath.c @@ -154,7 +154,7 @@ index fd33a01..c5c86fd 100644 /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ diff --git a/setup.py b/setup.py -index 99ac359..859b6c4 100644 +index 0288a6b..7905f6f 100644 --- a/setup.py +++ b/setup.py @@ -456,7 +456,7 @@ class PyBuildExt(build_ext): diff --git a/SOURCES/00146-hashlib-fips.patch b/SOURCES/00146-hashlib-fips.patch index badb629..52ae30e 100644 --- a/SOURCES/00146-hashlib-fips.patch +++ b/SOURCES/00146-hashlib-fips.patch @@ -1,44 +1,252 @@ -diff -up Python-2.7.2/Lib/hashlib.py.hashlib-fips Python-2.7.2/Lib/hashlib.py ---- Python-2.7.2/Lib/hashlib.py.hashlib-fips 2011-06-11 11:46:24.000000000 -0400 -+++ Python-2.7.2/Lib/hashlib.py 2011-09-14 00:21:26.194252001 -0400 -@@ -6,9 +6,12 @@ +From ece76465680b0df5b3fce7bf8ff1ff0253933889 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 2 Sep 2019 17:33:29 +0200 +Subject: [PATCH 01/11] Remove HASH_OBJ_CONSTRUCTOR + +See https://github.com/python/cpython/commit/c7e219132aff1e21cb9ccb0a9b570dc6c750039b +--- + Modules/_hashopenssl.c | 59 ------------------------------------------ + 1 file changed, 59 deletions(-) + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index 78445ebabdd3..cb81e9765251 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -48,10 +48,6 @@ + * to allow the user to optimize based on the platform they're using. */ + #define HASHLIB_GIL_MINSIZE 2048 + +-#ifndef HASH_OBJ_CONSTRUCTOR +-#define HASH_OBJ_CONSTRUCTOR 0 +-#endif +- + #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x00908000) + #define _OPENSSL_SUPPORTS_SHA2 + #endif +@@ -384,53 +380,6 @@ EVP_repr(PyObject *self) + return PyString_FromString(buf); + } - __doc__ = """hashlib module - A common interface to many hash functions. +-#if HASH_OBJ_CONSTRUCTOR +-static int +-EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) +-{ +- static char *kwlist[] = {"name", "string", NULL}; +- PyObject *name_obj = NULL; +- Py_buffer view = { 0 }; +- char *nameStr; +- const EVP_MD *digest; +- +- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*:HASH", kwlist, +- &name_obj, &view)) { +- return -1; +- } +- +- if (!PyArg_Parse(name_obj, "s", &nameStr)) { +- PyErr_SetString(PyExc_TypeError, "name must be a string"); +- PyBuffer_Release(&view); +- return -1; +- } +- +- digest = EVP_get_digestbyname(nameStr); +- if (!digest) { +- PyErr_SetString(PyExc_ValueError, "unknown hash function"); +- PyBuffer_Release(&view); +- return -1; +- } +- EVP_DigestInit(self->ctx, digest); +- +- self->name = name_obj; +- Py_INCREF(self->name); +- +- if (view.obj) { +- if (view.len >= HASHLIB_GIL_MINSIZE) { +- Py_BEGIN_ALLOW_THREADS +- EVP_hash(self, view.buf, view.len); +- Py_END_ALLOW_THREADS +- } else { +- EVP_hash(self, view.buf, view.len); +- } +- PyBuffer_Release(&view); +- } +- +- return 0; +-} +-#endif +- --new(name, string='') - returns a new hash object implementing the -- given hash function; initializing the hash -- using the given string data. -+new(name, string='', usedforsecurity=True) -+ - returns a new hash object implementing the given hash function; -+ initializing the hash using the given string data. -+ -+ "usedforsecurity" is a non-standard extension for better supporting -+ FIPS-compliant environments (see below) + PyDoc_STRVAR(hashtype_doc, + "A hash represents the object used to calculate a checksum of a\n\ +@@ -487,9 +436,6 @@ static PyTypeObject EVPtype = { + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + #endif +-#if HASH_OBJ_CONSTRUCTOR +- (initproc)EVP_tp_init, /* tp_init */ +-#endif + }; - Named constructor functions are also available, these are much faster - than using new(): -@@ -24,6 +27,20 @@ the zlib module. - Choose your hash function wisely. Some have known collision weaknesses. - sha384 and sha512 will be slow on 32 bit platforms. + static PyObject * +@@ -928,11 +874,6 @@ init_hashlib(void) + return; + } -+Our implementation of hashlib uses OpenSSL. -+ -+OpenSSL has a "FIPS mode", which, if enabled, may restrict the available hashes -+to only those that are compliant with FIPS regulations. For example, it may -+deny the use of MD5, on the grounds that this is not secure for uses such as -+authentication, system integrity checking, or digital signatures. -+ -+If you need to use such a hash for non-security purposes (such as indexing into -+a data structure for speed), you can override the keyword argument -+"usedforsecurity" from True to False to signify that your code is not relying -+on the hash for security purposes, and this will allow the hash to be usable -+even in FIPS mode. This is not a standard feature of Python 2.7's hashlib, and -+is included here to better support FIPS mode. +-#if HASH_OBJ_CONSTRUCTOR +- Py_INCREF(&EVPtype); +- PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); +-#endif +- + /* these constants are used by the convenience constructors */ + INIT_CONSTRUCTOR_CONSTANTS(md5); + INIT_CONSTRUCTOR_CONSTANTS(sha1); + +From d7339af75678c760f6d6c0eb455b0eb889c22574 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 2 Sep 2019 18:02:25 +0200 +Subject: [PATCH 02/11] Add the usedforsecurity argument to _hashopenssl + +--- + Modules/_hashopenssl.c | 63 ++++++++++++++++++++++++++++++++---------- + 1 file changed, 48 insertions(+), 15 deletions(-) + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index cb81e9765251..f2dbc095cc66 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -441,7 +441,7 @@ static PyTypeObject EVPtype = { + static PyObject * + EVPnew(PyObject *name_obj, + const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, +- const unsigned char *cp, Py_ssize_t len) ++ const unsigned char *cp, Py_ssize_t len, int usedforsecurity) + { + EVPobject *self; + +@@ -456,7 +456,23 @@ EVPnew(PyObject *name_obj, + if (initial_ctx) { + EVP_MD_CTX_copy(self->ctx, initial_ctx); + } else { +- EVP_DigestInit(self->ctx, digest); ++ EVP_MD_CTX_init(self->ctx); + - Hash objects have these methods: - - update(arg): Update the hash object with the string arg. Repeated calls - are equivalent to a single call with the concatenation of all -@@ -63,76 +80,41 @@ algorithms = __always_supported ++ /* ++ If the user has declared that this digest is being used in a ++ non-security role (e.g. indexing into a data structure), set ++ the exception flag for openssl to allow it ++ */ ++ if (!usedforsecurity) { ++#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW ++ EVP_MD_CTX_set_flags(self->ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); ++#endif ++ } ++ if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) { ++ _setException(PyExc_ValueError); ++ Py_DECREF(self); ++ return NULL; ++ } + } + + if (cp && len) { +@@ -485,15 +501,16 @@ The MD5 and SHA1 algorithms are always supported.\n"); + static PyObject * + EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) + { +- static char *kwlist[] = {"name", "string", NULL}; ++ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL}; + PyObject *name_obj = NULL; + Py_buffer view = { 0 }; + PyObject *ret_obj; + char *name; + const EVP_MD *digest; ++ int usedforsecurity = 1; + +- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*:new", kwlist, +- &name_obj, &view)) { ++ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*i:new", kwlist, ++ &name_obj, &view, &usedforsecurity)) { + return NULL; + } + +@@ -506,7 +523,7 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) + digest = EVP_get_digestbyname(name); + + ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, +- view.len); ++ view.len, usedforsecurity); + PyBuffer_Release(&view); + + return ret_obj; +@@ -771,30 +788,46 @@ generate_hash_name_list(void) + * the generic one passing it a python string and are noticeably + * faster than calling a python new() wrapper. Thats important for + * code that wants to make hashes of a bunch of small strings. ++ * ++ * For usedforsecurity=False, the optimization is not used. + */ + #define GEN_CONSTRUCTOR(NAME) \ + static PyObject * \ +- EVP_new_ ## NAME (PyObject *self, PyObject *args) \ ++ EVP_new_ ## NAME (PyObject *self, PyObject *args, PyObject *kwdict) \ + { \ ++ static char *kwlist[] = {"string", "usedforsecurity", NULL}; \ + Py_buffer view = { 0 }; \ + PyObject *ret_obj; \ ++ int usedforsecurity=1; \ + \ +- if (!PyArg_ParseTuple(args, "|s*:" #NAME , &view)) { \ ++ if (!PyArg_ParseTupleAndKeywords( \ ++ args, kwdict, "|s*i:" #NAME, kwlist, \ ++ &view, &usedforsecurity \ ++ )) { \ + return NULL; \ + } \ +- \ +- ret_obj = EVPnew( \ +- CONST_ ## NAME ## _name_obj, \ +- NULL, \ +- CONST_new_ ## NAME ## _ctx_p, \ +- (unsigned char*)view.buf, view.len); \ ++ if (usedforsecurity == 0) { \ ++ ret_obj = EVPnew( \ ++ CONST_ ## NAME ## _name_obj, \ ++ EVP_get_digestbyname(#NAME), \ ++ NULL, \ ++ (unsigned char*)view.buf, view.len, \ ++ usedforsecurity); \ ++ } else { \ ++ ret_obj = EVPnew( \ ++ CONST_ ## NAME ## _name_obj, \ ++ NULL, \ ++ CONST_new_ ## NAME ## _ctx_p, \ ++ (unsigned char*)view.buf, view.len, \ ++ usedforsecurity); \ ++ } \ + PyBuffer_Release(&view); \ + return ret_obj; \ + } + + /* a PyMethodDef structure for the constructor */ + #define CONSTRUCTOR_METH_DEF(NAME) \ +- {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \ ++ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS|METH_KEYWORDS, \ + PyDoc_STR("Returns a " #NAME \ + " hash object; optionally initialized with a string") \ + } + +From c8102e61fb3ade364d4bb7f2fe3f3452e2018ecd Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Mon, 2 Sep 2019 17:59:53 +0200 +Subject: [PATCH 03/11] hashlib.py: Avoid the builtin constructor + +--- + Lib/hashlib.py | 58 +++++++++++++------------------------------------- + 1 file changed, 15 insertions(+), 43 deletions(-) + +diff --git a/Lib/hashlib.py b/Lib/hashlib.py +index bbd06b9996ee..404ed6891fb9 100644 +--- a/Lib/hashlib.py ++++ b/Lib/hashlib.py +@@ -69,65 +69,37 @@ 'pbkdf2_hmac') @@ -85,20 +293,23 @@ diff -up Python-2.7.2/Lib/hashlib.py.hashlib-fips Python-2.7.2/Lib/hashlib.py return f except (AttributeError, ValueError): - return __get_builtin_constructor(name) -+ raise - +- - -def __py_new(name, string=''): - """new(name, string='') - Return a new hashing object using the named algorithm; - optionally initialized with a string. - """ - return __get_builtin_constructor(name)(string) -- -- ++ raise + + -def __hash_new(name, string=''): +- """new(name, string='') - Return a new hashing object using the named algorithm; +- optionally initialized with a string. +def __hash_new(name, string='', usedforsecurity=True): - """new(name, string='') - Return a new hashing object using the named algorithm; - optionally initialized with a string. ++ """new(name, string='', usedforsecurity=True) - Return a new hashing object ++ using the named algorithm; optionally initialized with a string. ++ + Override 'usedforsecurity' to False when using for non-security purposes in + a FIPS environment """ @@ -106,58 +317,184 @@ diff -up Python-2.7.2/Lib/hashlib.py.hashlib-fips Python-2.7.2/Lib/hashlib.py - return _hashlib.new(name, string) + return _hashlib.new(name, string, usedforsecurity) except ValueError: -- # If the _hashlib module (OpenSSL) doesn't support the named -- # hash, try using our builtin implementations. -- # This allows for SHA224/256 and SHA384/512 support even though -- # the OpenSSL library prior to 0.9.8 doesn't provide them. + # If the _hashlib module (OpenSSL) doesn't support the named + # hash, try using our builtin implementations. + # This allows for SHA224/256 and SHA384/512 support even though + # the OpenSSL library prior to 0.9.8 doesn't provide them. - return __get_builtin_constructor(name)(string) -- + raise + try: - import _hashlib - new = __hash_new - __get_hash = __get_openssl_constructor - algorithms_available = algorithms_available.union( - _hashlib.openssl_md_meth_names) - except ImportError: -- new = __py_new -- __get_hash = __get_builtin_constructor -+ # We don't build the legacy modules -+ raise - - for __func_name in __always_supported: - # try them all, some may not work due to the OpenSSL -@@ -143,4 +125,4 @@ for __func_name in __always_supported: +@@ -218,4 +190,4 @@ def prf(msg, inner=inner, outer=outer): # Cleanup locals() del __always_supported, __func_name, __get_hash -del __py_new, __hash_new, __get_openssl_constructor +del __hash_new, __get_openssl_constructor -diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/test/test_hashlib.py ---- Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips 2011-06-11 11:46:25.000000000 -0400 -+++ Python-2.7.2/Lib/test/test_hashlib.py 2011-09-14 01:08:55.525254195 -0400 -@@ -32,6 +32,19 @@ def hexstr(s): + +From 2ade3e5a6c5732c0692c4cc2235a2bbe0948f50b Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Mon, 2 Sep 2019 17:56:46 +0200 +Subject: [PATCH 04/11] Adjust docstrings & comments + +--- + Lib/hashlib.py | 29 ++++++++++++++++++++++------- + Modules/_hashopenssl.c | 9 ++++++++- + 2 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/Lib/hashlib.py b/Lib/hashlib.py +index 404ed6891fb9..46d0b470ab4a 100644 +--- a/Lib/hashlib.py ++++ b/Lib/hashlib.py +@@ -6,9 +6,12 @@ + + __doc__ = """hashlib module - A common interface to many hash functions. + +-new(name, string='') - returns a new hash object implementing the +- given hash function; initializing the hash +- using the given string data. ++new(name, string='', usedforsecurity=True) ++ - returns a new hash object implementing the given hash function; ++ initializing the hash using the given string data. ++ ++ "usedforsecurity" is a non-standard extension for better supporting ++ FIPS-compliant environments (see below) + + Named constructor functions are also available, these are much faster + than using new(): +@@ -25,6 +28,20 @@ + Choose your hash function wisely. Some have known collision weaknesses. + sha384 and sha512 will be slow on 32 bit platforms. + ++Our implementation of hashlib uses OpenSSL. ++ ++OpenSSL has a "FIPS mode", which, if enabled, may restrict the available hashes ++to only those that are compliant with FIPS regulations. For example, it may ++deny the use of MD5, on the grounds that this is not secure for uses such as ++authentication, system integrity checking, or digital signatures. ++ ++If you need to use such a hash for non-security purposes (such as indexing into ++a data structure for speed), you can override the keyword argument ++"usedforsecurity" from True to False to signify that your code is not relying ++on the hash for security purposes, and this will allow the hash to be usable ++even in FIPS mode. This is not a standard feature of Python 2.7's hashlib, and ++is included here to better support FIPS mode. ++ + Hash objects have these methods: + - update(arg): Update the hash object with the string arg. Repeated calls + are equivalent to a single call with the concatenation of all +@@ -82,6 +99,7 @@ def __get_openssl_constructor(name): + # Use the C function directly (very fast) + return f + except (AttributeError, ValueError): ++ # RHEL only: Fallbacks removed; we always use OpenSSL for hashes. + raise + + +@@ -95,10 +113,7 @@ def __hash_new(name, string='', usedforsecurity=True): + try: + return _hashlib.new(name, string, usedforsecurity) + except ValueError: +- # If the _hashlib module (OpenSSL) doesn't support the named +- # hash, try using our builtin implementations. +- # This allows for SHA224/256 and SHA384/512 support even though +- # the OpenSSL library prior to 0.9.8 doesn't provide them. ++ # RHEL only: Fallbacks removed; we always use OpenSSL for hashes. + raise + + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index f2dbc095cc66..d24432e048bf 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -496,7 +496,14 @@ PyDoc_STRVAR(EVP_new__doc__, + An optional string argument may be provided and will be\n\ + automatically hashed.\n\ + \n\ +-The MD5 and SHA1 algorithms are always supported.\n"); ++The MD5 and SHA1 algorithms are always supported.\n \ ++\n\ ++An optional \"usedforsecurity=True\" keyword argument is provided for use in\n\ ++environments that enforce FIPS-based restrictions. Some implementations of\n\ ++OpenSSL can be configured to prevent the usage of non-secure algorithms (such\n\ ++as MD5). If you have a non-security use for these algorithms (e.g. a hash\n\ ++table), you can override this argument by marking the callsite as\n\ ++\"usedforsecurity=False\"."); + + static PyObject * + EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) + +From 6698e1d84c3f19bbb4438b2b2c78a5ef8bd5ad42 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Thu, 29 Aug 2019 10:25:28 +0200 +Subject: [PATCH 05/11] Expose OpenSSL FIPS_mode as _hashlib.get_fips_mode + +--- + Modules/_hashopenssl.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index d24432e048bf..74f9ab9ec150 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -860,10 +860,32 @@ GEN_CONSTRUCTOR(sha384) + GEN_CONSTRUCTOR(sha512) + #endif + ++static PyObject * ++_hashlib_get_fips_mode(PyObject *module, PyObject *unused) ++{ ++ // XXX: This function skips error checking. ++ // This is only appropriate for RHEL. ++ ++ // From the OpenSSL docs: ++ // "If the library was built without support of the FIPS Object Module, ++ // then the function will return 0 with an error code of ++ // CRYPTO_R_FIPS_MODE_NOT_SUPPORTED (0x0f06d065)." ++ // In RHEL: ++ // * we do build with FIPS, so the function always succeeds ++ // * even if it didn't, people seem used to errors being left on the ++ // OpenSSL error stack. ++ ++ // For more info, see: ++ // https://bugzilla.redhat.com/show_bug.cgi?id=1745499 ++ ++ return PyInt_FromLong(FIPS_mode()); ++} ++ + /* List of functions exported by this module */ + + static struct PyMethodDef EVP_functions[] = { + {"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__}, ++ {"get_fips_mode", (PyCFunction)_hashlib_get_fips_mode, METH_NOARGS, NULL}, + CONSTRUCTOR_METH_DEF(md5), + CONSTRUCTOR_METH_DEF(sha1), + #ifdef _OPENSSL_SUPPORTS_SHA2 + +From 9a8833619658c6be5ca72c60189a64da05536d85 Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Mon, 2 Sep 2019 18:00:26 +0200 +Subject: [PATCH 06/11] Adjust tests + +--- + Lib/test/test_hashlib.py | 118 ++++++++++++++++++++++++--------------- + 1 file changed, 74 insertions(+), 44 deletions(-) + +diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py +index b8d6388feaf9..b03fc84f82b4 100644 +--- a/Lib/test/test_hashlib.py ++++ b/Lib/test/test_hashlib.py +@@ -34,6 +34,8 @@ def hexstr(s): r = r + h[(i >> 4) & 0xF] + h[i & 0xF] return r -+def openssl_enforces_fips(): -+ # Use the "openssl" command (if present) to try to determine if the local -+ # OpenSSL is configured to enforce FIPS -+ from subprocess import Popen, PIPE -+ try: -+ p = Popen(['openssl', 'md5'], -+ stdin=PIPE, stdout=PIPE, stderr=PIPE) -+ except OSError: -+ # "openssl" command not found -+ return False -+ stdout, stderr = p.communicate(input=b'abc') -+ return b'unknown cipher' in stderr -+OPENSSL_ENFORCES_FIPS = openssl_enforces_fips() ++from _hashlib import get_fips_mode ++ class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', -@@ -61,10 +74,10 @@ class HashLibTestCase(unittest.TestCase) +@@ -63,10 +65,10 @@ def __init__(self, *args, **kwargs): # of hashlib.new given the algorithm name. for algorithm, constructors in self.constructors_to_test.items(): constructors.add(getattr(hashlib, algorithm)) @@ -171,7 +508,7 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes constructors.add(_test_algorithm_via_hashlib_new) _hashlib = self._conditional_import_module('_hashlib') -@@ -78,28 +91,13 @@ class HashLibTestCase(unittest.TestCase) +@@ -80,28 +82,13 @@ def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm): if constructor: constructors.add(constructor) @@ -201,7 +538,7 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes c.hexdigest() def test_algorithms_attribute(self): -@@ -115,28 +113,9 @@ class HashLibTestCase(unittest.TestCase) +@@ -122,28 +109,9 @@ def test_unknown_hash(self): self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam') self.assertRaises(TypeError, hashlib.new, 1) @@ -231,7 +568,7 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes self.assertTrue(hexstr(h.digest()) == h.hexdigest()) def test_large_update(self): -@@ -145,16 +125,16 @@ class HashLibTestCase(unittest.TestCase) +@@ -153,16 +121,16 @@ def test_large_update(self): abcs = aas + bees + cees for name in self.supported_hash_names: @@ -251,7 +588,7 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes self.assertEqual(m1.digest(), m3.digest(), name+' new problem.') def check(self, name, data, digest): -@@ -162,7 +142,7 @@ class HashLibTestCase(unittest.TestCase) +@@ -170,7 +138,7 @@ def check(self, name, data, digest): # 2 is for hashlib.name(...) and hashlib.new(name, ...) self.assertGreaterEqual(len(constructors), 2) for hash_object_constructor in constructors: @@ -260,54 +597,53 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes self.assertEqual( computed, digest, "Hash algorithm %s constructed using %s returned hexdigest" -@@ -172,7 +152,8 @@ class HashLibTestCase(unittest.TestCase) +@@ -195,7 +163,7 @@ def check_update(self, name, data, digest): def check_unicode(self, algorithm_name): # Unicode objects are not allowed as input. - expected = hashlib.new(algorithm_name, str(u'spam')).hexdigest() -+ expected = hashlib.new(algorithm_name, str(u'spam'), -+ usedforsecurity=False).hexdigest() ++ expected = hashlib.new(algorithm_name, str(u'spam'), usedforsecurity=False).hexdigest() self.check(algorithm_name, u'spam', expected) def test_unicode(self): -@@ -354,6 +335,70 @@ class HashLibTestCase(unittest.TestCase) - self.assertEqual(expected_hash, hasher.hexdigest()) +@@ -393,6 +361,68 @@ def hash_in_chunks(chunk_size): + self.assertEqual(expected_hash, hasher.hexdigest()) + def test_issue9146(self): + # Ensure that various ways to use "MD5" from "hashlib" don't segfault: + m = hashlib.md5(usedforsecurity=False) + m.update(b'abc\n') + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = hashlib.new('md5', usedforsecurity=False) + m.update(b'abc\n') + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = hashlib.md5(b'abc\n', usedforsecurity=False) + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = hashlib.new('md5', b'abc\n', usedforsecurity=False) + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") + -+ def assertRaisesUnknownCipher(self, callable_obj=None, *args, **kwargs): ++ def assertRaisesDisabledForFIPS(self, callable_obj=None, *args, **kwargs): + try: + callable_obj(*args, **kwargs) + except ValueError, e: -+ if not e.args[0].endswith('unknown cipher'): ++ if not e.args[0].endswith('disabled for FIPS'): + self.fail('Incorrect exception raised') + else: + self.fail('Exception was not raised') + -+ @unittest.skipUnless(OPENSSL_ENFORCES_FIPS, ++ @unittest.skipUnless(get_fips_mode(), + 'FIPS enforcement required for this test.') -+ def test_hashlib_fips_mode(self): ++ def test_hashlib_fips_mode(self): + # Ensure that we raise a ValueError on vanilla attempts to use MD5 + # in hashlib in a FIPS-enforced setting: -+ self.assertRaisesUnknownCipher(hashlib.md5) -+ self.assertRaisesUnknownCipher(hashlib.new, 'md5') ++ self.assertRaisesDisabledForFIPS(hashlib.md5) ++ self.assertRaisesDisabledForFIPS(hashlib.new, 'md5') + -+ @unittest.skipUnless(OPENSSL_ENFORCES_FIPS, ++ @unittest.skipUnless(get_fips_mode(), + 'FIPS enforcement required for this test.') + def test_hashopenssl_fips_mode(self): + # Verify the _hashlib module's handling of md5: @@ -317,56 +653,43 @@ diff -up Python-2.7.2/Lib/test/test_hashlib.py.hashlib-fips Python-2.7.2/Lib/tes + + # Ensure that _hashlib raises a ValueError on vanilla attempts to + # use MD5 in a FIPS-enforced setting: -+ self.assertRaisesUnknownCipher(_hashlib.openssl_md5) -+ self.assertRaisesUnknownCipher(_hashlib.new, 'md5') ++ self.assertRaisesDisabledForFIPS(_hashlib.openssl_md5) ++ self.assertRaisesDisabledForFIPS(_hashlib.new, 'md5') + + # Ensure that in such a setting we can whitelist a callsite with + # usedforsecurity=False and have it succeed: + m = _hashlib.openssl_md5(usedforsecurity=False) + m.update('abc\n') + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = _hashlib.new('md5', usedforsecurity=False) + m.update('abc\n') + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = _hashlib.openssl_md5('abc\n', usedforsecurity=False) + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ ++ + m = _hashlib.new('md5', 'abc\n', usedforsecurity=False) + self.assertEquals(m.hexdigest(), "0bee89b07a248e27c83fc3d5951213c1") -+ -+ + + class KDFTests(unittest.TestCase): pbkdf2_test_vectors = [ - (b'password', b'salt', 1, None), -diff -up Python-2.7.2/Modules/Setup.dist.hashlib-fips Python-2.7.2/Modules/Setup.dist ---- Python-2.7.2/Modules/Setup.dist.hashlib-fips 2011-09-14 00:21:26.163252001 -0400 -+++ Python-2.7.2/Modules/Setup.dist 2011-09-14 00:21:26.201252001 -0400 -@@ -248,14 +248,14 @@ imageop imageop.c # Operations on images - # Message-Digest Algorithm, described in RFC 1321. The necessary files - # md5.c and md5.h are included here. - --_md5 md5module.c md5.c -+#_md5 md5module.c md5.c - - - # The _sha module implements the SHA checksum algorithms. - # (NIST's Secure Hash Algorithms.) --_sha shamodule.c --_sha256 sha256module.c --_sha512 sha512module.c -+#_sha shamodule.c -+#_sha256 sha256module.c -+#_sha512 sha512module.c - - - # SGI IRIX specific modules -- off by default. -diff -up Python-2.7.2/setup.py.hashlib-fips Python-2.7.2/setup.py ---- Python-2.7.2/setup.py.hashlib-fips 2011-09-14 00:21:25.722252001 -0400 -+++ Python-2.7.2/setup.py 2011-09-14 00:21:26.203252001 -0400 -@@ -768,21 +768,6 @@ class PyBuildExt(build_ext): + +From 31e527aa4f57845dfb0c3dd4f0e9192af5a5b4e2 Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Mon, 2 Sep 2019 18:00:47 +0200 +Subject: [PATCH 07/11] Don't build non-OpenSSL hash implementations + +--- + setup.py | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/setup.py b/setup.py +index 33cecc687573..272d2f1b5bb8 100644 +--- a/setup.py ++++ b/setup.py +@@ -874,21 +874,6 @@ def detect_modules(self): print ("warning: openssl 0x%08x is too old for _hashlib" % openssl_ver) missing.append('_hashlib') @@ -388,345 +711,134 @@ diff -up Python-2.7.2/setup.py.hashlib-fips Python-2.7.2/setup.py # Modules that provide persistent dictionary-like semantics. You will # probably want to arrange for at least one of them to be available on ---- Python-2.7.8/Modules/_hashopenssl.c.orig 2014-06-30 04:05:41.000000000 +0200 -+++ Python-2.7.8/Modules/_hashopenssl.c 2014-07-14 14:21:59.546386572 +0200 -@@ -36,6 +36,8 @@ - #endif - - /* EVP is the preferred interface to hashing in OpenSSL */ -+#include -+#include - #include - #include - #include -@@ -67,11 +69,19 @@ - - static PyTypeObject EVPtype; - -+/* Struct to hold all the cached information we need on a specific algorithm. -+ We have one of these per algorithm */ -+typedef struct { -+ PyObject *name_obj; -+ EVP_MD_CTX ctxs[2]; -+ /* ctx_ptrs will point to ctxs unless an error occurred, when it will -+ be NULL: */ -+ EVP_MD_CTX *ctx_ptrs[2]; -+ PyObject *error_msgs[2]; -+} EVPCachedInfo; - --#define DEFINE_CONSTS_FOR_NEW(Name) \ -- static PyObject *CONST_ ## Name ## _name_obj = NULL; \ -- static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \ -- static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; -+#define DEFINE_CONSTS_FOR_NEW(Name) \ -+ static EVPCachedInfo cached_info_ ##Name; - - DEFINE_CONSTS_FOR_NEW(md5) - DEFINE_CONSTS_FOR_NEW(sha1) -@@ -117,6 +127,48 @@ - } - } - -+static void -+mc_ctx_init(EVP_MD_CTX *ctx, int usedforsecurity) -+{ -+ EVP_MD_CTX_init(ctx); -+ -+ /* -+ If the user has declared that this digest is being used in a -+ non-security role (e.g. indexing into a data structure), set -+ the exception flag for openssl to allow it -+ */ -+ if (!usedforsecurity) { -+#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW -+ EVP_MD_CTX_set_flags(ctx, -+ EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); -+#endif -+ } -+} -+ -+/* Get an error msg for the last error as a PyObject */ -+static PyObject * -+error_msg_for_last_error(void) -+{ -+ char *errstr; -+ -+ errstr = ERR_error_string(ERR_peek_last_error(), NULL); -+ ERR_clear_error(); -+ -+ return PyString_FromString(errstr); /* Can be NULL */ -+} -+ -+static void -+set_evp_exception(void) -+{ -+ char *errstr; -+ -+ errstr = ERR_error_string(ERR_peek_last_error(), NULL); -+ ERR_clear_error(); -+ -+ PyErr_SetString(PyExc_ValueError, errstr); -+} -+ -+ - /* Internal methods for a hash object */ - - static void -@@ -315,14 +367,15 @@ - static int - EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) - { -- static char *kwlist[] = {"name", "string", NULL}; -+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL}; - PyObject *name_obj = NULL; -+ int usedforsecurity = 1; - Py_buffer view = { 0 }; - char *nameStr; - const EVP_MD *digest; - -- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*:HASH", kwlist, -- &name_obj, &view)) { -+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s*i:HASH", kwlist, -+ &name_obj, &view, &usedforsecurity)) { - return -1; - } - -@@ -338,7 +391,12 @@ - PyBuffer_Release(&view); - return -1; - } -- EVP_DigestInit(&self->ctx, digest); -+ mc_ctx_init(&self->ctx, usedforsecurity); -+ if (!EVP_DigestInit_ex(&self->ctx, digest, NULL)) { -+ set_evp_exception(); -+ PyBuffer_Release(&view); -+ return -1; -+ } - - self->name = name_obj; - Py_INCREF(self->name); -@@ -422,7 +480,8 @@ - static PyObject * - EVPnew(PyObject *name_obj, - const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, -- const unsigned char *cp, Py_ssize_t len) -+ const unsigned char *cp, Py_ssize_t len, -+ int usedforsecurity) - { - EVPobject *self; - -@@ -437,7 +496,12 @@ - if (initial_ctx) { - EVP_MD_CTX_copy(&self->ctx, initial_ctx); - } else { -- EVP_DigestInit(&self->ctx, digest); -+ mc_ctx_init(&self->ctx, usedforsecurity); -+ if (!EVP_DigestInit_ex(&self->ctx, digest, NULL)) { -+ set_evp_exception(); -+ Py_DECREF(self); -+ return NULL; -+ } - } - - if (cp && len) { -@@ -461,20 +525,28 @@ - An optional string argument may be provided and will be\n\ - automatically hashed.\n\ - \n\ --The MD5 and SHA1 algorithms are always supported.\n"); -+The MD5 and SHA1 algorithms are always supported.\n\ -+\n\ -+An optional \"usedforsecurity=True\" keyword argument is provided for use in\n\ -+environments that enforce FIPS-based restrictions. Some implementations of\n\ -+OpenSSL can be configured to prevent the usage of non-secure algorithms (such\n\ -+as MD5). If you have a non-security use for these algorithms (e.g. a hash\n\ -+table), you can override this argument by marking the callsite as\n\ -+\"usedforsecurity=False\"."); - - static PyObject * - EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) - { -- static char *kwlist[] = {"name", "string", NULL}; -+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL}; - PyObject *name_obj = NULL; - Py_buffer view = { 0 }; - PyObject *ret_obj; - char *name; - const EVP_MD *digest; -+ int usedforsecurity = 1; - -- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*:new", kwlist, -- &name_obj, &view)) { -+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s*i:new", kwlist, -+ &name_obj, &view, &usedforsecurity)) { - return NULL; - } - -@@ -487,7 +559,7 @@ - digest = EVP_get_digestbyname(name); - - ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, -- view.len); -+ view.len, usedforsecurity); - PyBuffer_Release(&view); - - return ret_obj; -@@ -713,51 +785,111 @@ - - - /* -- * This macro generates constructor function definitions for specific -- * hash algorithms. These constructors are much faster than calling -- * the generic one passing it a python string and are noticably -- * faster than calling a python new() wrapper. Thats important for -+ * This macro and function generates a family of constructor function -+ * definitions for specific hash algorithms. These constructors are much -+ * faster than calling the generic one passing it a python string and are -+ * noticably faster than calling a python new() wrapper. That's important for - * code that wants to make hashes of a bunch of small strings. - */ - #define GEN_CONSTRUCTOR(NAME) \ - static PyObject * \ -- EVP_new_ ## NAME (PyObject *self, PyObject *args) \ -+ EVP_new_ ## NAME (PyObject *self, PyObject *args, PyObject *kwdict) \ - { \ -- Py_buffer view = { 0 }; \ -- PyObject *ret_obj; \ -- \ -- if (!PyArg_ParseTuple(args, "|s*:" #NAME , &view)) { \ -- return NULL; \ -- } \ -- \ -- ret_obj = EVPnew( \ -- CONST_ ## NAME ## _name_obj, \ -- NULL, \ -- CONST_new_ ## NAME ## _ctx_p, \ -- (unsigned char*)view.buf, view.len); \ -- PyBuffer_Release(&view); \ -- return ret_obj; \ -+ return implement_specific_EVP_new(self, args, kwdict, \ -+ "|s*i:" #NAME, \ -+ &cached_info_ ## NAME ); \ - } - -+static PyObject * -+implement_specific_EVP_new(PyObject *self, PyObject *args, PyObject *kwdict, -+ const char *format, -+ EVPCachedInfo *cached_info) -+{ -+ static char *kwlist[] = {"string", "usedforsecurity", NULL}; -+ Py_buffer view = { 0 }; -+ int usedforsecurity = 1; -+ int idx; -+ PyObject *ret_obj = NULL; -+ -+ assert(cached_info); -+ -+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, format, kwlist, -+ &view, &usedforsecurity)) { -+ return NULL; -+ } -+ -+ idx = usedforsecurity ? 1 : 0; -+ -+ /* -+ * If an error occurred during creation of the global content, the ctx_ptr -+ * will be NULL, and the error_msg will hopefully be non-NULL: -+ */ -+ if (cached_info->ctx_ptrs[idx]) { -+ /* We successfully initialized this context; copy it: */ -+ ret_obj = EVPnew(cached_info->name_obj, -+ NULL, -+ cached_info->ctx_ptrs[idx], -+ (unsigned char*)view.buf, view.len, -+ usedforsecurity); -+ } else { -+ /* Some kind of error happened initializing the global context for -+ this (digest, usedforsecurity) pair. -+ Raise an exception with the saved error message: */ -+ if (cached_info->error_msgs[idx]) { -+ PyErr_SetObject(PyExc_ValueError, cached_info->error_msgs[idx]); -+ } else { -+ PyErr_SetString(PyExc_ValueError, "Error initializing hash"); -+ } -+ } -+ -+ PyBuffer_Release(&view); -+ -+ return ret_obj; -+} -+ - /* a PyMethodDef structure for the constructor */ - #define CONSTRUCTOR_METH_DEF(NAME) \ -- {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \ -+ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, \ -+ METH_VARARGS |METH_KEYWORDS, \ - PyDoc_STR("Returns a " #NAME \ - " hash object; optionally initialized with a string") \ - } - --/* used in the init function to setup a constructor: initialize OpenSSL -- constructor constants if they haven't been initialized already. */ --#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ -- if (CONST_ ## NAME ## _name_obj == NULL) { \ -- CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \ -- if (EVP_get_digestbyname(#NAME)) { \ -- CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ + +From e9cd6a63ce17a0120b1d017bf08f05f3ed223bb1 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 2 Sep 2019 18:33:22 +0200 +Subject: [PATCH 08/11] Allow for errros in pre-created context creation + +--- + Modules/_hashopenssl.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index 74f9ab9ec150..7609e9e490f0 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -813,7 +813,7 @@ generate_hash_name_list(void) + )) { \ + return NULL; \ + } \ +- if (usedforsecurity == 0) { \ ++ if (usedforsecurity == 0 || CONST_new_ ## NAME ## _ctx_p == NULL) { \ + ret_obj = EVPnew( \ + CONST_ ## NAME ## _name_obj, \ + EVP_get_digestbyname(#NAME), \ +@@ -846,7 +846,9 @@ generate_hash_name_list(void) + CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \ + if (EVP_get_digestbyname(#NAME)) { \ + CONST_new_ ## NAME ## _ctx_p = EVP_MD_CTX_new(); \ - EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ -- } \ -- } \ -+/* -+ Macro/function pair to set up the constructors. -+ -+ Try to initialize a context for each hash twice, once with -+ EVP_MD_CTX_FLAG_NON_FIPS_ALLOW and once without. -+ -+ Any that have errors during initialization will end up wit a NULL ctx_ptrs -+ entry, and err_msgs will be set (unless we're very low on memory) -+*/ -+#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ -+ init_constructor_constant(&cached_info_ ## NAME, #NAME); \ ++ if (!EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME))) { \ ++ CONST_new_ ## NAME ## _ctx_p = NULL; \ ++ } \ + } \ + } \ } while (0); - -+static void -+init_constructor_constant(EVPCachedInfo *cached_info, const char *name) -+{ -+ assert(cached_info); -+ cached_info->name_obj = PyString_FromString(name); -+ if (EVP_get_digestbyname(name)) { -+ int i; -+ for (i=0; i<2; i++) { -+ mc_ctx_init(&cached_info->ctxs[i], i); -+ if (EVP_DigestInit_ex(&cached_info->ctxs[i], -+ EVP_get_digestbyname(name), NULL)) { -+ /* Success: */ -+ cached_info->ctx_ptrs[i] = &cached_info->ctxs[i]; -+ } else { -+ /* Failure: */ -+ cached_info->ctx_ptrs[i] = NULL; -+ cached_info->error_msgs[i] = error_msg_for_last_error(); -+ } -+ } -+ } -+} + +From d0465ea1c07f24067b4d6f60f73a29c82f2ad03f Mon Sep 17 00:00:00 2001 +From: David Malcolm +Date: Mon, 2 Sep 2019 18:40:08 +0200 +Subject: [PATCH 09/11] use SHA-256 rather than MD5 in + multiprocessing.connection (patch 169; rhbz#879695) + +--- + Lib/multiprocessing/connection.py | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py +index 645a26f069ea..d4dc6ac19d53 100644 +--- a/Lib/multiprocessing/connection.py ++++ b/Lib/multiprocessing/connection.py +@@ -56,6 +56,10 @@ + # A very generous timeout when it comes to local connections... + CONNECTION_TIMEOUT = 20. + ++# The hmac module implicitly defaults to using MD5. ++# Support using a stronger algorithm for the challenge/response code: ++HMAC_DIGEST_NAME='sha256' + - GEN_CONSTRUCTOR(md5) - GEN_CONSTRUCTOR(sha1) - #ifdef _OPENSSL_SUPPORTS_SHA2 -@@ -794,14 +926,11 @@ - { - PyObject *m, *openssl_md_meth_names; + _mmap_counter = itertools.count() -+ SSL_load_error_strings(); -+ SSL_library_init(); - OpenSSL_add_all_digests(); - ERR_load_crypto_strings(); + default_family = 'AF_INET' +@@ -413,12 +417,16 @@ def PipeClient(address): + WELCOME = b'#WELCOME#' + FAILURE = b'#FAILURE#' -- /* TODO build EVP_functions openssl_* entries dynamically based -- * on what hashes are supported rather than listing many -- * but having some be unsupported. Only init appropriate -- * constants. */ -- - Py_TYPE(&EVPtype) = &PyType_Type; - if (PyType_Ready(&EVPtype) < 0) - return; ++def get_digestmod_for_hmac(): ++ import hashlib ++ return getattr(hashlib, HMAC_DIGEST_NAME) ++ + def deliver_challenge(connection, authkey): + import hmac + assert isinstance(authkey, bytes) + message = os.urandom(MESSAGE_LENGTH) + connection.send_bytes(CHALLENGE + message) +- digest = hmac.new(authkey, message).digest() ++ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest() + response = connection.recv_bytes(256) # reject large message + if response == digest: + connection.send_bytes(WELCOME) +@@ -432,7 +440,7 @@ def answer_challenge(connection, authkey): + message = connection.recv_bytes(256) # reject large message + assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message + message = message[len(CHALLENGE):] +- digest = hmac.new(authkey, message).digest() ++ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest() + connection.send_bytes(digest) + response = connection.recv_bytes(256) # reject large message + if response != WELCOME: + +From 82b181a2c55be0f0766fdf1f0a3e950d22fe0602 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 19 Aug 2019 13:59:40 +0200 +Subject: [PATCH 10/11] Make uuid.uuid3 work (using libuuid via ctypes) + +--- + Lib/uuid.py | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/Lib/uuid.py b/Lib/uuid.py +index 80d33c0bd83f..bfb7477b5f58 100644 +--- a/Lib/uuid.py ++++ b/Lib/uuid.py +@@ -455,6 +455,7 @@ def _netbios_getnode(): + + # If ctypes is available, use it to find system routines for UUID generation. + _uuid_generate_time = _UuidCreate = None ++_uuid_generate_md5 = None + try: + import ctypes, ctypes.util + import sys +@@ -471,6 +472,8 @@ def _netbios_getnode(): + continue + if hasattr(lib, 'uuid_generate_time'): + _uuid_generate_time = lib.uuid_generate_time ++ # The library that has uuid_generate_time should have md5 too. ++ _uuid_generate_md5 = getattr(lib, 'uuid_generate_md5') + break + del _libnames + +@@ -595,6 +598,11 @@ def uuid1(node=None, clock_seq=None): + + def uuid3(namespace, name): + """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" ++ if _uuid_generate_md5: ++ _buffer = ctypes.create_string_buffer(16) ++ _uuid_generate_md5(_buffer, namespace.bytes, name, len(name)) ++ return UUID(bytes=_buffer.raw) ++ + from hashlib import md5 + hash = md5(namespace.bytes + name).digest() + return UUID(bytes=hash[:16], version=3) + diff --git a/SOURCES/00147-add-debug-malloc-stats.patch b/SOURCES/00147-add-debug-malloc-stats.patch index 0d783f5..6c7d3f1 100644 --- a/SOURCES/00147-add-debug-malloc-stats.patch +++ b/SOURCES/00147-add-debug-malloc-stats.patch @@ -1,7 +1,8 @@ -diff -up Python-2.7.2/Include/dictobject.h.add-debug-malloc-stats Python-2.7.2/Include/dictobject.h ---- Python-2.7.2/Include/dictobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/dictobject.h 2011-09-16 19:03:25.105821625 -0400 -@@ -150,6 +150,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItemStr +diff --git a/Include/dictobject.h b/Include/dictobject.h +index 5a1e9fe..da89cec 100644 +--- a/Include/dictobject.h ++++ b/Include/dictobject.h +@@ -154,6 +154,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key); PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item); PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); @@ -10,10 +11,11 @@ diff -up Python-2.7.2/Include/dictobject.h.add-debug-malloc-stats Python-2.7.2/I #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/floatobject.h.add-debug-malloc-stats Python-2.7.2/Include/floatobject.h ---- Python-2.7.2/Include/floatobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/floatobject.h 2011-09-16 19:03:25.106821625 -0400 -@@ -132,6 +132,7 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAd +diff --git a/Include/floatobject.h b/Include/floatobject.h +index 54e8825..33c6ac0 100644 +--- a/Include/floatobject.h ++++ b/Include/floatobject.h +@@ -132,6 +132,7 @@ PyAPI_FUNC(PyObject *) _PyFloat_FormatAdvanced(PyObject *obj, failure. Used in builtin_round in bltinmodule.c. */ PyAPI_FUNC(PyObject *) _Py_double_round(double x, int ndigits); @@ -21,10 +23,11 @@ diff -up Python-2.7.2/Include/floatobject.h.add-debug-malloc-stats Python-2.7.2/ #ifdef __cplusplus -diff -up Python-2.7.2/Include/frameobject.h.add-debug-malloc-stats Python-2.7.2/Include/frameobject.h ---- Python-2.7.2/Include/frameobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/frameobject.h 2011-09-16 19:03:25.107821625 -0400 -@@ -80,6 +80,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(Py +diff --git a/Include/frameobject.h b/Include/frameobject.h +index 3460379..db89a4a 100644 +--- a/Include/frameobject.h ++++ b/Include/frameobject.h +@@ -80,6 +80,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(int) PyFrame_ClearFreeList(void); @@ -33,10 +36,11 @@ diff -up Python-2.7.2/Include/frameobject.h.add-debug-malloc-stats Python-2.7.2/ /* Return the line of code the frame is currently executing. */ PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); -diff -up Python-2.7.2/Include/intobject.h.add-debug-malloc-stats Python-2.7.2/Include/intobject.h ---- Python-2.7.2/Include/intobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/intobject.h 2011-09-16 19:03:25.107821625 -0400 -@@ -74,6 +74,8 @@ PyAPI_FUNC(PyObject *) _PyInt_FormatAdva +diff --git a/Include/intobject.h b/Include/intobject.h +index d198574..60cb9e0 100644 +--- a/Include/intobject.h ++++ b/Include/intobject.h +@@ -78,6 +78,8 @@ PyAPI_FUNC(PyObject *) _PyInt_FormatAdvanced(PyObject *obj, char *format_spec, Py_ssize_t format_spec_len); @@ -45,10 +49,11 @@ diff -up Python-2.7.2/Include/intobject.h.add-debug-malloc-stats Python-2.7.2/In #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/listobject.h.add-debug-malloc-stats Python-2.7.2/Include/listobject.h ---- Python-2.7.2/Include/listobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/listobject.h 2011-09-16 19:03:25.107821625 -0400 -@@ -62,6 +62,8 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(Py +diff --git a/Include/listobject.h b/Include/listobject.h +index f19b1c5..7fccb47 100644 +--- a/Include/listobject.h ++++ b/Include/listobject.h +@@ -62,6 +62,8 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); #define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v)) #define PyList_GET_SIZE(op) Py_SIZE(op) @@ -57,9 +62,10 @@ diff -up Python-2.7.2/Include/listobject.h.add-debug-malloc-stats Python-2.7.2/I #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/methodobject.h.add-debug-malloc-stats Python-2.7.2/Include/methodobject.h ---- Python-2.7.2/Include/methodobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/methodobject.h 2011-09-16 19:03:25.108821625 -0400 +diff --git a/Include/methodobject.h b/Include/methodobject.h +index 6e160b6..1944517 100644 +--- a/Include/methodobject.h ++++ b/Include/methodobject.h @@ -87,6 +87,10 @@ typedef struct { PyAPI_FUNC(int) PyCFunction_ClearFreeList(void); @@ -71,10 +77,11 @@ diff -up Python-2.7.2/Include/methodobject.h.add-debug-malloc-stats Python-2.7.2 #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/object.h.add-debug-malloc-stats Python-2.7.2/Include/object.h ---- Python-2.7.2/Include/object.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/object.h 2011-09-16 19:03:25.108821625 -0400 -@@ -980,6 +980,13 @@ PyAPI_DATA(PyObject *) _PyTrash_delete_l +diff --git a/Include/object.h b/Include/object.h +index 807b241..a9d2079 100644 +--- a/Include/object.h ++++ b/Include/object.h +@@ -1040,6 +1040,13 @@ PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void); _PyTrash_thread_deposit_object((PyObject*)op); \ } while (0); @@ -88,9 +95,10 @@ diff -up Python-2.7.2/Include/object.h.add-debug-malloc-stats Python-2.7.2/Inclu #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/objimpl.h.add-debug-malloc-stats Python-2.7.2/Include/objimpl.h ---- Python-2.7.2/Include/objimpl.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/objimpl.h 2011-09-16 19:03:25.108821625 -0400 +diff --git a/Include/objimpl.h b/Include/objimpl.h +index cbf6bc3..8c14ab8 100644 +--- a/Include/objimpl.h ++++ b/Include/objimpl.h @@ -101,13 +101,13 @@ PyAPI_FUNC(void) PyObject_Free(void *); /* Macros */ @@ -106,10 +114,23 @@ diff -up Python-2.7.2/Include/objimpl.h.add-debug-malloc-stats Python-2.7.2/Incl PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes); PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes); PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p); -diff -up Python-2.7.2/Include/stringobject.h.add-debug-malloc-stats Python-2.7.2/Include/stringobject.h ---- Python-2.7.2/Include/stringobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/stringobject.h 2011-09-16 19:03:25.109821625 -0400 -@@ -204,6 +204,8 @@ PyAPI_FUNC(PyObject *) _PyBytes_FormatAd +diff --git a/Include/setobject.h b/Include/setobject.h +index 52b07d5..73a37b6 100644 +--- a/Include/setobject.h ++++ b/Include/setobject.h +@@ -92,6 +92,7 @@ PyAPI_FUNC(int) _PySet_Next(PyObject *set, Py_ssize_t *pos, PyObject **key); + PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, long *hash); + PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); + PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); ++PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out); + + #ifdef __cplusplus + } +diff --git a/Include/stringobject.h b/Include/stringobject.h +index 12cc093..0a5fbd1 100644 +--- a/Include/stringobject.h ++++ b/Include/stringobject.h +@@ -204,6 +204,8 @@ PyAPI_FUNC(PyObject *) _PyBytes_FormatAdvanced(PyObject *obj, char *format_spec, Py_ssize_t format_spec_len); @@ -118,9 +139,22 @@ diff -up Python-2.7.2/Include/stringobject.h.add-debug-malloc-stats Python-2.7.2 #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Include/unicodeobject.h.add-debug-malloc-stats Python-2.7.2/Include/unicodeobject.h ---- Python-2.7.2/Include/unicodeobject.h.add-debug-malloc-stats 2011-06-11 11:46:23.000000000 -0400 -+++ Python-2.7.2/Include/unicodeobject.h 2011-09-16 19:03:25.109821625 -0400 +diff --git a/Include/tupleobject.h b/Include/tupleobject.h +index a5ab733..27e6ca6 100644 +--- a/Include/tupleobject.h ++++ b/Include/tupleobject.h +@@ -54,6 +54,7 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *); + #define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->ob_item[i] = v) + + PyAPI_FUNC(int) PyTuple_ClearFreeList(void); ++PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out); + + #ifdef __cplusplus + } +diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h +index 7781f96..321bd20 100644 +--- a/Include/unicodeobject.h ++++ b/Include/unicodeobject.h @@ -1406,6 +1406,8 @@ PyAPI_FUNC(int) _PyUnicode_IsAlpha( Py_UNICODE ch /* Unicode character */ ); @@ -130,10 +164,11 @@ diff -up Python-2.7.2/Include/unicodeobject.h.add-debug-malloc-stats Python-2.7. #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats Python-2.7.2/Lib/test/test_sys.py ---- Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats 2011-09-16 19:03:25.048821626 -0400 -+++ Python-2.7.2/Lib/test/test_sys.py 2011-09-16 19:03:25.110821625 -0400 -@@ -473,6 +473,32 @@ class SysModuleTest(unittest.TestCase): +diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py +index 9342716..8eeb5ab 100644 +--- a/Lib/test/test_sys.py ++++ b/Lib/test/test_sys.py +@@ -487,6 +487,32 @@ class SysModuleTest(unittest.TestCase): p.wait() self.assertIn(executable, ["''", repr(sys.executable)]) @@ -149,7 +184,7 @@ diff -up Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats Python-2.7.2/L + out, err = p.communicate() + p.wait() + self.assertIn("arenas allocated current", err) -+ ++ + # Verify that we can redirect the output to a file (not a file-like + # object, though): + with open('mallocstats.txt', 'w') as out: @@ -161,15 +196,16 @@ diff -up Python-2.7.2/Lib/test/test_sys.py.add-debug-malloc-stats Python-2.7.2/L + # Verify that the destination must be a file: + with self.assertRaises(TypeError): + sys._debugmallocstats(42) -+ ++ + @test.test_support.cpython_only class SizeofTest(unittest.TestCase): - -diff -up Python-2.7.2/Objects/classobject.c.add-debug-malloc-stats Python-2.7.2/Objects/classobject.c ---- Python-2.7.2/Objects/classobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/classobject.c 2011-09-16 19:03:25.110821625 -0400 -@@ -2670,3 +2670,12 @@ PyMethod_Fini(void) + +diff --git a/Objects/classobject.c b/Objects/classobject.c +index 02d7cfd..1c44a47 100644 +--- a/Objects/classobject.c ++++ b/Objects/classobject.c +@@ -2691,3 +2691,12 @@ PyMethod_Fini(void) { (void)PyMethod_ClearFreeList(); } @@ -182,9 +218,10 @@ diff -up Python-2.7.2/Objects/classobject.c.add-debug-malloc-stats Python-2.7.2/ + "free PyMethodObject", + numfree, sizeof(PyMethodObject)); +} -diff -up Python-2.7.2/Objects/dictobject.c.add-debug-malloc-stats Python-2.7.2/Objects/dictobject.c ---- Python-2.7.2/Objects/dictobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/dictobject.c 2011-09-16 19:03:25.111821625 -0400 +diff --git a/Objects/dictobject.c b/Objects/dictobject.c +index c544ecd..89ca39c 100644 +--- a/Objects/dictobject.c ++++ b/Objects/dictobject.c @@ -225,6 +225,15 @@ show_track(void) static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; @@ -201,10 +238,11 @@ diff -up Python-2.7.2/Objects/dictobject.c.add-debug-malloc-stats Python-2.7.2/O void PyDict_Fini(void) { -diff -up Python-2.7.2/Objects/floatobject.c.add-debug-malloc-stats Python-2.7.2/Objects/floatobject.c ---- Python-2.7.2/Objects/floatobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/floatobject.c 2011-09-16 19:03:25.111821625 -0400 -@@ -35,6 +35,22 @@ typedef struct _floatblock PyFloatBlock; +diff --git a/Objects/floatobject.c b/Objects/floatobject.c +index 5954d39..02acc8c 100644 +--- a/Objects/floatobject.c ++++ b/Objects/floatobject.c +@@ -34,6 +34,22 @@ typedef struct _floatblock PyFloatBlock; static PyFloatBlock *block_list = NULL; static PyFloatObject *free_list = NULL; @@ -227,10 +265,11 @@ diff -up Python-2.7.2/Objects/floatobject.c.add-debug-malloc-stats Python-2.7.2/ static PyFloatObject * fill_free_list(void) { -diff -up Python-2.7.2/Objects/frameobject.c.add-debug-malloc-stats Python-2.7.2/Objects/frameobject.c ---- Python-2.7.2/Objects/frameobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/frameobject.c 2011-09-16 19:03:25.112821625 -0400 -@@ -980,3 +980,13 @@ PyFrame_Fini(void) +diff --git a/Objects/frameobject.c b/Objects/frameobject.c +index 4c91dd0..03a66dc 100644 +--- a/Objects/frameobject.c ++++ b/Objects/frameobject.c +@@ -1019,3 +1019,13 @@ PyFrame_Fini(void) Py_XDECREF(builtin_object); builtin_object = NULL; } @@ -244,9 +283,10 @@ diff -up Python-2.7.2/Objects/frameobject.c.add-debug-malloc-stats Python-2.7.2/ + numfree, sizeof(PyFrameObject)); +} + -diff -up Python-2.7.2/Objects/intobject.c.add-debug-malloc-stats Python-2.7.2/Objects/intobject.c ---- Python-2.7.2/Objects/intobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/intobject.c 2011-09-16 19:03:25.112821625 -0400 +diff --git a/Objects/intobject.c b/Objects/intobject.c +index 9b27c35..703fa5a 100644 +--- a/Objects/intobject.c ++++ b/Objects/intobject.c @@ -44,6 +44,23 @@ typedef struct _intblock PyIntBlock; static PyIntBlock *block_list = NULL; static PyIntObject *free_list = NULL; @@ -271,9 +311,10 @@ diff -up Python-2.7.2/Objects/intobject.c.add-debug-malloc-stats Python-2.7.2/Ob static PyIntObject * fill_free_list(void) { -diff -up Python-2.7.2/Objects/listobject.c.add-debug-malloc-stats Python-2.7.2/Objects/listobject.c ---- Python-2.7.2/Objects/listobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/listobject.c 2011-09-16 19:03:25.113821625 -0400 +diff --git a/Objects/listobject.c b/Objects/listobject.c +index 24eff76..38848bd 100644 +--- a/Objects/listobject.c ++++ b/Objects/listobject.c @@ -109,6 +109,15 @@ PyList_Fini(void) } } @@ -290,9 +331,10 @@ diff -up Python-2.7.2/Objects/listobject.c.add-debug-malloc-stats Python-2.7.2/O PyObject * PyList_New(Py_ssize_t size) { -diff -up Python-2.7.2/Objects/methodobject.c.add-debug-malloc-stats Python-2.7.2/Objects/methodobject.c ---- Python-2.7.2/Objects/methodobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/methodobject.c 2011-09-16 19:03:25.113821625 -0400 +diff --git a/Objects/methodobject.c b/Objects/methodobject.c +index c1a99ab..ea5df77 100644 +--- a/Objects/methodobject.c ++++ b/Objects/methodobject.c @@ -412,6 +412,15 @@ PyCFunction_Fini(void) (void)PyCFunction_ClearFreeList(); } @@ -309,10 +351,11 @@ diff -up Python-2.7.2/Objects/methodobject.c.add-debug-malloc-stats Python-2.7.2 /* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(), but it's part of the API so we need to keep a function around that existing C extensions can call. -diff -up Python-2.7.2/Objects/object.c.add-debug-malloc-stats Python-2.7.2/Objects/object.c ---- Python-2.7.2/Objects/object.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/object.c 2011-09-16 19:04:46.463820849 -0400 -@@ -2334,6 +2334,23 @@ PyMem_Free(void *p) +diff --git a/Objects/object.c b/Objects/object.c +index 65366b0..acef3ce 100644 +--- a/Objects/object.c ++++ b/Objects/object.c +@@ -2360,6 +2360,23 @@ PyMem_Free(void *p) PyMem_FREE(p); } @@ -336,10 +379,11 @@ diff -up Python-2.7.2/Objects/object.c.add-debug-malloc-stats Python-2.7.2/Objec /* These methods are used to control infinite recursion in repr, str, print, etc. Container objects that may recursively contain themselves, -diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Objects/obmalloc.c ---- Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/obmalloc.c 2011-09-16 19:03:25.114821625 -0400 -@@ -508,12 +508,10 @@ static struct arena_object* usable_arena +diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c +index 0778c85..f049f5c 100644 +--- a/Objects/obmalloc.c ++++ b/Objects/obmalloc.c +@@ -541,12 +541,10 @@ static struct arena_object* usable_arenas = NULL; /* Number of arenas allocated that haven't been free()'d. */ static size_t narenas_currently_allocated = 0; @@ -352,7 +396,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj /* Allocate a new arena. If we run out of memory, return NULL. Else * allocate a new arena, and return the address of an arena_object -@@ -528,7 +526,7 @@ new_arena(void) +@@ -563,7 +561,7 @@ new_arena(void) #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -361,7 +405,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj #endif if (unused_arena_objects == NULL) { uint i; -@@ -588,11 +586,9 @@ new_arena(void) +@@ -631,11 +629,9 @@ new_arena(void) arenaobj->address = (uptr)address; ++narenas_currently_allocated; @@ -373,7 +417,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj arenaobj->freepools = NULL; /* pool_address <- first pool-aligned address in the arena nfreepools <- number of whole pools that fit after alignment */ -@@ -1694,17 +1690,19 @@ _PyObject_DebugDumpAddress(const void *p +@@ -1796,17 +1792,19 @@ _PyObject_DebugDumpAddress(const void *p) } } @@ -397,7 +441,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj /* Write the value with commas. */ i = 22; -@@ -1725,17 +1723,32 @@ printone(const char* msg, size_t value) +@@ -1827,17 +1825,32 @@ printone(const char* msg, size_t value) while (i >= 0) buf[i--] = ' '; @@ -433,7 +477,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj { uint i; const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; -@@ -1764,7 +1777,7 @@ _PyObject_DebugMallocStats(void) +@@ -1866,7 +1879,7 @@ _PyObject_DebugMallocStats(void) size_t total; char buf[128]; @@ -442,7 +486,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj SMALL_REQUEST_THRESHOLD, numclasses); for (i = 0; i < numclasses; ++i) -@@ -1818,10 +1831,10 @@ _PyObject_DebugMallocStats(void) +@@ -1920,10 +1933,10 @@ _PyObject_DebugMallocStats(void) } assert(narenas == narenas_currently_allocated); @@ -455,7 +499,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj for (i = 0; i < numclasses; ++i) { size_t p = numpools[i]; -@@ -1832,7 +1845,7 @@ _PyObject_DebugMallocStats(void) +@@ -1934,7 +1947,7 @@ _PyObject_DebugMallocStats(void) assert(b == 0 && f == 0); continue; } @@ -464,7 +508,7 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj "%11" PY_FORMAT_SIZE_T "u " "%15" PY_FORMAT_SIZE_T "u " "%13" PY_FORMAT_SIZE_T "u\n", -@@ -1842,36 +1855,35 @@ _PyObject_DebugMallocStats(void) +@@ -1944,36 +1957,35 @@ _PyObject_DebugMallocStats(void) pool_header_bytes += p * POOL_OVERHEAD; quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); } @@ -518,10 +562,11 @@ diff -up Python-2.7.2/Objects/obmalloc.c.add-debug-malloc-stats Python-2.7.2/Obj #ifdef Py_USING_MEMORY_DEBUGGER /* Make this function last so gcc won't inline it since the definition is * after the reference. -diff -up Python-2.7.2/Objects/setobject.c.add-debug-malloc-stats Python-2.7.2/Objects/setobject.c ---- Python-2.7.2/Objects/setobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/setobject.c 2011-09-16 19:03:25.115821625 -0400 -@@ -1088,6 +1088,16 @@ PySet_Fini(void) +diff --git a/Objects/setobject.c b/Objects/setobject.c +index 31da3db..da086ab 100644 +--- a/Objects/setobject.c ++++ b/Objects/setobject.c +@@ -1087,6 +1087,16 @@ PySet_Fini(void) Py_CLEAR(emptyfrozenset); } @@ -538,10 +583,11 @@ diff -up Python-2.7.2/Objects/setobject.c.add-debug-malloc-stats Python-2.7.2/Ob static PyObject * set_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { -diff -up Python-2.7.2/Objects/stringobject.c.add-debug-malloc-stats Python-2.7.2/Objects/stringobject.c ---- Python-2.7.2/Objects/stringobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/stringobject.c 2011-09-16 19:03:25.116821625 -0400 -@@ -4822,3 +4822,43 @@ void _Py_ReleaseInternedStrings(void) +diff --git a/Objects/stringobject.c b/Objects/stringobject.c +index c47d32f..b1ffa24 100644 +--- a/Objects/stringobject.c ++++ b/Objects/stringobject.c +@@ -4880,3 +4880,43 @@ void _Py_ReleaseInternedStrings(void) PyDict_Clear(interned); Py_CLEAR(interned); } @@ -585,9 +631,10 @@ diff -up Python-2.7.2/Objects/stringobject.c.add-debug-malloc-stats Python-2.7.2 + "%zi/%zi " + "mortal/immortal\n", mortal_size, immortal_size); +} -diff -up Python-2.7.2/Objects/tupleobject.c.add-debug-malloc-stats Python-2.7.2/Objects/tupleobject.c ---- Python-2.7.2/Objects/tupleobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/tupleobject.c 2011-09-16 19:03:25.116821625 -0400 +diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c +index 6f4b18c..e8e4490 100644 +--- a/Objects/tupleobject.c ++++ b/Objects/tupleobject.c @@ -44,6 +44,22 @@ show_track(void) } #endif @@ -611,10 +658,11 @@ diff -up Python-2.7.2/Objects/tupleobject.c.add-debug-malloc-stats Python-2.7.2/ PyObject * PyTuple_New(register Py_ssize_t size) -diff -up Python-2.7.2/Objects/unicodeobject.c.add-debug-malloc-stats Python-2.7.2/Objects/unicodeobject.c ---- Python-2.7.2/Objects/unicodeobject.c.add-debug-malloc-stats 2011-06-11 11:46:27.000000000 -0400 -+++ Python-2.7.2/Objects/unicodeobject.c 2011-09-16 19:03:25.118821625 -0400 -@@ -8883,6 +8883,12 @@ _PyUnicode_Fini(void) +diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c +index a859fa0..b6ff83e 100644 +--- a/Objects/unicodeobject.c ++++ b/Objects/unicodeobject.c +@@ -9018,6 +9018,12 @@ _PyUnicode_Fini(void) (void)PyUnicode_ClearFreeList(); } @@ -627,10 +675,11 @@ diff -up Python-2.7.2/Objects/unicodeobject.c.add-debug-malloc-stats Python-2.7. #ifdef __cplusplus } #endif -diff -up Python-2.7.2/Python/pythonrun.c.add-debug-malloc-stats Python-2.7.2/Python/pythonrun.c ---- Python-2.7.2/Python/pythonrun.c.add-debug-malloc-stats 2011-09-16 19:03:25.025821626 -0400 -+++ Python-2.7.2/Python/pythonrun.c 2011-09-16 19:03:25.118821625 -0400 -@@ -549,7 +549,7 @@ Py_Finalize(void) +diff --git a/Python/pythonrun.c b/Python/pythonrun.c +index b686317..c3b3e17 100644 +--- a/Python/pythonrun.c ++++ b/Python/pythonrun.c +@@ -605,7 +605,7 @@ Py_Finalize(void) #endif /* Py_TRACE_REFS */ #ifdef PYMALLOC_DEBUG if (Py_GETENV("PYTHONMALLOCSTATS")) @@ -639,10 +688,11 @@ diff -up Python-2.7.2/Python/pythonrun.c.add-debug-malloc-stats Python-2.7.2/Pyt #endif call_ll_exitfuncs(); -diff -up Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats Python-2.7.2/Python/sysmodule.c ---- Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats 2011-09-16 19:03:25.007821626 -0400 -+++ Python-2.7.2/Python/sysmodule.c 2011-09-16 19:03:25.119821625 -0400 -@@ -872,6 +872,57 @@ a 11-tuple where the entries in the tupl +diff --git a/Python/sysmodule.c b/Python/sysmodule.c +index 22238ba..60624f2 100644 +--- a/Python/sysmodule.c ++++ b/Python/sysmodule.c +@@ -890,6 +890,57 @@ a 11-tuple where the entries in the tuple are counts of:\n\ extern "C" { #endif @@ -676,7 +726,7 @@ diff -up Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats Python-2.7.2/Pyt + if (!fp) { + PyErr_SetString(PyExc_ValueError, "file is closed"); + Py_DECREF(file); -+ return NULL; ++ return NULL; + } + + _PyObject_DebugMallocStats(fp); @@ -700,7 +750,7 @@ diff -up Python-2.7.2/Python/sysmodule.c.add-debug-malloc-stats Python-2.7.2/Pyt #ifdef Py_TRACE_REFS /* Defined in objects.c because it uses static globals if that file */ extern PyObject *_Py_GetObjects(PyObject *, PyObject *); -@@ -970,6 +1021,8 @@ static PyMethodDef sys_methods[] = { +@@ -988,6 +1039,8 @@ static PyMethodDef sys_methods[] = { {"settrace", sys_settrace, METH_O, settrace_doc}, {"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc}, {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, diff --git a/SOURCES/00169-avoid-implicit-usage-of-md5-in-multiprocessing.patch b/SOURCES/00169-avoid-implicit-usage-of-md5-in-multiprocessing.patch deleted file mode 100644 index debf92f..0000000 --- a/SOURCES/00169-avoid-implicit-usage-of-md5-in-multiprocessing.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py ---- a/Lib/multiprocessing/connection.py -+++ b/Lib/multiprocessing/connection.py -@@ -41,6 +41,10 @@ - # A very generous timeout when it comes to local connections... - CONNECTION_TIMEOUT = 20. - -+# The hmac module implicitly defaults to using MD5. -+# Support using a stronger algorithm for the challenge/response code: -+HMAC_DIGEST_NAME='sha256' -+ - _mmap_counter = itertools.count() - - default_family = 'AF_INET' -@@ -700,12 +704,16 @@ - WELCOME = b'#WELCOME#' - FAILURE = b'#FAILURE#' - -+def get_digestmod_for_hmac(): -+ import hashlib -+ return getattr(hashlib, HMAC_DIGEST_NAME) -+ - def deliver_challenge(connection, authkey): - import hmac - assert isinstance(authkey, bytes) - message = os.urandom(MESSAGE_LENGTH) - connection.send_bytes(CHALLENGE + message) -- digest = hmac.new(authkey, message).digest() -+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest() - response = connection.recv_bytes(256) # reject large message - if response == digest: - connection.send_bytes(WELCOME) -@@ -719,7 +727,7 @@ - message = connection.recv_bytes(256) # reject large message - assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message - message = message[len(CHALLENGE):] -- digest = hmac.new(authkey, message).digest() -+ digest = hmac.new(authkey, message, get_digestmod_for_hmac()).digest() - connection.send_bytes(digest) - response = connection.recv_bytes(256) # reject large message - if response != WELCOME: diff --git a/SOURCES/00189-gdb-py-bt-dont-raise-exception-from-eval.patch b/SOURCES/00189-gdb-py-bt-dont-raise-exception-from-eval.patch deleted file mode 100644 index 4ef2a5d..0000000 --- a/SOURCES/00189-gdb-py-bt-dont-raise-exception-from-eval.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- Python-2.7.5-orig/Tools/gdb/libpython.py 2013-05-12 03:32:54.000000000 +0000 -+++ Python-2.7.5-orig/Tools/gdb/libpython.py 2013-09-15 09:56:25.494000000 +0000 -@@ -887,6 +887,8 @@ - newline character''' - if self.is_optimized_out(): - return '(frame information optimized out)' -+ if self.filename() == '': -+ return '(in an eval block)' - filename = self.filename() - try: - f = open(filename, 'r') diff --git a/SOURCES/00189-use-rpm-wheels.patch b/SOURCES/00189-use-rpm-wheels.patch new file mode 100644 index 0000000..dce22df --- /dev/null +++ b/SOURCES/00189-use-rpm-wheels.patch @@ -0,0 +1,70 @@ +diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py +index 5021ebf..29a7d1b 100644 +--- a/Lib/ensurepip/__init__.py ++++ b/Lib/ensurepip/__init__.py +@@ -1,9 +1,10 @@ + #!/usr/bin/env python2 + from __future__ import print_function + ++import distutils.version ++import glob + import os + import os.path +-import pkgutil + import shutil + import sys + import tempfile +@@ -12,9 +13,19 @@ import tempfile + __all__ = ["version", "bootstrap"] + + +-_SETUPTOOLS_VERSION = "40.6.2" ++_WHEEL_DIR = "/usr/share/python{}-wheels/".format(sys.version_info[0]) + +-_PIP_VERSION = "18.1" ++def _get_most_recent_wheel_version(pkg): ++ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg)) ++ suffix = "-py2.py3-none-any.whl" ++ pattern = "{}*{}".format(prefix, suffix) ++ versions = (p[len(prefix):-len(suffix)] for p in glob.glob(pattern)) ++ return str(max(versions, key=distutils.version.LooseVersion)) ++ ++ ++_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools") ++ ++_PIP_VERSION = _get_most_recent_wheel_version("pip") + + _PROJECTS = [ + ("setuptools", _SETUPTOOLS_VERSION), +@@ -28,8 +39,13 @@ def _run_pip(args, additional_paths=None): + sys.path = additional_paths + sys.path + + # Install the bundled software +- import pip._internal +- return pip._internal.main(args) ++ try: ++ # pip 10 ++ from pip._internal import main ++ except ImportError: ++ # pip 9 ++ from pip import main ++ return main(args) + + + def version(): +@@ -100,12 +116,9 @@ def _bootstrap(root=None, upgrade=False, user=False, + additional_paths = [] + for project, version in _PROJECTS: + wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version) +- whl = pkgutil.get_data( +- "ensurepip", +- "_bundled/{}".format(wheel_name), +- ) +- with open(os.path.join(tmpdir, wheel_name), "wb") as fp: +- fp.write(whl) ++ with open(os.path.join(_WHEEL_DIR, wheel_name), "rb") as sfp: ++ with open(os.path.join(tmpdir, wheel_name), "wb") as fp: ++ fp.write(sfp.read()) + + additional_paths.append(os.path.join(tmpdir, wheel_name)) + 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 new file mode 100644 index 0000000..df1f4e7 --- /dev/null +++ b/SOURCES/00190-gdb-py-bt-dont-raise-exception-from-eval.patch @@ -0,0 +1,14 @@ +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/00198-add-rewheel-module.patch b/SOURCES/00198-add-rewheel-module.patch deleted file mode 100644 index 0273f1b..0000000 --- a/SOURCES/00198-add-rewheel-module.patch +++ /dev/null @@ -1,249 +0,0 @@ -diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py -index 89ed1ef..e2a4c25 100644 ---- a/Lib/ensurepip/__init__.py -+++ b/Lib/ensurepip/__init__.py -@@ -7,6 +7,7 @@ import pkgutil - import shutil - import sys - import tempfile -+from ensurepip import rewheel - - - __all__ = ["version", "bootstrap"] -@@ -29,6 +30,8 @@ def _run_pip(args, additional_paths=None): - - # Install the bundled software - import pip -+ if args[0] in ["install", "list", "wheel"]: -+ args.append('--pre') - return pip.main(args) - - -@@ -93,21 +96,40 @@ def _bootstrap(root=None, upgrade=False, user=False, - # omit pip and easy_install - os.environ["ENSUREPIP_OPTIONS"] = "install" - -+ whls = [] -+ rewheel_dir = None -+ # try to see if we have system-wide versions of _PROJECTS -+ dep_records = rewheel.find_system_records([p[0] for p in _PROJECTS]) -+ # TODO: check if system-wide versions are the newest ones -+ # if --upgrade is used? -+ if all(dep_records): -+ # if we have all _PROJECTS installed system-wide, we'll recreate -+ # wheels from them and install those -+ rewheel_dir = tempfile.mkdtemp() -+ for dr in dep_records: -+ new_whl = rewheel.rewheel_from_record(dr, rewheel_dir) -+ whls.append(os.path.join(rewheel_dir, new_whl)) -+ else: -+ # if we don't have all the _PROJECTS installed system-wide, -+ # let's just fall back to bundled wheels -+ for project, version in _PROJECTS: -+ whl = os.path.join( -+ os.path.dirname(__file__), -+ "_bundled", -+ "{}-{}-py2.py3-none-any.whl".format(project, version) -+ ) -+ whls.append(whl) -+ - tmpdir = tempfile.mkdtemp() - try: - # Put our bundled wheels into a temporary directory and construct the - # additional paths that need added to sys.path - additional_paths = [] -- for project, version in _PROJECTS: -- wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version) -- whl = pkgutil.get_data( -- "ensurepip", -- "_bundled/{}".format(wheel_name), -- ) -- with open(os.path.join(tmpdir, wheel_name), "wb") as fp: -- fp.write(whl) -- -- additional_paths.append(os.path.join(tmpdir, wheel_name)) -+ for whl in whls: -+ shutil.copy(whl, tmpdir) -+ additional_paths.append(os.path.join(tmpdir, os.path.basename(whl))) -+ if rewheel_dir: -+ shutil.rmtree(rewheel_dir) - - # Construct the arguments to be passed to the pip command - args = ["install", "--no-index", "--find-links", tmpdir] -diff --git a/Lib/ensurepip/rewheel/__init__.py b/Lib/ensurepip/rewheel/__init__.py -new file mode 100644 -index 0000000..75c2094 ---- /dev/null -+++ b/Lib/ensurepip/rewheel/__init__.py -@@ -0,0 +1,158 @@ -+import argparse -+import codecs -+import csv -+import email.parser -+import os -+import io -+import re -+import site -+import subprocess -+import sys -+import zipfile -+ -+def run(): -+ parser = argparse.ArgumentParser(description='Recreate wheel of package with given RECORD.') -+ parser.add_argument('record_path', -+ help='Path to RECORD file') -+ parser.add_argument('-o', '--output-dir', -+ help='Dir where to place the wheel, defaults to current working dir.', -+ dest='outdir', -+ default=os.path.curdir) -+ -+ ns = parser.parse_args() -+ retcode = 0 -+ try: -+ print(rewheel_from_record(**vars(ns))) -+ except BaseException as e: -+ print('Failed: {}'.format(e)) -+ retcode = 1 -+ sys.exit(1) -+ -+def find_system_records(projects): -+ """Return list of paths to RECORD files for system-installed projects. -+ -+ If a project is not installed, the resulting list contains None instead -+ of a path to its RECORD -+ """ -+ records = [] -+ # get system site-packages dirs -+ if hasattr(sys, 'real_prefix'): -+ #we are in python2 virtualenv and sys.real_prefix is the original sys.prefix -+ _orig_prefixes = site.PREFIXES -+ setattr(site, 'PREFIXES', [sys.real_prefix]*2) -+ sys_sitepack = site.getsitepackages() -+ setattr(site, 'PREFIXES', _orig_prefixes) -+ elif hasattr(sys, 'base_prefix'): # python3 venv doesn't inject real_prefix to sys -+ # we are on python3 and base(_exec)_prefix is unchanged in venv -+ sys_sitepack = site.getsitepackages([sys.base_prefix, sys.base_exec_prefix]) -+ else: -+ # we are in python2 without virtualenv -+ sys_sitepack = site.getsitepackages() -+ -+ sys_sitepack = [sp for sp in sys_sitepack if os.path.exists(sp)] -+ # try to find all projects in all system site-packages -+ for project in projects: -+ path = None -+ for sp in sys_sitepack: -+ dist_info_re = os.path.join(sp, project) + '-[^\{0}]+\.dist-info'.format(os.sep) -+ candidates = [os.path.join(sp, p) for p in os.listdir(sp)] -+ # filter out candidate dirs based on the above regexp -+ filtered = [c for c in candidates if re.match(dist_info_re, c)] -+ # if we have 0 or 2 or more dirs, something is wrong... -+ if len(filtered) == 1: -+ path = filtered[0] -+ if path is not None: -+ records.append(os.path.join(path, 'RECORD')) -+ else: -+ records.append(None) -+ return records -+ -+def rewheel_from_record(record_path, outdir): -+ """Recreates a whee of package with given record_path and returns path -+ to the newly created wheel.""" -+ site_dir = os.path.dirname(os.path.dirname(record_path)) -+ record_relpath = record_path[len(site_dir):].strip(os.path.sep) -+ to_write, to_omit = get_records_to_pack(site_dir, record_relpath) -+ new_wheel_name = get_wheel_name(record_path) -+ new_wheel_path = os.path.join(outdir, new_wheel_name + '.whl') -+ -+ new_wheel = zipfile.ZipFile(new_wheel_path, mode='w', compression=zipfile.ZIP_DEFLATED) -+ # we need to write a new record with just the files that we will write, -+ # e.g. not binaries and *.pyc/*.pyo files -+ if sys.version_info[0] < 3: -+ new_record = io.BytesIO() -+ else: -+ new_record = io.StringIO() -+ writer = csv.writer(new_record) -+ -+ # handle files that we can write straight away -+ for f, sha_hash, size in to_write: -+ new_wheel.write(os.path.join(site_dir, f), arcname=f) -+ writer.writerow([f, sha_hash,size]) -+ -+ # rewrite the old wheel file with a new computed one -+ writer.writerow([record_relpath, '', '']) -+ new_wheel.writestr(record_relpath, new_record.getvalue()) -+ -+ new_wheel.close() -+ -+ return new_wheel.filename -+ -+def get_wheel_name(record_path): -+ """Return proper name of the wheel, without .whl.""" -+ -+ wheel_info_path = os.path.join(os.path.dirname(record_path), 'WHEEL') -+ with codecs.open(wheel_info_path, encoding='utf-8') as wheel_info_file: -+ wheel_info = email.parser.Parser().parsestr(wheel_info_file.read().encode('utf-8')) -+ -+ metadata_path = os.path.join(os.path.dirname(record_path), 'METADATA') -+ with codecs.open(metadata_path, encoding='utf-8') as metadata_file: -+ metadata = email.parser.Parser().parsestr(metadata_file.read().encode('utf-8')) -+ -+ # construct name parts according to wheel spec -+ distribution = metadata.get('Name') -+ version = metadata.get('Version') -+ build_tag = '' # nothing for now -+ lang_tag = [] -+ for t in wheel_info.get_all('Tag'): -+ lang_tag.append(t.split('-')[0]) -+ lang_tag = '.'.join(lang_tag) -+ abi_tag, plat_tag = wheel_info.get('Tag').split('-')[1:3] -+ # leave out build tag, if it is empty -+ to_join = filter(None, [distribution, version, build_tag, lang_tag, abi_tag, plat_tag]) -+ return '-'.join(list(to_join)) -+ -+def get_records_to_pack(site_dir, record_relpath): -+ """Accepts path of sitedir and path of RECORD file relative to it. -+ Returns two lists: -+ - list of files that can be written to new RECORD straight away -+ - list of files that shouldn't be written or need some processing -+ (pyc and pyo files, scripts) -+ """ -+ record_file_path = os.path.join(site_dir, record_relpath) -+ with codecs.open(record_file_path, encoding='utf-8') as record_file: -+ record_contents = record_file.read() -+ # temporary fix for https://github.com/pypa/pip/issues/1376 -+ # we need to ignore files under ".data" directory -+ data_dir = os.path.dirname(record_relpath).strip(os.path.sep) -+ data_dir = data_dir[:-len('dist-info')] + 'data' -+ -+ to_write = [] -+ to_omit = [] -+ for l in record_contents.splitlines(): -+ spl = l.split(',') -+ if len(spl) == 3: -+ # new record will omit (or write differently): -+ # - abs paths, paths with ".." (entry points), -+ # - pyc+pyo files -+ # - the old RECORD file -+ # TODO: is there any better way to recognize an entry point? -+ if os.path.isabs(spl[0]) or spl[0].startswith('..') or \ -+ spl[0].endswith('.pyc') or spl[0].endswith('.pyo') or \ -+ spl[0] == record_relpath or spl[0].startswith(data_dir): -+ to_omit.append(spl) -+ else: -+ to_write.append(spl) -+ else: -+ pass # bad RECORD or empty line -+ return to_write, to_omit -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 877698c..2c43611 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -1065,7 +1065,7 @@ LIBSUBDIRS= lib-tk lib-tk/test lib-tk/test/test_tkinter \ - test/tracedmodules \ - encodings compiler hotshot \ - email email/mime email/test email/test/data \ -- ensurepip ensurepip/_bundled \ -+ ensurepip ensurepip/_bundled ensurepip/rewheel\ - json json/tests \ - sqlite3 sqlite3/test \ - logging bsddb bsddb/test csv importlib wsgiref \ diff --git a/SOURCES/00310-use-xml-sethashsalt-in-elementtree.patch b/SOURCES/00310-use-xml-sethashsalt-in-elementtree.patch deleted file mode 100644 index 27d8d1c..0000000 --- a/SOURCES/00310-use-xml-sethashsalt-in-elementtree.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 554c48934c599b3fb04c73d740bba1a745b89b41 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 18 Sep 2018 14:38:58 +0200 -Subject: [PATCH] [2.7] bpo-34623: Use XML_SetHashSalt in _elementtree - (GH-9146) - -The C accelerated _elementtree module now initializes hash randomization -salt from _Py_HashSecret instead of libexpat's default CPRNG. - -Signed-off-by: Christian Heimes - -https://bugs.python.org/issue34623. -(cherry picked from commit cb5778f00ce48631c7140f33ba242496aaf7102b) - -Co-authored-by: Christian Heimes ---- - Include/pyexpat.h | 4 +++- - .../next/Security/2018-09-10-16-05-39.bpo-34623.Ua9jMv.rst | 2 ++ - Modules/_elementtree.c | 5 +++++ - Modules/pyexpat.c | 5 +++++ - 4 files changed, 15 insertions(+), 1 deletion(-) - create mode 100644 Misc/NEWS.d/next/Security/2018-09-10-16-05-39.bpo-34623.Ua9jMv.rst - -diff --git a/Include/pyexpat.h b/Include/pyexpat.h -index 5340ef5fa386..3fc5fa54da63 100644 ---- a/Include/pyexpat.h -+++ b/Include/pyexpat.h -@@ -3,7 +3,7 @@ - - /* note: you must import expat.h before importing this module! */ - --#define PyExpat_CAPI_MAGIC "pyexpat.expat_CAPI 1.0" -+#define PyExpat_CAPI_MAGIC "pyexpat.expat_CAPI 1.1" - #define PyExpat_CAPSULE_NAME "pyexpat.expat_CAPI" - - struct PyExpat_CAPI -@@ -43,6 +43,8 @@ struct PyExpat_CAPI - XML_Parser parser, XML_UnknownEncodingHandler handler, - void *encodingHandlerData); - void (*SetUserData)(XML_Parser parser, void *userData); -+ /* might be none for expat < 2.1.0 */ -+ int (*SetHashSalt)(XML_Parser parser, unsigned long hash_salt); - /* always add new stuff to the end! */ - }; - -diff --git a/Misc/NEWS.d/next/Security/2018-09-10-16-05-39.bpo-34623.Ua9jMv.rst b/Misc/NEWS.d/next/Security/2018-09-10-16-05-39.bpo-34623.Ua9jMv.rst -new file mode 100644 -index 000000000000..31ad92ef8582 ---- /dev/null -+++ b/Misc/NEWS.d/next/Security/2018-09-10-16-05-39.bpo-34623.Ua9jMv.rst -@@ -0,0 +1,2 @@ -+The C accelerated _elementtree module now initializes hash randomization -+salt from _Py_HashSecret instead of libexpat's default CSPRNG. -diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c -index f7f992dd3a95..b38e0ab329c7 100644 ---- a/Modules/_elementtree.c -+++ b/Modules/_elementtree.c -@@ -2574,6 +2574,11 @@ xmlparser(PyObject* self_, PyObject* args, PyObject* kw) - PyErr_NoMemory(); - return NULL; - } -+ /* expat < 2.1.0 has no XML_SetHashSalt() */ -+ if (EXPAT(SetHashSalt) != NULL) { -+ EXPAT(SetHashSalt)(self->parser, -+ (unsigned long)_Py_HashSecret.prefix); -+ } - - ALLOC(sizeof(XMLParserObject), "create expatparser"); - -diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c -index 2b4d31293c64..1f8c0d70a559 100644 ---- a/Modules/pyexpat.c -+++ b/Modules/pyexpat.c -@@ -2042,6 +2042,11 @@ MODULE_INITFUNC(void) - capi.SetProcessingInstructionHandler = XML_SetProcessingInstructionHandler; - capi.SetUnknownEncodingHandler = XML_SetUnknownEncodingHandler; - capi.SetUserData = XML_SetUserData; -+#if XML_COMBINED_VERSION >= 20100 -+ capi.SetHashSalt = XML_SetHashSalt; -+#else -+ capi.SetHashSalt = NULL; -+#endif - - /* export using capsule */ - capi_object = PyCapsule_New(&capi, PyExpat_CAPSULE_NAME, NULL); diff --git a/SOURCES/00311-fix-test_dbm_gnu.patch b/SOURCES/00311-fix-test_dbm_gnu.patch deleted file mode 100644 index 6ee2268..0000000 --- a/SOURCES/00311-fix-test_dbm_gnu.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/Lib/test/test_gdbm.py b/Lib/test/test_gdbm.py -index e9169a2..c4a498f 100644 ---- a/Lib/test/test_gdbm.py -+++ b/Lib/test/test_gdbm.py -@@ -62,9 +62,13 @@ class TestGdbm(unittest.TestCase): - self.g = gdbm.open(filename, 'c') - size0 = os.path.getsize(filename) - -- self.g['x'] = 'x' * 10000 -+ # bpo-33901: on macOS with gdbm 1.15, an empty database uses 16 MiB -+ # and adding an entry of 10,000 B has no effect on the file size. -+ # Add size0 bytes to make sure that the file size changes. -+ value_size = max(size0, 10000) -+ self.g['x'] = 'x' * value_size - size1 = os.path.getsize(filename) -- self.assertTrue(size0 < size1) -+ self.assertGreater(size1, size0) - - del self.g['x'] - # 'size' is supposed to be the same even after deleting an entry. -@@ -72,7 +76,8 @@ class TestGdbm(unittest.TestCase): - - self.g.reorganize() - size2 = os.path.getsize(filename) -- self.assertTrue(size1 > size2 >= size0) -+ self.assertLess(size2, size1) -+ self.assertGreaterEqual(size2, size0) - - - def test_main(): diff --git a/SOURCES/00313-cprofile-sort-option.patch b/SOURCES/00313-cprofile-sort-option.patch deleted file mode 100644 index 5eaaef2..0000000 --- a/SOURCES/00313-cprofile-sort-option.patch +++ /dev/null @@ -1,53 +0,0 @@ -diff --git a/Lib/cProfile.py b/Lib/cProfile.py -index d3770946db11..cd53cc0abd53 100755 ---- a/Lib/cProfile.py -+++ b/Lib/cProfile.py -@@ -161,7 +161,7 @@ def label(code): - # ____________________________________________________________ - - def main(): -- import os, sys -+ import os, sys, pstats - from optparse import OptionParser - usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..." - parser = OptionParser(usage=usage) -@@ -170,7 +170,8 @@ def main(): - help="Save stats to ", default=None) - parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", -- default=-1) -+ default=-1, -+ choices=sorted(pstats.Stats.sort_arg_dict_default)) - - if not sys.argv[1:]: - parser.print_usage() -diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py -index af3fe62a9a6b..83d52296e218 100644 ---- a/Lib/test/test_cprofile.py -+++ b/Lib/test/test_cprofile.py -@@ -1,7 +1,9 @@ - """Test suite for the cProfile module.""" - - import sys -+import unittest - from test.test_support import run_unittest, TESTFN, unlink -+from test.support.script_helper import assert_python_failure - - # rip off all interesting stuff from test_profile - import cProfile -@@ -26,8 +28,14 @@ def test_bad_counter_during_dealloc(self): - unlink(TESTFN) - - -+class TestCommandLine(unittest.TestCase): -+ def test_sort(self): -+ rc, out, err = assert_python_failure('-m', 'cProfile', '-s', 'demo') -+ self.assertGreater(rc, 0) -+ self.assertIn(b"option -s: invalid choice: 'demo'", err) -+ - def test_main(): -- run_unittest(CProfileTest) -+ run_unittest(CProfileTest, TestCommandLine) - - def main(): - if '-r' not in sys.argv: diff --git a/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch b/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch index e3ad026..670f3e4 100644 --- a/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch +++ b/SOURCES/00320-CVE-2019-9636-and-CVE-2019-10160.patch @@ -1,5 +1,5 @@ diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst -index b933dda..e3a5412 100644 +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: @@ -23,9 +23,9 @@ index b933dda..e3a5412 100644 + now raise :exc:`ValueError`. + - .. function:: parse_qs(qs[, keep_blank_values[, strict_parsing]]) + .. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]]) -@@ -220,11 +230,21 @@ The :mod:`urlparse` module defines the following functions: +@@ -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. @@ -105,7 +105,7 @@ index 4e1ded7..86c4a05 100644 test_support.run_unittest(UrlParseTestCase) diff --git a/Lib/urlparse.py b/Lib/urlparse.py -index 4cd3d67..55e9928 100644 +index f7c2b03..798b467 100644 --- a/Lib/urlparse.py +++ b/Lib/urlparse.py @@ -165,6 +165,25 @@ def _splitnetloc(url, start=0): diff --git a/SOURCES/00323-coverity-scan-fixes.patch b/SOURCES/00323-coverity-scan-fixes.patch new file mode 100644 index 0000000..4b12000 --- /dev/null +++ b/SOURCES/00323-coverity-scan-fixes.patch @@ -0,0 +1,423 @@ +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 new file mode 100644 index 0000000..84e788f --- /dev/null +++ b/SOURCES/00324-disallow-control-chars-in-http-urls.patch @@ -0,0 +1,172 @@ +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 new file mode 100644 index 0000000..890bf71 --- /dev/null +++ b/SOURCES/00325-CVE-2019-9948.patch @@ -0,0 +1,37 @@ +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/SOURCES/python-2.7.1-config.patch b/SOURCES/python-2.7.1-config.patch index 5c8bd43..4b076db 100644 --- a/SOURCES/python-2.7.1-config.patch +++ b/SOURCES/python-2.7.1-config.patch @@ -1,5 +1,7 @@ ---- Python-2.7.4/Modules/Setup.dist.rhconfig 2013-04-06 16:02:34.000000000 +0200 -+++ Python-2.7.4/Modules/Setup.dist 2013-04-08 10:05:16.369985654 +0200 +diff --git a/Modules/Setup.dist b/Modules/Setup.dist +index bbc9222..2cf35a9 100644 +--- a/Modules/Setup.dist ++++ b/Modules/Setup.dist @@ -153,7 +153,7 @@ GLHACK=-Dclear=__GLclear # modules are to be built as shared libraries (see above for more # detail; also note that *static* reverses this effect): @@ -9,7 +11,7 @@ # GNU readline. Unlike previous Python incarnations, GNU readline is # now incorporated in an optional module, configured in the Setup file -@@ -163,77 +163,77 @@ GLHACK=-Dclear=__GLclear +@@ -163,33 +163,33 @@ GLHACK=-Dclear=__GLclear # it, depending on your system -- see the GNU readline instructions. # It's okay for this to be a shared library, too. @@ -59,9 +61,7 @@ # Standard I/O baseline #_io -I$(srcdir)/Modules/_io _io/bufferedio.c _io/bytesio.c _io/fileio.c _io/iobase.c _io/_iomodule.c _io/stringio.c _io/textio.c - - - # Modules with some UNIX dependencies -- on by default: +@@ -199,41 +199,41 @@ GLHACK=-Dclear=__GLclear # (If you have a really backward UNIX, select and socket may not be # supported...) @@ -117,7 +117,7 @@ # Multimedia modules -- off by default. -@@ -238,8 +238,8 @@ GLHACK=-Dclear=__GLclear +@@ -241,8 +241,8 @@ GLHACK=-Dclear=__GLclear # #993173 says audioop works on 64-bit platforms, though. # These represent audio samples or images as strings: @@ -128,7 +128,7 @@ # Note that the _md5 and _sha modules are normally only built if the -@@ -249,14 +249,14 @@ GLHACK=-Dclear=__GLclear +@@ -252,14 +252,14 @@ GLHACK=-Dclear=__GLclear # Message-Digest Algorithm, described in RFC 1321. The necessary files # md5.c and md5.h are included here. @@ -147,7 +147,7 @@ # SGI IRIX specific modules -- off by default. -@@ -303,12 +303,12 @@ GLHACK=-Dclear=__GLclear +@@ -306,12 +306,12 @@ GLHACK=-Dclear=__GLclear # A Linux specific module -- off by default; this may also work on # some *BSDs. @@ -162,7 +162,7 @@ # The _tkinter module. -@@ -323,7 +323,7 @@ GLHACK=-Dclear=__GLclear +@@ -326,7 +326,7 @@ GLHACK=-Dclear=__GLclear # every system. # *** Always uncomment this (leave the leading underscore in!): @@ -171,7 +171,7 @@ # *** Uncomment and edit to reflect where your Tcl/Tk libraries are: # -L/usr/local/lib \ # *** Uncomment and edit to reflect where your Tcl/Tk headers are: -@@ -333,7 +333,7 @@ GLHACK=-Dclear=__GLclear +@@ -336,7 +336,7 @@ GLHACK=-Dclear=__GLclear # *** Or uncomment this for Solaris: # -I/usr/openwin/include \ # *** Uncomment and edit for Tix extension only: @@ -180,7 +180,7 @@ # *** Uncomment and edit for BLT extension only: # -DWITH_BLT -I/usr/local/blt/blt8.0-unoff/include -lBLT8.0 \ # *** Uncomment and edit for PIL (TkImaging) extension only: -@@ -342,7 +342,7 @@ GLHACK=-Dclear=__GLclear +@@ -345,7 +345,7 @@ GLHACK=-Dclear=__GLclear # *** Uncomment and edit for TOGL extension only: # -DWITH_TOGL togl.c \ # *** Uncomment and edit to reflect your Tcl/Tk versions: @@ -189,7 +189,7 @@ # *** Uncomment and edit to reflect where your X11 libraries are: # -L/usr/X11R6/lib \ # *** Or uncomment this for Solaris: -@@ -352,7 +352,7 @@ GLHACK=-Dclear=__GLclear +@@ -355,7 +355,7 @@ GLHACK=-Dclear=__GLclear # *** Uncomment for AIX: # -lld \ # *** Always uncomment this; X11 libraries to link with: @@ -198,7 +198,7 @@ # Lance Ellinghaus's syslog module #syslog syslogmodule.c # syslog daemon interface -@@ -374,7 +374,7 @@ GLHACK=-Dclear=__GLclear +@@ -377,7 +377,7 @@ GLHACK=-Dclear=__GLclear # it is a highly experimental and dangerous device for calling # *arbitrary* C functions in *arbitrary* shared libraries: @@ -207,7 +207,7 @@ # Modules that provide persistent dictionary-like semantics. You will -@@ -397,7 +397,7 @@ GLHACK=-Dclear=__GLclear +@@ -400,7 +400,7 @@ GLHACK=-Dclear=__GLclear # # First, look at Setup.config; configure may have set this for you. @@ -216,7 +216,7 @@ # Sleepycat Berkeley DB interface. -@@ -412,11 +412,9 @@ GLHACK=-Dclear=__GLclear +@@ -415,11 +415,9 @@ GLHACK=-Dclear=__GLclear # # Edit the variables DB and DBLIBVERto point to the db top directory # and the subdirectory of PORT where you built it. @@ -231,7 +231,7 @@ # Historical Berkeley DB 1.85 # -@@ -431,14 +430,14 @@ GLHACK=-Dclear=__GLclear +@@ -434,14 +432,14 @@ GLHACK=-Dclear=__GLclear # Helper module for various ascii-encoders @@ -250,7 +250,7 @@ # Lee Busby's SIGFPE modules. -@@ -461,7 +460,7 @@ GLHACK=-Dclear=__GLclear +@@ -464,7 +462,7 @@ GLHACK=-Dclear=__GLclear # Andrew Kuchling's zlib module. # This require zlib 1.1.3 (or later). # See http://www.gzip.org/zlib/ @@ -258,20 +258,21 @@ +zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz # Interface to the Expat XML parser - # -@@ -480,14 +479,14 @@ GLHACK=-Dclear=__GLclear + # More information on Expat can be found at www.libexpat.org. +@@ -475,14 +473,14 @@ GLHACK=-Dclear=__GLclear # Hye-Shik Chang's CJKCodecs # multibytecodec is required for all the other CJK codec modules -#_multibytecodec cjkcodecs/multibytecodec.c -+_multibytecodec cjkcodecs/multibytecodec.c - +- -#_codecs_cn cjkcodecs/_codecs_cn.c -#_codecs_hk cjkcodecs/_codecs_hk.c -#_codecs_iso2022 cjkcodecs/_codecs_iso2022.c -#_codecs_jp cjkcodecs/_codecs_jp.c -#_codecs_kr cjkcodecs/_codecs_kr.c -#_codecs_tw cjkcodecs/_codecs_tw.c ++_multibytecodec cjkcodecs/multibytecodec.c ++ +_codecs_cn cjkcodecs/_codecs_cn.c +_codecs_hk cjkcodecs/_codecs_hk.c +_codecs_iso2022 cjkcodecs/_codecs_iso2022.c diff --git a/SPECS/python2.spec b/SPECS/python2.spec index 3307be4..1c3cc9a 100644 --- a/SPECS/python2.spec +++ b/SPECS/python2.spec @@ -5,8 +5,9 @@ # Note that the bcond macros are named for the CLI option they create. # "%%bcond_without" means "ENABLE by default and create a --without option" -# Ability to reuse RPM-installed pip using rewheel -%bcond_without rewheel +# Whether to use RPM build wheels from the python2-{pip,setuptools}-wheel package +# Uses upstream bundled prebuilt wheels otherwise +%bcond_without rpmwheels # Extra build for debugging the interpreter or C-API extensions # (the -debug subpackages) @@ -102,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.15 -Release: 24%{?dist} +Version: 2.7.16 +Release: 12%{?dist} License: Python Group: Development/Languages Requires: %{python}-libs%{?_isa} = %{version}-%{release} @@ -165,19 +166,22 @@ BuildRequires: valgrind-devel BuildRequires: zlib-devel -%if %{with rewheel} -BuildRequires: python2-setuptools -Requires: python2-setuptools - -BuildRequires: python2-pip -Requires: python2-pip -%endif # rewheel +%if %{with rpmwheels} +BuildRequires: python2-setuptools-wheel +BuildRequires: python2-pip-wheel +%endif # Runtime require alternatives Requires: %{_sbindir}/alternatives Requires(post): %{_sbindir}/alternatives Requires(postun): %{_sbindir}/alternatives +# Previously, this was required for our rewheel patch to work. +# This is technically no longer needed, but we keep it recommended +# for the developer experience. +Recommends: python2-setuptools +Recommends: python2-pip + # ======================= # Source code and patches @@ -562,20 +566,15 @@ Patch144: 00144-no-gdbm.patch # 00146 # # Support OpenSSL FIPS mode (e.g. when OPENSSL_FORCE_FIPS_MODE=1 is set) -# - handle failures from OpenSSL (e.g. on attempts to use MD5 in a +# - handle some failures from OpenSSL (e.g. on attempts to use MD5 in a # FIPS-enforcing environment) # - add a new "usedforsecurity" keyword argument to the various digest # algorithms in hashlib so that you can whitelist a callsite with # "usedforsecurity=False" -# (sent upstream for python 3 as http://bugs.python.org/issue9216; this is a -# backport to python 2.7; see RHEL6 patch 119) -# - enforce usage of the _hashlib implementation: don't fall back to the _md5 -# and _sha* modules (leading to clearer error messages if fips selftests -# fail) # - don't build the _md5 and _sha* modules; rely on the _hashlib implementation # of hashlib (for example, md5.py will use _hashlib's implementation of MD5, # if permitted by the FIPS setting) -# (rhbz#563986) +# Resolves: rhbz#1734126 Patch146: 00146-hashlib-fips.patch # 00147 # @@ -650,14 +649,6 @@ Patch167: 00167-disable-stack-navigation-tests-when-optimized-in-test_gdb.patch # (rhbz#849994) Patch168: 00168-distutils-cflags.patch -# 00169 # -# Use SHA-256 rather than implicitly using MD5 within the challenge handling -# in multiprocessing.connection -# -# Sent upstream as http://bugs.python.org/issue17258 -# (rhbz#879695) -Patch169: 00169-avoid-implicit-usage-of-md5-in-multiprocessing.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 @@ -705,10 +696,15 @@ Patch185: 00185-urllib2-honors-noproxy-for-ftp.patch Patch187: 00187-add-RPATH-to-pyexpat.patch # 00189 # +# Instead of bundled wheels, use our RPM packaged wheels from +# /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) -Patch189: 00189-gdb-py-bt-dont-raise-exception-from-eval.patch +Patch190: 00190-gdb-py-bt-dont-raise-exception-from-eval.patch # 00191 # # Disabling NOOP test as it fails without internet connection @@ -721,9 +717,6 @@ Patch191: 00191-disable-NOOP.patch # Patch provided by John C. Peterson Patch193: 00193-enable-loading-sqlite-extensions.patch -# 00198 # -Patch198: 00198-add-rewheel-module.patch - # 00257 # # Python's threading library doesn't use the monotonic clock when handling wait timeouts, # so when the system clock is set backwards, the wait doesn't return after the timeout, @@ -743,32 +736,44 @@ Patch288: 00288-ambiguous-python-version-rpmbuild-warn.patch # (we handle it it in Setup.dist, see Patch0) Patch289: 00289-disable-nis-detection.patch -# 00310 # -# CVE-2018-14647 -# Use XML_SetHashSalt in _elementtree -# rhbz#1632095 -# Fixed upstream: https://bugs.python.org/issue34623 -Patch310: 00310-use-xml-sethashsalt-in-elementtree.patch - -# 00311 # -# Fix test_dbm_gnu for gdbm 1.15 which fails on ppc64le -# Resolves: rhbz#1638716 -# Fixed upstream: https://bugs.python.org/issue33901 -Patch311: 00311-fix-test_dbm_gnu.patch - -# 00313 # -# Add choices for sort option of cProfile for better output message -# Fixed upstream: http://bugs.python.org/issue23420 -# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1649473 -Patch313: 00313-cprofile-sort-option.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=1693974 -# and https://bugzilla.redhat.com/show_bug.cgi?id=1714757 +# 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., @@ -834,6 +839,14 @@ Requires: glibc%{?_isa} >= 2.24.90-26 Requires: gdbm%{?_isa} >= 1:1.13 %endif +%if %{with rpmwheels} +Requires: python2-setuptools-wheel +Requires: python2-pip-wheel +%else +Provides: bundled(python2-pip) = 18.1 +Provides: bundled(python2-setuptools) = 40.6.2 +%endif + %description libs This package contains files used to embed Python 2 into applications. @@ -852,6 +865,15 @@ Requires: pkgconfig Requires: python3-rpm-generators %endif +# This is not "API" (packages that need setuptools should still BuildRequire it) +# However some packages apparently can build both with and without setuptools +# producing egg-info as file or directory (depending on setuptools presence). +# Directory-to-file updates are problematic in RPM, so we ensure setuptools is +# installed when -devel is required. +# See https://bugzilla.redhat.com/show_bug.cgi?id=1623922 +# See https://fedoraproject.org/wiki/Packaging:Directory_Replacement +Requires: python2-setuptools + # https://bugzilla.redhat.com/show_bug.cgi?id=1217376 # https://bugzilla.redhat.com/show_bug.cgi?id=1496757 # https://bugzilla.redhat.com/show_bug.cgi?id=1218294 @@ -1038,7 +1060,7 @@ rm -r Modules/zlib || exit 1 %if !%{with_gdbm} %patch144 -p1 %endif -#patch146 -p1 +%patch146 -p1 %patch147 -p1 %patch153 -p0 %patch155 -p1 @@ -1048,26 +1070,28 @@ rm -r Modules/zlib || exit 1 mv Modules/cryptmodule.c Modules/_cryptmodule.c %patch167 -p1 %patch168 -p1 -%patch169 -p1 %patch170 -p1 %patch174 -p1 -b .fix-for-usr-move %patch180 -p1 %patch181 -p1 %patch185 -p1 %patch187 -p1 + +%if %{with rpmwheels} %patch189 -p1 +rm Lib/ensurepip/_bundled/*.whl +%endif + +%patch190 -p1 %patch191 -p1 %patch193 -p1 -%if %{with rewheel} -%patch198 -p1 -%endif %patch257 -p1 %patch288 -p1 %patch289 -p1 -%patch310 -p1 -%patch311 -p1 -%patch313 -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 @@ -1506,6 +1530,11 @@ find %{buildroot} -type f -a -name "*.py" -print0 | \ /usr/bin/chmod 755 %{buildroot}%{_libdir}/libpython%{pybasever}_d.so.1.0 %endif +# Remove pyc/pyo files from /usr/bin +# They are not needed, and due to them, the resulting RPM is not multilib-clean +# https://bugzilla.redhat.com/show_bug.cgi?id=1703575 +rm %{buildroot}%{_bindir}/*.py{c,o} + # Remove the /usr/bin/python executable so that the user can chose where it # points to by installing streams of the @python module rm %{buildroot}%{_bindir}/python @@ -1528,6 +1557,7 @@ rm %{buildroot}%{_bindir}/python-config touch %{buildroot}%{_bindir}/unversioned-python touch %{buildroot}%{_mandir}/man1/python.1.gz + # ====================================================== # Running the upstream test suite # ====================================================== @@ -1539,6 +1569,8 @@ CheckPython() { BinaryName=$2 ConfDir=$(pwd)/build/$ConfName + export OPENSSL_CONF=/non-existing-file + echo STARTING: CHECKING OF PYTHON FOR CONFIGURATION: $ConfName # Note that we're running the tests using the version of the code in the @@ -1567,8 +1599,7 @@ CheckPython() { # our non-standard decorators take effect on the relevant tests: # @unittest._skipInRpmBuild(reason) # @unittest._expectedFailureInRpmBuild - WITHIN_PYTHON_RPM_BUILD= EXTRATESTOPTS="$EXTRATESTOPTS -x test_ftplib test_poplib test_ssl \ - -x test_httplib -x test_urllib2_localnet -x test_distutils" make test + WITHIN_PYTHON_RPM_BUILD= EXTRATESTOPTS="$EXTRATESTOPTS -x test_distutils" make test popd @@ -1784,11 +1815,11 @@ fi %dir %{pylibdir}/ensurepip/ %{pylibdir}/ensurepip/*.py* +%if %{with rpmwheels} %exclude %{pylibdir}/ensurepip/_bundled - -%if %{with rewheel} -%dir %{pylibdir}/ensurepip/rewheel/ -%{pylibdir}/ensurepip/rewheel/*.py* +%else +%dir %{pylibdir}/ensurepip/_bundled +%{pylibdir}/ensurepip/_bundled/*.whl %endif @@ -1999,16 +2030,56 @@ fi # ====================================================== %changelog -* Fri Jun 07 2019 Charalampos Stratakis - 2.7.15-24 +* 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 + - Patch 169 has been removed, obsoleted by the updated patch 146 +Resolves: rhbz#1734126 + +* Tue Jul 02 2019 Miro HronĨok - 2.7.16-11 +- Use RPM built wheels of pip and setuptools in ensurepip instead of our rewheel patch +- Require python2-setuptools from python2-devel to prevent packaging errors +Resolves: rhbz#1718398 + +* Tue Jun 11 2019 Charalampos Stratakis - 2.7.16-10 +- Fix urlparse.urlsplit() error message for Unicode URL +Resolves: rhbz#1689327 + +* Fri Jun 07 2019 Charalampos Stratakis - 2.7.16-9 - Security fix for CVE-2019-10160 -Resolves: rhbz#1714757 +Resolves: rhbz#1689327 + +* Thu May 30 2019 Charalampos Stratakis - 2.7.16-8 +- Security fix for CVE-2019-9948 +Resolves: rhbz#1704176 + +* Thu May 30 2019 Charalampos Stratakis - 2.7.16-7 +- Disallow control chars in http URLs +- Fixes CVE-2019-9740 and CVE-2019-9947 +Resolves: rhbz#1703539 and rhbz#1704367 + +* Tue May 21 2019 Tomas Orsava - 2.7.16-6 +- Remove pyc/pyo files from /usr/bin +Resolves: rhbz#1696741 -* Fri May 03 2019 Charalampos Stratakis - 2.7.15-23 +* Fri May 03 2019 Charalampos Stratakis - 2.7.16-5 - Updated fix for CVE-2019-9636 -Resolves: rhbz#1714757 +Resolves: rhbz#1689327 + +* Thu Apr 25 2019 Tomas Orsava - 2.7.16-4 +- Bumping due to problems with modular RPM upgrade path +- Resolves: rhbz#1695587 + +* Fri Apr 12 2019 Charalampos Stratakis - 2.7.16-3 +- Fix coverity scan static analysis issues +Resolves: rhbz#1690919 + +* Wed Apr 3 2019 Charalampos Stratakis - 2.7.16-2 +- Security fix for CVE-2019-9636 (rhbz#1689327) -* Wed Apr 3 2019 Charalampos Stratakis - 2.7.15-22 -- Security fix for CVE-2019-9636 (rhbz#1693974) +* Tue Feb 19 2019 Charalampos Stratakis - 2.7.16-1 +- Update to 2.7.16 +Resolves: rhbz#1680967 * Wed Dec 12 2018 Tomas Orsava - 2.7.15-21 - Fix Tkinter