pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/ddc27f9c385f57db1c227b655ec84dcf097a8976

8faa60c69660fa.css" /> gh-128974: Fix `UnicodeError.__str__` when custom attributes have sid… · python/cpython@ddc27f9 · GitHub
Skip to content

Commit ddc27f9

Browse files
authored
gh-128974: Fix UnicodeError.__str__ when custom attributes have side-effects (#128975)
Fix some crashes when (custom) attributes of `UnicodeError` objects implement `object.__str__` with side-effects.
1 parent 75f38af commit ddc27f9

3 files changed

Lines changed: 84 additions & 15 deletions

File tree

Lib/test/test_exceptions.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,43 @@ def test_unicode_error_str_does_not_crash(self):
13601360
exc = UnicodeDecodeError('utf-8', encoded, start, end, '')
13611361
self.assertIsInstance(str(exc), str)
13621362

1363+
def test_unicode_error_evil_str_set_none_object(self):
1364+
def side_effect(exc):
1365+
exc.object = None
1366+
self.do_test_unicode_error_mutate(side_effect)
1367+
1368+
def test_unicode_error_evil_str_del_self_object(self):
1369+
def side_effect(exc):
1370+
del exc.object
1371+
self.do_test_unicode_error_mutate(side_effect)
1372+
1373+
def do_test_unicode_error_mutate(self, side_effect):
1374+
# Test that str(UnicodeError(...)) does not crash when
1375+
# side-effects mutate the underlying 'object' attribute.
1376+
# See https://github.com/python/cpython/issues/128974.
1377+
1378+
class Evil(str):
1379+
def __str__(self):
1380+
side_effect(exc)
1381+
return self
1382+
1383+
for reason, encoding in [
1384+
("reason", Evil("utf-8")),
1385+
(Evil("reason"), "utf-8"),
1386+
(Evil("reason"), Evil("utf-8")),
1387+
]:
1388+
with self.subTest(encoding=encoding, reason=reason):
1389+
with self.subTest(UnicodeEncodeError):
1390+
exc = UnicodeEncodeError(encoding, "x", 0, 1, reason)
1391+
self.assertRaises(TypeError, str, exc)
1392+
with self.subTest(UnicodeDecodeError):
1393+
exc = UnicodeDecodeError(encoding, b"x", 0, 1, reason)
1394+
self.assertRaises(TypeError, str, exc)
1395+
1396+
with self.subTest(UnicodeTranslateError):
1397+
exc = UnicodeTranslateError("x", 0, 1, Evil("reason"))
1398+
self.assertRaises(TypeError, str, exc)
1399+
13631400
@no_tracing
13641401
def test_badisinstance(self):
13651402
# Bug #2542: if issubclass(e, MyException) raises an exception,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in :meth:`UnicodeError.__str__ <object.__str__>` when custom
2+
attributes implement :meth:`~object.__str__` with side-effects.
3+
Patch by Bénédikt Tran.

Objects/exceptions.c

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,6 +2784,8 @@ SyntaxError_str(PyObject *op)
27842784
if (!filename && !have_lineno)
27852785
return PyObject_Str(self->msg ? self->msg : Py_None);
27862786

2787+
// Even if 'filename' can be an instance of a subclass of 'str',
2788+
// we only render its "true" content and do not use str(filename).
27872789
if (filename && have_lineno)
27882790
result = PyUnicode_FromFormat("%S (%U, line %ld)",
27892791
self->msg ? self->msg : Py_None,
@@ -2903,29 +2905,47 @@ SimpleExtendsException(PyExc_ValueError, UnicodeError,
29032905

29042906
/*
29052907
* Check the validity of 'attr' as a unicode or bytes object depending
2906-
* on 'as_bytes' and return a new reference on it if it is the case.
2908+
* on 'as_bytes'.
29072909
*
29082910
* The 'name' is the attribute name and is only used for error reporting.
29092911
*
2910-
* On success, this returns a strong reference on 'attr'.
2911-
* On failure, this sets a TypeError and returns NULL.
2912+
* On success, this returns 0.
2913+
* On failure, this sets a TypeError and returns -1.
29122914
*/
2913-
static PyObject *
2914-
as_unicode_error_attribute(PyObject *attr, const char *name, int as_bytes)
2915+
static int
2916+
check_unicode_error_attribute(PyObject *attr, const char *name, int as_bytes)
29152917
{
29162918
assert(as_bytes == 0 || as_bytes == 1);
29172919
if (attr == NULL) {
2918-
PyErr_Format(PyExc_TypeError, "%s attribute not set", name);
2919-
return NULL;
2920+
PyErr_Format(PyExc_TypeError,
2921+
"UnicodeError '%s' attribute is not set",
2922+
name);
2923+
return -1;
29202924
}
29212925
if (!(as_bytes ? PyBytes_Check(attr) : PyUnicode_Check(attr))) {
29222926
PyErr_Format(PyExc_TypeError,
2923-
"%s attribute must be %s",
2924-
name,
2925-
as_bytes ? "bytes" : "unicode");
2926-
return NULL;
2927+
"UnicodeError '%s' attribute must be a %s",
2928+
name, as_bytes ? "bytes" : "string");
2929+
return -1;
29272930
}
2928-
return Py_NewRef(attr);
2931+
return 0;
2932+
}
2933+
2934+
2935+
/*
2936+
* Check the validity of 'attr' as a unicode or bytes object depending
2937+
* on 'as_bytes' and return a new reference on it if it is the case.
2938+
*
2939+
* The 'name' is the attribute name and is only used for error reporting.
2940+
*
2941+
* On success, this returns a strong reference on 'attr'.
2942+
* On failure, this sets a TypeError and returns NULL.
2943+
*/
2944+
static PyObject *
2945+
as_unicode_error_attribute(PyObject *attr, const char *name, int as_bytes)
2946+
{
2947+
int rc = check_unicode_error_attribute(attr, name, as_bytes);
2948+
return rc < 0 ? NULL : Py_NewRef(attr);
29292949
}
29302950

29312951

@@ -3591,7 +3611,10 @@ UnicodeEncodeError_str(PyObject *self)
35913611
if (encoding_str == NULL) {
35923612
goto done;
35933613
}
3594-
3614+
// calls to PyObject_Str(...) above might mutate 'exc->object'
3615+
if (check_unicode_error_attribute(exc->object, "object", false) < 0) {
3616+
goto done;
3617+
}
35953618
Py_ssize_t len = PyUnicode_GET_LENGTH(exc->object);
35963619
Py_ssize_t start = exc->start, end = exc->end;
35973620

@@ -3711,7 +3734,10 @@ UnicodeDecodeError_str(PyObject *self)
37113734
if (encoding_str == NULL) {
37123735
goto done;
37133736
}
3714-
3737+
// calls to PyObject_Str(...) above might mutate 'exc->object'
3738+
if (check_unicode_error_attribute(exc->object, "object", true) < 0) {
3739+
goto done;
3740+
}
37153741
Py_ssize_t len = PyBytes_GET_SIZE(exc->object);
37163742
Py_ssize_t start = exc->start, end = exc->end;
37173743

@@ -3807,7 +3833,10 @@ UnicodeTranslateError_str(PyObject *self)
38073833
if (reason_str == NULL) {
38083834
goto done;
38093835
}
3810-
3836+
// call to PyObject_Str(...) above might mutate 'exc->object'
3837+
if (check_unicode_error_attribute(exc->object, "object", false) < 0) {
3838+
goto done;
3839+
}
38113840
Py_ssize_t len = PyUnicode_GET_LENGTH(exc->object);
38123841
Py_ssize_t start = exc->start, end = exc->end;
38133842

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy