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

Reply via email to