@@ -40,6 +40,7 @@ pub mod types;
4040pub mod recovery;
4141
4242use core:: { slice, ptr} ;
43+ use core:: sync:: atomic:: AtomicPtr ;
4344use types:: * ;
4445
4546/// Flag for context to enable no precomputation
@@ -576,6 +577,91 @@ pub unsafe fn secp256k1_context_destroy(ctx: *mut Context) {
576577 rustsecp256k1_v0_4_1_context_destroy ( ctx)
577578}
578579
580+ static ABORT_HANDLER : AtomicPtr < fn ( & dyn core:: fmt:: Display ) -> !> = AtomicPtr :: new ( core:: ptr:: null_mut ( ) ) ;
581+
582+ /// Registers custom abort handler globally.
583+ ///
584+ /// libsecp256k1 may want to abort in case of invalid inputs. These are definitely bugs.
585+ /// The default handler aborts with `std` and **loops forever without `std`**.
586+ /// You can provide your own handler if you wish to override this behavior.
587+ ///
588+ /// This function is `unsafe` because the supplied handler MUST NOT unwind
589+ /// since unwinding would cross FFI boundary.
590+ /// Double panic is *also* wrong!
591+ ///
592+ /// Supplying panicking function may be safe if your program is compiled with `panic = "abort"`.
593+ /// It's **not** recommended to call this function from library crates - only binaries, somewhere
594+ /// near the beginning of `main()`.
595+ ///
596+ /// The parameter passed to the handler is an error message that can be displayed (logged).
597+ ///
598+ /// Note that the handler is a reference to function pointer rather than a function pointer itself
599+ /// because of [a missing Rust feature](https://github.com/rust-lang/rfcs/issues/2481).
600+ /// It's a bit tricky to use it, so here's an example:
601+ ///
602+ /// ```
603+ /// fn custom_abort(message: &dyn std::fmt::Display) -> ! {
604+ /// eprintln!("this is a custom abort handler: {}", message);
605+ /// std::process::abort()
606+ /// }
607+ /// // We need to put the function pointer into a static variable because we need a 'static
608+ /// // reference to variable holding function pointer.
609+ /// static CUSTOM_ABORT: fn (&dyn std::fmt::Display) -> ! = custom_abort;
610+ ///
611+ /// unsafe {
612+ /// secp256k1_sys::set_abort_handler(&CUSTOM_ABORT);
613+ /// }
614+ /// ```
615+ ///
616+ /// The function does not guarantee any memory ordering so you MUST NOT abuse it for synchronization!
617+ /// Use some other synchronization primitive if you need to synchronize.
618+ pub unsafe fn set_abort_handler ( handler : & ' static fn ( & dyn core:: fmt:: Display ) -> !) {
619+ ABORT_HANDLER . store ( ptr_const_to_mut_cast ( handler) , core:: sync:: atomic:: Ordering :: Relaxed ) ;
620+ }
621+
622+ /// FFI-safe replacement for panic
623+ ///
624+ /// Prints to stderr and aborts with `std`, loops forever without `std`.
625+ #[ cfg_attr( not( feature = "std" ) , allow( unused) ) ]
626+ fn abort_fallback ( message : impl core:: fmt:: Display ) -> ! {
627+ #[ cfg( feature = "std" ) ]
628+ {
629+ eprintln ! ( "[libsecp256k1] {}" , message) ;
630+ std:: process:: abort ( )
631+ }
632+ #[ cfg( not( feature = "std" ) ) ]
633+ {
634+ // no better way to "abort" without std :(
635+ loop { }
636+ }
637+ }
638+
639+ /// Ensures that types both sides of cast stay in sync and only the constness changes.
640+ ///
641+ /// This elliminates the risk that if we change the type signature of abort handler the cast
642+ /// silently converts the types and causes UB.
643+ fn ptr_mut_to_const_cast < T > ( ptr : * mut T ) -> * const T {
644+ ptr as _
645+ }
646+
647+ /// Ensures that types both sides of cast stay in sync and only the constness changes.
648+ ///
649+ /// This elliminates the risk that if we change the type signature of abort handler the cast
650+ /// silently converts the types and causes UB.
651+ fn ptr_const_to_mut_cast < T > ( ptr : * const T ) -> * mut T {
652+ ptr as _
653+ }
654+
655+ fn abort_with_message ( message : impl core:: fmt:: Display ) -> ! {
656+ unsafe {
657+ let handler = ptr_mut_to_const_cast ( ABORT_HANDLER . load ( core:: sync:: atomic:: Ordering :: Relaxed ) ) ;
658+ if !handler. is_null ( ) {
659+ ( * handler) ( & message)
660+ } else {
661+ abort_fallback ( message)
662+ }
663+ }
664+ }
579665
580666/// **This function is an override for the C function, this is the an edited version of the original description:**
581667///
@@ -601,7 +687,7 @@ pub unsafe extern "C" fn rustsecp256k1_v0_4_1_default_illegal_callback_fn(messag
601687 use core:: str;
602688 let msg_slice = slice:: from_raw_parts ( message as * const u8 , strlen ( message) ) ;
603689 let msg = str:: from_utf8_unchecked ( msg_slice) ;
604- panic ! ( "[libsecp256k1] illegal argument. {}" , msg) ;
690+ abort_with_message ( format_args ! ( "illegal argument. {}" , msg) ) ;
605691}
606692
607693/// **This function is an override for the C function, this is the an edited version of the original description:**
@@ -624,7 +710,7 @@ pub unsafe extern "C" fn rustsecp256k1_v0_4_1_default_error_callback_fn(message:
624710 use core:: str;
625711 let msg_slice = slice:: from_raw_parts ( message as * const u8 , strlen ( message) ) ;
626712 let msg = str:: from_utf8_unchecked ( msg_slice) ;
627- panic ! ( "[libsecp256k1] internal consistency check failed {}" , msg) ;
713+ abort_with_message ( format_args ! ( "internal consistency check failed {}" , msg) ) ;
628714}
629715
630716#[ cfg( not( rust_secp_no_symbol_renaming) ) ]
@@ -833,7 +919,7 @@ mod fuzz_dummy {
833919 * output = 4 ;
834920 ptr:: copy ( ( * pk) . 0 . as_ptr ( ) , output. offset ( 1 ) , 64 ) ;
835921 } else {
836- panic ! ( "Bad flags" ) ;
922+ abort_with_message ( format_args ! ( "Bad flags" ) ) ;
837923 }
838924 1
839925 }
0 commit comments