@@ -224,7 +224,7 @@ where
224
224
} ) ;
225
225
match ret {
226
226
Ok ( r) => r,
227
- Err ( ref err) => {
227
+ Err ( err) => {
228
228
// Protect against panics in C::error_value() causing UB
229
229
let guard = AbortOnDrop ( "handle_panic() / C::error_value()" ) ;
230
230
handle_panic ( Python :: assume_gil_acquired ( ) , err) ;
@@ -235,8 +235,29 @@ where
235
235
}
236
236
}
237
237
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
+ } ;
240
261
unsafe {
241
262
ffi:: PyErr_SetString ( ffi:: PyExc_SystemError , msg. as_ptr ( ) ) ;
242
263
}
0 commit comments