Wed Oct 13 21:04:01 2021 UTC ()
Pullup ticket #6513 - requested by gutteridge
lang/python27: security fix

Revisions pulled up:
- lang/python27/Makefile                                        1.94
- lang/python27/distinfo                                        1.85
- lang/python27/patches/patch-Doc_library_cgi.rst               1.1
- lang/python27/patches/patch-Doc_library_urlparse.rst          1.1
- lang/python27/patches/patch-Lib_cgi.py                        1.1
- lang/python27/patches/patch-Lib_ctypes_test_test__parameters.py 1.1
- lang/python27/patches/patch-Lib_httplib.py                    1.4
- lang/python27/patches/patch-Lib_test_multibytecodec__support.py 1.1
- lang/python27/patches/patch-Lib_test_test__cgi.py             1.1
- lang/python27/patches/patch-Lib_test_test__httplib.py         1.4
- lang/python27/patches/patch-Lib_test_test__urlparse.py        1.1
- lang/python27/patches/patch-Lib_urllib2.py                    1.3
- lang/python27/patches/patch-Lib_urlparse.py                   1.1
- lang/python27/patches/patch-Modules___ctypes_callproc.c       1.2

---
   Module Name:    pkgsrc
   Committed By:   gutteridge
   Date:           Sun Oct 10 03:00:59 UTC 2021

   Modified Files:
           pkgsrc/lang/python27: Makefile distinfo
           pkgsrc/lang/python27/patches: patch-Lib_httplib.py
               patch-Lib_test_test__httplib.py patch-Lib_urllib2.py
               patch-Modules___ctypes_callproc.c
   Added Files:
           pkgsrc/lang/python27/patches: patch-Doc_library_cgi.rst
               patch-Doc_library_urlparse.rst patch-Lib_cgi.py
               patch-Lib_ctypes_test_test__parameters.py
               patch-Lib_test_multibytecodec__support.py
               patch-Lib_test_test__cgi.py patch-Lib_test_test__urlparse.py
               patch-Lib_urlparse.py

   Log Message:
   python27: fix various security issues

   Addresses CVE-2020-27619, CVE-2021-3177, CVE-2021-3733, CVE-2021-3737
   and CVE-2021-23336. Patches mostly sourced via Fedora.


(tm)
diff -r1.93 -r1.93.8.1 pkgsrc/lang/python27/Makefile
diff -r1.82 -r1.82.2.1 pkgsrc/lang/python27/distinfo
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Doc_library_cgi.rst
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Doc_library_urlparse.rst
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_cgi.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_ctypes_test_test__parameters.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_multibytecodec__support.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_test__cgi.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_test_test__urlparse.py
diff -r0 -r1.1.2.2 pkgsrc/lang/python27/patches/patch-Lib_urlparse.py
diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_httplib.py
diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py
diff -r1.2 -r1.2.10.1 pkgsrc/lang/python27/patches/patch-Lib_urllib2.py
diff -r1.1 -r1.1.2.1 pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c

cvs diff -r1.93 -r1.93.8.1 pkgsrc/lang/python27/Makefile (expand / switch to unified diff)

--- pkgsrc/lang/python27/Makefile 2020/12/07 13:14:38 1.93
+++ pkgsrc/lang/python27/Makefile 2021/10/13 21:04:00 1.93.8.1
@@ -1,19 +1,19 @@ @@ -1,19 +1,19 @@
1# $NetBSD: Makefile,v 1.93 2020/12/07 13:14:38 nia Exp $ 1# $NetBSD: Makefile,v 1.93.8.1 2021/10/13 21:04:00 tm Exp $
2 2
3.include "dist.mk" 3.include "dist.mk"
4 4
5PKGNAME= python27-${PY_DISTVERSION} 5PKGNAME= python27-${PY_DISTVERSION}
6PKGREVISION= 3 6PKGREVISION= 4
7CATEGORIES= lang python 7CATEGORIES= lang python
8 8
9MAINTAINER= pkgsrc-users@NetBSD.org 9MAINTAINER= pkgsrc-users@NetBSD.org
10HOMEPAGE= https://www.python.org/ 10HOMEPAGE= https://www.python.org/
11COMMENT= Interpreted, interactive, object-oriented programming language 11COMMENT= Interpreted, interactive, object-oriented programming language
12LICENSE= python-software-foundation 12LICENSE= python-software-foundation
13 13
14DEPENDS= mozilla-rootcerts>=1.0.20150804nb1:../../security/mozilla-rootcerts 14DEPENDS= mozilla-rootcerts>=1.0.20150804nb1:../../security/mozilla-rootcerts
15 15
16CONFLICTS+= python-[0-9]* 16CONFLICTS+= python-[0-9]*
17 17
18USE_LANGUAGES= c c++ 18USE_LANGUAGES= c c++
19GNU_CONFIGURE= yes 19GNU_CONFIGURE= yes

cvs diff -r1.82 -r1.82.2.1 pkgsrc/lang/python27/distinfo (expand / switch to unified diff)

--- pkgsrc/lang/python27/distinfo 2021/06/23 18:30:24 1.82
+++ pkgsrc/lang/python27/distinfo 2021/10/13 21:04:00 1.82.2.1
@@ -1,47 +1,55 @@ @@ -1,47 +1,55 @@
1$NetBSD: distinfo,v 1.82 2021/06/23 18:30:24 schmonz Exp $ 1$NetBSD: distinfo,v 1.82.2.1 2021/10/13 21:04:00 tm Exp $
2 2
3SHA1 (Python-2.7.18.tar.xz) = 678d4cf483a1c92efd347ee8e1e79326dc82810b 3SHA1 (Python-2.7.18.tar.xz) = 678d4cf483a1c92efd347ee8e1e79326dc82810b
4RMD160 (Python-2.7.18.tar.xz) = 40a514bb05c9e631454ea8466e28f5bb229428ad 4RMD160 (Python-2.7.18.tar.xz) = 40a514bb05c9e631454ea8466e28f5bb229428ad
5SHA512 (Python-2.7.18.tar.xz) = a7bb62b51f48ff0b6df0b18f5b0312a523e3110f49c3237936bfe56ed0e26838c0274ff5401bda6fc21bf24337477ccac49e8026c5d651e4b4cafb5eb5086f6c 5SHA512 (Python-2.7.18.tar.xz) = a7bb62b51f48ff0b6df0b18f5b0312a523e3110f49c3237936bfe56ed0e26838c0274ff5401bda6fc21bf24337477ccac49e8026c5d651e4b4cafb5eb5086f6c
6Size (Python-2.7.18.tar.xz) = 12854736 bytes 6Size (Python-2.7.18.tar.xz) = 12854736 bytes
 7SHA1 (patch-Doc_library_cgi.rst) = ed9ac101b0857dc573e9a648694d1ee5fabe61fb
 8SHA1 (patch-Doc_library_urlparse.rst) = f9714b945a2bacb4ec5360c151a42192e00f08ad
7SHA1 (patch-Include_pyerrors.h) = 0d2cd52d18cc719b895fa32ed7e11c6cb15bae54 9SHA1 (patch-Include_pyerrors.h) = 0d2cd52d18cc719b895fa32ed7e11c6cb15bae54
8SHA1 (patch-Include_pyport.h) = f3e4ddbc954425a65301465410911222ca471320 10SHA1 (patch-Include_pyport.h) = f3e4ddbc954425a65301465410911222ca471320
9SHA1 (patch-Lib___osx__support.py) = 4389472565616b3875c699f6e3e74850d5fde712 11SHA1 (patch-Lib___osx__support.py) = 4389472565616b3875c699f6e3e74850d5fde712
 12SHA1 (patch-Lib_cgi.py) = 9653904acfd2dbe03655a7cfa5688c450556671b
10SHA1 (patch-Lib_ctypes_____init____.py) = 31dd0546bbe29ad1b1d481edc525ba43479c06da 13SHA1 (patch-Lib_ctypes_____init____.py) = 31dd0546bbe29ad1b1d481edc525ba43479c06da
11SHA1 (patch-Lib_ctypes_macholib_dyld.py) = 9b7e972d4c71311742ca8b3501382182a4c9e2fe 14SHA1 (patch-Lib_ctypes_macholib_dyld.py) = 9b7e972d4c71311742ca8b3501382182a4c9e2fe
12SHA1 (patch-Lib_ctypes_test_test__macholib.py) = 4479d315cd037f4c9138e8f5baa8eb1685932baa 15SHA1 (patch-Lib_ctypes_test_test__macholib.py) = 4479d315cd037f4c9138e8f5baa8eb1685932baa
 16SHA1 (patch-Lib_ctypes_test_test__parameters.py) = 8f8bb50515bc7e89ab59363b10af4d5391957eb7
13SHA1 (patch-Lib_ctypes_util.py) = 6fa516c7b43f08992427a0afcbe80c17bcc070f1 17SHA1 (patch-Lib_ctypes_util.py) = 6fa516c7b43f08992427a0afcbe80c17bcc070f1
14SHA1 (patch-Lib_distutils_command_build__ext.py) = ea4feba4e93dbcff07050c82a00d591bb650e934 18SHA1 (patch-Lib_distutils_command_build__ext.py) = ea4feba4e93dbcff07050c82a00d591bb650e934
15SHA1 (patch-Lib_distutils_command_install.py) = e6aef090b444b455fe351308d251e670329b7dc3 19SHA1 (patch-Lib_distutils_command_install.py) = e6aef090b444b455fe351308d251e670329b7dc3
16SHA1 (patch-Lib_distutils_command_install__egg__info.py) = ec7f9e0cd04489b1f6497c44d75bff6864ad1047 20SHA1 (patch-Lib_distutils_command_install__egg__info.py) = ec7f9e0cd04489b1f6497c44d75bff6864ad1047
17SHA1 (patch-Lib_distutils_tests_test__build__ext.py) = 6b3c8c8d1d351836b239c049d34d132953bd4786 21SHA1 (patch-Lib_distutils_tests_test__build__ext.py) = 6b3c8c8d1d351836b239c049d34d132953bd4786
18SHA1 (patch-Lib_distutils_unixccompiler.py) = db16c9aca2f29730945f28247b88b18828739bbb 22SHA1 (patch-Lib_distutils_unixccompiler.py) = db16c9aca2f29730945f28247b88b18828739bbb
19SHA1 (patch-Lib_distutils_util.py) = 5bcfad96f8e490351160f1a7c1f4ece7706a33fa 23SHA1 (patch-Lib_distutils_util.py) = 5bcfad96f8e490351160f1a7c1f4ece7706a33fa
20SHA1 (patch-Lib_httplib.py) = 375d80eb79209f53046c62db128d8d3f64d9e765 24SHA1 (patch-Lib_httplib.py) = b8eeaa203e2a86ece94148d192b2a7e0c078602a
21SHA1 (patch-Lib_lib2to3_pgen2_driver.py) = 5d6dab14197f27363394ff1aeee22a8ced8026d2 25SHA1 (patch-Lib_lib2to3_pgen2_driver.py) = 5d6dab14197f27363394ff1aeee22a8ced8026d2
22SHA1 (patch-Lib_multiprocessing_process.py) = 15699bd8ec822bf54a0631102e00e0a34f882803 26SHA1 (patch-Lib_multiprocessing_process.py) = 15699bd8ec822bf54a0631102e00e0a34f882803
23SHA1 (patch-Lib_plistlib.py) = 96ae702995d434e2d7ec0ac62e37427a90b61d13 27SHA1 (patch-Lib_plistlib.py) = 96ae702995d434e2d7ec0ac62e37427a90b61d13
24SHA1 (patch-Lib_sysconfig.py) = 8a7a0e5cbfec279a05945dffafea1b1131a76f0e 28SHA1 (patch-Lib_sysconfig.py) = 8a7a0e5cbfec279a05945dffafea1b1131a76f0e
25SHA1 (patch-Lib_tarfile.py) = df00aa1941367c42dcbbed4b6658b724a22ddcde 29SHA1 (patch-Lib_tarfile.py) = df00aa1941367c42dcbbed4b6658b724a22ddcde
26SHA1 (patch-Lib_test_test__httplib.py) = 9d37263e36110838e0b5f413ff4747deb3966dfe 30SHA1 (patch-Lib_test_multibytecodec__support.py) = a18c40e8009f1a8f63e15196d3e751d7dccf8367
 31SHA1 (patch-Lib_test_test__cgi.py) = 724355e8d2195f8a4b76d7ea61133e9b14fa3a68
 32SHA1 (patch-Lib_test_test__httplib.py) = f7cfa5501a63eaca539bfa53d38cf931f3a6c3ac
27SHA1 (patch-Lib_test_test__platform.py) = 3a3b8c05f9bf9adf4862b1022ce864127d36b8b0 33SHA1 (patch-Lib_test_test__platform.py) = 3a3b8c05f9bf9adf4862b1022ce864127d36b8b0
28SHA1 (patch-Lib_test_test__unicode.py) = 1bd182bdbd880d0a847f9d8b69277a607f9f0526 34SHA1 (patch-Lib_test_test__unicode.py) = 1bd182bdbd880d0a847f9d8b69277a607f9f0526
29SHA1 (patch-Lib_test_test__urllib2.py) = 89baa57daf2f3282e4fc5009915dbc4910b96ef1 35SHA1 (patch-Lib_test_test__urllib2.py) = 89baa57daf2f3282e4fc5009915dbc4910b96ef1
30SHA1 (patch-Lib_urllib2.py) = 33a85593da702447fa3ea74b4e3d36d0016f70b5 36SHA1 (patch-Lib_test_test__urlparse.py) = 257cb3bf7a0e9b5e0dcb204f675959b10953ba7b
 37SHA1 (patch-Lib_urllib2.py) = 0cc0dc811bb9544496962e08b040b5c96fb9073c
 38SHA1 (patch-Lib_urlparse.py) = ec45dd48966eb806a5c0e79af6a7369fb45b9859
31SHA1 (patch-Mac_Tools_pythonw.c) = 2b9a60d4b349c240471fd305be69c28e0f654cdc 39SHA1 (patch-Mac_Tools_pythonw.c) = 2b9a60d4b349c240471fd305be69c28e0f654cdc
32SHA1 (patch-Makefile.pre.in) = ceaf34237588b527478ce1f9163c9168382fa201 40SHA1 (patch-Makefile.pre.in) = ceaf34237588b527478ce1f9163c9168382fa201
33SHA1 (patch-Modules___ctypes_callbacks.c) = 8c335edfc9d2ef47988c5bdf1c3dd8473757637b 41SHA1 (patch-Modules___ctypes_callbacks.c) = 8c335edfc9d2ef47988c5bdf1c3dd8473757637b
34SHA1 (patch-Modules___ctypes_callproc.c) = adac5eb047eb58c14003ea9237d5d34e8b327b2f 42SHA1 (patch-Modules___ctypes_callproc.c) = 7b669f9c081bbc2b7fce2c827703f52b7389d592
35SHA1 (patch-Modules___ctypes_ctypes.h) = 07e9d5ecf8309a3ca4bf8382411d56dda08d7b27 43SHA1 (patch-Modules___ctypes_ctypes.h) = 07e9d5ecf8309a3ca4bf8382411d56dda08d7b27
36SHA1 (patch-Modules___ctypes_malloc__closure.c) = 25d470cc66d218446227c7c1bd7ade409c53b8d0 44SHA1 (patch-Modules___ctypes_malloc__closure.c) = 25d470cc66d218446227c7c1bd7ade409c53b8d0
37SHA1 (patch-Modules___multiprocessing_multiprocessing.h) = 7ca8fe22ba4bdcde6d39dd50fe2e86c25994c146 45SHA1 (patch-Modules___multiprocessing_multiprocessing.h) = 7ca8fe22ba4bdcde6d39dd50fe2e86c25994c146
38SHA1 (patch-Modules___multiprocessing_semaphore.c) = 03b9c33ef38da383d5f7c2c84c17fe38cdd2911e 46SHA1 (patch-Modules___multiprocessing_semaphore.c) = 03b9c33ef38da383d5f7c2c84c17fe38cdd2911e
39SHA1 (patch-Modules__ssl.c) = 6e68f88ad205106691900f091a897ffe0a4c363c 47SHA1 (patch-Modules__ssl.c) = 6e68f88ad205106691900f091a897ffe0a4c363c
40SHA1 (patch-Modules_getaddrinfo.c) = aa699d257f1bc98b9a3183a21324053e134409d1 48SHA1 (patch-Modules_getaddrinfo.c) = aa699d257f1bc98b9a3183a21324053e134409d1
41SHA1 (patch-Modules_getpath.c) = 4e6445be9da49626800c03eaaab28fb3826be9f9 49SHA1 (patch-Modules_getpath.c) = 4e6445be9da49626800c03eaaab28fb3826be9f9
42SHA1 (patch-Modules_makesetup) = 9aad78714c4fe1a21cf66a6627d97d164ecea196 50SHA1 (patch-Modules_makesetup) = 9aad78714c4fe1a21cf66a6627d97d164ecea196
43SHA1 (patch-Modules_nismodule.c) = 129ef7b32779944c2f1827c6b078a3aafab60729 51SHA1 (patch-Modules_nismodule.c) = 129ef7b32779944c2f1827c6b078a3aafab60729
44SHA1 (patch-Modules_posixmodule.c) = 5105d380cd49bf49b8adbd9aa5ffb245195728ed 52SHA1 (patch-Modules_posixmodule.c) = 5105d380cd49bf49b8adbd9aa5ffb245195728ed
45SHA1 (patch-Modules_selectmodule.c) = 01e113b0bd251978b555caaaa60b79c372edebce 53SHA1 (patch-Modules_selectmodule.c) = 01e113b0bd251978b555caaaa60b79c372edebce
46SHA1 (patch-Modules_socketmodule.c) = 16848d90947b3de1f921a0813fa5c317f76961d4 54SHA1 (patch-Modules_socketmodule.c) = 16848d90947b3de1f921a0813fa5c317f76961d4
47SHA1 (patch-Modules_sunaudiodev.c) = d836d77854a2b3d79fa34a06a8e2493bf0a503e6 55SHA1 (patch-Modules_sunaudiodev.c) = d836d77854a2b3d79fa34a06a8e2493bf0a503e6

File Added: pkgsrc/lang/python27/patches/patch-Doc_library_cgi.rst
$NetBSD: patch-Doc_library_cgi.rst,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Doc/library/cgi.rst.orig	2020-04-19 21:13:39.000000000 +0000
+++ Doc/library/cgi.rst
@@ -285,10 +285,10 @@ These are useful if you want more contro
 algorithms implemented in this module in other circumstances.
 
 
-.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing]]])
+.. function:: parse(fp[, environ[, keep_blank_values[, strict_parsing[, separator]]]])
 
    Parse a query in the environment or from a file (the file defaults to
-   ``sys.stdin`` and environment defaults to ``os.environ``).  The *keep_blank_values* and *strict_parsing* parameters are
+   ``sys.stdin`` and environment defaults to ``os.environ``).  The *keep_blank_values*, *strict_parsing* and *separator* parameters are
    passed to :func:`urlparse.parse_qs` unchanged.
 
 
@@ -316,7 +316,6 @@ algorithms implemented in this module in
    Note that this does not parse nested multipart parts --- use
    :class:`FieldStorage` for that.
 
-
 .. function:: parse_header(string)
 
    Parse a MIME header (such as :mailheader:`Content-Type`) into a main value and a

File Added: pkgsrc/lang/python27/patches/patch-Doc_library_urlparse.rst
$NetBSD: patch-Doc_library_urlparse.rst,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Doc/library/urlparse.rst.orig	2020-04-19 21:13:39.000000000 +0000
+++ Doc/library/urlparse.rst
@@ -136,7 +136,7 @@ The :mod:`urlparse` module defines the f
       now raise :exc:`ValueError`.
 
 
-.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]])
+.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields[, separator]]]])
 
    Parse a query string given as a string argument (data of type
    :mimetype:`application/x-www-form-urlencoded`).  Data are returned as a
@@ -157,6 +157,15 @@ The :mod:`urlparse` module defines the f
    read. If set, then throws a :exc:`ValueError` if there are more than
    *max_num_fields* fields read.
 
+   The optional argument *separator* is the symbol to use for separating the
+   query arguments. It is recommended to set it to ``'&'`` or ``';'``.
+   It defaults to ``'&'``; a warning is raised if this default is used.
+   This default may be changed with the following environment variable settings:
+
+   - ``PYTHON_URLLIB_QS_SEPARATOR='&'``: use only ``&`` as separator, without warning (as in Python 3.6.13+ or 3.10)
+   - ``PYTHON_URLLIB_QS_SEPARATOR=';'``: use only ``;`` as separator
+   - ``PYTHON_URLLIB_QS_SEPARATOR=legacy``: use both ``&`` and ``;`` (as in previous versions of Python)
+
    Use the :func:`urllib.urlencode` function to convert such dictionaries into
    query strings.
 
@@ -186,6 +195,9 @@ The :mod:`urlparse` module defines the f
    read. If set, then throws a :exc:`ValueError` if there are more than
    *max_num_fields* fields read.
 
+   The optional argument *separator* is the symbol to use for separating the
+   query arguments. It works as in :py:func:`parse_qs`.
+
    Use the :func:`urllib.urlencode` function to convert such lists of pairs into
    query strings.
 
@@ -195,6 +207,7 @@ The :mod:`urlparse` module defines the f
    .. versionchanged:: 2.7.16
       Added *max_num_fields* parameter.
 
+
 .. function:: urlunparse(parts)
 
    Construct a URL from a tuple as returned by ``urlparse()``. The *parts* argument

File Added: pkgsrc/lang/python27/patches/patch-Lib_cgi.py
$NetBSD: patch-Lib_cgi.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Lib/cgi.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/cgi.py
@@ -121,7 +121,8 @@ log = initlog           # The current lo
 # 0 ==> unlimited input
 maxlen = 0
 
-def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
+def parse(fp=None, environ=os.environ, keep_blank_values=0,
+          strict_parsing=0, separator=None):
     """Parse a query in the environment or from a file (default stdin)
 
         Arguments, all optional:
@@ -140,6 +141,8 @@ def parse(fp=None, environ=os.environ, k
         strict_parsing: flag indicating what to do with parsing errors.
             If false (the default), errors are silently ignored.
             If true, errors raise a ValueError exception.
+
+        separator: str. The symbol to use for separating the query arguments.
     """
     if fp is None:
         fp = sys.stdin
@@ -171,25 +174,26 @@ def parse(fp=None, environ=os.environ, k
         else:
             qs = ""
         environ['QUERY_STRING'] = qs    # XXX Shouldn't, really
-    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
+    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing, separator=separator)
 
 
 # parse query string function called from urlparse,
 # this is done in order to maintain backward compatibility.
 
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, separator=None):
     """Parse a query given as a string argument."""
     warn("cgi.parse_qs is deprecated, use urlparse.parse_qs instead",
          PendingDeprecationWarning, 2)
-    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing)
+    return urlparse.parse_qs(qs, keep_blank_values, strict_parsing,
+                             separator=separator)
 
 
-def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None, separator=None):
     """Parse a query given as a string argument."""
     warn("cgi.parse_qsl is deprecated, use urlparse.parse_qsl instead",
          PendingDeprecationWarning, 2)
     return urlparse.parse_qsl(qs, keep_blank_values, strict_parsing,
-                              max_num_fields)
+                              max_num_fields, separator=separator)
 
 def parse_multipart(fp, pdict):
     """Parse multipart input.
@@ -288,7 +292,6 @@ def parse_multipart(fp, pdict):
 
     return partdict
 
-
 def _parseparam(s):
     while s[:1] == ';':
         s = s[1:]
@@ -395,7 +398,7 @@ class FieldStorage:
 
     def __init__(self, fp=None, headers=None, outerboundary="",
                  environ=os.environ, keep_blank_values=0, strict_parsing=0,
-                 max_num_fields=None):
+                 max_num_fields=None, separator=None):
         """Constructor.  Read multipart/* until last part.
 
         Arguments, all optional:
@@ -430,6 +433,7 @@ class FieldStorage:
         self.keep_blank_values = keep_blank_values
         self.strict_parsing = strict_parsing
         self.max_num_fields = max_num_fields
+        self.separator = separator
         if 'REQUEST_METHOD' in environ:
             method = environ['REQUEST_METHOD'].upper()
         self.qs_on_post = None
@@ -613,7 +617,8 @@ class FieldStorage:
         if self.qs_on_post:
             qs += '&' + self.qs_on_post
         query = urlparse.parse_qsl(qs, self.keep_blank_values,
-                                   self.strict_parsing, self.max_num_fields)
+                                   self.strict_parsing, self.max_num_fields,
+                                   self.separator)
         self.list = [MiniFieldStorage(key, value) for key, value in query]
         self.skip_lines()
 
@@ -629,7 +634,8 @@ class FieldStorage:
             query = urlparse.parse_qsl(self.qs_on_post,
                                        self.keep_blank_values,
                                        self.strict_parsing,
-                                       self.max_num_fields)
+                                       self.max_num_fields,
+                                       self.separator)
             self.list.extend(MiniFieldStorage(key, value)
                              for key, value in query)
             FieldStorageClass = None
@@ -649,7 +655,8 @@ class FieldStorage:
             headers = rfc822.Message(self.fp)
             part = klass(self.fp, headers, ib,
                          environ, keep_blank_values, strict_parsing,
-                         max_num_fields)
+                         max_num_fields,
+                         separator=self.separator)
 
             if max_num_fields is not None:
                 max_num_fields -= 1
@@ -817,10 +824,11 @@ class FormContentDict(UserDict.UserDict)
     form.dict == {key: [val, val, ...], ...}
 
     """
-    def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0):
+    def __init__(self, environ=os.environ, keep_blank_values=0, strict_parsing=0, separator=None):
         self.dict = self.data = parse(environ=environ,
                                       keep_blank_values=keep_blank_values,
-                                      strict_parsing=strict_parsing)
+                                      strict_parsing=strict_parsing,
+                                      separator=separator)
         self.query_string = environ['QUERY_STRING']
 
 

File Added: pkgsrc/lang/python27/patches/patch-Lib_ctypes_test_test__parameters.py
$NetBSD: patch-Lib_ctypes_test_test__parameters.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00357-CVE-2021-3177.patch

--- Lib/ctypes/test/test_parameters.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/ctypes/test/test_parameters.py
@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestC
         with self.assertRaises(ZeroDivisionError):
             WorseStruct().__setstate__({}, b'foo')
 
+    def test_parameter_repr(self):
+        from ctypes import (
+            c_bool,
+            c_char,
+            c_wchar,
+            c_byte,
+            c_ubyte,
+            c_short,
+            c_ushort,
+            c_int,
+            c_uint,
+            c_long,
+            c_ulong,
+            c_longlong,
+            c_ulonglong,
+            c_float,
+            c_double,
+            c_longdouble,
+            c_char_p,
+            c_wchar_p,
+            c_void_p,
+        )
+        self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
+        self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' ('a')>")
+        self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
+        self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
+        self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
+        self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
+        self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
+        self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+        self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+        self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
+        self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
+        self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
+        self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
+        self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
+        self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
+        self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
+        self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
+        self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
+        self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
+        self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
+
 ################################################################
 
 if __name__ == '__main__':

File Added: pkgsrc/lang/python27/patches/patch-Lib_test_multibytecodec__support.py
$NetBSD: patch-Lib_test_multibytecodec__support.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2020-27619: No longer call eval() on content received via HTTP in the CJK codec tests
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00355-CVE-2020-27619.patch

--- Lib/test/multibytecodec_support.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/test/multibytecodec_support.py
@@ -279,30 +279,22 @@ class TestBase_Mapping(unittest.TestCase
             self._test_mapping_file_plain()
 
     def _test_mapping_file_plain(self):
-        _unichr = lambda c: eval("u'\\U%08x'" % int(c, 16))
-        unichrs = lambda s: u''.join(_unichr(c) for c in s.split('+'))
+        def unichrs(s):
+            return ''.join(unichr(int(x, 16)) for x in s.split('+'))
         urt_wa = {}
 
         with self.open_mapping_file() as f:
             for line in f:
                 if not line:
                     break
-                data = line.split('#')[0].strip().split()
+                data = line.split('#')[0].split()
                 if len(data) != 2:
                     continue
 
-                csetval = eval(data[0])
-                if csetval <= 0x7F:
-                    csetch = chr(csetval & 0xff)
-                elif csetval >= 0x1000000:
-                    csetch = chr(csetval >> 24) + chr((csetval >> 16) & 0xff) + \
-                             chr((csetval >> 8) & 0xff) + chr(csetval & 0xff)
-                elif csetval >= 0x10000:
-                    csetch = chr(csetval >> 16) + \
-                             chr((csetval >> 8) & 0xff) + chr(csetval & 0xff)
-                elif csetval >= 0x100:
-                    csetch = chr(csetval >> 8) + chr(csetval & 0xff)
-                else:
+                if data[0][:2] != '0x':
+                    self.fail("Invalid line: {!r}".format(line))
+                csetch = bytes.fromhex(data[0][2:])
+                if len(csetch) == 1 and 0x80 <= csetch[0]:
                     continue
 
                 unich = unichrs(data[1])

File Added: pkgsrc/lang/python27/patches/patch-Lib_test_test__cgi.py
$NetBSD: patch-Lib_test_test__cgi.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Lib/test/test_cgi.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/test/test_cgi.py
@@ -61,12 +61,9 @@ parse_strict_test_cases = [
     ("", ValueError("bad query field: ''")),
     ("&", ValueError("bad query field: ''")),
     ("&&", ValueError("bad query field: ''")),
-    (";", ValueError("bad query field: ''")),
-    (";&;", ValueError("bad query field: ''")),
     # Should the next few really be valid?
     ("=", {}),
     ("=&=", {}),
-    ("=;=", {}),
     # This rest seem to make sense
     ("=a", {'': ['a']}),
     ("&=a", ValueError("bad query field: ''")),
@@ -81,8 +78,6 @@ parse_strict_test_cases = [
     ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
     ("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
     ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
-    ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
-    ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
     ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
      {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
       'cuyer': ['r'],
@@ -143,6 +138,60 @@ class CgiTests(unittest.TestCase):
             if isinstance(expect, dict):
                 # test dict interface
                 self.assertEqual(len(expect), len(fcd))
+                self.assertItemsEqual(expect.keys(), fcd.keys())
+                self.assertItemsEqual(expect.values(), fcd.values())
+                self.assertItemsEqual(expect.items(), fcd.items())
+                self.assertEqual(fcd.get("nonexistent field", "default"), "default")
+                self.assertEqual(len(sd), len(fs))
+                self.assertItemsEqual(sd.keys(), fs.keys())
+                self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
+                # test individual fields
+                for key in expect.keys():
+                    expect_val = expect[key]
+                    self.assertTrue(fcd.has_key(key))
+                    self.assertItemsEqual(fcd[key], expect[key])
+                    self.assertEqual(fcd.get(key, "default"), fcd[key])
+                    self.assertTrue(fs.has_key(key))
+                    if len(expect_val) > 1:
+                        single_value = 0
+                    else:
+                        single_value = 1
+                    try:
+                        val = sd[key]
+                    except IndexError:
+                        self.assertFalse(single_value)
+                        self.assertEqual(fs.getvalue(key), expect_val)
+                    else:
+                        self.assertTrue(single_value)
+                        self.assertEqual(val, expect_val[0])
+                        self.assertEqual(fs.getvalue(key), expect_val[0])
+                    self.assertItemsEqual(sd.getlist(key), expect_val)
+                    if single_value:
+                        self.assertItemsEqual(sd.values(),
+                                                first_elts(expect.values()))
+                        self.assertItemsEqual(sd.items(),
+                                                first_second_elts(expect.items()))
+
+    def test_separator(self):
+        parse_semicolon = [
+            ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}),
+            ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
+            (";", ValueError("bad query field: ''")),
+            (";;", ValueError("bad query field: ''")),
+            ("=;a", ValueError("bad query field: 'a'")),
+            (";b=a", ValueError("bad query field: ''")),
+            ("b;=a", ValueError("bad query field: 'b'")),
+            ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}),
+            ("a=a+b;a=b+a", {'a': ['a b', 'b a']}),
+        ]
+        for orig, expect in parse_semicolon:
+            env = {'QUERY_STRING': orig}
+            fcd = cgi.FormContentDict(env, separator=';')
+            sd = cgi.SvFormContentDict(env, separator=';')
+            fs = cgi.FieldStorage(environ=env, separator=';')
+            if isinstance(expect, dict):
+                # test dict interface
+                self.assertEqual(len(expect), len(fcd))
                 self.assertItemsEqual(expect.keys(), fcd.keys())
                 self.assertItemsEqual(expect.values(), fcd.values())
                 self.assertItemsEqual(expect.items(), fcd.items())

File Added: pkgsrc/lang/python27/patches/patch-Lib_test_test__urlparse.py
$NetBSD: patch-Lib_test_test__urlparse.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Lib/test/test_urlparse.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/test/test_urlparse.py
@@ -3,6 +3,12 @@ import sys
 import unicodedata
 import unittest
 import urlparse
+from test.support import EnvironmentVarGuard
+from warnings import catch_warnings, filterwarnings
+import tempfile
+import contextlib
+import os.path
+import shutil
 
 RFC1808_BASE = "http://a/b/c/d;p?q#f"
 RFC2396_BASE = "http://a/b/c/d;p?q"
@@ -24,16 +30,29 @@ parse_qsl_test_cases = [
     ("&a=b", [('a', 'b')]),
     ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]),
     ("a=1&a=2", [('a', '1'), ('a', '2')]),
+]
+
+parse_qsl_test_cases_semicolon = [
     (";", []),
     (";;", []),
     (";a=b", [('a', 'b')]),
     ("a=a+b;b=b+c", [('a', 'a b'), ('b', 'b c')]),
     ("a=1;a=2", [('a', '1'), ('a', '2')]),
-    (b";", []),
-    (b";;", []),
-    (b";a=b", [(b'a', b'b')]),
-    (b"a=a+b;b=b+c", [(b'a', b'a b'), (b'b', b'b c')]),
-    (b"a=1;a=2", [(b'a', b'1'), (b'a', b'2')]),
+]
+
+parse_qsl_test_cases_legacy = [
+    ("a=1;a=2&a=3", [('a', '1'), ('a', '2'), ('a', '3')]),
+    ("a=1;b=2&c=3", [('a', '1'), ('b', '2'), ('c', '3')]),
+    ("a=1&b=2&c=3;", [('a', '1'), ('b', '2'), ('c', '3')]),
+]
+
+parse_qsl_test_cases_warn = [
+    (";a=b", [(';a', 'b')]),
+    ("a=a+b;b=b+c", [('a', 'a b;b=b c')]),
+    (b";a=b", [(b';a', b'b')]),
+    (b"a=a+b;b=b+c", [(b'a', b'a b;b=b c')]),
+    ("a=1;a=2&a=3", [('a', '1;a=2'), ('a', '3')]),
+    (b"a=1;a=2&a=3", [(b'a', b'1;a=2'), (b'a', b'3')]),
 ]
 
 parse_qs_test_cases = [
@@ -57,6 +76,9 @@ parse_qs_test_cases = [
     (b"&a=b", {b'a': [b'b']}),
     (b"a=a+b&b=b+c", {b'a': [b'a b'], b'b': [b'b c']}),
     (b"a=1&a=2", {b'a': [b'1', b'2']}),
+]
+
+parse_qs_test_cases_semicolon = [
     (";", {}),
     (";;", {}),
     (";a=b", {'a': ['b']}),
@@ -69,6 +91,24 @@ parse_qs_test_cases = [
     (b"a=1;a=2", {b'a': [b'1', b'2']}),
 ]
 
+parse_qs_test_cases_legacy = [
+    ("a=1;a=2&a=3", {'a': ['1', '2', '3']}),
+    ("a=1;b=2&c=3", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+    ("a=1&b=2&c=3;", {'a': ['1'], 'b': ['2'], 'c': ['3']}),
+    (b"a=1;a=2&a=3", {b'a': [b'1', b'2', b'3']}),
+    (b"a=1;b=2&c=3", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+    (b"a=1&b=2&c=3;", {b'a': [b'1'], b'b': [b'2'], b'c': [b'3']}),
+]
+
+parse_qs_test_cases_warn = [
+    (";a=b", {';a': ['b']}),
+    ("a=a+b;b=b+c", {'a': ['a b;b=b c']}),
+    (b";a=b", {b';a': [b'b']}),
+    (b"a=a+b;b=b+c", {b'a':[ b'a b;b=b c']}),
+    ("a=1;a=2&a=3", {'a': ['1;a=2', '3']}),
+    (b"a=1;a=2&a=3", {b'a': [b'1;a=2', b'3']}),
+]
+
 class UrlParseTestCase(unittest.TestCase):
 
     def checkRoundtrips(self, url, parsed, split):
@@ -141,6 +181,40 @@ class UrlParseTestCase(unittest.TestCase
             self.assertEqual(result, expect_without_blanks,
                     "Error parsing %r" % orig)
 
+    def test_qs_default_warn(self):
+        for orig, expect in parse_qs_test_cases_warn:
+            with catch_warnings(record=True) as w:
+                filterwarnings(action='always',
+                                        category=urlparse._QueryStringSeparatorWarning)
+                result = urlparse.parse_qs(orig, keep_blank_values=True)
+                self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 1)
+            self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
+
+    def test_qsl_default_warn(self):
+        for orig, expect in parse_qsl_test_cases_warn:
+            with catch_warnings(record=True) as w:
+                filterwarnings(action='always',
+                               category=urlparse._QueryStringSeparatorWarning)
+                result = urlparse.parse_qsl(orig, keep_blank_values=True)
+                self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 1)
+            self.assertEqual(w[0].category, urlparse._QueryStringSeparatorWarning)
+
+    def test_default_qs_no_warnings(self):
+        for orig, expect in parse_qs_test_cases:
+            with catch_warnings(record=True) as w:
+                result = urlparse.parse_qs(orig, keep_blank_values=True)
+                self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
+    def test_default_qsl_no_warnings(self):
+        for orig, expect in parse_qsl_test_cases:
+            with catch_warnings(record=True) as w:
+                result = urlparse.parse_qsl(orig, keep_blank_values=True)
+                self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
     def test_roundtrips(self):
         testcases = [
             ('file:///tmp/junk.txt',
@@ -626,6 +700,132 @@ class UrlParseTestCase(unittest.TestCase
         self.assertEqual(urlparse.urlparse("http://www.python.org:80"),
                 ('http','www.python.org:80','','','',''))
 
+    def test_parse_qs_separator_bytes(self):
+        expected = {b'a': [b'1'], b'b': [b'2']}
+
+        result = urlparse.parse_qs(b'a=1;b=2', separator=b';')
+        self.assertEqual(result, expected)
+        result = urlparse.parse_qs(b'a=1;b=2', separator=';')
+        self.assertEqual(result, expected)
+        result = urlparse.parse_qs('a=1;b=2', separator=';')
+        self.assertEqual(result, {'a': ['1'], 'b': ['2']})
+
+    @contextlib.contextmanager
+    def _qsl_sep_config(self, sep):
+        """Context for the given parse_qsl default separator configured in config file"""
+        old_filename = urlparse._QS_SEPARATOR_CONFIG_FILENAME
+        urlparse._default_qs_separator = None
+        try:
+            tmpdirname = tempfile.mkdtemp()
+            filename = os.path.join(tmpdirname, 'conf.cfg')
+            with open(filename, 'w') as file:
+                file.write('[parse_qs]\n')
+                file.write('PYTHON_URLLIB_QS_SEPARATOR = {}'.format(sep))
+            urlparse._QS_SEPARATOR_CONFIG_FILENAME = filename
+            yield
+        finally:
+            urlparse._QS_SEPARATOR_CONFIG_FILENAME = old_filename
+            urlparse._default_qs_separator = None
+            shutil.rmtree(tmpdirname)
+
+    def test_parse_qs_separator_semicolon(self):
+        for orig, expect in parse_qs_test_cases_semicolon:
+            result = urlparse.parse_qs(orig, separator=';')
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+                result = urlparse.parse_qs(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+            with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+                result = urlparse.parse_qs(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
+    def test_parse_qsl_separator_semicolon(self):
+        for orig, expect in parse_qsl_test_cases_semicolon:
+            result = urlparse.parse_qsl(orig, separator=';')
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                environ['PYTHON_URLLIB_QS_SEPARATOR'] = ';'
+                result = urlparse.parse_qsl(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+            with self._qsl_sep_config(';'), catch_warnings(record=True) as w:
+                result = urlparse.parse_qsl(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
+    def test_parse_qs_separator_legacy(self):
+        for orig, expect in parse_qs_test_cases_legacy:
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+                result = urlparse.parse_qs(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+            with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+                result = urlparse.parse_qs(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
+    def test_parse_qsl_separator_legacy(self):
+        for orig, expect in parse_qsl_test_cases_legacy:
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                environ['PYTHON_URLLIB_QS_SEPARATOR'] = 'legacy'
+                result = urlparse.parse_qsl(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+            with self._qsl_sep_config('legacy'), catch_warnings(record=True) as w:
+                result = urlparse.parse_qsl(orig)
+            self.assertEqual(result, expect, "Error parsing %r" % orig)
+            self.assertEqual(len(w), 0)
+
+    def test_parse_qs_separator_bad_value_env_or_config(self):
+        for bad_sep in '', 'abc', 'safe', '&;', 'SEP':
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                environ['PYTHON_URLLIB_QS_SEPARATOR'] = bad_sep
+                with self.assertRaises(ValueError):
+                    urlparse.parse_qsl('a=1;b=2')
+            with self._qsl_sep_config('bad_sep'), catch_warnings(record=True) as w:
+                with self.assertRaises(ValueError):
+                    urlparse.parse_qsl('a=1;b=2')
+
+    def test_parse_qs_separator_bad_value_arg(self):
+        for bad_sep in True, {}, '':
+            with self.assertRaises(ValueError):
+                urlparse.parse_qsl('a=1;b=2', separator=bad_sep)
+
+    def test_parse_qs_separator_num_fields(self):
+        for qs, sep in (
+            ('a&b&c', '&'),
+            ('a;b;c', ';'),
+            ('a&b;c', 'legacy'),
+        ):
+            with EnvironmentVarGuard() as environ, catch_warnings(record=True) as w:
+                if sep != 'legacy':
+                    with self.assertRaises(ValueError):
+                        urlparse.parse_qsl(qs, separator=sep, max_num_fields=2)
+                if sep:
+                    environ['PYTHON_URLLIB_QS_SEPARATOR'] = sep
+                with self.assertRaises(ValueError):
+                    urlparse.parse_qsl(qs, max_num_fields=2)
+
+    def test_parse_qs_separator_priority(self):
+        # env variable trumps config file
+        with self._qsl_sep_config('~'), EnvironmentVarGuard() as environ:
+            environ['PYTHON_URLLIB_QS_SEPARATOR'] = '!'
+            result = urlparse.parse_qs('a=1!b=2~c=3')
+            self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+        # argument trumps config file
+        with self._qsl_sep_config('~'):
+            result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
+            self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+        # argument trumps env variable
+        with EnvironmentVarGuard() as environ:
+            environ['PYTHON_URLLIB_QS_SEPARATOR'] = '~'
+            result = urlparse.parse_qs('a=1$b=2~c=3', separator='$')
+            self.assertEqual(result, {'a': ['1'], 'b': ['2~c=3']})
+
     def test_urlsplit_normalization(self):
         # Certain characters should never occur in the netloc,
         # including under normalization.

File Added: pkgsrc/lang/python27/patches/patch-Lib_urlparse.py
$NetBSD: patch-Lib_urlparse.py,v 1.1.2.2 2021/10/13 21:04:01 tm Exp $

Fix CVE-2021-23336: Add `separator` argument to parse_qs; warn with default
Via Fedora:
https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00359-CVE-2021-23336.patch

--- Lib/urlparse.py.orig	2020-04-19 21:13:39.000000000 +0000
+++ Lib/urlparse.py
@@ -29,6 +29,7 @@ test_urlparse.py provides a good indicat
 """
 
 import re
+import os
 
 __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
            "urlsplit", "urlunsplit", "parse_qs", "parse_qsl"]
@@ -382,7 +383,8 @@ def unquote(s):
             append(item)
     return ''.join(res)
 
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
+def parse_qs(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None,
+             separator=None):
     """Parse a query given as a string argument.
 
         Arguments:
@@ -405,14 +407,23 @@ def parse_qs(qs, keep_blank_values=0, st
     """
     dict = {}
     for name, value in parse_qsl(qs, keep_blank_values, strict_parsing,
-                                 max_num_fields):
+                                 max_num_fields, separator):
         if name in dict:
             dict[name].append(value)
         else:
             dict[name] = [value]
     return dict
 
-def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None):
+class _QueryStringSeparatorWarning(RuntimeWarning):
+    """Warning for using default `separator` in parse_qs or parse_qsl"""
+
+# The default "separator" for parse_qsl can be specified in a config file.
+# It's cached after first read.
+#_QS_SEPARATOR_CONFIG_FILENAME = '/etc/python/urllib.cfg'
+_default_qs_separator = None
+
+def parse_qsl(qs, keep_blank_values=0, strict_parsing=0, max_num_fields=None,
+              separator=None):
     """Parse a query given as a string argument.
 
     Arguments:
@@ -434,15 +445,72 @@ def parse_qsl(qs, keep_blank_values=0, s
 
     Returns a list, as G-d intended.
     """
+
+    if (not separator or (not isinstance(separator, (str, bytes)))) and separator is not None:
+        raise ValueError("Separator must be of type string or bytes.")
+
+    # Used when both "&" and ";" act as separators. (Need a non-string value.)
+    _legacy = object()
+
+    if separator is None:
+        global _default_qs_separator
+        separator = _default_qs_separator
+        envvar_name = 'PYTHON_URLLIB_QS_SEPARATOR'
+        if separator is None:
+            # Set default separator from environment variable
+            separator = os.environ.get(envvar_name)
+            config_source = 'environment variable'
+        if separator is None:
+            # Set default separator from the configuration file
+            try:
+                file = open(_QS_SEPARATOR_CONFIG_FILENAME)
+            except EnvironmentError:
+                pass
+            else:
+                with file:
+                    import ConfigParser
+                    config = ConfigParser.ConfigParser()
+                    config.readfp(file)
+                    separator = config.get('parse_qs', envvar_name)
+                    _default_qs_separator = separator
+                config_source = _QS_SEPARATOR_CONFIG_FILENAME
+        if separator is None:
+            # The default is '&', but warn if not specified explicitly
+            if ';' in qs:
+                from warnings import warn
+                warn("The default separator of urlparse.parse_qsl and "
+                    + "parse_qs was changed to '&' to avoid a web cache "
+                    + "poisoning issue (CVE-2021-23336). "
+                    + "By default, semicolons no longer act as query field "
+                    + "separators. "
+                    + "See https://access.redhat.com/articles/5860431 for "
+                    + "more details.",
+                    _QueryStringSeparatorWarning, stacklevel=2)
+            separator = '&'
+        elif separator == 'legacy':
+            separator = _legacy
+        elif len(separator) != 1:
+            raise ValueError(
+                '{} (from {}) must contain '.format(envvar_name, config_source)
+                + '1 character, or "legacy". See '
+                + 'https://access.redhat.com/articles/5860431 for more details.'
+            )
+
     # If max_num_fields is defined then check that the number of fields
     # is less than max_num_fields. This prevents a memory exhaustion DOS
     # attack via post bodies with many fields.
     if max_num_fields is not None:
-        num_fields = 1 + qs.count('&') + qs.count(';')
+        if separator is _legacy:
+            num_fields = 1 + qs.count('&') + qs.count(';')
+        else:
+            num_fields = 1 + qs.count(separator)
         if max_num_fields < num_fields:
             raise ValueError('Max number of fields exceeded')
 
-    pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+    if separator is _legacy:
+        pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')]
+    else:
+        pairs = [s1 for s1 in qs.split(separator)]
     r = []
     for name_value in pairs:
         if not name_value and not strict_parsing:

cvs diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_httplib.py (expand / switch to unified diff)

--- pkgsrc/lang/python27/patches/patch-Lib_httplib.py 2020/09/27 14:57:22 1.3
+++ pkgsrc/lang/python27/patches/patch-Lib_httplib.py 2021/10/13 21:04:01 1.3.8.1
@@ -1,43 +1,93 @@ @@ -1,43 +1,93 @@
1$NetBSD: patch-Lib_httplib.py,v 1.3 2020/09/27 14:57:22 leot Exp $ 1$NetBSD: patch-Lib_httplib.py,v 1.3.8.1 2021/10/13 21:04:01 tm Exp $
2 2
3bpo-39603 (CVE-2020-26116): header injection via HTTP method 3bpo-39603 (CVE-2020-26116): header injection via HTTP method
4 4
5taken from: 5Taken from:
6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4
7 7
 8Fix CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue
 9Via Fedora:
 10https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00368-CVE-2021-3737.patch
 11
8--- Lib/httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 12--- Lib/httplib.py.orig 2020-04-19 21:13:39.000000000 +0000
9+++ Lib/httplib.py 13+++ Lib/httplib.py
10@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.c 14@@ -257,6 +257,10 @@ _contains_disallowed_url_pchar_re = re.c
11 # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") 15 # _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
12 # We are more lenient for assumed real world compatibility purposes. 16 # We are more lenient for assumed real world compatibility purposes.
13  17
14+# These characters are not allowed within HTTP method names 18+# These characters are not allowed within HTTP method names
15+# to prevent http header injection. 19+# to prevent http header injection.
16+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]') 20+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]')
17+ 21+
18 # We always set the Content-Length header for these methods because some 22 # We always set the Content-Length header for these methods because some
19 # servers will otherwise respond with a 411 23 # servers will otherwise respond with a 411
20 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} 24 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'}
21@@ -935,6 +939,8 @@ class HTTPConnection: 25@@ -361,6 +365,25 @@ class HTTPMessage(mimetools.Message):
 26 # It's not a header line; skip it and try the next line.
 27 self.status = 'Non-header line where header expected'
 28
 29+
 30+def _read_headers(fp):
 31+ """Reads potential header lines into a list from a file pointer.
 32+ Length of line is limited by _MAXLINE, and number of
 33+ headers is limited by _MAXHEADERS.
 34+ """
 35+ headers = []
 36+ while True:
 37+ line = fp.readline(_MAXLINE + 1)
 38+ if len(line) > _MAXLINE:
 39+ raise LineTooLong("header line")
 40+ headers.append(line)
 41+ if len(headers) > _MAXHEADERS:
 42+ raise HTTPException("got more than %d headers" % _MAXHEADERS)
 43+ if line in (b'\r\n', b'\n', b''):
 44+ break
 45+ return headers
 46+
 47+
 48 class HTTPResponse:
 49
 50 # strict: If true, raise BadStatusLine if the status line can't be
 51@@ -449,15 +472,10 @@ class HTTPResponse:
 52 if status != CONTINUE:
 53 break
 54 # skip the header from the 100 response
 55- while True:
 56- skip = self.fp.readline(_MAXLINE + 1)
 57- if len(skip) > _MAXLINE:
 58- raise LineTooLong("header line")
 59- skip = skip.strip()
 60- if not skip:
 61- break
 62- if self.debuglevel > 0:
 63- print "header:", skip
 64+ skipped_headers = _read_headers(self.fp)
 65+ if self.debuglevel > 0:
 66+ print("headers:", skipped_headers)
 67+ del skipped_headers
 68
 69 self.status = status
 70 self.reason = reason.strip()
 71@@ -935,6 +953,8 @@ class HTTPConnection:
22 else: 72 else:
23 raise CannotSendRequest() 73 raise CannotSendRequest()
24  74
25+ self._validate_method(method) 75+ self._validate_method(method)
26+ 76+
27 # Save the method for use later in the response phase 77 # Save the method for use later in the response phase
28 self._method = method 78 self._method = method
29  79
30@@ -1020,6 +1026,17 @@ class HTTPConnection: 80@@ -1020,6 +1040,17 @@ class HTTPConnection:
31 # On Python 2, request is already encoded (default) 81 # On Python 2, request is already encoded (default)
32 return request 82 return request
33  83
34+ def _validate_method(self, method): 84+ def _validate_method(self, method):
35+ """Validate a method name for putrequest.""" 85+ """Validate a method name for putrequest."""
36+ # prevent http header injection 86+ # prevent http header injection
37+ match = _contains_disallowed_method_pchar_re.search(method) 87+ match = _contains_disallowed_method_pchar_re.search(method)
38+ if match: 88+ if match:
39+ msg = ( 89+ msg = (
40+ "method can't contain control characters. {method!r} " 90+ "method can't contain control characters. {method!r} "
41+ "(found at least {matched!r})" 91+ "(found at least {matched!r})"
42+ ).format(matched=match.group(), method=method) 92+ ).format(matched=match.group(), method=method)
43+ raise ValueError(msg) 93+ raise ValueError(msg)

cvs diff -r1.3 -r1.3.8.1 pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py (expand / switch to unified diff)

--- pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py 2020/09/27 14:57:22 1.3
+++ pkgsrc/lang/python27/patches/patch-Lib_test_test__httplib.py 2021/10/13 21:04:01 1.3.8.1
@@ -1,20 +1,24 @@ @@ -1,20 +1,24 @@
1$NetBSD: patch-Lib_test_test__httplib.py,v 1.3 2020/09/27 14:57:22 leot Exp $ 1$NetBSD: patch-Lib_test_test__httplib.py,v 1.3.8.1 2021/10/13 21:04:01 tm Exp $
2 2
3bpo-39603 (CVE-2020-26116): header injection via HTTP method 3bpo-39603 (CVE-2020-26116): header injection via HTTP method
4 4
5taken from: 5Taken from:
6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4 6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=138e2caeb4827ccfd1eaff2cf63afb79dfeeb3c4
7 7
 8Fix CVE-2021-3737: http client infinite line reading (DoS) after a HTTP 100 Continue
 9Via Fedora:
 10https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00368-CVE-2021-3737.patch
 11
8--- Lib/test/test_httplib.py.orig 2020-04-19 21:13:39.000000000 +0000 12--- Lib/test/test_httplib.py.orig 2020-04-19 21:13:39.000000000 +0000
9+++ Lib/test/test_httplib.py 13+++ Lib/test/test_httplib.py
10@@ -384,6 +384,26 @@ class HeaderTests(TestCase): 14@@ -384,6 +384,26 @@ class HeaderTests(TestCase):
11 with self.assertRaisesRegexp(ValueError, 'Invalid header'): 15 with self.assertRaisesRegexp(ValueError, 'Invalid header'):
12 conn.putheader(name, value) 16 conn.putheader(name, value)
13  17
14+ def test_invalid_method_names(self): 18+ def test_invalid_method_names(self):
15+ methods = ( 19+ methods = (
16+ 'GET\r', 20+ 'GET\r',
17+ 'POST\n', 21+ 'POST\n',
18+ 'PUT\n\r', 22+ 'PUT\n\r',
19+ 'POST\nValue', 23+ 'POST\nValue',
20+ 'POST\nHOST:abc', 24+ 'POST\nHOST:abc',
@@ -24,13 +28,28 @@ https://gitweb.gentoo.org/fork/cpython.g @@ -24,13 +28,28 @@ https://gitweb.gentoo.org/fork/cpython.g
24+ '\nPUT' 28+ '\nPUT'
25+ ) 29+ )
26+ 30+
27+ for method in methods: 31+ for method in methods:
28+ with self.assertRaisesRegexp( 32+ with self.assertRaisesRegexp(
29+ ValueError, "method can't contain control characters"): 33+ ValueError, "method can't contain control characters"):
30+ conn = httplib.HTTPConnection('example.com') 34+ conn = httplib.HTTPConnection('example.com')
31+ conn.sock = FakeSocket(None) 35+ conn.sock = FakeSocket(None)
32+ conn.request(method=method, url="/") 36+ conn.request(method=method, url="/")
33+ 37+
34  38
35 class BasicTest(TestCase): 39 class BasicTest(TestCase):
36 def test_status_lines(self): 40 def test_status_lines(self):
 41@@ -655,6 +675,14 @@ class BasicTest(TestCase):
 42 resp = httplib.HTTPResponse(FakeSocket(body))
 43 self.assertRaises(httplib.LineTooLong, resp.begin)
 44
 45+ def test_overflowing_header_limit_after_100(self):
 46+ body = (
 47+ 'HTTP/1.1 100 OK\r\n'
 48+ 'r\n' * 32768
 49+ )
 50+ resp = httplib.HTTPResponse(FakeSocket(body))
 51+ self.assertRaises(httplib.HTTPException, resp.begin)
 52+
 53 def test_overflowing_chunked_line(self):
 54 body = (
 55 'HTTP/1.1 200 OK\r\n'

cvs diff -r1.2 -r1.2.10.1 pkgsrc/lang/python27/patches/patch-Lib_urllib2.py (expand / switch to unified diff)

--- pkgsrc/lang/python27/patches/patch-Lib_urllib2.py 2020/09/20 12:10:27 1.2
+++ pkgsrc/lang/python27/patches/patch-Lib_urllib2.py 2021/10/13 21:04:01 1.2.10.1
@@ -1,31 +1,36 @@ @@ -1,31 +1,36 @@
1$NetBSD: patch-Lib_urllib2.py,v 1.2 2020/09/20 12:10:27 mgorny Exp $ 1$NetBSD: patch-Lib_urllib2.py,v 1.2.10.1 2021/10/13 21:04:01 tm Exp $
2 2
3bpo-39503 (CVE-2020-8492): ReDoS on AbstractBasicAuthHandler 3bpo-39503 (CVE-2020-8492): ReDoS on AbstractBasicAuthHandler
4 4
5taken from: 5Taken from:
6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=2273e65e11dd0234f2f51ebaef61fc6e848d4059 6https://gitweb.gentoo.org/fork/cpython.git/commit/?h=gentoo-2.7-vanilla&id=2273e65e11dd0234f2f51ebaef61fc6e848d4059
7 7
 8bpo-43075 (CVE-2021-3733): Fix ReDoS in request
 9
 10Taken from:
 11https://github.com/python/cpython/pull/24391/
 12
8--- Lib/urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000 13--- Lib/urllib2.py.orig 2020-04-19 21:13:39.000000000 +0000
9+++ Lib/urllib2.py 14+++ Lib/urllib2.py
10@@ -856,8 +856,15 @@ class AbstractBasicAuthHandler: 15@@ -856,8 +856,15 @@ class AbstractBasicAuthHandler:
11  16
12 # allow for double- and single-quoted realm values 17 # allow for double- and single-quoted realm values
13 # (single quotes are a violation of the RFC, but appear in the wild) 18 # (single quotes are a violation of the RFC, but appear in the wild)
14- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' 19- rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
15- 'realm=(["\']?)([^"\']*)\\2', re.I) 20- 'realm=(["\']?)([^"\']*)\\2', re.I)
16+ rx = re.compile('(?:^|,)' # start of the string or ',' 21+ rx = re.compile('(?:^|,)' # start of the string or ','
17+ '[ \t]*' # optional whitespaces 22+ '[ \t]*' # optional whitespaces
18+ '([^ \t]+)' # scheme like "Basic" 23+ '([^ \t,]+)' # scheme like "Basic"
19+ '[ \t]+' # mandatory whitespaces 24+ '[ \t]+' # mandatory whitespaces
20+ # realm=xxx 25+ # realm=xxx
21+ # realm='xxx' 26+ # realm='xxx'
22+ # realm="xxx" 27+ # realm="xxx"
23+ 'realm=(["\']?)([^"\']*)\\2', 28+ 'realm=(["\']?)([^"\']*)\\2',
24+ re.I) 29+ re.I)
25  30
26 # XXX could pre-emptively send auth info already accepted (RFC 2617, 31 # XXX could pre-emptively send auth info already accepted (RFC 2617,
27 # end of section 2, and section 1.2 immediately after "credentials" 32 # end of section 2, and section 1.2 immediately after "credentials"
28@@ -869,23 +876,52 @@ class AbstractBasicAuthHandler: 33@@ -869,23 +876,52 @@ class AbstractBasicAuthHandler:
29 self.passwd = password_mgr 34 self.passwd = password_mgr
30 self.add_password = self.passwd.add_password 35 self.add_password = self.passwd.add_password
31  36

cvs diff -r1.1 -r1.1.2.1 pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c (expand / switch to unified diff)

--- pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c 2021/06/23 18:30:24 1.1
+++ pkgsrc/lang/python27/patches/patch-Modules___ctypes_callproc.c 2021/10/13 21:04:01 1.1.2.1
@@ -1,41 +1,150 @@ @@ -1,41 +1,150 @@
1$NetBSD: patch-Modules___ctypes_callproc.c,v 1.1 2021/06/23 18:30:24 schmonz Exp $ 1$NetBSD: patch-Modules___ctypes_callproc.c,v 1.1.2.1 2021/10/13 21:04:01 tm Exp $
2 2
3macOS arm64 support, via MacPorts. 3macOS arm64 support, via MacPorts.
4 4
5--- Modules/_ctypes/callproc.c.orig 2021-06-22 19:20:28.000000000 +0000 5Fix CVE-2021-3177: Replace snprintf with Python unicode formatting in ctypes param reprs
 6Via Fedora:
 7https://src.fedoraproject.org/rpms/python2.7/blob/rawhide/f/00357-CVE-2021-3177.patch
 8
 9--- Modules/_ctypes/callproc.c.orig 2020-04-19 21:13:39.000000000 +0000
6+++ Modules/_ctypes/callproc.c 10+++ Modules/_ctypes/callproc.c
7@@ -74,6 +74,10 @@ 11@@ -74,6 +74,10 @@
8 #include <malloc.h> 12 #include <malloc.h>
9 #endif 13 #endif
10  14
11+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 15+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
12+#include <mach-o/dyld.h> 16+#include <mach-o/dyld.h>
13+#endif 17+#endif
14+ 18+
15 #include <ffi.h> 19 #include <ffi.h>
16 #include "ctypes.h" 20 #include "ctypes.h"
17 #ifdef HAVE_ALLOCA_H 21 #ifdef HAVE_ALLOCA_H
18@@ -773,7 +777,8 @@ static int _call_function_pointer(int fl 22@@ -460,50 +464,62 @@ PyCArg_dealloc(PyCArgObject *self)
 23 static PyObject *
 24 PyCArg_repr(PyCArgObject *self)
 25 {
 26- char buffer[256];
 27 switch(self->tag) {
 28 case 'b':
 29 case 'B':
 30- sprintf(buffer, "<cparam '%c' (%d)>",
 31+ return PyString_FromFormat("<cparam '%c' (%d)>",
 32 self->tag, self->value.b);
 33- break;
 34 case 'h':
 35 case 'H':
 36- sprintf(buffer, "<cparam '%c' (%d)>",
 37+ return PyString_FromFormat("<cparam '%c' (%d)>",
 38 self->tag, self->value.h);
 39- break;
 40 case 'i':
 41 case 'I':
 42- sprintf(buffer, "<cparam '%c' (%d)>",
 43+ return PyString_FromFormat("<cparam '%c' (%d)>",
 44 self->tag, self->value.i);
 45- break;
 46 case 'l':
 47 case 'L':
 48- sprintf(buffer, "<cparam '%c' (%ld)>",
 49+ return PyString_FromFormat("<cparam '%c' (%ld)>",
 50 self->tag, self->value.l);
 51- break;
 52
 53 #ifdef HAVE_LONG_LONG
 54 case 'q':
 55 case 'Q':
 56- sprintf(buffer,
 57- "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>",
 58+ return PyString_FromFormat("<cparam '%c' (%lld)>",
 59 self->tag, self->value.q);
 60- break;
 61 #endif
 62 case 'd':
 63- sprintf(buffer, "<cparam '%c' (%f)>",
 64- self->tag, self->value.d);
 65- break;
 66- case 'f':
 67- sprintf(buffer, "<cparam '%c' (%f)>",
 68- self->tag, self->value.f);
 69- break;
 70-
 71+ case 'f': {
 72+ PyObject *s = PyString_FromFormat("<cparam '%c' (", self->tag);
 73+ if (s == NULL) {
 74+ return NULL;
 75+ }
 76+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d);
 77+ if (f == NULL) {
 78+ Py_DECREF(s);
 79+ return NULL;
 80+ }
 81+ PyObject *r = PyObject_Repr(f);
 82+ Py_DECREF(f);
 83+ if (r == NULL) {
 84+ Py_DECREF(s);
 85+ return NULL;
 86+ }
 87+ PyString_ConcatAndDel(&s, r);
 88+ if (s == NULL) {
 89+ return NULL;
 90+ }
 91+ r = PyString_FromString(")>");
 92+ if (r == NULL) {
 93+ Py_DECREF(s);
 94+ return NULL;
 95+ }
 96+ PyString_ConcatAndDel(&s, r);
 97+ return s;
 98+ }
 99 case 'c':
 100- sprintf(buffer, "<cparam '%c' (%c)>",
 101+ return PyString_FromFormat("<cparam '%c' ('%c')>",
 102 self->tag, self->value.c);
 103- break;
 104
 105 /* Hm, are these 'z' and 'Z' codes useful at all?
 106 Shouldn't they be replaced by the functionality of c_string
 107@@ -512,16 +528,13 @@ PyCArg_repr(PyCArgObject *self)
 108 case 'z':
 109 case 'Z':
 110 case 'P':
 111- sprintf(buffer, "<cparam '%c' (%p)>",
 112+ return PyUnicode_FromFormat("<cparam '%c' (%p)>",
 113 self->tag, self->value.p);
 114- break;
 115
 116 default:
 117- sprintf(buffer, "<cparam '%c' at %p>",
 118- self->tag, self);
 119- break;
 120+ return PyString_FromFormat("<cparam '%c' at %p>",
 121+ (unsigned char)self->tag, (void *)self);
 122 }
 123- return PyString_FromString(buffer);
 124 }
 125
 126 static PyMemberDef PyCArgType_members[] = {
 127@@ -773,7 +786,8 @@ static int _call_function_pointer(int fl
19 ffi_type **atypes, 128 ffi_type **atypes,
20 ffi_type *restype, 129 ffi_type *restype,
21 void *resmem, 130 void *resmem,
22- int argcount) 131- int argcount)
23+ int argcount, 132+ int argcount,
24+ int argtypecount) 133+ int argtypecount)
25 { 134 {
26 #ifdef WITH_THREAD 135 #ifdef WITH_THREAD
27 PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */ 136 PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
28@@ -801,6 +806,37 @@ static int _call_function_pointer(int fl 137@@ -801,6 +815,37 @@ static int _call_function_pointer(int fl
29 if ((flags & FUNCFLAG_CDECL) == 0) 138 if ((flags & FUNCFLAG_CDECL) == 0)
30 cc = FFI_STDCALL; 139 cc = FFI_STDCALL;
31 #endif 140 #endif
32+ 141+
33+ /* Even on Apple-arm64 the calling convention for variadic functions conincides 142+ /* Even on Apple-arm64 the calling convention for variadic functions conincides
34+ * with the standard calling convention in the case that the function called 143+ * with the standard calling convention in the case that the function called
35+ * only with its fixed arguments. Thus, we do not need a special flag to be 144+ * only with its fixed arguments. Thus, we do not need a special flag to be
36+ * set on variadic functions. We treat a function as variadic if it is called 145+ * set on variadic functions. We treat a function as variadic if it is called
37+ * with a nonzero number of variadic arguments */ 146+ * with a nonzero number of variadic arguments */
38+ int is_variadic = (argtypecount != 0 && argcount > argtypecount); 147+ int is_variadic = (argtypecount != 0 && argcount > argtypecount);
39+ (void) is_variadic; 148+ (void) is_variadic;
40+ 149+
41+#if defined(__APPLE__) && defined(__arm64__) && !defined(HAVE_FFI_PREP_CIF_VAR) 150+#if defined(__APPLE__) && defined(__arm64__) && !defined(HAVE_FFI_PREP_CIF_VAR)
@@ -53,67 +162,67 @@ macOS arm64 support, via MacPorts. @@ -53,67 +162,67 @@ macOS arm64 support, via MacPorts.
53+ argcount, 162+ argcount,
54+ restype, 163+ restype,
55+ atypes)) { 164+ atypes)) {
56+ PyErr_SetString(PyExc_RuntimeError, 165+ PyErr_SetString(PyExc_RuntimeError,
57+ "ffi_prep_cif_var failed"); 166+ "ffi_prep_cif_var failed");
58+ return -1; 167+ return -1;
59+ } 168+ }
60+ } else 169+ } else
61+ #endif 170+ #endif
62+ { 171+ {
63 if (FFI_OK != ffi_prep_cif(&cif, 172 if (FFI_OK != ffi_prep_cif(&cif,
64 cc, 173 cc,
65 argcount, 174 argcount,
66@@ -810,6 +846,7 @@ static int _call_function_pointer(int fl 175@@ -810,6 +855,7 @@ static int _call_function_pointer(int fl
67 "ffi_prep_cif failed"); 176 "ffi_prep_cif failed");
68 return -1; 177 return -1;
69 } 178 }
70+ } 179+ }
71  180
72 if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { 181 if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
73 error_object = _ctypes_get_errobj(&space); 182 error_object = _ctypes_get_errobj(&space);
74@@ -1183,6 +1220,9 @@ PyObject *_ctypes_callproc(PPROC pProc, 183@@ -1183,6 +1229,9 @@ PyObject *_ctypes_callproc(PPROC pProc,
75 rtype, resbuf, 184 rtype, resbuf,
76 Py_SAFE_DOWNCAST(argcount, 185 Py_SAFE_DOWNCAST(argcount,
77 Py_ssize_t, 186 Py_ssize_t,
78+ int), 187+ int),
79+ Py_SAFE_DOWNCAST(argtype_count, 188+ Py_SAFE_DOWNCAST(argtype_count,
80+ Py_ssize_t, 189+ Py_ssize_t,
81 int))) 190 int)))
82 goto cleanup; 191 goto cleanup;
83  192
84@@ -1416,6 +1456,25 @@ copy_com_pointer(PyObject *self, PyObjec 193@@ -1416,6 +1465,25 @@ copy_com_pointer(PyObject *self, PyObjec
85 } 194 }
86 #else 195 #else
87  196
88+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 197+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
89+static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args) 198+static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args)
90+{ 199+{
91+ if (__builtin_available(macos 11, ios 14, watchos 7, tvos 14, *)) { 200+ if (__builtin_available(macos 11, ios 14, watchos 7, tvos 14, *)) {
92+ char *name_str; 201+ char *name_str;
93+ if (!PyArg_ParseTuple(args, "z", &name_str)) 202+ if (!PyArg_ParseTuple(args, "z", &name_str))
94+ return NULL; 203+ return NULL;
95+ 204+
96+ if(_dyld_shared_cache_contains_path(name_str)) 205+ if(_dyld_shared_cache_contains_path(name_str))
97+ Py_RETURN_TRUE; 206+ Py_RETURN_TRUE;
98+ else 207+ else
99+ Py_RETURN_FALSE; 208+ Py_RETURN_FALSE;
100+ } else { 209+ } else {
101+ PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing"); 210+ PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing");
102+ return NULL; 211+ return NULL;
103+ } 212+ }
104+} 213+}
105+#endif 214+#endif
106+ 215+
107 static PyObject *py_dl_open(PyObject *self, PyObject *args) 216 static PyObject *py_dl_open(PyObject *self, PyObject *args)
108 { 217 {
109 char *name; 218 char *name;
110@@ -1940,6 +1999,9 @@ PyMethodDef _ctypes_module_methods[] = { 219@@ -1940,6 +2008,9 @@ PyMethodDef _ctypes_module_methods[] = {
111 "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"}, 220 "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"},
112 {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"}, 221 {"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"},
113 {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"}, 222 {"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"},
114+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 223+#if __APPLE__ && HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
115+ {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"}, 224+ {"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},
116+#endif 225+#endif
117 #endif 226 #endif
118 {"alignment", align_func, METH_O, alignment_doc}, 227 {"alignment", align_func, METH_O, alignment_doc},
119 {"sizeof", sizeof_func, METH_O, sizeof_doc}, 228 {"sizeof", sizeof_func, METH_O, sizeof_doc},