1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
//! Controlling the behavior of BVE when it panics. //! //! Panicking is by definition a bug in BVE, but it does happen, and it needs to be handled //! before the application is shutdown. The global panic handler consists of two parts. //! A function pointer and a void* data. They are both stored globally and can be controlled with //! the functions in this module. The function is called with the void* and a string which //! contains printable information about the panic. //! //! There is a [`bve_default_panic_handler`] which is called if you don't manually set your own. //! This takes the provided string and prints it to stderr and returns. //! //! # Safety //! //! There is a race condition between [`bve_set_panic_handler`] and [`bve_set_panic_data`]. //! //! This race is, in practice, little cause for concern. As long as no other rust code is executing, there is //! no problem. If you call these at the beginning of the program, like would be expected, there's no chance of a race. //! //! If: //! - you set the panic handler, //! - you haven't set the panic data to the proper pointer, //! - there is rust code that is currently panicking, //! //! the new panic handler will be called with the wrong pointer. This can cause all kinds of bad things if the panic //! handler expects that pointer to be of a specific type. use std::ffi::{c_void, CStr, CString}; use std::io::Write; use std::os::raw::c_char; use std::panic::PanicInfo; use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; /// Function pointer type for the Panic Handler. /// /// # Arguments /// /// - `void*`: The data pointer provided using [`bve_set_panic_data`]. Allowed to be null. /// - `const char*`: String containing human readable information about the panic, including a backtrace. Will never be /// null. /// /// # Safety /// /// Always allow `void*` to be null. You may assume the string is never null, utf8, and null terminated. pub type PanicHandler = unsafe extern "C" fn(*mut c_void, *const c_char) -> (); // Due to the lack of Atomic Function Pointers, we must do some bullshit that is very questionable. type PanicHandlerProxy = *mut PanicHandler; // Internal statics for the Panic Handler and its data static PANIC_HANDLER: AtomicPtr<PanicHandler> = AtomicPtr::new(bve_default_panic_handler as PanicHandlerProxy); static PANIC_HANDLER_DATA: AtomicPtr<c_void> = AtomicPtr::new(null_mut()); /// The default panic handler that is automatically installed. Does not touch the data pointer. /// Prints the string to stderr, panicking (and aborting) if it fails. /// /// # Safety /// /// The string must be non-null per the contract of [`PanicHandler`]. #[no_mangle] pub unsafe extern "C" fn bve_default_panic_handler(_: *mut c_void, string: *const c_char) { std::io::stderr() .lock() .write_all(CStr::from_ptr(string).to_bytes()) .expect("Writing to stderr in panic handler failed."); } /// Sets the panic handler to the provided function pointer. /// /// # Safety /// /// - `handler` must not be null and must point to a valid function of the proper signature. /// - The function `handler` points to must uphold the invariants of the contract of [`PanicHandler`] /// - There is a minor race between this function and [`bve_set_panic_data`]. See module documentation. #[no_mangle] pub unsafe extern "C" fn bve_set_panic_handler(handler: PanicHandler) { let handler_transmute: PanicHandlerProxy = handler as PanicHandlerProxy; PANIC_HANDLER.store(handler_transmute, Ordering::SeqCst); } /// Sets the data passed to the panic handler. /// /// # Safety /// /// - If the installed panic handler touches this data, it must be non-null and point to the data it expects /// - There is a minor race between this function and [`bve_set_panic_handler`]. See module documentation. #[no_mangle] pub unsafe extern "C" fn bve_set_panic_data(data: *mut c_void) { PANIC_HANDLER_DATA.store(data, Ordering::SeqCst); } /// Returns the currently set panic handler. Non-null. #[no_mangle] pub extern "C" fn bve_get_panic_handler() -> PanicHandler { let handler_transmute: PanicHandlerProxy = PANIC_HANDLER.load(Ordering::SeqCst); let handler: PanicHandler = unsafe { std::mem::transmute(handler_transmute) }; handler } /// Returns the currently set data to be passed to the panic handler. May be null. #[no_mangle] pub extern "C" fn bve_get_panic_data() -> *mut c_void { PANIC_HANDLER_DATA.load(Ordering::SeqCst) } /// Hooks up our panic hook to the standard library hook. pub(crate) fn init_panic_handler() { std::panic::set_hook(Box::new(panic_dispatch)) } /// Direct callback from rust that provides the information for the panic handler and calls it. fn panic_dispatch(info: &PanicInfo<'_>) { let bt = backtrace::Backtrace::new(); let msg = format!( "Panic: {:?} {:}\n\n{:?}", info.payload() .downcast_ref::<&str>() .expect("Panic payload must be a &str"), info.location().expect("Panic info must have location"), bt ); let c_msg = CString::new(msg).expect("Formatted message must not have interior null byte"); unsafe { let handler_transmute: PanicHandlerProxy = PANIC_HANDLER.load(Ordering::SeqCst); let handler: PanicHandler = std::mem::transmute(handler_transmute); handler(PANIC_HANDLER_DATA.load(Ordering::SeqCst), c_msg.as_ptr()) } }