@@ -224,7 +224,7 @@ where
224224 } ) ;
225225 match ret {
226226 Ok ( r) => r,
227- Err ( ref err) => {
227+ Err ( err) => {
228228 // Protect against panics in C::error_value() causing UB
229229 let guard = AbortOnDrop ( "handle_panic() / C::error_value()" ) ;
230230 handle_panic ( Python :: assume_gil_acquired ( ) , err) ;
@@ -235,8 +235,29 @@ where
235235 }
236236}
237237
238- fn handle_panic ( _py : Python , _panic : & dyn any:: Any ) {
239- let msg = cstr ! ( "Rust panic" ) ;
238+ // This only needs `&dyn Any`, but we keep a `Box` all the way to avoid the
239+ // risk of a subtle bug in the caller where `&Box<dyn Any>` is coerced to
240+ // `&dyn Any` by unsizing with another layer of vtable and wide pointer,
241+ // instead of the expected auto-deref.
242+ fn handle_panic ( _py : Python , panic : Box < dyn any:: Any > ) {
243+ let panic_str = if let Some ( s) = panic. downcast_ref :: < String > ( ) {
244+ Some ( s. as_str ( ) )
245+ } else if let Some ( s) = panic. downcast_ref :: < & ' static str > ( ) {
246+ Some ( * s)
247+ } else {
248+ None
249+ } ;
250+ let panic_cstring = panic_str. and_then ( |s| {
251+ let result = CString :: new ( format ! ( "Rust panic: {}" , s) ) ;
252+ // Give up on representing the panic payload if it contains a null byte
253+ // TODO: use PyErr_SetObject instead, so a `char*` string isn’t needed?
254+ result. ok ( )
255+ } ) ;
256+ let msg = if let Some ( s) = & panic_cstring {
257+ s. as_c_str ( )
258+ } else {
259+ cstr ! ( "Rust panic" )
260+ } ;
240261 unsafe {
241262 ffi:: PyErr_SetString ( ffi:: PyExc_SystemError , msg. as_ptr ( ) ) ;
242263 }
0 commit comments