sprintf strikes again. https://nvd.nist.gov/vuln/detail/CVE-2021-3177#vulnCurrentDescriptionTitle
This was made public last night UTC and looks pretty bad, but there seem to be no releases for in-tree Pythons available as of now. I'm not familiar with how Python deals with security issues... This page links to commits against the various releases: https://python-security.readthedocs.io/vuln/ctypes-buffer-overflow-pycarg_repr.html I expect the 3.9 patches to apply as easily as the present one. I can do that if I'm told to do it. A simple test is from https://bugs.python.org/issue42938 >>> from ctypes import * >>> c_double.from_param(1e300) Trace/BPT trap (core dumped) This is fixed with this patch. I tried running python's test suite, but there are lots of issues with threading and async io... Index: Makefile =================================================================== RCS file: /cvs/ports/lang/python/3.8/Makefile,v retrieving revision 1.11 diff -u -p -r1.11 Makefile --- Makefile 4 Jan 2021 14:04:42 -0000 1.11 +++ Makefile 16 Feb 2021 09:41:01 -0000 @@ -9,6 +9,7 @@ VERSION = 3.8 PATCHLEVEL = .7 SHARED_LIBS = python3.8 0.0 VERSION_SPEC = >=3.8,<3.9 +REVISION = 0 CONFIGURE_ARGS += --with-ensurepip=no CONFIGURE_ARGS += --enable-loadable-sqlite-extensions Index: files/CHANGES.OpenBSD =================================================================== RCS file: /cvs/ports/lang/python/3.8/files/CHANGES.OpenBSD,v retrieving revision 1.1 diff -u -p -r1.1 CHANGES.OpenBSD --- files/CHANGES.OpenBSD 7 Nov 2019 16:14:09 -0000 1.1 +++ files/CHANGES.OpenBSD 16 Feb 2021 09:40:51 -0000 @@ -14,5 +14,7 @@ http://bugs.python.org/issue25191 4. Disable libuuid, otherwise Python prefers it over the libc uuid functions. +5. Applied a patch for CVE-2021-3177 + These changes are available in the OpenBSD CVS repository <http://www.openbsd.org/anoncvs.html> in ports/lang/python/3.8. Index: patches/patch-2021-01-18-09-27-31_bpo-42938_4Zn4Mp_rst =================================================================== RCS file: patches/patch-2021-01-18-09-27-31_bpo-42938_4Zn4Mp_rst diff -N patches/patch-2021-01-18-09-27-31_bpo-42938_4Zn4Mp_rst --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-2021-01-18-09-27-31_bpo-42938_4Zn4Mp_rst 16 Feb 2021 09:43:37 -0000 @@ -0,0 +1,11 @@ +$OpenBSD$ + +CVE-2021-3177 +https://github.com/python/cpython/pull/24239 + +Index: 2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst +--- 2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst.orig ++++ 2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst +@@ -0,0 +1,2 @@ ++Avoid static buffers when computing the repr of :class:`ctypes.c_double` and ++:class:`ctypes.c_longdouble` values. Index: patches/patch-Lib_ctypes_test_test_parameters_py =================================================================== RCS file: patches/patch-Lib_ctypes_test_test_parameters_py diff -N patches/patch-Lib_ctypes_test_test_parameters_py --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-Lib_ctypes_test_test_parameters_py 16 Feb 2021 09:44:20 -0000 @@ -0,0 +1,58 @@ +$OpenBSD$ + +CVE-2021-3177 +https://github.com/python/cpython/pull/24239 + +Index: Lib/ctypes/test/test_parameters.py +--- Lib/ctypes/test/test_parameters.py.orig ++++ Lib/ctypes/test/test_parameters.py +@@ -201,6 +201,49 @@ class SimpleTypesTestCase(unittest.TestCase): + 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.assertRegex(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$") ++ self.assertEqual(repr(c_char.from_param(97)), "<cparam 'c' ('a')>") ++ self.assertRegex(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.assertRegex(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$") ++ self.assertRegex(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$") ++ self.assertRegex(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$") ++ self.assertRegex(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$") ++ self.assertRegex(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$") ++ self.assertRegex(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.assertRegex(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$") ++ self.assertRegex(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$") ++ self.assertRegex(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$") ++ self.assertRegex(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$") ++ + ################################################################ + + if __name__ == '__main__': Index: patches/patch-Modules__ctypes_callproc_c =================================================================== RCS file: patches/patch-Modules__ctypes_callproc_c diff -N patches/patch-Modules__ctypes_callproc_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-Modules__ctypes_callproc_c 16 Feb 2021 09:43:58 -0000 @@ -0,0 +1,109 @@ +$OpenBSD$ + +CVE-2021-3177 +https://github.com/python/cpython/pull/24239 + +Index: Modules/_ctypes/callproc.c +--- Modules/_ctypes/callproc.c.orig ++++ Modules/_ctypes/callproc.c +@@ -484,58 +484,47 @@ is_literal_char(unsigned char c) + static PyObject * + PyCArg_repr(PyCArgObject *self) + { +- char buffer[256]; + switch(self->tag) { + case 'b': + case 'B': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.b); +- break; + case 'h': + case 'H': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.h); +- break; + case 'i': + case 'I': +- sprintf(buffer, "<cparam '%c' (%d)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%d)>", + self->tag, self->value.i); +- break; + case 'l': + case 'L': +- sprintf(buffer, "<cparam '%c' (%ld)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%ld)>", + self->tag, self->value.l); +- break; + + case 'q': + case 'Q': +- sprintf(buffer, +-#ifdef MS_WIN32 +- "<cparam '%c' (%I64d)>", +-#else +- "<cparam '%c' (%lld)>", +-#endif ++ return PyUnicode_FromFormat("<cparam '%c' (%lld)>", + self->tag, self->value.q); +- break; + case 'd': +- sprintf(buffer, "<cparam '%c' (%f)>", +- self->tag, self->value.d); +- break; +- case 'f': +- sprintf(buffer, "<cparam '%c' (%f)>", +- self->tag, self->value.f); +- break; +- ++ case 'f': { ++ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d); ++ if (f == NULL) { ++ return NULL; ++ } ++ PyObject *result = PyUnicode_FromFormat("<cparam '%c' (%R)>", self->tag, f); ++ Py_DECREF(f); ++ return result; ++ } + case 'c': + if (is_literal_char((unsigned char)self->value.c)) { +- sprintf(buffer, "<cparam '%c' ('%c')>", ++ return PyUnicode_FromFormat("<cparam '%c' ('%c')>", + self->tag, self->value.c); + } + else { +- sprintf(buffer, "<cparam '%c' ('\\x%02x')>", ++ return PyUnicode_FromFormat("<cparam '%c' ('\\x%02x')>", + self->tag, (unsigned char)self->value.c); + } +- break; + + /* Hm, are these 'z' and 'Z' codes useful at all? + Shouldn't they be replaced by the functionality of c_string +@@ -544,22 +533,20 @@ PyCArg_repr(PyCArgObject *self) + case 'z': + case 'Z': + case 'P': +- sprintf(buffer, "<cparam '%c' (%p)>", ++ return PyUnicode_FromFormat("<cparam '%c' (%p)>", + self->tag, self->value.p); + break; + + default: + if (is_literal_char((unsigned char)self->tag)) { +- sprintf(buffer, "<cparam '%c' at %p>", ++ return PyUnicode_FromFormat("<cparam '%c' at %p>", + (unsigned char)self->tag, (void *)self); + } + else { +- sprintf(buffer, "<cparam 0x%02x at %p>", ++ return PyUnicode_FromFormat("<cparam 0x%02x at %p>", + (unsigned char)self->tag, (void *)self); + } +- break; + } +- return PyUnicode_FromString(buffer); + } + + static PyMemberDef PyCArgType_members[] = { Index: patches/patch-Modules__hashopenssl_c =================================================================== RCS file: /cvs/ports/lang/python/3.8/patches/patch-Modules__hashopenssl_c,v retrieving revision 1.1 diff -u -p -r1.1 patch-Modules__hashopenssl_c --- patches/patch-Modules__hashopenssl_c 7 Nov 2019 16:14:09 -0000 1.1 +++ patches/patch-Modules__hashopenssl_c 16 Feb 2021 09:43:51 -0000 @@ -3,9 +3,9 @@ $OpenBSD: patch-Modules__hashopenssl_c,v Index: Modules/_hashopenssl.c --- Modules/_hashopenssl.c.orig +++ Modules/_hashopenssl.c -@@ -26,7 +26,8 @@ - #include <openssl/objects.h> - #include "openssl/err.h" +@@ -30,7 +30,8 @@ + # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" + #endif -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ Index: patches/patch-Modules__ssl_c =================================================================== RCS file: /cvs/ports/lang/python/3.8/patches/patch-Modules__ssl_c,v retrieving revision 1.1 diff -u -p -r1.1 patch-Modules__ssl_c --- patches/patch-Modules__ssl_c 7 Nov 2019 16:14:09 -0000 1.1 +++ patches/patch-Modules__ssl_c 16 Feb 2021 09:41:55 -0000 @@ -6,7 +6,7 @@ exactly what python's lock protects Index: Modules/_ssl.c --- Modules/_ssl.c.orig +++ Modules/_ssl.c -@@ -135,7 +135,8 @@ static void _PySSLFixErrno(void) { +@@ -139,7 +139,8 @@ static void _PySSLFixErrno(void) { /* Include generated data (error codes) */ #include "_ssl_data.h" @@ -16,7 +16,7 @@ Index: Modules/_ssl.c # define OPENSSL_VERSION_1_1 1 # define PY_OPENSSL_1_1_API 1 #endif -@@ -200,6 +201,9 @@ static void _PySSLFixErrno(void) { +@@ -213,6 +214,9 @@ static void _PySSLFixErrno(void) { #if defined(OPENSSL_VERSION_1_1) && !defined(OPENSSL_NO_SSL2) #define OPENSSL_NO_SSL2 Index: patches/patch-configure_ac =================================================================== RCS file: /cvs/ports/lang/python/3.8/patches/patch-configure_ac,v retrieving revision 1.1 diff -u -p -r1.1 patch-configure_ac --- patches/patch-configure_ac 7 Nov 2019 16:14:09 -0000 1.1 +++ patches/patch-configure_ac 16 Feb 2021 09:41:55 -0000 @@ -15,7 +15,7 @@ Index: configure.ac # The later defininition of _XOPEN_SOURCE disables certain features # on Linux, so we need _GNU_SOURCE to re-enable them (makedev, tm_zone). -@@ -2775,18 +2775,7 @@ AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/S +@@ -2788,18 +2788,7 @@ AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/S AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX # checks for uuid.h location