import python2-2.7.17-1.module+el8.2.0+4561+f4e0d66a

This commit is contained in:
CentOS Sources 2020-01-21 13:45:50 -05:00 committed by Stepan Oksanichenko
parent 0dd19ae1fe
commit f452db3efc
15 changed files with 719 additions and 1745 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/Python-2.7.16-noexe.tar.xz
SOURCES/Python-2.7.17-noexe.tar.xz

View File

@ -1 +1 @@
c3f14ebccf0b8848a154eb510c7fcf6a8bb038f4 SOURCES/Python-2.7.16-noexe.tar.xz
e63124a9a86b4b52c09384915a0842adf00b9d45 SOURCES/Python-2.7.17-noexe.tar.xz

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
--- Lib/test/test_gdb.py.old 2012-04-11 21:04:01.367073855 -0400
+++ Lib/test/test_gdb.py 2012-04-12 08:52:58.320288761 -0400
@@ -211,6 +211,10 @@
# ignore all warnings
'warning: ',
)
+ ignore_patterns += ('warning: Unable to open',
+ 'Missing separate debuginfo for',
+ 'Try: yum --disablerepo=',
+ 'Undefined set print command')
for line in errlines:
if not line:
continue

View File

@ -1,49 +0,0 @@
diff -up Python-2.7.3/Lib/test/test_os.py.uid-gid-overflows Python-2.7.3/Lib/test/test_os.py
--- Python-2.7.3/Lib/test/test_os.py.uid-gid-overflows 2012-04-09 19:07:32.000000000 -0400
+++ Python-2.7.3/Lib/test/test_os.py 2012-06-26 14:51:36.000817929 -0400
@@ -677,30 +677,36 @@ if sys.platform != 'win32':
def test_setuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setuid, 0)
+ self.assertRaises(TypeError, os.setuid, 'not an int')
self.assertRaises(OverflowError, os.setuid, 1<<32)
@unittest.skipUnless(hasattr(os, 'setgid'), 'test needs os.setgid()')
def test_setgid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setgid, 0)
+ self.assertRaises(TypeError, os.setgid, 'not an int')
self.assertRaises(OverflowError, os.setgid, 1<<32)
@unittest.skipUnless(hasattr(os, 'seteuid'), 'test needs os.seteuid()')
def test_seteuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.seteuid, 0)
+ self.assertRaises(TypeError, os.seteuid, 'not an int')
self.assertRaises(OverflowError, os.seteuid, 1<<32)
@unittest.skipUnless(hasattr(os, 'setegid'), 'test needs os.setegid()')
def test_setegid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setegid, 0)
+ self.assertRaises(TypeError, os.setegid, 'not an int')
self.assertRaises(OverflowError, os.setegid, 1<<32)
@unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()')
def test_setreuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setreuid, 0, 0)
+ self.assertRaises(TypeError, os.setreuid, 'not an int', 0)
+ self.assertRaises(TypeError, os.setreuid, 0, 'not an int')
self.assertRaises(OverflowError, os.setreuid, 1<<32, 0)
self.assertRaises(OverflowError, os.setreuid, 0, 1<<32)
@@ -715,6 +721,8 @@ if sys.platform != 'win32':
def test_setregid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setregid, 0, 0)
+ self.assertRaises(TypeError, os.setregid, 'not an int', 0)
+ self.assertRaises(TypeError, os.setregid, 0, 'not an int')
self.assertRaises(OverflowError, os.setregid, 1<<32, 0)
self.assertRaises(OverflowError, os.setregid, 0, 1<<32)

View File

@ -1,12 +0,0 @@
diff -up Python-2.6.6/Lib/distutils/sysconfig.py.distutils-cflags Python-2.6.6/Lib/distutils/sysconfig.py
--- Python-2.6.6/Lib/distutils/sysconfig.py.distutils-cflags 2011-08-12 17:18:17.833091153 -0400
+++ Python-2.6.6/Lib/distutils/sysconfig.py 2011-08-12 17:18:27.449106938 -0400
@@ -187,7 +187,7 @@ def customize_compiler(compiler):
if 'LDFLAGS' in os.environ:
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
if 'CFLAGS' in os.environ:
- cflags = opt + ' ' + os.environ['CFLAGS']
+ cflags = cflags + ' ' + os.environ['CFLAGS']
ldshared = ldshared + ' ' + os.environ['CFLAGS']
if 'CPPFLAGS' in os.environ:
cpp = cpp + ' ' + os.environ['CPPFLAGS']

View File

@ -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:

View File

@ -1,14 +0,0 @@
diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py
index 9def56e..c0df208 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -939,6 +939,9 @@ class PyFrameObjectPtr(PyObjectPtr):
if self.is_optimized_out():
return '(frame information optimized out)'
+ if self.filename() == '<string>':
+ return '(in an eval block)'
+
lineno = self.current_line_num()
if lineno is None:
return '(failed to get frame line number)'

View File

@ -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 = "41.2.0"
+_WHEEL_DIR = "/usr/share/python{}-wheels/".format(sys.version_info[0])
-_PIP_VERSION = "19.2.3"
+def _get_most_recent_wheel_version(pkg):
+ prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg))
+ suffix = "-py2.py3-none-any.whl"
+ 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))

View File

@ -1,249 +0,0 @@
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
index 5021ebf..63c763a 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._internal
+ if args[0] in ["install", "list", "wheel"]:
+ args.append('--pre')
return pip._internal.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 \

View File

@ -1,142 +0,0 @@
diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst
index 22249da..0989c88 100644
--- a/Doc/library/urlparse.rst
+++ b/Doc/library/urlparse.rst
@@ -119,12 +119,22 @@ The :mod:`urlparse` module defines the following functions:
See section :ref:`urlparse-result-object` for more information on the result
object.
+ Characters in the :attr:`netloc` attribute that decompose under NFKC
+ normalization (as used by the IDNA encoding) into any of ``/``, ``?``,
+ ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is
+ decomposed before parsing, or is not a Unicode string, no error will be
+ raised.
+
.. versionchanged:: 2.5
Added attributes to return value.
.. versionchanged:: 2.7
Added IPv6 URL parsing capabilities.
+ .. versionchanged:: 2.7.17
+ Characters that affect netloc parsing under NFKC normalization will
+ now raise :exc:`ValueError`.
+
.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]])
@@ -232,11 +242,21 @@ The :mod:`urlparse` module defines the following functions:
See section :ref:`urlparse-result-object` for more information on the result
object.
+ Characters in the :attr:`netloc` attribute that decompose under NFKC
+ normalization (as used by the IDNA encoding) into any of ``/``, ``?``,
+ ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is
+ decomposed before parsing, or is not a Unicode string, no error will be
+ raised.
+
.. versionadded:: 2.2
.. versionchanged:: 2.5
Added attributes to return value.
+ .. versionchanged:: 2.7.17
+ Characters that affect netloc parsing under NFKC normalization will
+ now raise :exc:`ValueError`.
+
.. function:: urlunsplit(parts)
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
index 4e1ded7..857ed96 100644
--- a/Lib/test/test_urlparse.py
+++ b/Lib/test/test_urlparse.py
@@ -1,4 +1,6 @@
from test import test_support
+import sys
+import unicodedata
import unittest
import urlparse
@@ -624,6 +626,36 @@ class UrlParseTestCase(unittest.TestCase):
self.assertEqual(urlparse.urlparse("http://www.python.org:80"),
('http','www.python.org:80','','','',''))
+ def test_urlsplit_normalization(self):
+ # Certain characters should never occur in the netloc,
+ # including under normalization.
+ # Ensure that ALL of them are detected and cause an error
+ illegal_chars = u'/:#?@'
+ hex_chars = {'{:04X}'.format(ord(c)) for c in illegal_chars}
+ denorm_chars = [
+ c for c in map(unichr, range(128, sys.maxunicode))
+ if (hex_chars & set(unicodedata.decomposition(c).split()))
+ and c not in illegal_chars
+ ]
+ # Sanity check that we found at least one such character
+ self.assertIn(u'\u2100', denorm_chars)
+ self.assertIn(u'\uFF03', denorm_chars)
+
+ # bpo-36742: Verify port separators are ignored when they
+ # existed prior to decomposition
+ urlparse.urlsplit(u'http://\u30d5\u309a:80')
+ with self.assertRaises(ValueError):
+ urlparse.urlsplit(u'http://\u30d5\u309a\ufe1380')
+
+ for scheme in [u"http", u"https", u"ftp"]:
+ for netloc in [u"netloc{}false.netloc", u"n{}user@netloc"]:
+ for c in denorm_chars:
+ url = u"{}://{}/path".format(scheme, netloc.format(c))
+ if test_support.verbose:
+ print "Checking %r" % url
+ with self.assertRaises(ValueError):
+ urlparse.urlsplit(url)
+
def test_main():
test_support.run_unittest(UrlParseTestCase)
diff --git a/Lib/urlparse.py b/Lib/urlparse.py
index f7c2b03..6834f3c 100644
--- a/Lib/urlparse.py
+++ b/Lib/urlparse.py
@@ -165,6 +165,24 @@ def _splitnetloc(url, start=0):
delim = min(delim, wdelim) # use earliest delim position
return url[start:delim], url[delim:] # return (domain, rest)
+def _checknetloc(netloc):
+ if not netloc or not isinstance(netloc, unicode):
+ return
+ # looking for characters like \u2100 that expand to 'a/c'
+ # IDNA uses NFKC equivalence, so normalize for this check
+ import unicodedata
+ n = netloc.replace(u'@', u'') # ignore characters already included
+ n = n.replace(u':', u'') # but not the surrounding text
+ n = n.replace(u'#', u'')
+ n = n.replace(u'?', u'')
+ netloc2 = unicodedata.normalize('NFKC', n)
+ if n == netloc2:
+ return
+ for c in '/?#@:':
+ if c in netloc2:
+ raise ValueError(u"netloc '" + netloc + u"' contains invalid " +
+ u"characters under NFKC normalization")
+
def urlsplit(url, scheme='', allow_fragments=True):
"""Parse a URL into 5 components:
<scheme>://<netloc>/<path>?<query>#<fragment>
@@ -193,6 +211,7 @@ def urlsplit(url, scheme='', allow_fragments=True):
url, fragment = url.split('#', 1)
if '?' in url:
url, query = url.split('?', 1)
+ _checknetloc(netloc)
v = SplitResult(scheme, netloc, url, query, fragment)
_parse_cache[key] = v
return v
@@ -216,6 +235,7 @@ def urlsplit(url, scheme='', allow_fragments=True):
url, fragment = url.split('#', 1)
if '?' in url:
url, query = url.split('?', 1)
+ _checknetloc(netloc)
v = SplitResult(scheme, netloc, url, query, fragment)
_parse_cache[key] = v
return v

View File

@ -1,423 +0,0 @@
diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
index 2097342..defcde1 100644
--- a/Modules/_ctypes/callproc.c
+++ b/Modules/_ctypes/callproc.c
@@ -1831,6 +1831,7 @@ POINTER(PyObject *self, PyObject *cls)
"s(O){}",
buf,
&PyCPointer_Type);
+ PyMem_Free(buf);
if (result == NULL)
return result;
key = PyLong_FromVoidPtr(result);
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index 46f041b..1b495fc 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -1291,24 +1291,16 @@ U_set(void *ptr, PyObject *value, Py_ssize_t length)
static PyObject *
s_get(void *ptr, Py_ssize_t size)
{
- PyObject *result;
- size_t slen;
+ Py_ssize_t i;
+ char *p;
- result = PyString_FromString((char *)ptr);
- if (!result)
- return NULL;
- /* chop off at the first NUL character, if any.
- * On error, result will be deallocated and set to NULL.
- */
- slen = strlen(PyString_AS_STRING(result));
- size = min(size, (Py_ssize_t)slen);
- if (result->ob_refcnt == 1) {
- /* shorten the result */
- _PyString_Resize(&result, size);
- return result;
- } else
- /* cannot shorten the result */
- return PyString_FromStringAndSize(ptr, size);
+ p = (char *)ptr;
+ for (i = 0; i < size; ++i) {
+ if (*p++ == '\0')
+ break;
+ }
+
+ return PyBytes_FromStringAndSize((char *)ptr, (Py_ssize_t)i);
}
static PyObject *
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index de69f6f..78445eb 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -133,12 +133,6 @@ newEVPobject(PyObject *name)
if (retval == NULL)
return NULL;
- retval->ctx = EVP_MD_CTX_new();
- if (retval->ctx == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
-
/* save the name for .name to return */
Py_INCREF(name);
retval->name = name;
@@ -146,6 +140,13 @@ newEVPobject(PyObject *name)
retval->lock = NULL;
#endif
+ retval->ctx = EVP_MD_CTX_new();
+ if (retval->ctx == NULL) {
+ Py_DECREF(retval);
+ PyErr_NoMemory();
+ return NULL;
+ }
+
return retval;
}
@@ -205,6 +206,7 @@ EVP_copy(EVPobject *self, PyObject *unused)
return NULL;
if (!locked_EVP_MD_CTX_copy(newobj->ctx, self)) {
+ Py_DECREF(newobj);
return _setException(PyExc_ValueError);
}
return (PyObject *)newobj;
diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c
index 33cd38d..4fc5cee 100644
--- a/Modules/_hotshot.c
+++ b/Modules/_hotshot.c
@@ -482,8 +482,11 @@ restart:
}
else if (!err) {
result = PyTuple_New(4);
- if (result == NULL)
+ if (result == NULL) {
+ Py_XDECREF(s1);
+ Py_XDECREF(s2);
return NULL;
+ }
PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what));
PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno));
if (s1 == NULL)
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index b8c98a4..d68f7d8 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -1363,6 +1363,7 @@ _bufferedreader_read_all(buffered *self)
res = buffered_flush_and_rewind_unlocked(self);
if (res == NULL) {
Py_DECREF(chunks);
+ Py_XDECREF(data);
return NULL;
}
Py_CLEAR(res);
diff --git a/Modules/_json.c b/Modules/_json.c
index 3a88882..050d37d 100644
--- a/Modules/_json.c
+++ b/Modules/_json.c
@@ -1375,8 +1375,10 @@ _match_number_str(PyScannerObject *s, PyObject *pystr, Py_ssize_t start, Py_ssiz
else {
double d = PyOS_string_to_double(PyString_AS_STRING(numstr),
NULL, NULL);
- if (d == -1.0 && PyErr_Occurred())
+ if (d == -1.0 && PyErr_Occurred()) {
+ Py_DECREF(numstr);
return NULL;
+ }
rval = PyFloat_FromDouble(d);
}
}
diff --git a/Modules/linuxaudiodev.c b/Modules/linuxaudiodev.c
index 7fe20ae..f5135d9 100644
--- a/Modules/linuxaudiodev.c
+++ b/Modules/linuxaudiodev.c
@@ -126,10 +126,12 @@ newladobject(PyObject *arg)
}
if (imode == O_WRONLY && ioctl(fd, SNDCTL_DSP_NONBLOCK, NULL) == -1) {
PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev);
+ close(fd);
return NULL;
}
if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) {
PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev);
+ close(fd);
return NULL;
}
/* Create and initialize the object */
diff --git a/Parser/myreadline.c b/Parser/myreadline.c
index 59db41a..5376214 100644
--- a/Parser/myreadline.c
+++ b/Parser/myreadline.c
@@ -108,7 +108,7 @@ char *
PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
{
size_t n;
- char *p;
+ char *p, *pr;
n = 100;
if ((p = (char *)PyMem_MALLOC(n)) == NULL)
return NULL;
@@ -140,17 +140,29 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
n = strlen(p);
while (n > 0 && p[n-1] != '\n') {
size_t incr = n+2;
- p = (char *)PyMem_REALLOC(p, n + incr);
- if (p == NULL)
- return NULL;
if (incr > INT_MAX) {
+ PyMem_FREE(p);
PyErr_SetString(PyExc_OverflowError, "input line too long");
+ return NULL;
+ }
+ pr = (char *)PyMem_REALLOC(p, n + incr);
+ if (pr == NULL) {
+ PyMem_FREE(p);
+ PyErr_NoMemory();
+ return NULL;
}
+ p = pr;
if (my_fgets(p+n, (int)incr, sys_stdin) != 0)
break;
n += strlen(p+n);
}
- return (char *)PyMem_REALLOC(p, n+1);
+ pr = (char *)PyMem_REALLOC(p, n+1);
+ if (pr == NULL) {
+ PyMem_FREE(p);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ return pr;
}
diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c
index c6e61df..8966661 100644
--- a/Parser/tokenizer.c
+++ b/Parser/tokenizer.c
@@ -656,9 +656,14 @@ translate_newlines(const char *s, int exec_input, struct tok_state *tok) {
}
*current = '\0';
final_length = current - buf + 1;
- if (final_length < needed_length && final_length)
+ if (final_length < needed_length && final_length) {
/* should never fail */
- buf = PyMem_REALLOC(buf, final_length);
+ char* result = PyMem_REALLOC(buf, final_length);
+ if (result == NULL) {
+ PyMem_FREE(buf);
+ }
+ buf = result;
+ }
return buf;
}
diff --git a/Python/dtoa.c b/Python/dtoa.c
index 73e23af..25eb9a7 100644
--- a/Python/dtoa.c
+++ b/Python/dtoa.c
@@ -1514,8 +1514,9 @@ _Py_dg_strtod(const char *s00, char **se)
ULong y, z, abs_exp;
Long L;
BCinfo bc;
- Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+ Bigint *bb = NULL, *bd = NULL, *bd0 = NULL, *bs = NULL, *delta = NULL;
size_t ndigits, fraclen;
+ double result;
dval(&rv) = 0.;
@@ -1707,7 +1708,6 @@ _Py_dg_strtod(const char *s00, char **se)
if (k > 9) {
dval(&rv) = tens[k - 9] * dval(&rv) + z;
}
- bd0 = 0;
if (nd <= DBL_DIG
&& Flt_Rounds == 1
) {
@@ -1877,14 +1877,11 @@ _Py_dg_strtod(const char *s00, char **se)
bd = Balloc(bd0->k);
if (bd == NULL) {
- Bfree(bd0);
goto failed_malloc;
}
Bcopy(bd, bd0);
bb = sd2b(&rv, bc.scale, &bbe); /* srv = bb * 2^bbe */
if (bb == NULL) {
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
/* Record whether lsb of bb is odd, in case we need this
@@ -1894,9 +1891,6 @@ _Py_dg_strtod(const char *s00, char **se)
/* tdv = bd * 10**e; srv = bb * 2**bbe */
bs = i2b(1);
if (bs == NULL) {
- Bfree(bb);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
@@ -1945,56 +1939,39 @@ _Py_dg_strtod(const char *s00, char **se)
/* Scale bb, bd, bs by the appropriate powers of 2 and 5. */
if (bb5 > 0) {
+ Bigint *bb1;
bs = pow5mult(bs, bb5);
if (bs == NULL) {
- Bfree(bb);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
bb1 = mult(bs, bb);
Bfree(bb);
bb = bb1;
if (bb == NULL) {
- Bfree(bs);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
}
if (bb2 > 0) {
bb = lshift(bb, bb2);
if (bb == NULL) {
- Bfree(bs);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
}
if (bd5 > 0) {
bd = pow5mult(bd, bd5);
if (bd == NULL) {
- Bfree(bb);
- Bfree(bs);
- Bfree(bd0);
goto failed_malloc;
}
}
if (bd2 > 0) {
bd = lshift(bd, bd2);
if (bd == NULL) {
- Bfree(bb);
- Bfree(bs);
- Bfree(bd0);
goto failed_malloc;
}
}
if (bs2 > 0) {
bs = lshift(bs, bs2);
if (bs == NULL) {
- Bfree(bb);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
}
@@ -2005,10 +1982,6 @@ _Py_dg_strtod(const char *s00, char **se)
delta = diff(bb, bd);
if (delta == NULL) {
- Bfree(bb);
- Bfree(bs);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
dsign = delta->sign;
@@ -2062,10 +2035,6 @@ _Py_dg_strtod(const char *s00, char **se)
}
delta = lshift(delta,Log2P);
if (delta == NULL) {
- Bfree(bb);
- Bfree(bs);
- Bfree(bd);
- Bfree(bd0);
goto failed_malloc;
}
if (cmp(delta, bs) > 0)
@@ -2167,11 +2136,6 @@ _Py_dg_strtod(const char *s00, char **se)
if ((word0(&rv) & Exp_mask) >=
Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
if (word0(&rv0) == Big0 && word1(&rv0) == Big1) {
- Bfree(bb);
- Bfree(bd);
- Bfree(bs);
- Bfree(bd0);
- Bfree(delta);
goto ovfl;
}
word0(&rv) = Big0;
@@ -2213,16 +2177,11 @@ _Py_dg_strtod(const char *s00, char **se)
}
}
cont:
- Bfree(bb);
- Bfree(bd);
- Bfree(bs);
- Bfree(delta);
+ Bfree(bb); bb = NULL;
+ Bfree(bd); bd = NULL;
+ Bfree(bs); bs = NULL;
+ Bfree(delta); delta = NULL;
}
- Bfree(bb);
- Bfree(bd);
- Bfree(bs);
- Bfree(bd0);
- Bfree(delta);
if (bc.nd > nd) {
error = bigcomp(&rv, s0, &bc);
if (error)
@@ -2236,24 +2195,37 @@ _Py_dg_strtod(const char *s00, char **se)
}
ret:
- return sign ? -dval(&rv) : dval(&rv);
+ result = sign ? -dval(&rv) : dval(&rv);
+ goto done;
parse_error:
- return 0.0;
+ result = 0.0;
+ goto done;
failed_malloc:
errno = ENOMEM;
- return -1.0;
+ result = -1.0;
+ goto done;
undfl:
- return sign ? -0.0 : 0.0;
+ result = sign ? -0.0 : 0.0;
+ goto done;
ovfl:
errno = ERANGE;
/* Can't trust HUGE_VAL */
word0(&rv) = Exp_mask;
word1(&rv) = 0;
- return sign ? -dval(&rv) : dval(&rv);
+ result = sign ? -dval(&rv) : dval(&rv);
+ goto done;
+
+ done:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+ return result;
}

View File

@ -1,172 +0,0 @@
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 60a8fb4e355f..1b41c346e090 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -247,6 +247,16 @@
_is_legal_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match
_is_illegal_header_value = re.compile(r'\n(?![ \t])|\r(?![ \t\n])').search
+# These characters are not allowed within HTTP URL paths.
+# See https://tools.ietf.org/html/rfc3986#section-3.3 and the
+# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition.
+# Prevents CVE-2019-9740. Includes control characters such as \r\n.
+# Restrict non-ASCII characters above \x7f (0x80-0xff).
+_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]')
+# Arguably only these _should_ allowed:
+# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
+# We are more lenient for assumed real world compatibility purposes.
+
# We always set the Content-Length header for these methods because some
# servers will otherwise respond with a 411
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
@@ -927,6 +937,12 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
self._method = method
if not url:
url = '/'
+ # Prevent CVE-2019-9740.
+ match = _contains_disallowed_url_pchar_re.search(url)
+ if match:
+ raise InvalidURL("URL can't contain control characters. %r "
+ "(found at least %r)"
+ % (url, match.group()))
hdr = '%s %s %s' % (method, url, self._http_vsn_str)
self._output(hdr)
diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py
index 1ce9201c0693..d7778d4194f3 100644
--- a/Lib/test/test_urllib.py
+++ b/Lib/test/test_urllib.py
@@ -257,6 +257,31 @@ def test_url_fragment(self):
finally:
self.unfakehttp()
+ def test_url_with_control_char_rejected(self):
+ for char_no in range(0, 0x21) + range(0x7f, 0x100):
+ char = chr(char_no)
+ schemeless_url = "//localhost:7777/test%s/" % char
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ try:
+ # urllib quotes the URL so there is no injection.
+ resp = urllib.urlopen("http:" + schemeless_url)
+ self.assertNotIn(char, resp.geturl())
+ finally:
+ self.unfakehttp()
+
+ def test_url_with_newline_header_injection_rejected(self):
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
+ schemeless_url = "//" + host + ":8080/test/?test=a"
+ try:
+ # urllib quotes the URL so there is no injection.
+ resp = urllib.urlopen("http:" + schemeless_url)
+ self.assertNotIn(' ', resp.geturl())
+ self.assertNotIn('\r', resp.geturl())
+ self.assertNotIn('\n', resp.geturl())
+ finally:
+ self.unfakehttp()
+
def test_read_bogus(self):
# urlopen() should raise IOError for many error codes.
self.fakehttp('''HTTP/1.1 401 Authentication Required
diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py
index 6d24d5ddf83c..9531818e16b2 100644
--- a/Lib/test/test_urllib2.py
+++ b/Lib/test/test_urllib2.py
@@ -15,6 +15,9 @@
except ImportError:
ssl = None
+from test.test_urllib import FakeHTTPMixin
+
+
# XXX
# Request
# CacheFTPHandler (hard to write)
@@ -1262,7 +1265,7 @@ def _test_basic_auth(self, opener, auth_handler, auth_header,
self.assertEqual(len(http_handler.requests), 1)
self.assertFalse(http_handler.requests[0].has_header(auth_header))
-class MiscTests(unittest.TestCase):
+class MiscTests(unittest.TestCase, FakeHTTPMixin):
def test_build_opener(self):
class MyHTTPHandler(urllib2.HTTPHandler): pass
@@ -1317,6 +1320,52 @@ def test_unsupported_algorithm(self):
"Unsupported digest authentication algorithm 'invalid'"
)
+ @unittest.skipUnless(ssl, "ssl module required")
+ def test_url_with_control_char_rejected(self):
+ for char_no in range(0, 0x21) + range(0x7f, 0x100):
+ char = chr(char_no)
+ schemeless_url = "//localhost:7777/test%s/" % char
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ try:
+ # We explicitly test urllib.request.urlopen() instead of the top
+ # level 'def urlopen()' function defined in this... (quite ugly)
+ # test suite. They use different url opening codepaths. Plain
+ # urlopen uses FancyURLOpener which goes via a codepath that
+ # calls urllib.parse.quote() on the URL which makes all of the
+ # above attempts at injection within the url _path_ safe.
+ escaped_char_repr = repr(char).replace('\\', r'\\')
+ InvalidURL = httplib.InvalidURL
+ with self.assertRaisesRegexp(
+ InvalidURL, "contain control.*" + escaped_char_repr):
+ urllib2.urlopen("http:" + schemeless_url)
+ with self.assertRaisesRegexp(
+ InvalidURL, "contain control.*" + escaped_char_repr):
+ urllib2.urlopen("https:" + schemeless_url)
+ finally:
+ self.unfakehttp()
+
+ @unittest.skipUnless(ssl, "ssl module required")
+ def test_url_with_newline_header_injection_rejected(self):
+ self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello.")
+ host = "localhost:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123"
+ schemeless_url = "//" + host + ":8080/test/?test=a"
+ try:
+ # We explicitly test urllib2.urlopen() instead of the top
+ # level 'def urlopen()' function defined in this... (quite ugly)
+ # test suite. They use different url opening codepaths. Plain
+ # urlopen uses FancyURLOpener which goes via a codepath that
+ # calls urllib.parse.quote() on the URL which makes all of the
+ # above attempts at injection within the url _path_ safe.
+ InvalidURL = httplib.InvalidURL
+ with self.assertRaisesRegexp(
+ InvalidURL, r"contain control.*\\r.*(found at least . .)"):
+ urllib2.urlopen("http:" + schemeless_url)
+ with self.assertRaisesRegexp(InvalidURL, r"contain control.*\\n"):
+ urllib2.urlopen("https:" + schemeless_url)
+ finally:
+ self.unfakehttp()
+
+
class RequestTests(unittest.TestCase):
diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
index 36b3be67fd6b..90ccb30716ff 100644
--- a/Lib/test/test_xmlrpc.py
+++ b/Lib/test/test_xmlrpc.py
@@ -659,7 +659,13 @@ def test_dotted_attribute(self):
def test_partial_post(self):
# Check that a partial POST doesn't make the server loop: issue #14001.
conn = httplib.HTTPConnection(ADDR, PORT)
- conn.request('POST', '/RPC2 HTTP/1.0\r\nContent-Length: 100\r\n\r\nbye')
+ conn.send('POST /RPC2 HTTP/1.0\r\n'
+ 'Content-Length: 100\r\n\r\n'
+ 'bye HTTP/1.1\r\n'
+ 'Host: %s:%s\r\n'
+ 'Accept-Encoding: identity\r\n'
+ 'Content-Length: 0\r\n\r\n'
+ % (ADDR, PORT))
conn.close()
class SimpleServerEncodingTestCase(BaseServerTestCase):
diff --git a/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst
new file mode 100644
index 000000000000..47cb899df1af
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2019-04-10-08-53-30.bpo-30458.51E-DA.rst
@@ -0,0 +1 @@
+Address CVE-2019-9740 by disallowing URL paths with embedded whitespace or control characters through into the underlying http client request. Such potentially malicious header injection URLs now cause an httplib.InvalidURL exception to be raised.

View File

@ -1,37 +0,0 @@
diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py
index d2da0f8..7813b9f 100644
--- a/Lib/test/test_urllib.py
+++ b/Lib/test/test_urllib.py
@@ -872,6 +872,17 @@ class URLopener_Tests(unittest.TestCase):
"spam://c:|windows%/:=&?~#+!$,;'@()*[]|/path/"),
"//c:|windows%/:=&?~#+!$,;'@()*[]|/path/")
+ def test_local_file_open(self):
+ # bpo-35907, CVE-2019-9948: urllib must reject local_file:// scheme
+ class DummyURLopener(urllib.URLopener):
+ def open_local_file(self, url):
+ return url
+ for url in ('local_file://example', 'local-file://example'):
+ self.assertRaises(IOError, urllib.urlopen, url)
+ self.assertRaises(IOError, urllib.URLopener().open, url)
+ self.assertRaises(IOError, urllib.URLopener().retrieve, url)
+ self.assertRaises(IOError, DummyURLopener().open, url)
+ self.assertRaises(IOError, DummyURLopener().retrieve, url)
# Just commented them out.
# Can't really tell why keep failing in windows and sparc.
diff --git a/Lib/urllib.py b/Lib/urllib.py
index 2201e3e..71e3637 100644
--- a/Lib/urllib.py
+++ b/Lib/urllib.py
@@ -198,7 +198,9 @@ class URLopener:
name = 'open_' + urltype
self.type = urltype
name = name.replace('-', '_')
- if not hasattr(self, name):
+
+ # bpo-35907: disallow the file reading with the type not allowed
+ if not hasattr(self, name) or name == 'open_local_file':
if proxy:
return self.open_unknown_proxy(proxy, fullurl, data)
else:

View File

@ -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.16
Release: 9%{?dist}
Version: 2.7.17
Release: 1%{?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 #
@ -584,13 +583,6 @@ Patch146: 00146-hashlib-fips.patch
# Sent upstream as http://bugs.python.org/issue14785
Patch147: 00147-add-debug-malloc-stats.patch
# 00153 #
# Strip out lines of the form "warning: Unable to open ..." from gdb's stderr
# when running test_gdb.py; also cope with change to gdb in F17 onwards in
# which values are printed as "v@entry" rather than just "v":
# Not yet sent upstream
Patch153: 00153-fix-test_gdb-noise.patch
# 00155 #
# Avoid allocating thunks in ctypes unless absolutely necessary, to avoid
# generating SELinux denials on "import ctypes" and "import uuid" when
@ -604,22 +596,6 @@ Patch155: 00155-avoid-ctypes-thunks.patch
# Not yet sent upstream
Patch156: 00156-gdb-autoload-safepath.patch
# 00157 #
# Update uid/gid handling throughout the standard library: uid_t and gid_t are
# unsigned 32-bit values, but existing code often passed them through C long
# values, which are signed 32-bit values on 32-bit architectures, leading to
# negative int objects for uid/gid values >= 2^31 on 32-bit architectures.
#
# Introduce _PyObject_FromUid/Gid to convert uid_t/gid_t values to python
# objects, using int objects where the value will fit (long objects otherwise),
# and _PyArg_ParseUid/Gid to convert int/long to uid_t/gid_t, with -1 allowed
# as a special case (since this is given special meaning by the chown syscall)
#
# Update standard library to use this throughout for uid/gid values, so that
# very large uid/gid values are round-trippable, and -1 remains usable.
# (rhbz#697470)
Patch157: 00157-uid-gid-overflows.patch
# 00165 #
# Backport to Python 2 from Python 3.3 of improvements to the "crypt" module
# adding precanned ways of salting a password (rhbz#835021)
@ -638,26 +614,6 @@ Patch165: 00165-crypt-module-salt-backport.patch
# Not yet sent upstream
Patch167: 00167-disable-stack-navigation-tests-when-optimized-in-test_gdb.patch
# 00168 #
# Update distutils.sysconfig so that if CFLAGS is defined in the environment,
# when building extension modules, it is appended to the full compilation
# flags from Python's Makefile, rather than instead reducing the compilation
# flags to the subset within OPT and adding it to those.
#
# In particular, this should ensure that "-fno-strict-aliasing" is used by
# "python setup.py build" even when CFLAGS is defined in the environment.
#
# (rhbz#849994)
Patch168: 00168-distutils-cflags.patch
# 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 +661,9 @@ Patch185: 00185-urllib2-honors-noproxy-for-ftp.patch
Patch187: 00187-add-RPATH-to-pyexpat.patch
# 00189 #
# 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
# Instead of bundled wheels, use our RPM packaged wheels from
# /usr/share/python2-wheels
Patch189: 00189-use-rpm-wheels.patch
# 00191 #
# Disabling NOOP test as it fails without internet connection
@ -721,9 +676,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,44 +695,6 @@ Patch288: 00288-ambiguous-python-version-rpmbuild-warn.patch
# (we handle it it in Setup.dist, see Patch0)
Patch289: 00289-disable-nis-detection.patch
# 00320 #
# Security fix for CVE-2019-9636: Information Disclosure due to urlsplit improper NFKC normalization
# Fixed upstream: https://bugs.python.org/issue36216 and https://bugs.python.org/issue36742
# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1689327
Patch320: 00320-CVE-2019-9636.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.,
@ -846,6 +760,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.
@ -864,6 +786,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
@ -1050,36 +981,30 @@ rm -r Modules/zlib || exit 1
%if !%{with_gdbm}
%patch144 -p1
%endif
#patch146 -p1
%patch146 -p1
%patch147 -p1
%patch153 -p0
%patch155 -p1
%patch156 -p1
%patch157 -p1
%patch165 -p1
mv Modules/cryptmodule.c Modules/_cryptmodule.c
%patch167 -p1
%patch168 -p1
%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
%patch191 -p1
%patch193 -p1
%if %{with rewheel}
%patch198 -p1
%endif
%patch257 -p1
%patch288 -p1
%patch289 -p1
%patch320 -p1
%patch323 -p1
%patch324 -p1
%patch325 -p1
# This shouldn't be necesarry, but is right now (2.2a3)
find -name "*~" |xargs rm -f
@ -1803,11 +1728,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
@ -2018,8 +1943,27 @@ fi
# ======================================================
%changelog
* Wed Oct 23 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.17-1
- Update to 2.7.17
Resolves: rhbz#1759944
* Tue Sep 03 2019 Tomas Orsava <torsava@redhat.com> - 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 <mhroncok@redhat.com> - 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 <cstratak@redhat.com> - 2.7.16-10
- Fix urlparse.urlsplit() error message for Unicode URL
Resolves: rhbz#1689327
* Fri Jun 07 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-9
- Yet another fix for CVE-2019-9636
- Security fix for CVE-2019-10160
Resolves: rhbz#1689327
* Thu May 30 2019 Charalampos Stratakis <cstratak@redhat.com> - 2.7.16-8