File tree Expand file tree Collapse file tree 3 files changed +34
-7
lines changed Expand file tree Collapse file tree 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 {
501501 std::string message_error_string;
502502 if (m_value) {
503503 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>" ;
504506 if (!value_str) {
505507 message_error_string = detail::error_string ();
506- result = " <MESSAGE UNAVAILABLE DUE TO ANOTHER EXCEPTION> " ;
508+ result = message_unavailable_exc ;
507509 } 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+ }
509527 }
510528 } else {
511529 result = " <MESSAGE UNAVAILABLE>" ;
Original file line number Diff line number Diff line change @@ -105,11 +105,6 @@ struct PythonAlreadySetInDestructor {
105105 py::str s;
106106};
107107
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-
113108TEST_SUBMODULE (exceptions, m) {
114109 m.def (" throw_std_exception" ,
115110 []() { 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):
275275 assert msg (excinfo .value ) == "this mod"
276276
277277
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+
278292class FlakyException (Exception ):
279293 def __init__ (self , failure_point ):
280294 if failure_point == "failure_point_init" :
You can’t perform that action at this time.
0 commit comments