diff -Nru python3.7-3.7.13/configure python3.7-3.7.14/configure --- python3.7-3.7.13/configure 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/configure 2022-09-06 07:11:33.000000000 +0000 @@ -2,7 +2,7 @@ # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for python 3.7. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -267,7 +267,7 @@ $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: https://bugs.python.org/ about your system, including +$0: https://github.com/python/cpython/issues/ about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." @@ -582,7 +582,7 @@ PACKAGE_TARNAME='python' PACKAGE_VERSION='3.7' PACKAGE_STRING='python 3.7' -PACKAGE_BUGREPORT='https://bugs.python.org/' +PACKAGE_BUGREPORT='https://github.com/python/cpython/issues/' PACKAGE_URL='' ac_unique_file="Include/object.h" @@ -1568,7 +1568,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to . _ACEOF ac_status=$? fi @@ -1837,7 +1837,7 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## --------------------------------------- ## -## Report this to https://bugs.python.org/ ## +## Report this to https://github.com/python/cpython/issues/ ## ## --------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; @@ -17941,7 +17941,7 @@ Configuration headers: $config_headers -Report bugs to ." +Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 diff -Nru python3.7-3.7.13/configure.ac python3.7-3.7.14/configure.ac --- python3.7-3.7.13/configure.ac 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/configure.ac 2022-09-06 07:11:33.000000000 +0000 @@ -7,7 +7,7 @@ AC_PREREQ(2.65) -AC_INIT(python, PYTHON_VERSION, https://bugs.python.org/) +AC_INIT(python, PYTHON_VERSION, https://github.com/python/cpython/issues/) AC_CONFIG_MACRO_DIR(m4) diff -Nru python3.7-3.7.13/debian/changelog python3.7-3.7.14/debian/changelog --- python3.7-3.7.13/debian/changelog 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/changelog 2022-09-08 00:06:44.000000000 +0000 @@ -1,3 +1,9 @@ +python3.7 (3.7.14-1+bionic1) bionic; urgency=medium + + * Python 3.7.14 release. + + -- Anthony Sottile (deadsnakes) Thu, 08 Sep 2022 00:06:44 +0000 + python3.7 (3.7.13-1+bionic3) bionic; urgency=medium * remove accidentally added pyvenv binary. diff -Nru python3.7-3.7.13/debian/patches/0029-destshared-location.patch python3.7-3.7.14/debian/patches/0029-destshared-location.patch --- python3.7-3.7.13/debian/patches/0029-destshared-location.patch 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/0029-destshared-location.patch 2022-09-08 00:06:44.000000000 +0000 @@ -12,7 +12,7 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in -index 817575d..8d2cc17 100644 +index 46e475f..57ab3e4 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -160,7 +160,7 @@ EXT_SUFFIX= @EXT_SUFFIX@ diff -Nru python3.7-3.7.13/debian/patches/0033-sysconfigdata-name.patch python3.7-3.7.14/debian/patches/0033-sysconfigdata-name.patch --- python3.7-3.7.13/debian/patches/0033-sysconfigdata-name.patch 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/0033-sysconfigdata-name.patch 2022-09-08 00:06:44.000000000 +0000 @@ -40,10 +40,10 @@ )) diff --git a/Makefile.pre.in b/Makefile.pre.in -index 8d2cc17..4a16557 100644 +index 57ab3e4..62921a3 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -1402,8 +1402,10 @@ libinstall: build_all $(srcdir)/Modules/xxmodule.c +@@ -1409,8 +1409,10 @@ libinstall: build_all $(srcdir)/Modules/xxmodule.c esac; \ done; \ done @@ -56,7 +56,7 @@ $(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt if test -d $(DESTDIR)$(LIBDEST)/distutils/tests; then \ $(INSTALL_DATA) $(srcdir)/Modules/xxmodule.c \ -@@ -1545,6 +1547,7 @@ sharedinstall: sharedmods +@@ -1552,6 +1554,7 @@ sharedinstall: sharedmods --install-scripts=$(BINDIR) \ --install-platlib=$(DESTSHARED) \ --root=$(DESTDIR)/ @@ -65,7 +65,7 @@ -rm -r $(DESTDIR)$(DESTSHARED)/__pycache__ diff --git a/configure.ac b/configure.ac -index 8f35835..8ead612 100644 +index 38dd457..43d0232 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ if test "$cross_compiling" = yes; then diff -Nru python3.7-3.7.13/debian/patches/build-math-object.diff python3.7-3.7.14/debian/patches/build-math-object.diff --- python3.7-3.7.13/debian/patches/build-math-object.diff 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/build-math-object.diff 2022-09-08 00:06:44.000000000 +0000 @@ -7,7 +7,7 @@ 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in -index e3cff4e..817575d 100644 +index 15867ba..46e475f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -22,7 +22,8 @@ diff -Nru python3.7-3.7.13/debian/patches/disable-sem-check.diff python3.7-3.7.14/debian/patches/disable-sem-check.diff --- python3.7-3.7.13/debian/patches/disable-sem-check.diff 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/disable-sem-check.diff 2022-09-08 00:06:44.000000000 +0000 @@ -8,7 +8,7 @@ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac -index f1b2afb..8f35835 100644 +index 769c732..38dd457 100644 --- a/configure.ac +++ b/configure.ac @@ -4616,8 +4616,13 @@ int main(void) { diff -Nru python3.7-3.7.13/debian/patches/link-opt.diff python3.7-3.7.14/debian/patches/link-opt.diff --- python3.7-3.7.13/debian/patches/link-opt.diff 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/link-opt.diff 2022-09-08 00:06:44.000000000 +0000 @@ -8,7 +8,7 @@ 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac -index 1afcba3..f1b2afb 100644 +index edd2051..769c732 100644 --- a/configure.ac +++ b/configure.ac @@ -2617,8 +2617,8 @@ then diff -Nru python3.7-3.7.13/debian/patches/multiarch.diff python3.7-3.7.14/debian/patches/multiarch.diff --- python3.7-3.7.13/debian/patches/multiarch.diff 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/multiarch.diff 2022-09-08 00:06:44.000000000 +0000 @@ -41,10 +41,10 @@ srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) if os.name == 'posix': diff --git a/Makefile.pre.in b/Makefile.pre.in -index 35ca1a8..ed5be97 100644 +index ef2bfb1..2d3a52e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -792,6 +792,7 @@ Modules/signalmodule.o: $(srcdir)/Modules/signalmodule.c $(srcdir)/Modules/posix +@@ -799,6 +799,7 @@ Modules/signalmodule.o: $(srcdir)/Modules/signalmodule.c $(srcdir)/Modules/posix Python/dynload_shlib.o: $(srcdir)/Python/dynload_shlib.c Makefile $(CC) -c $(PY_CORE_CFLAGS) \ diff -Nru python3.7-3.7.13/debian/patches/reproducible-buildinfo.diff python3.7-3.7.14/debian/patches/reproducible-buildinfo.diff --- python3.7-3.7.13/debian/patches/reproducible-buildinfo.diff 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/patches/reproducible-buildinfo.diff 2022-09-08 00:06:44.000000000 +0000 @@ -8,10 +8,10 @@ 1 file changed, 2 insertions(+) diff --git a/Makefile.pre.in b/Makefile.pre.in -index ed5be97..e3cff4e 100644 +index 2d3a52e..15867ba 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -764,6 +764,8 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ +@@ -771,6 +771,8 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ -DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \ -DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \ -DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \ diff -Nru python3.7-3.7.13/debian/rules python3.7-3.7.14/debian/rules --- python3.7-3.7.13/debian/rules 2022-04-24 01:04:09.000000000 +0000 +++ python3.7-3.7.14/debian/rules 2022-09-08 00:06:44.000000000 +0000 @@ -83,7 +83,7 @@ else echo Unknown; fi) VER=3.7 -SVER=3.7.13 +SVER=3.7.14 NVER=3.8 PVER=python$(VER) EXT_VER=$(subst .,,$(VER)) diff -Nru python3.7-3.7.13/Doc/bugs.rst python3.7-3.7.14/Doc/bugs.rst --- python3.7-3.7.13/Doc/bugs.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/bugs.rst 2022-09-06 07:11:33.000000000 +0000 @@ -32,38 +32,39 @@ Using the Python issue tracker ============================== -Bug reports for Python itself should be submitted via the Python Bug Tracker -(https://bugs.python.org/). The bug tracker offers a Web form which allows -pertinent information to be entered and submitted to the developers. +Issue reports for Python itself should be submitted via the GitHub issues +tracker (https://github.com/python/cpython/issues). +The GitHub issues tracker offers a web form which allows pertinent information +to be entered and submitted to the developers. The first step in filing a report is to determine whether the problem has already been reported. The advantage in doing so, aside from saving the -developers time, is that you learn what has been done to fix it; it may be that +developers' time, is that you learn what has been done to fix it; it may be that the problem has already been fixed for the next release, or additional information is needed (in which case you are welcome to provide it if you can!). -To do this, search the bug database using the search box on the top of the page. +To do this, search the tracker using the search box at the top of the page. -If the problem you're reporting is not already in the bug tracker, go back to -the Python Bug Tracker and log in. If you don't already have a tracker account, -select the "Register" link or, if you use OpenID, one of the OpenID provider -logos in the sidebar. It is not possible to submit a bug report anonymously. - -Being now logged in, you can submit a bug. Select the "Create New" link in the -sidebar to open the bug reporting form. - -The submission form has a number of fields. For the "Title" field, enter a -*very* short description of the problem; less than ten words is good. In the -"Type" field, select the type of your problem; also select the "Component" and -"Versions" to which the bug relates. +If the problem you're reporting is not already in the list, log in to GitHub. +If you don't already have a GitHub account, create a new account using the +"Sign up" link. +It is not possible to submit a bug report anonymously. + +Being now logged in, you can submit an issue. +Click on the "New issue" button in the top bar to report a new issue. + +The submission form has two fields, "Title" and "Comment". + +For the "Title" field, enter a *very* short description of the problem; +less than ten words is good. In the "Comment" field, describe the problem in detail, including what you expected to happen and what did happen. Be sure to include whether any extension modules were involved, and what hardware and software platform you were using (including version information as appropriate). -Each bug report will be assigned to a developer who will determine what needs to -be done to correct the problem. You will receive an update each time action is -taken on the bug. +Each issue report will be reviewed by a developer who will determine what needs to +be done to correct the problem. You will receive an update each time an action is +taken on the issue. .. seealso:: @@ -87,6 +88,6 @@ the `core-mentorship mailing list`_ is a friendly place to get answers to any and all questions pertaining to the process of fixing issues in Python. -.. _Documentation bugs: https://bugs.python.org/issue?@filter=status&@filter=components&components=4&status=1&@columns=id,activity,title,status&@sort=-activity +.. _Documentation bugs: https://github.com/python/cpython/issues?q=is%3Aissue+is%3Aopen+label%3Adocs .. _Python Developer's Guide: https://devguide.python.org/ .. _core-mentorship mailing list: https://mail.python.org/mailman3/lists/core-mentorship.python.org/ diff -Nru python3.7-3.7.13/Doc/c-api/datetime.rst python3.7-3.7.14/Doc/c-api/datetime.rst --- python3.7-3.7.13/Doc/c-api/datetime.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/c-api/datetime.rst 2022-09-06 07:11:33.000000000 +0000 @@ -186,6 +186,13 @@ Return the microsecond, as an int from 0 through 999999. +.. c:function:: int PyDateTime_DATE_GET_FOLD(PyDateTime_DateTime *o) + + Return the fold, as an int from 0 through 1. + + .. versionadded:: 3.6 + + Macros to extract fields from time objects. The argument must be an instance of :c:data:`PyDateTime_Time`, including subclasses. The argument must not be ``NULL``, and the type is not checked: @@ -210,6 +217,13 @@ Return the microsecond, as an int from 0 through 999999. +.. c:function:: int PyDateTime_TIME_GET_FOLD(PyDateTime_Time *o) + + Return the fold, as an int from 0 through 1. + + .. versionadded:: 3.6 + + Macros to extract fields from time delta objects. The argument must be an instance of :c:data:`PyDateTime_Delta`, including subclasses. The argument must not be ``NULL``, and the type is not checked: diff -Nru python3.7-3.7.13/Doc/data/python3.7m.abi python3.7-3.7.14/Doc/data/python3.7m.abi --- python3.7-3.7.13/Doc/data/python3.7m.abi 1970-01-01 00:00:00.000000000 +0000 +++ python3.7-3.7.14/Doc/data/python3.7m.abi 2022-09-06 07:11:33.000000000 +0000 @@ -0,0 +1,14618 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru python3.7-3.7.13/Doc/library/functions.rst python3.7-3.7.14/Doc/library/functions.rst --- python3.7-3.7.13/Doc/library/functions.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/functions.rst 2022-09-06 07:11:33.000000000 +0000 @@ -774,6 +774,14 @@ .. versionchanged:: 3.7 *x* is now a positional-only parameter. + .. versionchanged:: 3.7.14 + :class:`int` string inputs and string representations can be limited to + help avoid denial of service attacks. A :exc:`ValueError` is raised when + the limit is exceeded while converting a string *x* to an :class:`int` or + when converting an :class:`int` into a string would exceed the limit. + See the :ref:`integer string conversion length limitation + ` documentation. + .. function:: isinstance(object, classinfo) diff -Nru python3.7-3.7.13/Doc/library/http.server.rst python3.7-3.7.14/Doc/library/http.server.rst --- python3.7-3.7.13/Doc/library/http.server.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/http.server.rst 2022-09-06 07:11:33.000000000 +0000 @@ -19,7 +19,7 @@ .. warning:: :mod:`http.server` is not recommended for production. It only implements - basic security checks. + :ref:`basic security checks `. One class, :class:`HTTPServer`, is a :class:`socketserver.TCPServer` subclass. It creates and listens at the HTTP socket, dispatching the requests to a @@ -470,3 +470,14 @@ the ``--cgi`` option:: python -m http.server --cgi 8000 + +.. _http.server-security: + +Security Considerations +----------------------- + +.. index:: pair: http.server; security + +:class:`SimpleHTTPRequestHandler` will follow symbolic links when handling +requests, this makes it possible for files outside of the specified directory +to be served. diff -Nru python3.7-3.7.13/Doc/library/json.rst python3.7-3.7.14/Doc/library/json.rst --- python3.7-3.7.13/Doc/library/json.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/json.rst 2022-09-06 07:11:33.000000000 +0000 @@ -18,6 +18,11 @@ `JavaScript `_ object literal syntax (although it is not a strict subset of JavaScript [#rfc-errata]_ ). +.. warning:: + Be cautious when parsing JSON data from untrusted sources. A malicious + JSON string may cause the decoder to consume considerable CPU and memory + resources. Limiting the size of data to be parsed is recommended. + :mod:`json` exposes an API familiar to users of the standard library :mod:`marshal` and :mod:`pickle` modules. @@ -243,6 +248,12 @@ be used to use another datatype or parser for JSON integers (e.g. :class:`float`). + .. versionchanged:: 3.7.14 + The default *parse_int* of :func:`int` now limits the maximum length of + the integer string via the interpreter's :ref:`integer string + conversion length limitation ` to help avoid denial + of service attacks. + *parse_constant*, if specified, will be called with one of the following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This can be used to raise an exception if invalid JSON numbers diff -Nru python3.7-3.7.13/Doc/library/stdtypes.rst python3.7-3.7.14/Doc/library/stdtypes.rst --- python3.7-3.7.13/Doc/library/stdtypes.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/stdtypes.rst 2022-09-06 07:11:33.000000000 +0000 @@ -4741,6 +4741,165 @@ [] +.. _int_max_str_digits: + +Integer string conversion length limitation +=========================================== + +CPython has a global limit for converting between :class:`int` and :class:`str` +to mitigate denial of service attacks. This limit *only* applies to decimal or +other non-power-of-two number bases. Hexadecimal, octal, and binary conversions +are unlimited. The limit can be configured. + +The :class:`int` type in CPython is an abitrary length number stored in binary +form (commonly known as a "bignum"). There exists no algorithm that can convert +a string to a binary integer or a binary integer to a string in linear time, +*unless* the base is a power of 2. Even the best known algorithms for base 10 +have sub-quadratic complexity. Converting a large value such as ``int('1' * +500_000)`` can take over a second on a fast CPU. + +Limiting conversion size offers a practical way to avoid `CVE-2020-10735 +`_. + +The limit is applied to the number of digit characters in the input or output +string when a non-linear conversion algorithm would be involved. Underscores +and the sign are not counted towards the limit. + +When an operation would exceed the limit, a :exc:`ValueError` is raised: + +.. doctest:: + + >>> import sys + >>> sys.set_int_max_str_digits(4300) # Illustrative, this is the default. + >>> _ = int('2' * 5432) + Traceback (most recent call last): + ... + ValueError: Exceeds the limit (4300) for integer string conversion: value has 5432 digits. + >>> i = int('2' * 4300) + >>> len(str(i)) + 4300 + >>> i_squared = i*i + >>> len(str(i_squared)) + Traceback (most recent call last): + ... + ValueError: Exceeds the limit (4300) for integer string conversion: value has 8599 digits. + >>> len(hex(i_squared)) + 7144 + >>> assert int(hex(i_squared), base=16) == i*i # Hexadecimal is unlimited. + +The default limit is 4300 digits as provided in +:data:`sys.int_info.default_max_str_digits `. +The lowest limit that can be configured is 640 digits as provided in +:data:`sys.int_info.str_digits_check_threshold `. + +Verification: + +.. doctest:: + + >>> import sys + >>> assert sys.int_info.default_max_str_digits == 4300, sys.int_info + >>> assert sys.int_info.str_digits_check_threshold == 640, sys.int_info + >>> msg = int('578966293710682886880994035146873798396722250538762761564' + ... '9252925514383915483333812743580549779436104706260696366600' + ... '571186405732').to_bytes(53, 'big') + ... + +.. versionadded:: 3.7.14 + +Affected APIs +------------- + +The limitation only applies to potentially slow conversions between :class:`int` +and :class:`str` or :class:`bytes`: + +* ``int(string)`` with default base 10. +* ``int(string, base)`` for all bases that are not a power of 2. +* ``str(integer)``. +* ``repr(integer)`` +* any other string conversion to base 10, for example ``f"{integer}"``, + ``"{}".format(integer)``, or ``b"%d" % integer``. + +The limitations do not apply to functions with a linear algorithm: + +* ``int(string, base)`` with base 2, 4, 8, 16, or 32. +* :func:`int.from_bytes` and :func:`int.to_bytes`. +* :func:`hex`, :func:`oct`, :func:`bin`. +* :ref:`formatspec` for hex, octal, and binary numbers. +* :class:`str` to :class:`float`. +* :class:`str` to :class:`decimal.Decimal`. + +Configuring the limit +--------------------- + +Before Python starts up you can use an environment variable or an interpreter +command line flag to configure the limit: + +* :envvar:`PYTHONINTMAXSTRDIGITS`, e.g. + ``PYTHONINTMAXSTRDIGITS=640 python3`` to set the limit to 640 or + ``PYTHONINTMAXSTRDIGITS=0 python3`` to disable the limitation. +* :option:`-X int_max_str_digits <-X>`, e.g. + ``python3 -X int_max_str_digits=640`` +* :data:`sys.flags.int_max_str_digits` contains the value of + :envvar:`PYTHONINTMAXSTRDIGITS` or :option:`-X int_max_str_digits <-X>`. + If both the env var and the ``-X`` option are set, the ``-X`` option takes + precedence. A value of *-1* indicates that both were unset, thus a value of + :data:`sys.int_info.default_max_str_digits` was used during initilization. + +From code, you can inspect the current limit and set a new one using these +:mod:`sys` APIs: + +* :func:`sys.get_int_max_str_digits` and :func:`sys.set_int_max_str_digits` are + a getter and setter for the interpreter-wide limit. Subinterpreters have + their own limit. + +Information about the default and minimum can be found in :attr:`sys.int_info`: + +* :data:`sys.int_info.default_max_str_digits ` is the compiled-in + default limit. +* :data:`sys.int_info.str_digits_check_threshold ` is the lowest + accepted value for the limit (other than 0 which disables it). + +.. versionadded:: 3.7.14 + +.. caution:: + + Setting a low limit *can* lead to problems. While rare, code exists that + contains integer constants in decimal in their source that exceed the + minimum threshold. A consequence of setting the limit is that Python source + code containing decimal integer literals longer than the limit will + encounter an error during parsing, usually at startup time or import time or + even at installation time - anytime an up to date ``.pyc`` does not already + exist for the code. A workaround for source that contains such large + constants is to convert them to ``0x`` hexadecimal form as it has no limit. + + Test your application thoroughly if you use a low limit. Ensure your tests + run with the limit set early via the environment or flag so that it applies + during startup and even during any installation step that may invoke Python + to precompile ``.py`` sources to ``.pyc`` files. + +Recommended configuration +------------------------- + +The default :data:`sys.int_info.default_max_str_digits` is expected to be +reasonable for most applications. If your application requires a different +limit, set it from your main entry point using Python version agnostic code as +these APIs were added in security patch releases in versions before 3.11. + +Example:: + + >>> import sys + >>> if hasattr(sys, "set_int_max_str_digits"): + ... upper_bound = 68000 + ... lower_bound = 4004 + ... current_limit = sys.get_int_max_str_digits() + ... if current_limit == 0 or current_limit > upper_bound: + ... sys.set_int_max_str_digits(upper_bound) + ... elif current_limit < lower_bound: + ... sys.set_int_max_str_digits(lower_bound) + +If you need to disable it entirely, set it to ``0``. + + .. rubric:: Footnotes .. [1] Additional information on these special methods may be found in the Python diff -Nru python3.7-3.7.13/Doc/library/sys.rst python3.7-3.7.14/Doc/library/sys.rst --- python3.7-3.7.13/Doc/library/sys.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/sys.rst 2022-09-06 07:11:33.000000000 +0000 @@ -327,9 +327,9 @@ The :term:`named tuple` *flags* exposes the status of command line flags. The attributes are read only. - ============================= ============================= + ============================= ============================================================================================================== attribute flag - ============================= ============================= + ============================= ============================================================================================================== :const:`debug` :option:`-d` :const:`inspect` :option:`-i` :const:`interactive` :option:`-i` @@ -345,7 +345,8 @@ :const:`hash_randomization` :option:`-R` :const:`dev_mode` :option:`-X` ``dev`` :const:`utf8_mode` :option:`-X` ``utf8`` - ============================= ============================= + :const:`int_max_str_digits` :option:`-X int_max_str_digits <-X>` (:ref:`integer string conversion length limitation `) + ============================= ============================================================================================================== .. versionchanged:: 3.2 Added ``quiet`` attribute for the new :option:`-q` flag. @@ -363,6 +364,9 @@ Added ``dev_mode`` attribute for the new :option:`-X` ``dev`` flag and ``utf8_mode`` attribute for the new :option:`-X` ``utf8`` flag. + .. versionchanged:: 3.7.14 + Added the ``int_max_str_digits`` attribute. + .. data:: float_info @@ -539,6 +543,15 @@ .. versionadded:: 3.6 + +.. function:: get_int_max_str_digits() + + Returns the current value for the :ref:`integer string conversion length + limitation `. See also :func:`set_int_max_str_digits`. + + .. versionadded:: 3.7.14 + + .. function:: getrefcount(object) Return the reference count of the *object*. The count returned is generally one @@ -821,19 +834,31 @@ .. tabularcolumns:: |l|L| - +-------------------------+----------------------------------------------+ - | Attribute | Explanation | - +=========================+==============================================+ - | :const:`bits_per_digit` | number of bits held in each digit. Python | - | | integers are stored internally in base | - | | ``2**int_info.bits_per_digit`` | - +-------------------------+----------------------------------------------+ - | :const:`sizeof_digit` | size in bytes of the C type used to | - | | represent a digit | - +-------------------------+----------------------------------------------+ + +----------------------------------------+-----------------------------------------------+ + | Attribute | Explanation | + +========================================+===============================================+ + | :const:`bits_per_digit` | number of bits held in each digit. Python | + | | integers are stored internally in base | + | | ``2**int_info.bits_per_digit`` | + +----------------------------------------+-----------------------------------------------+ + | :const:`sizeof_digit` | size in bytes of the C type used to | + | | represent a digit | + +----------------------------------------+-----------------------------------------------+ + | :const:`default_max_str_digits` | default value for | + | | :func:`sys.get_int_max_str_digits` when it | + | | is not otherwise explicitly configured. | + +----------------------------------------+-----------------------------------------------+ + | :const:`str_digits_check_threshold` | minimum non-zero value for | + | | :func:`sys.set_int_max_str_digits`, | + | | :envvar:`PYTHONINTMAXSTRDIGITS`, or | + | | :option:`-X int_max_str_digits <-X>`. | + +----------------------------------------+-----------------------------------------------+ .. versionadded:: 3.1 + .. versionchanged:: 3.7.14 + Added ``default_max_str_digits`` and ``str_digits_check_threshold``. + .. data:: __interactivehook__ @@ -1092,6 +1117,14 @@ .. availability:: Unix. +.. function:: set_int_max_str_digits(n) + + Set the :ref:`integer string conversion length limitation + ` used by this interpreter. See also + :func:`get_int_max_str_digits`. + + .. versionadded:: 3.7.14 + .. function:: setprofile(profilefunc) .. index:: diff -Nru python3.7-3.7.13/Doc/library/test.rst python3.7-3.7.14/Doc/library/test.rst --- python3.7-3.7.13/Doc/library/test.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/library/test.rst 2022-09-06 07:11:33.000000000 +0000 @@ -1207,6 +1207,16 @@ .. versionadded:: 3.6 +.. function:: adjust_int_max_str_digits(max_digits) + + This function returns a context manager that will change the global + :func:`sys.set_int_max_str_digits` setting for the duration of the + context to allow execution of test code that needs a different limit + on the number of digits when converting between an integer and string. + + .. versionadded:: 3.7.14 + + The :mod:`test.support` module defines the following classes: .. class:: TransientResource(exc, **kwargs) diff -Nru python3.7-3.7.13/Doc/make.bat python3.7-3.7.14/Doc/make.bat --- python3.7-3.7.13/Doc/make.bat 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/make.bat 2022-09-06 07:11:33.000000000 +0000 @@ -13,7 +13,7 @@ %PYTHON% -c "import sphinx" > nul 2> nul if errorlevel 1 ( echo Installing sphinx with %PYTHON% - %PYTHON% -m pip install sphinx==2.2.0 + %PYTHON% -m pip install sphinx==2.3.1 blurb docutils==0.17.1 Jinja2==3.0.3 if errorlevel 1 exit /B ) set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())" diff -Nru python3.7-3.7.13/Doc/Makefile python3.7-3.7.14/Doc/Makefile --- python3.7-3.7.13/Doc/Makefile 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/Makefile 2022-09-06 07:11:33.000000000 +0000 @@ -127,7 +127,8 @@ venv: $(PYTHON) -m venv $(VENVDIR) - $(VENVDIR)/bin/python3 -m pip install -U Sphinx==2.3.1 blurb docutils==0.17.1 + $(VENVDIR)/bin/python3 -m pip install -U pip setuptools + $(VENVDIR)/bin/python3 -m pip install -r requirements.txt @echo "The venv has been created in the $(VENVDIR) directory" dist: diff -Nru python3.7-3.7.13/Doc/requirements.txt python3.7-3.7.14/Doc/requirements.txt --- python3.7-3.7.13/Doc/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ python3.7-3.7.14/Doc/requirements.txt 2022-09-06 07:11:33.000000000 +0000 @@ -0,0 +1,18 @@ +# Requirements to build the Python documentation + +# Sphinx version is pinned so that new versions that introduce new warnings +# won't suddenly cause build failures. Updating the version is fine as long +# as no warnings are raised by doing so. +sphinx==2.3.1 +# Docutils version is pinned to a version compatible with Sphinx +# version 2.3.1. It can be removed after bumping Sphinx version to at +# least 3.5.4. +docutils==0.17.1 +# Jinja version is pinned to a version compatible with Sphinx version 2.3.1. +jinja2==3.0.3 + +blurb + +# The theme used by the documentation is stored separately, so we need +# to install that as well. +python-docs-theme diff -Nru python3.7-3.7.13/Doc/tools/extensions/pyspecific.py python3.7-3.7.14/Doc/tools/extensions/pyspecific.py --- python3.7-3.7.13/Doc/tools/extensions/pyspecific.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/tools/extensions/pyspecific.py 2022-09-06 07:11:33.000000000 +0000 @@ -35,7 +35,8 @@ import suspicious -ISSUE_URI = 'https://bugs.python.org/issue%s' +ISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s' +GH_ISSUE_URI = 'https://github.com/python/cpython/issues/%s' SOURCE_URI = 'https://github.com/python/cpython/tree/3.7/%s' # monkey-patch reST parser to disable alphabetic and roman enumerated lists @@ -81,11 +82,33 @@ def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): issue = utils.unescape(text) + # sanity check: there are no bpo issues within these two values + if 47261 < int(issue) < 400000: + msg = inliner.reporter.error(f'The BPO ID {text!r} seems too high -- ' + 'use :gh:`...` for GitHub IDs', line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] text = 'bpo-' + issue refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue) return [refnode], [] +# Support for marking up and linking to GitHub issues + +def gh_issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + issue = utils.unescape(text) + # sanity check: all GitHub issues have ID >= 32426 + # even though some of them are also valid BPO IDs + if int(issue) < 32426: + msg = inliner.reporter.error(f'The GitHub ID {text!r} seems too low -- ' + 'use :issue:`...` for BPO IDs', line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + text = 'gh-' + issue + refnode = nodes.reference(text, text, refuri=GH_ISSUE_URI % issue) + return [refnode], [] + + # Support for linking to Python source files easily def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): @@ -286,7 +309,8 @@ # Support for including Misc/NEWS -issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)') +issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I) +gh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I) whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$") @@ -313,8 +337,9 @@ text = 'The NEWS file is not available.' node = nodes.strong(text, text) return [node] - content = issue_re.sub(r'`bpo-\1 `__', - content) + content = issue_re.sub(r':issue:`\1`', content) + # Fallback handling for the GitHub issue + content = gh_issue_re.sub(r':gh:`\1`', content) content = whatsnew_re.sub(r'\1', content) # remove first 3 lines as they are the main heading lines = ['.. default-role:: obj', ''] + content.splitlines()[3:] @@ -430,6 +455,7 @@ def setup(app): app.add_role('issue', issue_role) + app.add_role('gh', gh_issue_role) app.add_role('source', source_role) app.add_directive('impl-detail', ImplementationDetail) app.add_directive('availability', Availability) diff -Nru python3.7-3.7.13/Doc/tools/extensions/suspicious.py python3.7-3.7.14/Doc/tools/extensions/suspicious.py --- python3.7-3.7.13/Doc/tools/extensions/suspicious.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/tools/extensions/suspicious.py 2022-09-06 07:11:33.000000000 +0000 @@ -150,7 +150,6 @@ return False def report_issue(self, text, lineno, issue): - if not self.any_issue: self.logger.info() self.any_issue = True self.write_log_entry(lineno, issue, text) if py3: diff -Nru python3.7-3.7.13/Doc/using/cmdline.rst python3.7-3.7.14/Doc/using/cmdline.rst --- python3.7-3.7.13/Doc/using/cmdline.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/using/cmdline.rst 2022-09-06 07:11:33.000000000 +0000 @@ -432,6 +432,9 @@ * ``-X showalloccount`` to output the total count of allocated objects for each type when the program finishes. This only works when Python was built with ``COUNT_ALLOCS`` defined. + * ``-X int_max_str_digits`` configures the :ref:`integer string conversion + length limitation `. See also + :envvar:`PYTHONINTMAXSTRDIGITS`. * ``-X importtime`` to show how long each import takes. It shows module name, cumulative time (including nested imports) and self time (excluding nested imports). Note that its output may be broken in multi-threaded @@ -474,6 +477,9 @@ .. versionadded:: 3.7 The ``-X importtime``, ``-X dev`` and ``-X utf8`` options. + .. versionadded:: 3.7.14 + The ``-X int_max_str_digits`` option. + Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -618,6 +624,13 @@ .. versionadded:: 3.2.3 +.. envvar:: PYTHONINTMAXSTRDIGITS + + If this variable is set to an integer, it is used to configure the + interpreter's global :ref:`integer string conversion length limitation + `. + + .. versionadded:: 3.7.14 .. envvar:: PYTHONIOENCODING diff -Nru python3.7-3.7.13/Doc/whatsnew/3.7.rst python3.7-3.7.14/Doc/whatsnew/3.7.rst --- python3.7-3.7.13/Doc/whatsnew/3.7.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Doc/whatsnew/3.7.rst 2022-09-06 07:11:33.000000000 +0000 @@ -539,7 +539,6 @@ the timing of each module import. (Contributed by Victor Stinner in :issue:`31415`.) - New Modules =========== @@ -2603,4 +2602,16 @@ characters are controlled by a new module level variable ``urllib.parse._UNSAFE_URL_BYTES_TO_REMOVE``. (See :issue:`43882`) +Notable security feature in 3.7.14 +================================== +Converting between :class:`int` and :class:`str` in bases other than 2 +(binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) +now raises a :exc:`ValueError` if the number of digits in string form is +above a limit to avoid potential denial of service attacks due to the +algorithmic complexity. This is a mitigation for `CVE-2020-10735 +`_. +This limit can be configured or disabled by environment variable, command +line flag, or :mod:`sys` APIs. See the :ref:`integer string conversion +length limitation ` documentation. The default limit +is 4300 digits in string form. diff -Nru python3.7-3.7.13/Include/internal/hamt.h python3.7-3.7.14/Include/internal/hamt.h --- python3.7-3.7.13/Include/internal/hamt.h 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Include/internal/hamt.h 2022-09-06 07:11:33.000000000 +0000 @@ -2,7 +2,19 @@ #define Py_INTERNAL_HAMT_H -#define _Py_HAMT_MAX_TREE_DEPTH 7 + +/* +HAMT tree is shaped by hashes of keys. Every group of 5 bits of a hash denotes +the exact position of the key in one level of the tree. Since we're using +32 bit hashes, we can have at most 7 such levels. Although if there are +two distinct keys with equal hashes, they will have to occupy the same +cell in the 7th level of the tree -- so we'd put them in a "collision" node. +Which brings the total possible tree depth to 8. Read more about the actual +layout of the HAMT tree in `hamt.c`. + +This constant is used to define a datastucture for storing iteration state. +*/ +#define _Py_HAMT_MAX_TREE_DEPTH 8 #define PyHamt_Check(o) (Py_TYPE(o) == &_PyHamt_Type) diff -Nru python3.7-3.7.13/Include/internal/pycore_long.h python3.7-3.7.14/Include/internal/pycore_long.h --- python3.7-3.7.13/Include/internal/pycore_long.h 1970-01-01 00:00:00.000000000 +0000 +++ python3.7-3.7.14/Include/internal/pycore_long.h 2022-09-06 07:11:33.000000000 +0000 @@ -0,0 +1,49 @@ +#ifndef Py_INTERNAL_LONG_H +#define Py_INTERNAL_LONG_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +/* + * Default int base conversion size limitation: Denial of Service prevention. + * + * Chosen such that this isn't wildly slow on modern hardware and so that + * everyone's existing deployed numpy test suite passes before + * https://github.com/numpy/numpy/issues/22098 is widely available. + * + * $ python -m timeit -s 's = "1"*4300' 'int(s)' + * 2000 loops, best of 5: 125 usec per loop + * $ python -m timeit -s 's = "1"*4300; v = int(s)' 'str(v)' + * 1000 loops, best of 5: 311 usec per loop + * (zen2 cloud VM) + * + * 4300 decimal digits fits a ~14284 bit number. + */ +#define _PY_LONG_DEFAULT_MAX_STR_DIGITS 4300 +/* + * Threshold for max digits check. For performance reasons int() and + * int.__str__() don't checks values that are smaller than this + * threshold. Acts as a guaranteed minimum size limit for bignums that + * applications can expect from CPython. + * + * % python -m timeit -s 's = "1"*640; v = int(s)' 'str(int(s))' + * 20000 loops, best of 5: 12 usec per loop + * + * "640 digits should be enough for anyone." - gps + * fits a ~2126 bit decimal number. + */ +#define _PY_LONG_MAX_STR_DIGITS_THRESHOLD 640 + +#if ((_PY_LONG_DEFAULT_MAX_STR_DIGITS != 0) && \ + (_PY_LONG_DEFAULT_MAX_STR_DIGITS < _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) +# error "_PY_LONG_DEFAULT_MAX_STR_DIGITS smaller than threshold." +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_LONG_H */ diff -Nru python3.7-3.7.13/Include/internal/pystate.h python3.7-3.7.14/Include/internal/pystate.h --- python3.7-3.7.13/Include/internal/pystate.h 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Include/internal/pystate.h 2022-09-06 07:11:33.000000000 +0000 @@ -105,6 +105,8 @@ struct _gilstate_runtime_state gilstate; // XXX Consolidate globals found via the check-c-globals script. + + int int_max_str_digits; } _PyRuntimeState; #define _PyRuntimeState_INIT {.initialized = 0, .core_initialized = 0} @@ -120,6 +122,10 @@ PyAPI_FUNC(void) _PyRuntime_Finalize(void); +/* Excluded from public struct _PyCoreConfig for backporting reasons. */ +/* Modules/main.c config_init_int_max_str_digits() configures it. */ +/* Storage declared in pylifecycle.c */ +extern int _Py_global_config_int_max_str_digits; #define _Py_CURRENTLY_FINALIZING(tstate) \ (_PyRuntime.finalizing == tstate) diff -Nru python3.7-3.7.13/Include/patchlevel.h python3.7-3.7.14/Include/patchlevel.h --- python3.7-3.7.13/Include/patchlevel.h 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Include/patchlevel.h 2022-09-06 07:11:33.000000000 +0000 @@ -18,12 +18,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 3 #define PY_MINOR_VERSION 7 -#define PY_MICRO_VERSION 13 +#define PY_MICRO_VERSION 14 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "3.7.13" +#define PY_VERSION "3.7.14" /*--end constants--*/ /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. diff -Nru python3.7-3.7.13/Lib/ctypes/test/test_macholib.py python3.7-3.7.14/Lib/ctypes/test/test_macholib.py --- python3.7-3.7.13/Lib/ctypes/test/test_macholib.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/ctypes/test/test_macholib.py 2022-09-06 07:11:33.000000000 +0000 @@ -32,6 +32,10 @@ # -bob from ctypes.macholib.dyld import dyld_find +try: + from _ctypes import _dyld_shared_cache_contains_path +except ImportError: + _dyld_shared_cache_contains_path = None def find_lib(name): possible = ['lib'+name+'.dylib', name+'.dylib', name+'.framework/'+name] @@ -44,20 +48,24 @@ class MachOTest(unittest.TestCase): @unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test') + @unittest.skipUnless(_dyld_shared_cache_contains_path, 'macOS 11+ _ctypes support not present.') def test_find(self): - - self.assertEqual(find_lib('pthread'), - '/usr/lib/libSystem.B.dylib') + # On Mac OS 11, system dylibs are only present in the shared cache, + # so symlinks like libpthread.dylib -> libSystem.B.dylib will not + # be resolved by dyld_find + self.assertIn(find_lib('pthread'), + ('/usr/lib/libSystem.B.dylib', '/usr/lib/libpthread.dylib')) result = find_lib('z') # Issue #21093: dyld default search path includes $HOME/lib and # /usr/local/lib before /usr/lib, which caused test failures if # a local copy of libz exists in one of them. Now ignore the head # of the path. - self.assertRegex(result, r".*/lib/libz\..*.*\.dylib") + self.assertRegex(result, r".*/lib/libz.*\.dylib") - self.assertEqual(find_lib('IOKit'), - '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit') + self.assertIn(find_lib('IOKit'), + ('/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit', + '/System/Library/Frameworks/IOKit.framework/IOKit')) if __name__ == "__main__": unittest.main() diff -Nru python3.7-3.7.13/Lib/http/server.py python3.7-3.7.14/Lib/http/server.py --- python3.7-3.7.13/Lib/http/server.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/http/server.py 2022-09-06 07:11:33.000000000 +0000 @@ -331,6 +331,13 @@ return False self.command, self.path = command, path + # gh-87389: The purpose of replacing '//' with '/' is to protect + # against open redirect attacks possibly triggered if the path starts + # with '//' because http clients treat //path as an absolute URI + # without scheme (similar to http://path) rather than a path. + if self.path.startswith('//'): + self.path = '/' + self.path.lstrip('/') # Reduce to a single / + # Examine the headers and look for a Connection directive. try: self.headers = http.client.parse_headers(self.rfile, diff -Nru python3.7-3.7.13/Lib/pydoc_data/topics.py python3.7-3.7.14/Lib/pydoc_data/topics.py --- python3.7-3.7.13/Lib/pydoc_data/topics.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/pydoc_data/topics.py 2022-09-06 07:11:33.000000000 +0000 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Wed Mar 16 09:24:05 2022 +# Autogenerated by Sphinx on Tue Sep 6 02:37:38 2022 topics = {'assert': 'The "assert" statement\n' '**********************\n' '\n' @@ -7799,31 +7799,7 @@ ' still alive. Example:\n' '\n' ' >>> int.__subclasses__()\n' - " []\n" - '\n' - '-[ Footnotes ]-\n' - '\n' - '[1] Additional information on these special methods may be ' - 'found in\n' - ' the Python Reference Manual (Basic customization).\n' - '\n' - '[2] As a consequence, the list "[1, 2]" is considered equal ' - 'to "[1.0,\n' - ' 2.0]", and similarly for tuples.\n' - '\n' - '[3] They must have since the parser can’t tell the type of ' - 'the\n' - ' operands.\n' - '\n' - '[4] Cased characters are those with general category ' - 'property being\n' - ' one of “Lu” (Letter, uppercase), “Ll” (Letter, ' - 'lowercase), or “Lt”\n' - ' (Letter, titlecase).\n' - '\n' - '[5] To format only a tuple you should therefore provide a ' - 'singleton\n' - ' tuple whose only element is the tuple to be formatted.\n', + " []\n", 'specialnames': 'Special method names\n' '********************\n' '\n' diff -Nru python3.7-3.7.13/Lib/sqlite3/test/regression.py python3.7-3.7.14/Lib/sqlite3/test/regression.py --- python3.7-3.7.13/Lib/sqlite3/test/regression.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/sqlite3/test/regression.py 2022-09-06 07:11:33.000000000 +0000 @@ -27,6 +27,9 @@ import weakref from test import support +from unittest.mock import patch + + class RegressionTests(unittest.TestCase): def setUp(self): self.con = sqlite.connect(":memory:") @@ -444,11 +447,50 @@ self.con.execute('SELECT %s()' % aggr_name) +class RecursiveUseOfCursors(unittest.TestCase): + # GH-80254: sqlite3 should not segfault for recursive use of cursors. + msg = "Recursive use of cursors not allowed" + + def setUp(self): + self.con = sqlite.connect(":memory:", + detect_types=sqlite.PARSE_COLNAMES) + self.cur = self.con.cursor() + self.cur.execute("create table test(x foo)") + self.cur.executemany("insert into test(x) values (?)", + [("foo",), ("bar",)]) + + def tearDown(self): + self.cur.close() + self.con.close() + del self.cur + del self.con + + def test_recursive_cursor_init(self): + conv = lambda x: self.cur.__init__(self.con) + with patch.dict(sqlite.converters, {"INIT": conv}): + with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): + self.cur.execute(f'select x as "x [INIT]", x from test') + + def test_recursive_cursor_close(self): + conv = lambda x: self.cur.close() + with patch.dict(sqlite.converters, {"CLOSE": conv}): + with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): + self.cur.execute(f'select x as "x [CLOSE]", x from test') + + def test_recursive_cursor_fetch(self): + conv = lambda x, l=[]: self.cur.fetchone() if l else l.append(None) + with patch.dict(sqlite.converters, {"ITER": conv}): + self.cur.execute(f'select x as "x [ITER]", x from test') + with self.assertRaisesRegex(sqlite.ProgrammingError, self.msg): + self.cur.fetchall() + + def suite(): regression_suite = unittest.makeSuite(RegressionTests, "Check") return unittest.TestSuite(( regression_suite, unittest.makeSuite(UnhashableCallbacksTestCase), + unittest.makeSuite(RecursiveUseOfCursors), )) def test(): diff -Nru python3.7-3.7.13/Lib/test/support/__init__.py python3.7-3.7.14/Lib/test/support/__init__.py --- python3.7-3.7.13/Lib/test/support/__init__.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/support/__init__.py 2022-09-06 07:11:33.000000000 +0000 @@ -2985,3 +2985,13 @@ return False SMALLEST = _SMALLEST() + +@contextlib.contextmanager +def adjust_int_max_str_digits(max_digits): + """Temporarily change the integer string conversion length limit.""" + current = sys.get_int_max_str_digits() + try: + sys.set_int_max_str_digits(max_digits) + yield + finally: + sys.set_int_max_str_digits(current) diff -Nru python3.7-3.7.13/Lib/test/test_ast.py python3.7-3.7.14/Lib/test/test_ast.py --- python3.7-3.7.13/Lib/test/test_ast.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_ast.py 2022-09-06 07:11:33.000000000 +0000 @@ -632,6 +632,14 @@ self.assertRaises(ValueError, ast.literal_eval, '+True') self.assertRaises(ValueError, ast.literal_eval, '2+3') + def test_literal_eval_str_int_limit(self): + with support.adjust_int_max_str_digits(4000): + ast.literal_eval('3'*4000) # no error + with self.assertRaises(SyntaxError) as err_ctx: + ast.literal_eval('3'*4001) + self.assertIn('Exceeds the limit ', str(err_ctx.exception)) + self.assertIn(' Consider hexadecimal ', str(err_ctx.exception)) + def test_literal_eval_complex(self): # Issue #4907 self.assertEqual(ast.literal_eval('6j'), 6j) diff -Nru python3.7-3.7.13/Lib/test/test_cmd_line.py python3.7-3.7.14/Lib/test/test_cmd_line.py --- python3.7-3.7.13/Lib/test/test_cmd_line.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_cmd_line.py 2022-09-06 07:11:33.000000000 +0000 @@ -711,6 +711,40 @@ self.assertEqual(proc.returncode, 0, proc) self.assertEqual(proc.stdout.strip(), b'0') + def test_int_max_str_digits(self): + code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())" + + assert_python_failure('-X', 'int_max_str_digits', '-c', code) + assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code) + assert_python_failure('-X', 'int_max_str_digits=100', '-c', code) + + assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo') + assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100') + + def res2int(res): + out = res.out.strip().decode("utf-8") + return tuple(int(i) for i in out.split()) + + res = assert_python_ok('-c', code) + self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits())) + res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code) + self.assertEqual(res2int(res), (0, 0)) + res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code) + self.assertEqual(res2int(res), (4000, 4000)) + res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code) + self.assertEqual(res2int(res), (100000, 100000)) + + res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0') + self.assertEqual(res2int(res), (0, 0)) + res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000') + self.assertEqual(res2int(res), (4000, 4000)) + res = assert_python_ok( + '-X', 'int_max_str_digits=6000', '-c', code, + PYTHONINTMAXSTRDIGITS='4000' + ) + self.assertEqual(res2int(res), (6000, 6000)) + + @unittest.skipIf(interpreter_requires_environment(), 'Cannot run -I tests when PYTHON env vars are required.') class IgnoreEnvironmentTest(unittest.TestCase): diff -Nru python3.7-3.7.13/Lib/test/test_compile.py python3.7-3.7.14/Lib/test/test_compile.py --- python3.7-3.7.13/Lib/test/test_compile.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_compile.py 2022-09-06 07:11:33.000000000 +0000 @@ -189,6 +189,19 @@ self.assertEqual(eval("0o777"), 511) self.assertEqual(eval("-0o0000010"), -8) + def test_int_literals_too_long(self): + n = 3000 + source = f"a = 1\nb = 2\nc = {'3'*n}\nd = 4" + with support.adjust_int_max_str_digits(n): + compile(source, "", "exec") # no errors. + with support.adjust_int_max_str_digits(n-1): + with self.assertRaises(SyntaxError) as err_ctx: + compile(source, "", "exec") + exc = err_ctx.exception + self.assertEqual(exc.lineno, 3) + self.assertIn('Exceeds the limit ', str(exc)) + self.assertIn(' Consider hexadecimal ', str(exc)) + def test_unary_minus(self): # Verify treatment of unary minus on negative numbers SF bug #660455 if sys.maxsize == 2147483647: diff -Nru python3.7-3.7.13/Lib/test/test_context.py python3.7-3.7.14/Lib/test/test_context.py --- python3.7-3.7.13/Lib/test/test_context.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_context.py 2022-09-06 07:11:33.000000000 +0000 @@ -537,6 +537,41 @@ self.assertEqual(len(h4), 2) self.assertEqual(len(h5), 3) + def test_hamt_collision_3(self): + # Test that iteration works with the deepest tree possible. + # https://github.com/python/cpython/issues/93065 + + C = HashKey(0b10000000_00000000_00000000_00000000, 'C') + D = HashKey(0b10000000_00000000_00000000_00000000, 'D') + + E = HashKey(0b00000000_00000000_00000000_00000000, 'E') + + h = hamt() + h = h.set(C, 'C') + h = h.set(D, 'D') + h = h.set(E, 'E') + + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=2 count=1 bitmap=0b1): + # NULL: + # BitmapNode(size=4 count=2 bitmap=0b101): + # : 'E' + # NULL: + # CollisionNode(size=4 id=0x107a24520): + # : 'C' + # : 'D' + + self.assertEqual({k.name for k in h.keys()}, {'C', 'D', 'E'}) + def test_hamt_stress(self): COLLECTION_SIZE = 7000 TEST_ITERS_EVERY = 647 diff -Nru python3.7-3.7.13/Lib/test/test_decimal.py python3.7-3.7.14/Lib/test/test_decimal.py --- python3.7-3.7.13/Lib/test/test_decimal.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_decimal.py 2022-09-06 07:11:33.000000000 +0000 @@ -2446,6 +2446,15 @@ class PyUsabilityTest(UsabilityTest): decimal = P + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + class PythonAPItests(unittest.TestCase): def test_abc(self): @@ -4503,6 +4512,15 @@ class PyCoverage(Coverage): decimal = P + def setUp(self): + super().setUp() + self._previous_int_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(7000) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_int_limit) + super().tearDown() + class PyFunctionality(unittest.TestCase): """Extra functionality in decimal.py""" diff -Nru python3.7-3.7.13/Lib/test/test_httpservers.py python3.7-3.7.14/Lib/test/test_httpservers.py --- python3.7-3.7.13/Lib/test/test_httpservers.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_httpservers.py 2022-09-06 07:11:33.000000000 +0000 @@ -329,7 +329,7 @@ pass def setUp(self): - BaseTestCase.setUp(self) + super().setUp() self.cwd = os.getcwd() basetempdir = tempfile.gettempdir() os.chdir(basetempdir) @@ -357,7 +357,7 @@ except: pass finally: - BaseTestCase.tearDown(self) + super().tearDown() def check_status_and_reason(self, response, status, data=None): def close_conn(): @@ -413,6 +413,55 @@ self.check_status_and_reason(response, HTTPStatus.OK, data=support.TESTFN_UNDECODABLE) + def test_get_dir_redirect_location_domain_injection_bug(self): + """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. + + //netloc/ in a Location header is a redirect to a new host. + https://github.com/python/cpython/issues/87389 + + This checks that a path resolving to a directory on our server cannot + resolve into a redirect to another server. + """ + os.mkdir(os.path.join(self.tempdir, 'existing_directory')) + url = f'/python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../{self.tempdir_name}/existing_directory' + expected_location = f'{url}/' # /python.org.../ single slash single prefix, trailing slash + # Canonicalizes to /tmp/tempdir_name/existing_directory which does + # exist and is a dir, triggering the 301 redirect logic. + response = self.request(url) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) + location = response.getheader('Location') + self.assertEqual(location, expected_location, msg='non-attack failed!') + + # //python.org... multi-slash prefix, no trailing slash + attack_url = f'/{url}' + response = self.request(attack_url) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) + location = response.getheader('Location') + self.assertFalse(location.startswith('//'), msg=location) + self.assertEqual(location, expected_location, + msg='Expected Location header to start with a single / and ' + 'end with a / as this is a directory redirect.') + + # ///python.org... triple-slash prefix, no trailing slash + attack3_url = f'//{url}' + response = self.request(attack3_url) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) + self.assertEqual(response.getheader('Location'), expected_location) + + # If the second word in the http request (Request-URI for the http + # method) is a full URI, we don't worry about it, as that'll be parsed + # and reassembled as a full URI within BaseHTTPRequestHandler.send_head + # so no errant scheme-less //netloc//evil.co/ domain mixup can happen. + attack_scheme_netloc_2slash_url = f'https://pypi.org/{url}' + expected_scheme_netloc_location = f'{attack_scheme_netloc_2slash_url}/' + response = self.request(attack_scheme_netloc_2slash_url) + self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) + location = response.getheader('Location') + # We're just ensuring that the scheme and domain make it through, if + # there are or aren't multiple slashes at the start of the path that + # follows that isn't important in this Location: header. + self.assertTrue(location.startswith('https://pypi.org/'), msg=location) + def test_get(self): #constructs the path relative to the root directory of the HTTPServer response = self.request(self.base_url + '/test') diff -Nru python3.7-3.7.13/Lib/test/test_int.py python3.7-3.7.14/Lib/test/test_int.py --- python3.7-3.7.13/Lib/test/test_int.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_int.py 2022-09-06 07:11:33.000000000 +0000 @@ -1,4 +1,5 @@ import sys +import time import unittest from test import support @@ -516,5 +517,200 @@ self.assertEqual(int('1_2_3_4_5_6_7', 32), 1144132807) +class IntStrDigitLimitsTests(unittest.TestCase): + + int_class = int # Override this in subclasses to reuse the suite. + + def setUp(self): + super().setUp() + self._previous_limit = sys.get_int_max_str_digits() + sys.set_int_max_str_digits(2048) + + def tearDown(self): + sys.set_int_max_str_digits(self._previous_limit) + super().tearDown() + + def test_disabled_limit(self): + self.assertGreater(sys.get_int_max_str_digits(), 0) + self.assertLess(sys.get_int_max_str_digits(), 20_000) + with support.adjust_int_max_str_digits(0): + self.assertEqual(sys.get_int_max_str_digits(), 0) + i = self.int_class('1' * 20_000) + str(i) + self.assertGreater(sys.get_int_max_str_digits(), 0) + + def test_max_str_digits_edge_cases(self): + """Ignore the +/- sign and space padding.""" + int_class = self.int_class + maxdigits = sys.get_int_max_str_digits() + + int_class('1' * maxdigits) + int_class(' ' + '1' * maxdigits) + int_class('1' * maxdigits + ' ') + int_class('+' + '1' * maxdigits) + int_class('-' + '1' * maxdigits) + self.assertEqual(len(str(10 ** (maxdigits - 1))), maxdigits) + + def check(self, i, base=None): + with self.assertRaises(ValueError): + if base is None: + self.int_class(i) + else: + self.int_class(i, base) + + def test_max_str_digits(self): + maxdigits = sys.get_int_max_str_digits() + + self.check('1' * (maxdigits + 1)) + self.check(' ' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1) + ' ') + self.check('+' + '1' * (maxdigits + 1)) + self.check('-' + '1' * (maxdigits + 1)) + self.check('1' * (maxdigits + 1)) + + i = 10 ** maxdigits + with self.assertRaises(ValueError): + str(i) + + def test_denial_of_service_prevented_int_to_str(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 50_000, maxdigits # A test prerequisite. + get_time = time.process_time + if get_time() <= 0: # some platforms like WASM lack process_time() + get_time = time.monotonic + + huge_int = int(f'0x{"c"*65_000}', base=16) # 78268 decimal digits. + digits = 78_268 + with support.adjust_int_max_str_digits(digits): + start = get_time() + huge_decimal = str(huge_int) + seconds_to_convert = get_time() - start + self.assertEqual(len(huge_decimal), digits) + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + if seconds_to_convert < 0.005: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + + # We test with the limit almost at the size needed to check performance. + # The performant limit check is slightly fuzzy, give it a some room. + with support.adjust_int_max_str_digits(int(.995 * digits)): + with self.assertRaises(ValueError) as err: + start = get_time() + str(huge_int) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge_int = int(f'0x{"c"*500_000}', base=16) # 602060 digits. + with self.assertRaises(ValueError) as err: + start = get_time() + # If not limited, 8 seconds said Zen based cloud VM. + str(extra_huge_int) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) + + def test_denial_of_service_prevented_str_to_int(self): + """Regression test: ensure we fail before performing O(N**2) work.""" + maxdigits = sys.get_int_max_str_digits() + assert maxdigits < 100_000, maxdigits # A test prerequisite. + get_time = time.process_time + if get_time() <= 0: # some platforms like WASM lack process_time() + get_time = time.monotonic + + digits = 133700 + huge = '8'*digits + with support.adjust_int_max_str_digits(digits): + start = get_time() + int(huge) + seconds_to_convert = get_time() - start + # Ensuring that we chose a slow enough conversion to measure. + # It takes 0.1 seconds on a Zen based cloud VM in an opt build. + if seconds_to_convert < 0.005: + raise unittest.SkipTest('"slow" conversion took only ' + f'{seconds_to_convert} seconds.') + + with support.adjust_int_max_str_digits(digits - 1): + with self.assertRaises(ValueError) as err: + start = get_time() + int(huge) + seconds_to_fail_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLess(seconds_to_fail_huge, seconds_to_convert/8) + + # Now we test that a conversion that would take 30x as long also fails + # in a similarly fast fashion. + extra_huge = '7'*1_200_000 + with self.assertRaises(ValueError) as err: + start = get_time() + # If not limited, 8 seconds in the Zen based cloud VM. + int(extra_huge) + seconds_to_fail_extra_huge = get_time() - start + self.assertIn('conversion', str(err.exception)) + self.assertLess(seconds_to_fail_extra_huge, seconds_to_convert/8) + + def test_power_of_two_bases_unlimited(self): + """The limit does not apply to power of 2 bases.""" + maxdigits = sys.get_int_max_str_digits() + + for base in (2, 4, 8, 16, 32): + with self.subTest(base=base): + self.int_class('1' * (maxdigits + 1), base) + assert maxdigits < 100_000 + self.int_class('1' * 100_000, base) + + def test_underscores_ignored(self): + maxdigits = sys.get_int_max_str_digits() + + triples = maxdigits // 3 + s = '111' * triples + s_ = '1_11' * triples + self.int_class(s) # succeeds + self.int_class(s_) # succeeds + self.check(f'{s}111') + self.check(f'{s_}_111') + + def test_sign_not_counted(self): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '5' * max_digits + i = int_class(s) + pos_i = int_class(f'+{s}') + assert i == pos_i + neg_i = int_class(f'-{s}') + assert -pos_i == neg_i + str(pos_i) + str(neg_i) + + def _other_base_helper(self, base): + int_class = self.int_class + max_digits = sys.get_int_max_str_digits() + s = '2' * max_digits + i = int_class(s, base) + if base > 10: + with self.assertRaises(ValueError): + str(i) + elif base < 10: + str(i) + with self.assertRaises(ValueError) as err: + int_class(f'{s}1', base) + + def test_int_from_other_bases(self): + base = 3 + with self.subTest(base=base): + self._other_base_helper(base) + base = 36 + with self.subTest(base=base): + self._other_base_helper(base) + + +class IntSubclassStrDigitLimitsTests(IntStrDigitLimitsTests): + int_class = IntSubclass + + if __name__ == "__main__": unittest.main() diff -Nru python3.7-3.7.13/Lib/test/test_json/test_decode.py python3.7-3.7.14/Lib/test/test_json/test_decode.py --- python3.7-3.7.13/Lib/test/test_json/test_decode.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_json/test_decode.py 2022-09-06 07:11:33.000000000 +0000 @@ -2,6 +2,7 @@ from io import StringIO from collections import OrderedDict from test.test_json import PyTest, CTest +from test import support class TestDecode: @@ -95,5 +96,12 @@ d = self.json.JSONDecoder() self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000) + def test_limit_int(self): + maxdigits = 5000 + with support.adjust_int_max_str_digits(maxdigits): + self.loads('1' * maxdigits) + with self.assertRaises(ValueError): + self.loads('1' * (maxdigits + 1)) + class TestPyDecode(TestDecode, PyTest): pass class TestCDecode(TestDecode, CTest): pass diff -Nru python3.7-3.7.13/Lib/test/test_ssl.py python3.7-3.7.14/Lib/test/test_ssl.py --- python3.7-3.7.13/Lib/test/test_ssl.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_ssl.py 2022-09-06 07:11:33.000000000 +0000 @@ -1131,8 +1131,12 @@ @skip_if_broken_ubuntu_ssl def test_constructor(self): for protocol in PROTOCOLS: - ssl.SSLContext(protocol) - ctx = ssl.SSLContext() + if has_tls_protocol(protocol): + with support.check_warnings(): + ctx = ssl.SSLContext(protocol) + self.assertEqual(ctx.protocol, protocol) + with support.check_warnings(): + ctx = ssl.SSLContext() self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLS) self.assertRaises(ValueError, ssl.SSLContext, -1) self.assertRaises(ValueError, ssl.SSLContext, 42) @@ -1284,7 +1288,7 @@ ctx.maximum_version = ssl.TLSVersion.MINIMUM_SUPPORTED self.assertIn( ctx.maximum_version, - {ssl.TLSVersion.TLSv1, ssl.TLSVersion.SSLv3} + {ssl.TLSVersion.TLSv1, ssl.TLSVersion.TLSv1_1, ssl.TLSVersion.SSLv3} ) ctx.minimum_version = ssl.TLSVersion.MAXIMUM_SUPPORTED @@ -1296,19 +1300,19 @@ with self.assertRaises(ValueError): ctx.minimum_version = 42 - ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) - - self.assertIn( - ctx.minimum_version, minimum_range - ) - self.assertEqual( - ctx.maximum_version, ssl.TLSVersion.MAXIMUM_SUPPORTED - ) - with self.assertRaises(ValueError): - ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED - with self.assertRaises(ValueError): - ctx.maximum_version = ssl.TLSVersion.TLSv1 + if has_tls_protocol(ssl.PROTOCOL_TLSv1_1): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1) + self.assertIn( + ctx.minimum_version, minimum_range + ) + self.assertEqual( + ctx.maximum_version, ssl.TLSVersion.MAXIMUM_SUPPORTED + ) + with self.assertRaises(ValueError): + ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED + with self.assertRaises(ValueError): + ctx.maximum_version = ssl.TLSVersion.TLSv1 @unittest.skipUnless(have_verify_flags(), "verify_flags need OpenSSL > 0.9.8") @@ -1690,10 +1694,12 @@ self.assertFalse(ctx.check_hostname) self._assert_context_options(ctx) - ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) - self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) - self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self._assert_context_options(ctx) + if has_tls_protocol(ssl.PROTOCOL_TLSv1): + with support.check_warnings(): + ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, @@ -3363,10 +3369,12 @@ client_options=ssl.OP_NO_TLSv1_2) try_protocol_combo(ssl.PROTOCOL_TLS, ssl.PROTOCOL_TLSv1_2, 'TLSv1.2') - try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) - try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) - try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) - try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) + if has_tls_protocol(ssl.PROTOCOL_TLSv1): + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) + if has_tls_protocol(ssl.PROTOCOL_TLSv1_1): + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) def test_starttls(self): """Switching from clear text to encrypted and back again.""" diff -Nru python3.7-3.7.13/Lib/test/test_sys.py python3.7-3.7.14/Lib/test/test_sys.py --- python3.7-3.7.13/Lib/test/test_sys.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_sys.py 2022-09-06 07:11:33.000000000 +0000 @@ -447,11 +447,17 @@ self.assertIsInstance(sys.executable, str) self.assertEqual(len(sys.float_info), 11) self.assertEqual(sys.float_info.radix, 2) - self.assertEqual(len(sys.int_info), 2) + self.assertEqual(len(sys.int_info), 4) self.assertTrue(sys.int_info.bits_per_digit % 5 == 0) self.assertTrue(sys.int_info.sizeof_digit >= 1) + self.assertGreaterEqual(sys.int_info.default_max_str_digits, 500) + self.assertGreaterEqual(sys.int_info.str_digits_check_threshold, 100) + self.assertGreater(sys.int_info.default_max_str_digits, + sys.int_info.str_digits_check_threshold) self.assertEqual(type(sys.int_info.bits_per_digit), int) self.assertEqual(type(sys.int_info.sizeof_digit), int) + self.assertIsInstance(sys.int_info.default_max_str_digits, int) + self.assertIsInstance(sys.int_info.str_digits_check_threshold, int) self.assertIsInstance(sys.hexversion, int) self.assertEqual(len(sys.hash_info), 9) @@ -554,7 +560,7 @@ "inspect", "interactive", "optimize", "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose", "bytes_warning", "quiet", "hash_randomization", "isolated", - "dev_mode", "utf8_mode") + "dev_mode", "utf8_mode", "int_max_str_digits") for attr in attrs: self.assertTrue(hasattr(sys.flags, attr), attr) attr_type = bool if attr == "dev_mode" else int diff -Nru python3.7-3.7.13/Lib/test/test_xmlrpc.py python3.7-3.7.14/Lib/test/test_xmlrpc.py --- python3.7-3.7.13/Lib/test/test_xmlrpc.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_xmlrpc.py 2022-09-06 07:11:33.000000000 +0000 @@ -283,6 +283,16 @@ check('9876543210.0123456789', decimal.Decimal('9876543210.0123456789')) + def test_limit_int(self): + check = self.check_loads + maxdigits = 5000 + with support.adjust_int_max_str_digits(maxdigits): + s = '1' * (maxdigits + 1) + with self.assertRaises(ValueError): + check(f'{s}', None) + with self.assertRaises(ValueError): + check(f'{s}', None) + def test_get_host_info(self): # see bug #3613, this raised a TypeError transp = xmlrpc.client.Transport() diff -Nru python3.7-3.7.13/Lib/test/test_zipfile.py python3.7-3.7.14/Lib/test/test_zipfile.py --- python3.7-3.7.13/Lib/test/test_zipfile.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/test/test_zipfile.py 2022-09-06 07:11:33.000000000 +0000 @@ -2077,6 +2077,7 @@ self.assertRaises(RuntimeError, self.zip.read, "test.txt") self.assertRaises(RuntimeError, self.zip2.read, "zero") + @requires_zlib def test_bad_password(self): self.zip.setpassword(b"perl") self.assertRaises(RuntimeError, self.zip.read, "test.txt") diff -Nru python3.7-3.7.13/Lib/tkinter/test/test_tkinter/test_widgets.py python3.7-3.7.14/Lib/tkinter/test/test_tkinter/test_widgets.py --- python3.7-3.7.13/Lib/tkinter/test/test_tkinter/test_widgets.py 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Lib/tkinter/test/test_tkinter/test_widgets.py 2022-09-06 07:11:33.000000000 +0000 @@ -864,7 +864,8 @@ def test_from(self): widget = self.create() - self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv) def test_label(self): widget = self.create() diff -Nru python3.7-3.7.13/Makefile.pre.in python3.7-3.7.14/Makefile.pre.in --- python3.7-3.7.13/Makefile.pre.in 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Makefile.pre.in 2022-09-06 07:11:33.000000000 +0000 @@ -744,6 +744,13 @@ $(srcdir)/Python/importlib.h.new $(UPDATE_FILE) $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib.h.new +regen-abidump: all + @$(MKDIR_P) $(srcdir)/Doc/data/ + abidw "libpython$(LDVERSION).so" --no-architecture --out-file $(srcdir)/Doc/data/python$(LDVERSION).abi.new + @$(UPDATE_FILE) $(srcdir)/Doc/data/python$(LDVERSION).abi $(srcdir)/Doc/data/python$(LDVERSION).abi.new + +check-abidump: all + abidiff "libpython$(LDVERSION).so" $(srcdir)/Doc/data/python$(LDVERSION).abi --drop-private-types --no-architecture --no-added-syms ############################################################################ # Regenerate all generated files diff -Nru python3.7-3.7.13/Misc/ACKS python3.7-3.7.14/Misc/ACKS --- python3.7-3.7.13/Misc/ACKS 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Misc/ACKS 2022-09-06 07:11:33.000000000 +0000 @@ -963,6 +963,8 @@ Xuanji Li Zekun Li Zheao Li +Eli Libman +Dan Lidral-Porter Robert van Liere Ross Light Shawn Ligocki diff -Nru python3.7-3.7.13/Misc/NEWS python3.7-3.7.14/Misc/NEWS --- python3.7-3.7.13/Misc/NEWS 2022-03-16 14:04:10.000000000 +0000 +++ python3.7-3.7.14/Misc/NEWS 2022-09-06 07:51:37.000000000 +0000 @@ -2,6 +2,78 @@ Python News +++++++++++ +What's New in Python 3.7.14 final? +================================== + +*Release date: 2022-09-06* + +Security +-------- + +- gh-issue-95778: Converting between :class:`int` and :class:`str` in bases + other than 2 (binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base + 10 (decimal) now raises a :exc:`ValueError` if the number of digits in + string form is above a limit to avoid potential denial of service attacks + due to the algorithmic complexity. This is a mitigation for + `CVE-2020-10735 + `_. + + This new limit can be configured or disabled by environment variable, + command line flag, or :mod:`sys` APIs. See the :ref:`integer string + conversion length limitation ` documentation. The + default limit is 4300 digits in string form. + + Patch by Gregory P. Smith [Google] and Christian Heimes [Red Hat] with + feedback from Victor Stinner, Thomas Wouters, Steve Dower, Ned Deily, and + Mark Dickinson. + +- gh-issue-87389: :mod:`http.server`: Fix an open redirection vulnerability + in the HTTP server when an URI path starts with ``//``. Vulnerability + discovered, and initial fix proposed, by Hamza Avvan. + +Core and Builtins +----------------- + +- gh-issue-93065: Fix contextvars HAMT implementation to handle iteration + over deep trees. + + The bug was discovered and fixed by Eli Libman. See + `MagicStack/immutables#84 + `_ for more details. + +Library +------- + +- bpo-36073: Raise :exc:`~sqlite3.ProgrammingError` instead of segfaulting + on recursive usage of cursors in :mod:`sqlite3` converters. Patch by + Sergey Fedoseev. + +Documentation +------------- + +- gh-issue-91888: Add a new ``gh`` role to the documentation to link to + GitHub issues. + +- bpo-47138: Pin Jinja to a version compatible with Sphinx version 2.3.1. + +Tests +----- + +- gh-issue-94208: ``test_ssl`` is now checking for supported TLS version and + protocols in more tests. + +- bpo-47016: Create a GitHub Actions workflow for verifying bundled pip and + setuptools. Patch by Illia Volochii and Adam Turner. + +- bpo-41306: Fixed a failure in ``test_tk.test_widgets.ScaleTest`` happening + when executing the test with Tk 8.6.10. + +Windows +------- + +- bpo-47194: Update ``zlib`` to v1.2.12 to resolve CVE-2018-25032. + + What's New in Python 3.7.13 final? ================================== @@ -675,7 +747,7 @@ bdb.Bdb.format_stack_entry. - bpo-27657: The original fix for bpo-27657, "Fix urlparse() with numeric - paths" (GH-16839) included in 3.7.6, inadvertently introduced a behavior + paths" (PR 16839) included in 3.7.6, inadvertently introduced a behavior change that broke several third-party packages relying on the original undefined parsing behavior. The change is reverted in 3.7.7, restoring the behavior of 3.7.5 and earlier releases. @@ -1109,7 +1181,7 @@ - bpo-38368: Prevent ctypes crash when handling arrays in structs/unions. -- bpo-38449: Revert GH-15522, which introduces a regression in +- bpo-38449: Revert PR 15522, which introduces a regression in :meth:`mimetypes.guess_type` due to improper handling of filenames as urls. @@ -5465,7 +5537,7 @@ when getting the file size. Fixed hang of all threads with inaccessible NFS server. Patch by Nir Soffer. -- bpo-321010: Add :attr:`sys.flags.dev_mode` flag +- bpo-32101: Add :attr:`sys.flags.dev_mode` flag - bpo-32154: The ``asyncio.windows_utils.socketpair()`` function has been removed: use directly :func:`socket.socketpair` which is available on all diff -Nru python3.7-3.7.13/Modules/main.c python3.7-3.7.14/Modules/main.c --- python3.7-3.7.13/Modules/main.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Modules/main.c 2022-09-06 07:11:33.000000000 +0000 @@ -3,6 +3,7 @@ #include "Python.h" #include "osdefs.h" #include "internal/import.h" +#include "internal/pycore_long.h" #include "internal/pygetopt.h" #include "internal/pystate.h" @@ -142,6 +143,9 @@ -X utf8: enable UTF-8 mode for operating system interfaces, overriding the default\n\ locale-aware mode. -X utf8=0 explicitly disables UTF-8 mode (even when it would\n\ otherwise activate automatically)\n\ + -X int_max_str_digits=number: limit the size of int<->str conversions.\n\ + This helps avoid denial of service attacks when parsing untrusted data.\n\ + The default is sys.int_info.default_max_str_digits. 0 disables.\n\ \n\ --check-hash-based-pycs always|default|never:\n\ control how Python invalidates hash-based .pyc files\n\ @@ -167,6 +171,10 @@ " to seed the hashes of str, bytes and datetime objects. It can also be\n" " set to an integer in the range [0,4294967295] to get hash values with a\n" " predictable seed.\n" +"PYTHONINTMAXSTRDIGITS: limits the maximum digit characters in an int value\n" +" when converting from a string and when converting an int back to a str.\n" +" A value of 0 disables the limit. Conversions to or from bases 2, 4, 8,\n" +" 16, and 32 are never limited.\n" "PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n" " on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n" " hooks.\n" @@ -1801,6 +1809,48 @@ return _Py_INIT_OK(); } +static _PyInitError +config_init_int_max_str_digits(_PyCoreConfig *config) +{ + int maxdigits; + int valid = 0; + + const char *env = config_get_env_var("PYTHONINTMAXSTRDIGITS"); + if (env) { + if (!pymain_str_to_int(env, &maxdigits)) { + valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); + } + if (!valid) { +#define STRINGIFY(VAL) _STRINGIFY(VAL) +#define _STRINGIFY(VAL) #VAL + return _Py_INIT_USER_ERR( + "PYTHONINTMAXSTRDIGITS: invalid limit; must be >= " + STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) + " or 0 for unlimited."); + } + _Py_global_config_int_max_str_digits = maxdigits; + } + + const wchar_t *xoption = config_get_xoption(config, L"int_max_str_digits"); + if (xoption) { + const wchar_t *sep = wcschr(xoption, L'='); + if (sep) { + if (!pymain_wstr_to_int(sep + 1, &maxdigits)) { + valid = ((maxdigits == 0) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)); + } + } + if (!valid) { + return _Py_INIT_USER_ERR( + "-X int_max_str_digits: invalid limit; must be >= " + STRINGIFY(_PY_LONG_MAX_STR_DIGITS_THRESHOLD) + " or 0 for unlimited."); +#undef _STRINGIFY +#undef STRINGIFY + } + _Py_global_config_int_max_str_digits = maxdigits; + } + return _Py_INIT_OK(); +} static void get_env_flag(int *flag, const char *name) @@ -2019,6 +2069,12 @@ if (_Py_INIT_FAILED(err)) { return err; } + } + if (_Py_global_config_int_max_str_digits < 0) { + _PyInitError err = config_init_int_max_str_digits(config); + if (_Py_INIT_FAILED(err)) { + return err; + } } return _Py_INIT_OK(); } diff -Nru python3.7-3.7.13/Modules/_sqlite/cursor.c python3.7-3.7.14/Modules/_sqlite/cursor.c --- python3.7-3.7.13/Modules/_sqlite/cursor.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Modules/_sqlite/cursor.c 2022-09-06 07:11:33.000000000 +0000 @@ -27,10 +27,25 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self); +static inline int +check_cursor_locked(pysqlite_Cursor *cur) +{ + if (cur->locked) { + PyErr_SetString(pysqlite_ProgrammingError, + "Recursive use of cursors not allowed."); + return 0; + } + return 1; +} + static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from."; static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) { + if (!check_cursor_locked(self)) { + return -1; + } + pysqlite_Connection* connection; if (!PyArg_ParseTuple(args, "O!", &pysqlite_ConnectionType, &connection)) @@ -376,12 +391,9 @@ return 0; } - if (cur->locked) { - PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed."); - return 0; - } - - return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); + return (pysqlite_check_thread(cur->connection) + && pysqlite_check_connection(cur->connection) + && check_cursor_locked(cur)); } PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args) @@ -790,27 +802,29 @@ if (self->statement) { rc = pysqlite_step(self->statement->st, self->connection); if (PyErr_Occurred()) { - (void)pysqlite_statement_reset(self->statement); - Py_DECREF(next_row); - return NULL; + goto error; } if (rc != SQLITE_DONE && rc != SQLITE_ROW) { - (void)pysqlite_statement_reset(self->statement); - Py_DECREF(next_row); _pysqlite_seterror(self->connection->db, NULL); - return NULL; + goto error; } if (rc == SQLITE_ROW) { + self->locked = 1; // GH-80254: Prevent recursive use of cursors. self->next_row = _pysqlite_fetch_one_row(self); + self->locked = 0; if (self->next_row == NULL) { - (void)pysqlite_statement_reset(self->statement); - return NULL; + goto error; } } } return next_row; + +error: + (void)pysqlite_statement_reset(self->statement); + Py_DECREF(next_row); + return NULL; } PyObject* pysqlite_cursor_fetchone(pysqlite_Cursor* self, PyObject* args) @@ -905,6 +919,10 @@ PyObject* pysqlite_cursor_close(pysqlite_Cursor* self, PyObject* args) { + if (!check_cursor_locked(self)) { + return NULL; + } + if (!self->connection) { PyErr_SetString(pysqlite_ProgrammingError, "Base Cursor.__init__ not called."); diff -Nru python3.7-3.7.13/Objects/longobject.c python3.7-3.7.14/Objects/longobject.c --- python3.7-3.7.13/Objects/longobject.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Objects/longobject.c 2022-09-06 07:11:33.000000000 +0000 @@ -3,6 +3,8 @@ /* XXX The functional organization of this file is terrible */ #include "Python.h" +#include "internal/pycore_long.h" +#include "internal/pystate.h" // _Py_global_config_int_max_str_digits #include "longintrepr.h" #include @@ -45,6 +47,9 @@ Py_ssize_t quick_int_allocs, quick_neg_int_allocs; #endif +#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d) for integer string conversion: value has %zd digits" +#define _MAX_STR_DIGITS_ERROR_FMT_TO_STR "Exceeds the limit (%d) for integer string conversion" + static PyObject * get_small_int(sdigit ival) { @@ -1602,6 +1607,22 @@ size_a = Py_ABS(Py_SIZE(a)); negative = Py_SIZE(a) < 0; + /* quick and dirty pre-check for overflowing the decimal digit limit, + based on the inequality 10/3 >= log2(10) + + explanation in https://github.com/python/cpython/pull/96537 + */ + if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD + / (3 * PyLong_SHIFT) + 2) { + int max_str_digits = _PyRuntime.int_max_str_digits; + if ((max_str_digits > 0) && + (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, + max_str_digits); + return -1; + } + } + /* quick and dirty upper bound for the number of digits required to express a in base _PyLong_DECIMAL_BASE: @@ -1661,6 +1682,16 @@ tenpow *= 10; strlen++; } + if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + int max_str_digits = _PyRuntime.int_max_str_digits; + Py_ssize_t strlen_nosign = strlen - negative; + if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { + Py_DECREF(scratch); + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, + max_str_digits); + return -1; + } + } if (writer) { if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) { Py_DECREF(scratch); @@ -2174,6 +2205,7 @@ start = str; if ((base & (base - 1)) == 0) { + /* binary bases are not limited by int_max_str_digits */ int res = long_from_binary_base(&str, base, &z); if (res < 0) { /* Syntax error. */ @@ -2325,6 +2357,16 @@ goto onError; } + /* Limit the size to avoid excessive computation attacks. */ + if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + int max_str_digits = _PyRuntime.int_max_str_digits; + if ((max_str_digits > 0) && (digits > max_str_digits)) { + PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, + max_str_digits, digits); + return NULL; + } + } + /* Create an int object that can contain the largest possible * integer with this base and length. Note that there's no * need to initialize z->ob_digit -- no slot is read up before @@ -4807,6 +4849,7 @@ } return PyLong_FromLong(0L); } + /* default base and limit, forward to standard implementation */ if (obase == NULL) return PyNumber_Long(x); @@ -5430,6 +5473,8 @@ static PyStructSequence_Field int_info_fields[] = { {"bits_per_digit", "size of a digit in bits"}, {"sizeof_digit", "size in bytes of the C type used to represent a digit"}, + {"default_max_str_digits", "maximum string conversion digits limitation"}, + {"str_digits_check_threshold", "minimum positive value for int_max_str_digits"}, {NULL, NULL} }; @@ -5437,7 +5482,7 @@ "sys.int_info", /* name */ int_info__doc__, /* doc */ int_info_fields, /* fields */ - 2 /* number of fields */ + 4 /* number of fields */ }; PyObject * @@ -5452,6 +5497,17 @@ PyLong_FromLong(PyLong_SHIFT)); PyStructSequence_SET_ITEM(int_info, field++, PyLong_FromLong(sizeof(digit))); + /* + * The following two fields were added after investigating uses of + * sys.int_info in the wild: Exceedingly rarely used. The ONLY use found was + * numba using sys.int_info.bits_per_digit as attribute access rather than + * sequence unpacking. Cython and sympy also refer to sys.int_info but only + * as info for debugging. No concern about adding these in a backport. + */ + PyStructSequence_SET_ITEM(int_info, field++, + PyLong_FromLong(_PY_LONG_DEFAULT_MAX_STR_DIGITS)); + PyStructSequence_SET_ITEM(int_info, field++, + PyLong_FromLong(_PY_LONG_MAX_STR_DIGITS_THRESHOLD)); if (PyErr_Occurred()) { Py_CLEAR(int_info); return NULL; @@ -5503,6 +5559,10 @@ if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) return 0; } + _PyRuntime.int_max_str_digits = _Py_global_config_int_max_str_digits; + if (_PyRuntime.int_max_str_digits == -1) { + _PyRuntime.int_max_str_digits = _PY_LONG_DEFAULT_MAX_STR_DIGITS; + } return 1; } diff -Nru python3.7-3.7.13/PCbuild/get_externals.bat python3.7-3.7.14/PCbuild/get_externals.bat --- python3.7-3.7.13/PCbuild/get_externals.bat 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/PCbuild/get_externals.bat 2022-09-06 07:11:33.000000000 +0000 @@ -55,7 +55,7 @@ if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.9.0 if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tix-8.4.3.6 set libraries=%libraries% xz-5.2.2 -set libraries=%libraries% zlib-1.2.11 +set libraries=%libraries% zlib-1.2.12 for %%e in (%libraries%) do ( if exist "%EXTERNALS_DIR%\%%e" ( diff -Nru python3.7-3.7.13/PCbuild/python.props python3.7-3.7.14/PCbuild/python.props --- python3.7-3.7.13/PCbuild/python.props 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/PCbuild/python.props 2022-09-06 07:11:33.000000000 +0000 @@ -53,7 +53,7 @@ $(ExternalsDir)openssl-bin-1.1.1n\$(ArchName)\ $(opensslOutDir)include $(ExternalsDir)\nasm-2.11.06\ - $(ExternalsDir)\zlib-1.2.11\ + $(ExternalsDir)\zlib-1.2.12\ _d diff -Nru python3.7-3.7.13/Python/ast.c python3.7-3.7.14/Python/ast.c --- python3.7-3.7.13/Python/ast.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Python/ast.c 2022-09-06 07:11:33.000000000 +0000 @@ -9,6 +9,11 @@ #include "ast.h" #include "token.h" #include "pythonrun.h" +/* A Windows header defines its own Yield macro, so we don't use the one + * from Python-ast.h and instead call _Py_Yield() directly. [ugh] */ +#undef Yield +#include "internal/pystate.h" +#undef Yield #include #include @@ -2138,8 +2143,32 @@ } case NUMBER: { PyObject *pynum = parsenumber(c, STR(ch)); - if (!pynum) + if (!pynum) { + PyThreadState *tstate = PyThreadState_GET(); + // The only way a ValueError should happen in _this_ code is via + // PyLong_FromString hitting a length limit. + if (tstate->curexc_type == PyExc_ValueError && + tstate->curexc_value != NULL) { + PyObject *type, *value, *tb; + // This acts as PyErr_Clear() as we're replacing curexc. + PyErr_Fetch(&type, &value, &tb); + Py_XDECREF(tb); + Py_DECREF(type); + PyObject *helpful_msg = PyUnicode_FromFormat( + "%S - Consider hexadecimal for huge integer literals " + "to avoid decimal conversion limits.", + value); + if (helpful_msg) { + const char* error_msg = PyUnicode_AsUTF8(helpful_msg); + if (error_msg) { + ast_error(c, ch, error_msg); + } + Py_DECREF(helpful_msg); + } + Py_DECREF(value); + } return NULL; + } if (PyArena_AddPyObject(c->c_arena, pynum) < 0) { Py_DECREF(pynum); @@ -2678,7 +2707,7 @@ } if (is_from) return YieldFrom(exp, LINENO(n), n->n_col_offset, c->c_arena); - return Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); + return _Py_Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); } case factor: if (NCH(n) == 1) { diff -Nru python3.7-3.7.13/Python/clinic/sysmodule.c.h python3.7-3.7.14/Python/clinic/sysmodule.c.h --- python3.7-3.7.13/Python/clinic/sysmodule.c.h 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Python/clinic/sysmodule.c.h 2022-09-06 07:11:33.000000000 +0000 @@ -63,4 +63,52 @@ exit: return return_value; } -/*[clinic end generated code: output=4a3ac42b97d710ff input=a9049054013a1b77]*/ + +PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, +"get_int_max_str_digits($module, /)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ + {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module); + +static PyObject * +sys_get_int_max_str_digits(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_get_int_max_str_digits_impl(module); +} + +PyDoc_STRVAR(sys_set_int_max_str_digits__doc__, +"set_int_max_str_digits($module, /, maxdigits)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_SET_INT_MAX_STR_DIGITS_METHODDEF \ + {"set_int_max_str_digits", (PyCFunction)sys_set_int_max_str_digits, METH_FASTCALL|METH_KEYWORDS, sys_set_int_max_str_digits__doc__}, + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits); + +static PyObject * +sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"maxdigits", NULL}; + static _PyArg_Parser _parser = {"i:set_int_max_str_digits", _keywords, 0}; + int maxdigits; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + &maxdigits)) { + goto exit; + } + return_value = sys_set_int_max_str_digits_impl(module, maxdigits); + +exit: + return return_value; +} +/*[clinic end generated code: output=c566fcdbb8f6ae2c input=a9049054013a1b77]*/ diff -Nru python3.7-3.7.13/Python/hamt.c python3.7-3.7.14/Python/hamt.c --- python3.7-3.7.13/Python/hamt.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Python/hamt.c 2022-09-06 07:11:33.000000000 +0000 @@ -406,14 +406,22 @@ return -1; } - /* While it's suboptimal to reduce Python's 64 bit hash to + /* While it's somewhat suboptimal to reduce Python's 64 bit hash to 32 bits via XOR, it seems that the resulting hash function is good enough (this is also how Long type is hashed in Java.) Storing 10, 100, 1000 Python strings results in a relatively shallow and uniform tree structure. - Please don't change this hashing algorithm, as there are many - tests that test some exact tree shape to cover all code paths. + Also it's worth noting that it would be possible to adapt the tree + structure to 64 bit hashes, but that would increase memory pressure + and provide little to no performance benefits for collections with + fewer than billions of key/value pairs. + + Important: do not change this hash reducing function. There are many + tests that need an exact tree shape to cover all code paths and + we do that by specifying concrete values for test data's `__hash__`. + If this function is changed most of the regression tests would + become useless. */ int32_t xored = (int32_t)(hash & 0xffffffffl) ^ (int32_t)(hash >> 32); return xored == -1 ? -2 : xored; diff -Nru python3.7-3.7.13/Python/pylifecycle.c python3.7-3.7.14/Python/pylifecycle.c --- python3.7-3.7.13/Python/pylifecycle.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Python/pylifecycle.c 2022-09-06 07:11:33.000000000 +0000 @@ -6,6 +6,7 @@ #undef Yield /* undefine macro conflicting with winbase.h */ #include "internal/context.h" #include "internal/hamt.h" +#include "internal/pycore_long.h" #include "internal/pystate.h" #include "grammar.h" #include "node.h" @@ -130,6 +131,9 @@ int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ #endif +/* Unusual name compared to the above for backporting from 3.12 reasons. */ +int _Py_global_config_int_max_str_digits = -1; /* -X int_max_str_digits or PYTHONINTMAXSTRDIGITS */ + /* Hack to force loading of object files */ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ PyOS_mystrnicmp; /* Python/pystrcmp.o */ diff -Nru python3.7-3.7.13/Python/sysmodule.c python3.7-3.7.14/Python/sysmodule.c --- python3.7-3.7.13/Python/sysmodule.c 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/Python/sysmodule.c 2022-09-06 07:11:33.000000000 +0000 @@ -15,6 +15,7 @@ */ #include "Python.h" +#include "internal/pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD #include "internal/pystate.h" #include "code.h" #include "frameobject.h" @@ -1218,6 +1219,43 @@ } #endif /* USE_MALLOPT */ + +/*[clinic input] +sys.get_int_max_str_digits + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module) +/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +{ + return PyLong_FromSsize_t(_PyRuntime.int_max_str_digits); +} + +/*[clinic input] +sys.set_int_max_str_digits + + maxdigits: int + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) +/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ +{ + if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { + _PyRuntime.int_max_str_digits = maxdigits; + Py_RETURN_NONE; + } else { + PyErr_Format( + PyExc_ValueError, "maxdigits must be 0 or larger than %d", + _PY_LONG_MAX_STR_DIGITS_THRESHOLD); + return NULL; + } +} + size_t _PySys_GetSizeOf(PyObject *o) { @@ -1605,6 +1643,8 @@ {"getandroidapilevel", (PyCFunction)sys_getandroidapilevel, METH_NOARGS, getandroidapilevel_doc}, #endif + SYS_GET_INT_MAX_STR_DIGITS_METHODDEF + SYS_SET_INT_MAX_STR_DIGITS_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -2051,6 +2091,7 @@ {"isolated", "-I"}, {"dev_mode", "-X dev"}, {"utf8_mode", "-X utf8"}, + {"int_max_str_digits", "-X int_max_str_digits"}, {0} }; @@ -2058,7 +2099,7 @@ "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 15 + 16 }; static PyObject* @@ -2092,6 +2133,7 @@ SetFlag(Py_IsolatedFlag); PyStructSequence_SET_ITEM(seq, pos++, PyBool_FromLong(core_config->dev_mode)); SetFlag(Py_UTF8Mode); + SetFlag(_Py_global_config_int_max_str_digits); #undef SetFlag if (PyErr_Occurred()) { diff -Nru python3.7-3.7.13/README.rst python3.7-3.7.14/README.rst --- python3.7-3.7.13/README.rst 2022-03-16 13:27:21.000000000 +0000 +++ python3.7-3.7.14/README.rst 2022-09-06 07:11:33.000000000 +0000 @@ -1,4 +1,4 @@ -This is Python version 3.7.13 +This is Python version 3.7.14 ============================= .. image:: https://travis-ci.org/python/cpython.svg?branch=3.7 @@ -24,7 +24,7 @@ - Website: https://www.python.org - Source code: https://github.com/python/cpython -- Issue tracker: https://bugs.python.org +- Issue tracker: https://github.com/python/cpython/issues/ - Documentation: https://docs.python.org - Developer's Guide: https://devguide.python.org/ @@ -185,7 +185,7 @@ make test TESTOPTS="-v test_os test_gdb" If the failure persists and appears to be a problem with Python rather than -your environment, you can `file a bug report `_ and +your environment, you can `file a bug report `_ and include relevant output from that command to show the issue. See `Running & Writing Tests `_ @@ -214,7 +214,7 @@ ------------------------------ Bug reports are welcome! You can use the `issue tracker -`_ to report bugs, and/or submit pull requests `on +`_ to report bugs, and/or submit pull requests `on GitHub `_. You can also follow development discussion on the `python-dev mailing list diff -Nru python3.7-3.7.13/Tools/scripts/verify_ensurepip_wheels.py python3.7-3.7.14/Tools/scripts/verify_ensurepip_wheels.py --- python3.7-3.7.13/Tools/scripts/verify_ensurepip_wheels.py 1970-01-01 00:00:00.000000000 +0000 +++ python3.7-3.7.14/Tools/scripts/verify_ensurepip_wheels.py 2022-09-06 07:11:33.000000000 +0000 @@ -0,0 +1,98 @@ +#! /usr/bin/env python3 + +""" +Compare checksums for wheels in :mod:`ensurepip` against the Cheeseshop. + +When GitHub Actions executes the script, output is formatted accordingly. +https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message +""" + +import hashlib +import json +import os +import re +from pathlib import Path +from urllib.request import urlopen + +PACKAGE_NAMES = ("pip", "setuptools") +ENSURE_PIP_ROOT = Path(__file__).parent.parent.parent / "Lib/ensurepip" +WHEEL_DIR = ENSURE_PIP_ROOT / "_bundled" +ENSURE_PIP_INIT_PY_TEXT = (ENSURE_PIP_ROOT / "__init__.py").read_text(encoding="utf-8") +GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" + + +def print_notice(file_path: str, message: str) -> None: + if GITHUB_ACTIONS: + message = f"::notice file={file_path}::{message}" + print(message, end="\n\n") + + +def print_error(file_path: str, message: str) -> None: + if GITHUB_ACTIONS: + message = f"::error file={file_path}::{message}" + print(message, end="\n\n") + + +def verify_wheel(package_name: str) -> bool: + # Find the package on disk + package_path = next(WHEEL_DIR.glob(f"{package_name}*.whl"), None) + if not package_path: + print_error("", f"Could not find a {package_name} wheel on disk.") + return False + + print(f"Verifying checksum for {package_path}.") + + # Find the version of the package used by ensurepip + package_version_match = re.search( + f'_{package_name.upper()}_VERSION = "([^"]+)', ENSURE_PIP_INIT_PY_TEXT + ) + if not package_version_match: + print_error( + package_path, + f"No {package_name} version found in Lib/ensurepip/__init__.py.", + ) + return False + package_version = package_version_match[1] + + # Get the SHA 256 digest from the Cheeseshop + try: + raw_text = urlopen(f"https://pypi.org/pypi/{package_name}/json").read() + except (OSError, ValueError): + print_error(package_path, f"Could not fetch JSON metadata for {package_name}.") + return False + + release_files = json.loads(raw_text)["releases"][package_version] + for release_info in release_files: + if package_path.name != release_info["filename"]: + continue + expected_digest = release_info["digests"].get("sha256", "") + break + else: + print_error(package_path, f"No digest for {package_name} found from PyPI.") + return False + + # Compute the SHA 256 digest of the wheel on disk + actual_digest = hashlib.sha256(package_path.read_bytes()).hexdigest() + + print(f"Expected digest: {expected_digest}") + print(f"Actual digest: {actual_digest}") + + if actual_digest != expected_digest: + print_error( + package_path, f"Failed to verify the checksum of the {package_name} wheel." + ) + return False + + print_notice( + package_path, + f"Successfully verified the checksum of the {package_name} wheel.", + ) + return True + + +if __name__ == "__main__": + exit_status = 0 + for package_name in PACKAGE_NAMES: + if not verify_wheel(package_name): + exit_status = 1 + raise SystemExit(exit_status)