0035-closes-bpo-42938-Replace-snprintf-with-Python-unicod.patch 7.3 KB

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