Skip to content

Commit 91cf656

Browse files
Merge branch 'main' into fix-textwrap-color-argparse
2 parents dbebdc4 + 46acb79 commit 91cf656

12 files changed

Lines changed: 151 additions & 32 deletions

File tree

Doc/library/logging.handlers.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,8 @@ supports sending logging messages to a remote or local Unix syslog.
631631
the form of a ``(host, port)`` tuple. If *address* is not specified,
632632
``('localhost', 514)`` is used. The address is used to open a socket. An
633633
alternative to providing a ``(host, port)`` tuple is providing an address as a
634-
string, for example '/dev/log'. In this case, a Unix domain socket is used to
634+
string or a :class:`bytes` object, for example '/dev/log'.
635+
In this case, a Unix domain socket is used to
635636
send the message to the syslog. If *facility* is not specified,
636637
:const:`LOG_USER` is used. The type of socket opened depends on the
637638
*socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus
@@ -664,6 +665,9 @@ supports sending logging messages to a remote or local Unix syslog.
664665
.. versionchanged:: 3.14
665666
*timeout* was added.
666667

668+
.. versionchanged:: next
669+
*address* can now be a :class:`bytes` object.
670+
667671
.. method:: close()
668672

669673
Closes the socket to the remote host.

Lib/logging/handlers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,9 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
886886
"""
887887
Initialize a handler.
888888
889-
If address is specified as a string, a UNIX socket is used. To log to a
890-
local syslogd, "SysLogHandler(address="/dev/log")" can be used.
889+
If address is specified as a string or bytes, a UNIX socket is used.
890+
To log to a local syslogd, "SysLogHandler(address="/dev/log")" can be
891+
used.
891892
If facility is not specified, LOG_USER is used. If socktype is
892893
specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
893894
socket type will be used. For Unix sockets, you can also specify a
@@ -938,7 +939,7 @@ def createSocket(self):
938939
address = self.address
939940
socktype = self.socktype
940941

941-
if isinstance(address, str):
942+
if not isinstance(address, (list, tuple)):
942943
self.unixsocket = True
943944
# Syslog server may be unavailable during handler initialisation.
944945
# C's openlog() function also ignores connection errors.

Lib/test/test_bytes.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,6 +2226,46 @@ def __len__(self):
22262226

22272227
self.assertRaises(BufferError, ba.hex, S(b':'))
22282228

2229+
def test_no_init_called(self):
2230+
# A bytearray created without calling bytearray.__init__
2231+
# should not crash the interpreter (see gh-153419).
2232+
def bytearray_new():
2233+
return bytearray.__new__(bytearray)
2234+
2235+
bytearray_new().insert(0, 1)
2236+
bytearray_new().extend(b"x")
2237+
bytearray_new().extend([1, 2, 3])
2238+
bytearray_new().resize(4)
2239+
bytearray_new().__init__(5)
2240+
bytearray_new().__init__(b"xyz")
2241+
bytearray_new().take_bytes()
2242+
bytearray_new().take_bytes(0)
2243+
2244+
a = bytearray_new()
2245+
a.append(1)
2246+
2247+
a = bytearray_new()
2248+
a += b"x"
2249+
2250+
a = bytearray_new()
2251+
a[:] = b"xyz"
2252+
2253+
def test_reinit_length(self):
2254+
# There is a shortcut taken when resizing, where alloc/2 < newsize.
2255+
# In this case, the existing buffer is reused, rather than reset.
2256+
# If this happens when newsize == 0 and alloc == 1, then various
2257+
# code assumptions can be violated. This test should catch those
2258+
# in debug builds. (see gh-153419)
2259+
a = bytearray(1)
2260+
a.__init__()
2261+
self.assertEqual(a, b"")
2262+
2263+
def test_reinit_with_view(self):
2264+
a = bytearray()
2265+
with memoryview(a):
2266+
self.assertRaises(BufferError, a.__init__, "x", "ascii")
2267+
self.assertEqual(a, b"")
2268+
22292269

22302270
class AssortedBytesTest(unittest.TestCase):
22312271
#

Lib/test/test_gdb/test_pretty_print.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,29 +114,28 @@ def test_bytes(self):
114114
@support.requires_resource('cpu')
115115
def test_strings(self):
116116
'Verify the pretty-printing of unicode strings'
117-
# We cannot simply call locale.getpreferredencoding() here,
118-
# as GDB might have been linked against a different version
119-
# of Python with a different encoding and coercion policy
120-
# with respect to PEP 538 and PEP 540.
117+
# gdb emits its output in the host charset, which is not necessarily the
118+
# getpreferredencoding() of the (possibly differently coerced) embedded
119+
# Python.
121120
stdout, stderr = run_gdb(
122121
'--eval-command',
123-
'python import locale; print(locale.getpreferredencoding())')
122+
'python import gdb; print(gdb.host_charset())')
124123

125-
encoding = stdout
124+
encoding = stdout.strip()
126125
if stderr or not encoding:
127126
raise RuntimeError(
128-
f'unable to determine the Python locale preferred encoding '
129-
f'of embedded Python in GDB\n'
127+
f'unable to determine the host charset of gdb\n'
130128
f'stdout={stdout!r}\n'
131129
f'stderr={stderr!r}')
132130

133131
def check_repr(text):
134132
try:
135133
text.encode(encoding)
136-
except UnicodeEncodeError:
134+
# LookupError or ValueError if the host charset is unknown or invalid.
135+
except (UnicodeEncodeError, LookupError, ValueError):
137136
self.assertGdbRepr(text, ascii(text))
138137
else:
139-
self.assertGdbRepr(text)
138+
self.assertGdbRepr(text, repr(text).encode(encoding).decode('ascii', 'surrogateescape'))
140139

141140
self.assertGdbRepr('')
142141
self.assertGdbRepr('And now for something hopefully the same')

Lib/test/test_gdb/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def run_gdb(*args, exitcode=0, check=True, **env_vars):
7878
stdin=subprocess.PIPE,
7979
stdout=subprocess.PIPE,
8080
stderr=subprocess.PIPE,
81-
encoding="utf8", errors="backslashreplace",
81+
encoding="ascii", errors="surrogateescape",
8282
env=env)
8383

8484
stdout = proc.stdout

Lib/test/test_logging.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,45 @@ def setUp(self):
21382138
self.addCleanup(os_helper.unlink, self.address)
21392139
SysLogHandlerTest.setUp(self)
21402140

2141+
def test_bytes_address(self):
2142+
# The Unix socket address can also be specified as bytes.
2143+
if self.server_exception:
2144+
self.skipTest(self.server_exception)
2145+
hdlr = logging.handlers.SysLogHandler(os.fsencode(self.address))
2146+
self.addCleanup(hdlr.close)
2147+
self.assertTrue(hdlr.unixsocket)
2148+
logger = logging.getLogger("slh-bytes")
2149+
logger.addHandler(hdlr)
2150+
self.addCleanup(logger.removeHandler, hdlr)
2151+
logger.error("sp\xe4m")
2152+
self.handled.wait(support.LONG_TIMEOUT)
2153+
self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
2154+
2155+
@unittest.skipUnless(sys.platform in ('linux', 'android'),
2156+
'Linux specific test')
2157+
class AbstractNamespaceSysLogHandlerTest(BaseTest):
2158+
2159+
"""Test for SysLogHandler with a socket in the abstract namespace."""
2160+
2161+
def check(self, address):
2162+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
2163+
self.addCleanup(sock.close)
2164+
sock.bind(address)
2165+
sock.settimeout(support.LONG_TIMEOUT)
2166+
hdlr = logging.handlers.SysLogHandler(address)
2167+
self.addCleanup(hdlr.close)
2168+
self.assertTrue(hdlr.unixsocket)
2169+
hdlr.emit(logging.makeLogRecord({'msg': 'sp\xe4m'}))
2170+
self.assertEqual(sock.recv(1024), b'<12>sp\xc3\xa4m\x00')
2171+
2172+
def test_str_address(self):
2173+
# A str address is encoded with the filesystem encoding.
2174+
self.check('\0' + os_helper.TESTFN)
2175+
2176+
def test_bytes_address(self):
2177+
# The name is an arbitrary byte sequence, it need not be decodable.
2178+
self.check(b'\0test_logging_%d_\xff\xfe' % os.getpid())
2179+
21412180
@unittest.skipUnless(socket_helper.IPV6_ENABLED,
21422181
'IPv6 support required for this test.')
21432182
class IPv6SysLogHandlerTest(SysLogHandlerTest):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix multiple :class:`bytearray` crashes and reference leaks caused by skipping :meth:`~object.__init__` and broken state setup code.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:class:`logging.handlers.SysLogHandler` now accepts a :class:`bytes` address
2+
of a Unix domain socket, including an address in the abstract namespace.
3+
Previously only :class:`str` was recognized,
4+
and a :class:`bytes` address raised :exc:`ValueError`.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix ``python-gdb.py`` raising :exc:`UnicodeEncodeError` when pretty-printing a
2+
non-ASCII :class:`str` in a locale whose host charset cannot encode it, such as
3+
any non-ASCII string in the C locale.

Objects/bytearrayobject.c

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
213213
{
214214
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
215215
PyByteArrayObject *obj = ((PyByteArrayObject *)self);
216+
217+
assert(obj->ob_bytes_object != NULL);
218+
216219
/* All computations are done unsigned to avoid integer overflows
217220
(see issue #22335). */
218221
size_t alloc = (size_t) obj->ob_alloc;
@@ -236,6 +239,14 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
236239
return -1;
237240
}
238241

242+
/* Resize to 0 resets to empty bytes (see issue #153419). */
243+
if (requested_size == 0) {
244+
Py_SETREF(obj->ob_bytes_object,
245+
Py_GetConstant(Py_CONSTANT_EMPTY_BYTES));
246+
bytearray_reinit_from_bytes(obj, 0, 0);
247+
return 0;
248+
}
249+
239250
if (size + logical_offset <= alloc) {
240251
/* Current buffer is large enough to host the requested size,
241252
decide on a strategy. */
@@ -902,6 +913,20 @@ bytearray_ass_subscript(PyObject *op, PyObject *index, PyObject *values)
902913
return ret;
903914
}
904915

916+
static PyObject *
917+
bytearray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
918+
{
919+
PyObject *op = PyType_GenericNew(type, args, kwds);
920+
if (op == NULL) {
921+
return NULL;
922+
}
923+
PyByteArrayObject *self = _PyByteArray_CAST(op);
924+
self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
925+
bytearray_reinit_from_bytes(self, 0, 0);
926+
self->ob_exports = 0;
927+
return op;
928+
}
929+
905930
/*[clinic input]
906931
bytearray.__init__
907932
@@ -920,20 +945,16 @@ bytearray___init___impl(PyByteArrayObject *self, PyObject *arg,
920945
PyObject *it;
921946
PyObject *(*iternext)(PyObject *);
922947

923-
/* First __init__; set ob_bytes_object so ob_bytes is always non-null. */
924-
if (self->ob_bytes_object == NULL) {
925-
self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
926-
bytearray_reinit_from_bytes(self, 0, 0);
927-
self->ob_exports = 0;
948+
/* Disallow any __init__ call if the object is not resizable (has exports)
949+
to make the handling of non-null `source` init values simpler. */
950+
if (!_canresize(self)) {
951+
return -1;
928952
}
929953

930-
if (Py_SIZE(self) != 0) {
931-
/* Empty previous contents (yes, do this first of all!) */
932-
if (PyByteArray_Resize((PyObject *)self, 0) < 0)
933-
return -1;
954+
/* Empty any previous contents (do this first of all!). */
955+
if (PyByteArray_Resize((PyObject *)self, 0) < 0) {
956+
return -1;
934957
}
935-
936-
/* Should be caused by first init or the resize to 0. */
937958
assert(self->ob_bytes_object == Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_BYTES));
938959
assert(self->ob_exports == 0);
939960

@@ -1609,6 +1630,9 @@ bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n)
16091630
}
16101631

16111632
if (_PyBytes_Resize(&self->ob_bytes_object, to_take) == -1) {
1633+
assert(self->ob_bytes_object == NULL);
1634+
self->ob_bytes_object = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
1635+
bytearray_reinit_from_bytes(self, 0, 0);
16121636
Py_DECREF(remaining);
16131637
return NULL;
16141638
}
@@ -2939,7 +2963,7 @@ PyTypeObject PyByteArray_Type = {
29392963
0, /* tp_dictoffset */
29402964
bytearray___init__, /* tp_init */
29412965
PyType_GenericAlloc, /* tp_alloc */
2942-
PyType_GenericNew, /* tp_new */
2966+
bytearray_new, /* tp_new */
29432967
PyObject_Free, /* tp_free */
29442968
.tp_version_tag = _Py_TYPE_VERSION_BYTEARRAY,
29452969
};

0 commit comments

Comments
 (0)