File tree 3 files changed +34
-7
lines changed
3 files changed +34
-7
lines changed Original file line number Diff line number Diff line change @@ -501,11 +501,29 @@ struct error_fetch_and_normalize {
501
501
std::string message_error_string;
502
502
if (m_value) {
503
503
auto value_str = reinterpret_steal<object>(PyObject_Str (m_value.ptr ()));
504
+ constexpr const char *message_unavailable_exc
505
+ = " <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION>" ;
504
506
if (!value_str) {
505
507
message_error_string = detail::error_string ();
506
- result = " <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION> " ;
508
+ result = message_unavailable_exc ;
507
509
} else {
508
- result = value_str.cast <std::string>();
510
+ // Not using `value_str.cast<std::string>()`, to not potentially throw a secondary
511
+ // error_already_set that will then result in process termination (#4288).
512
+ auto value_bytes = reinterpret_steal<object>(
513
+ PyUnicode_AsEncodedString (value_str.ptr (), " utf-8" , " backslashreplace" ));
514
+ if (!value_bytes) {
515
+ message_error_string = detail::error_string ();
516
+ result = message_unavailable_exc;
517
+ } else {
518
+ char *buffer = nullptr ;
519
+ Py_ssize_t length = 0 ;
520
+ if (PyBytes_AsStringAndSize (value_bytes.ptr (), &buffer, &length) == -1 ) {
521
+ message_error_string = detail::error_string ();
522
+ result = message_unavailable_exc;
523
+ } else {
524
+ result = std::string (buffer, static_cast <std::size_t >(length));
525
+ }
526
+ }
509
527
}
510
528
} else {
511
529
result = " <MESSAGE UNAVAILABLE>" ;
Original file line number Diff line number Diff line change @@ -105,11 +105,6 @@ struct PythonAlreadySetInDestructor {
105
105
py::str s;
106
106
};
107
107
108
- std::string error_already_set_what (const py::object &exc_type, const py::object &exc_value) {
109
- PyErr_SetObject (exc_type.ptr (), exc_value.ptr ());
110
- return py::error_already_set ().what ();
111
- }
112
-
113
108
TEST_SUBMODULE (exceptions, m) {
114
109
m.def (" throw_std_exception" ,
115
110
[]() { throw std::runtime_error (" This exception was intentionally thrown." ); });
Original file line number Diff line number Diff line change @@ -275,6 +275,20 @@ def test_local_translator(msg):
275
275
assert msg (excinfo .value ) == "this mod"
276
276
277
277
278
+ def test_error_already_set_message_with_unicode_surrogate (): # Issue #4288
279
+ assert m .error_already_set_what (RuntimeError , "\ud927 " ) == (
280
+ "RuntimeError: \\ ud927" ,
281
+ False ,
282
+ )
283
+
284
+
285
+ def test_error_already_set_message_with_malformed_utf8 ():
286
+ assert m .error_already_set_what (RuntimeError , b"\x80 " ) == (
287
+ "RuntimeError: b'\\ x80'" ,
288
+ False ,
289
+ )
290
+
291
+
278
292
class FlakyException (Exception ):
279
293
def __init__ (self , failure_point ):
280
294
if failure_point == "failure_point_init" :
You can’t perform that action at this time.
0 commit comments