1From fab838b2ee7cfb9037c24f0f18dfe01aa379b3f7 Mon Sep 17 00:00:00 2001
2From: Benjamin Peterson <benjamin@python.org>
3Date: Mon, 18 Jan 2021 15:11:46 -0600
4Subject: [3.6] closes bpo-42938: Replace snprintf with Python unicode
5 formatting in ctypes param reprs. (GH-24250)
6MIME-Version: 1.0
7Content-Type: text/plain; charset=UTF-8
8Content-Transfer-Encoding: 8bit
9
10(cherry picked from commit 916610ef90a0d0761f08747f7b0905541f0977c7)
11
12Co-authored-by: Benjamin Peterson <benjamin@python.org>
13Rebased for Python 2.7 by Michał Górny <mgorny@gentoo.org>
14---
15 Lib/ctypes/test/test_parameters.py | 43 +++++++++++++++++++
16 .../2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst | 2 +
17 Modules/_ctypes/callproc.c | 49 +++++++++++-----------
18 3 files changed, 69 insertions(+), 25 deletions(-)
19 create mode 100644 Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
20
21diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py
22index 23c1b6e225..3456882ccb 100644
23--- a/Lib/ctypes/test/test_parameters.py
24+++ b/Lib/ctypes/test/test_parameters.py
25@@ -206,6 +206,49 @@ class SimpleTypesTestCase(unittest.TestCase):
26 with self.assertRaises(ZeroDivisionError):
27 WorseStruct().__setstate__({}, b'foo')
28
29+ def test_parameter_repr(self):
30+ from ctypes import (
31+ c_bool,
32+ c_char,
33+ c_wchar,
34+ c_byte,
35+ c_ubyte,
36+ c_short,
37+ c_ushort,
38+ c_int,
39+ c_uint,
40+ c_long,
41+ c_ulong,
42+ c_longlong,
43+ c_ulonglong,
44+ c_float,
45+ c_double,
46+ c_longdouble,
47+ c_char_p,
48+ c_wchar_p,
49+ c_void_p,
50+ )
51+ self.assertRegexpMatches(repr(c_bool.from_param(True)), r"^<cparam '\?' at 0x[A-Fa-f0-9]+>$")
52+ self.assertEqual(repr(c_char.from_param('a')), "<cparam 'c' (a)>")
53+ self.assertRegexpMatches(repr(c_wchar.from_param('a')), r"^<cparam 'u' at 0x[A-Fa-f0-9]+>$")
54+ self.assertEqual(repr(c_byte.from_param(98)), "<cparam 'b' (98)>")
55+ self.assertEqual(repr(c_ubyte.from_param(98)), "<cparam 'B' (98)>")
56+ self.assertEqual(repr(c_short.from_param(511)), "<cparam 'h' (511)>")
57+ self.assertEqual(repr(c_ushort.from_param(511)), "<cparam 'H' (511)>")
58+ self.assertRegexpMatches(repr(c_int.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
59+ self.assertRegexpMatches(repr(c_uint.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
60+ self.assertRegexpMatches(repr(c_long.from_param(20000)), r"^<cparam '[li]' \(20000\)>$")
61+ self.assertRegexpMatches(repr(c_ulong.from_param(20000)), r"^<cparam '[LI]' \(20000\)>$")
62+ self.assertRegexpMatches(repr(c_longlong.from_param(20000)), r"^<cparam '[liq]' \(20000\)>$")
63+ self.assertRegexpMatches(repr(c_ulonglong.from_param(20000)), r"^<cparam '[LIQ]' \(20000\)>$")
64+ self.assertEqual(repr(c_float.from_param(1.5)), "<cparam 'f' (1.5)>")
65+ self.assertEqual(repr(c_double.from_param(1.5)), "<cparam 'd' (1.5)>")
66+ self.assertEqual(repr(c_double.from_param(1e300)), "<cparam 'd' (1e+300)>")
67+ self.assertRegexpMatches(repr(c_longdouble.from_param(1.5)), r"^<cparam ('d' \(1.5\)|'g' at 0x[A-Fa-f0-9]+)>$")
68+ self.assertRegexpMatches(repr(c_char_p.from_param(b'hihi')), "^<cparam 'z' \(0x[A-Fa-f0-9]+\)>$")
69+ self.assertRegexpMatches(repr(c_wchar_p.from_param('hihi')), "^<cparam 'Z' \(0x[A-Fa-f0-9]+\)>$")
70+ self.assertRegexpMatches(repr(c_void_p.from_param(0x12)), r"^<cparam 'P' \(0x0*12\)>$")
71+
72 ################################################################
73
74 if __name__ == '__main__':
75diff --git a/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
76new file mode 100644
77index 0000000000..7df65a156f
78--- /dev/null
79+++ b/Misc/NEWS.d/next/Security/2021-01-18-09-27-31.bpo-42938.4Zn4Mp.rst
80@@ -0,0 +1,2 @@
81+Avoid static buffers when computing the repr of :class:`ctypes.c_double` and
82+:class:`ctypes.c_longdouble` values.
83diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c
84index 066fefc0cc..421addf353 100644
85--- a/Modules/_ctypes/callproc.c
86+++ b/Modules/_ctypes/callproc.c
87@@ -460,50 +460,51 @@ PyCArg_dealloc(PyCArgObject *self)
88 static PyObject *
89 PyCArg_repr(PyCArgObject *self)
90 {
91- char buffer[256];
92 switch(self->tag) {
93 case 'b':
94 case 'B':
95- sprintf(buffer, "<cparam '%c' (%d)>",
96+ return PyString_FromFormat("<cparam '%c' (%d)>",
97 self->tag, self->value.b);
98- break;
99 case 'h':
100 case 'H':
101- sprintf(buffer, "<cparam '%c' (%d)>",
102+ return PyString_FromFormat("<cparam '%c' (%d)>",
103 self->tag, self->value.h);
104- break;
105 case 'i':
106 case 'I':
107- sprintf(buffer, "<cparam '%c' (%d)>",
108+ return PyString_FromFormat("<cparam '%c' (%d)>",
109 self->tag, self->value.i);
110- break;
111 case 'l':
112 case 'L':
113- sprintf(buffer, "<cparam '%c' (%ld)>",
114+ return PyString_FromFormat("<cparam '%c' (%ld)>",
115 self->tag, self->value.l);
116- break;
117
118 #ifdef HAVE_LONG_LONG
119 case 'q':
120 case 'Q':
121- sprintf(buffer,
122+ return PyString_FromFormat(
123 "<cparam '%c' (%" PY_FORMAT_LONG_LONG "d)>",
124 self->tag, self->value.q);
125- break;
126 #endif
127 case 'd':
128- sprintf(buffer, "<cparam '%c' (%f)>",
129- self->tag, self->value.d);
130- break;
131- case 'f':
132- sprintf(buffer, "<cparam '%c' (%f)>",
133- self->tag, self->value.f);
134- break;
135-
136+ case 'f': {
137+ PyObject *f = PyFloat_FromDouble((self->tag == 'f') ? self->value.f : self->value.d);
138+ if (f == NULL) {
139+ return NULL;
140+ }
141+ PyObject *r = PyObject_Repr(f);
142+ if (r == NULL) {
143+ Py_DECREF(f);
144+ return NULL;
145+ }
146+ PyObject *result = PyString_FromFormat(
147+ "<cparam '%c' (%s)>", self->tag, PyString_AsString(r));
148+ Py_DECREF(r);
149+ Py_DECREF(f);
150+ return result;
151+ }
152 case 'c':
153- sprintf(buffer, "<cparam '%c' (%c)>",
154+ return PyString_FromFormat("<cparam '%c' (%c)>",
155 self->tag, self->value.c);
156- break;
157
158 /* Hm, are these 'z' and 'Z' codes useful at all?
159 Shouldn't they be replaced by the functionality of c_string
160@@ -512,16 +513,14 @@ PyCArg_repr(PyCArgObject *self)
161 case 'z':
162 case 'Z':
163 case 'P':
164- sprintf(buffer, "<cparam '%c' (%p)>",
165+ return PyString_FromFormat("<cparam '%c' (%p)>",
166 self->tag, self->value.p);
167 break;
168
169 default:
170- sprintf(buffer, "<cparam '%c' at %p>",
171+ return PyString_FromFormat("<cparam '%c' at %p>",
172 self->tag, self);
173- break;
174 }
175- return PyString_FromString(buffer);
176 }
177
178 static PyMemberDef PyCArgType_members[] = {
179--
180cgit v1.2.3
181