11//! Implements calling functions from a native library.
22
3+ use std:: borrow:: Cow ;
4+ use std:: cell:: RefCell ;
35use std:: ops:: Deref ;
6+ use std:: os:: raw:: c_void;
47use std:: sync:: atomic:: AtomicBool ;
58
69use libffi:: low:: CodePtr ;
@@ -16,6 +19,14 @@ use self::helpers::ToSoft;
1619
1720mod ffi;
1821
22+ struct CallbackError {
23+ message : Cow < ' static , str > ,
24+ }
25+
26+ thread_local ! {
27+ pub static CALLBACK_MESSAGES : RefCell <Vec <CallbackError >> = RefCell :: new( Vec :: new( ) ) ;
28+ }
29+
1930#[ cfg_attr(
2031 not( all(
2132 target_os = "linux" ,
@@ -92,6 +103,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
92103 let alloc = ( ) ;
93104
94105 trace:: Supervisor :: do_ffi ( alloc, || {
106+ // clear the callback error buffer
107+ CALLBACK_MESSAGES . with_borrow_mut ( |c| c. clear ( ) ) ;
95108 // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
96109 // as the specified primitive integer type
97110 let scalar = match dest. layout . ty . kind ( ) {
@@ -166,6 +179,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
166179 ) )
167180 . into ( ) ,
168181 } ;
182+ let callback_error_messages = CALLBACK_MESSAGES . take ( ) ;
183+ if !callback_error_messages. is_empty ( ) {
184+ let first = callback_error_messages. first ( ) . unwrap ( ) ;
185+ return Err ( err_unsup_format ! ( "{}" , first. message) ) . into ( ) ;
186+ }
169187 interp_ok ( ImmTy :: from_scalar ( scalar, dest. layout ) )
170188 } )
171189 }
@@ -285,7 +303,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
285303
286304 /// Extract the value from the result of reading an operand from the machine
287305 /// and convert it to a `OwnedArg`.
288- fn op_to_ffi_arg ( & self , v : & OpTy < ' tcx > , tracing : bool ) -> InterpResult < ' tcx , OwnedArg > {
306+ fn op_to_ffi_arg ( & self , v : & OpTy < ' tcx > , tracing : bool , link_name : & Symbol ) -> InterpResult < ' tcx , OwnedArg > {
289307 let this = self . eval_context_ref ( ) ;
290308
291309 // This should go first so that we emit unsupported before doing a bunch
@@ -310,6 +328,46 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
310328 // casting the integer in `byte` to a pointer and using that.
311329 let bytes = match v. as_mplace_or_imm ( ) {
312330 either:: Either :: Left ( mplace) => {
331+ let ptr_overwrite = match v. layout . ty . kind ( ) {
332+ ty:: Adt ( _adt_def, args) =>
333+ if let ty:: FnPtr ( fn_ptr, _header) =
334+ args. type_at ( 0 ) . kind ( )
335+ {
336+ let args = fn_ptr
337+ . skip_binder ( )
338+ . inputs ( )
339+ . into_iter ( )
340+ . map ( |i| {
341+ let layout = this. layout_of ( i. clone ( ) ) ?;
342+ this. ty_to_ffitype ( layout)
343+ } )
344+ . collect :: < InterpResult < ' _ , Vec < _ > > > ( ) ?;
345+ let res_type = fn_ptr. skip_binder ( ) . output ( ) ;
346+ let res_type = {
347+ let layout = this. layout_of ( res_type) ?;
348+ this. ty_to_ffitype ( layout) ?
349+ } ;
350+ let closure_builder = libffi:: middle:: Builder :: new ( )
351+ . args ( args)
352+ . res ( res_type)
353+ . abi ( libffi:: raw:: ffi_abi_FFI_UNIX64) ;
354+ let data = CallbackData {
355+ args : fn_ptr. skip_binder ( ) . inputs ( ) . to_vec ( ) ,
356+ result : fn_ptr. skip_binder ( ) . output ( ) ,
357+ this,
358+ link_name : link_name. clone ( ) ,
359+ ty : v. layout . ty ,
360+ } ;
361+ // todo: leaking is likely not optimal here
362+ let data = Box :: leak ( Box :: new ( data) ) ;
363+
364+ let closure = closure_builder. into_closure ( callback_callback, data) ;
365+ Some ( closure)
366+ } else {
367+ None
368+ } ,
369+ _ => None ,
370+ } ;
313371 // Get the alloc id corresponding to this mplace, alongside
314372 // a pointer that's offset to point to this particular
315373 // mplace (not one at the base addr of the allocation).
@@ -330,7 +388,35 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
330388 // Read the bytes that make up this argument. We cannot use the normal getter as
331389 // those would fail if any part of the argument is uninitialized. Native code
332390 // is kind of outside the interpreter, after all...
333- Box :: from ( alloc. inspect_with_uninit_and_ptr_outside_interpreter ( range) )
391+ let ret: Box < [ u8 ] > =
392+ Box :: from ( alloc. inspect_with_uninit_and_ptr_outside_interpreter ( range) ) ;
393+ if ret. iter ( ) . any ( |b| * b != 0 )
394+ && let Some ( ptr_overwrite) = ptr_overwrite
395+ {
396+ // we need to leak the closure here as we don't know when it's actually called
397+ // I'm not sure if it's possible to have a better solution for that
398+ let ptr_overwrite = Box :: leak ( Box :: new ( ptr_overwrite) ) ;
399+
400+ // we get a **reference** to a function ptr here
401+ // (The actual argument type doesn't matter)
402+ let ptr = unsafe {
403+ ptr_overwrite. instantiate_code_ptr :: < unsafe extern "C" fn ( * const c_void ) > ( )
404+ } ;
405+ // so deref away the reference
406+ let ptr = * ptr;
407+ // cast it to void as the actual function type doesn't matter
408+ let ptr = ptr as * const c_void ;
409+ // get a the address as usize to write it into the
410+ // right memory location
411+ let bytes = ptr. addr ( ) ;
412+ // bytes are in native endian, as that's literally
413+ // the definition of native endian
414+ let bytes = usize:: to_ne_bytes ( bytes) ;
415+ // return the bytes of the ptr
416+ Box :: from ( bytes)
417+ } else {
418+ ret
419+ }
334420 }
335421 either:: Either :: Right ( imm) => {
336422 let mut bytes: Box < [ u8 ] > = vec ! [ 0 ; imm. layout. size. bytes_usize( ) ] . into ( ) ;
@@ -439,7 +525,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
439525 interp_ok ( match layout. ty . kind ( ) {
440526 // Scalar types have already been handled above.
441527 ty:: Adt ( adt_def, args) => self . adt_to_ffitype ( layout. ty , * adt_def, args) ?,
442- _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , layout. ty) ,
528+ // Functions with no declared return type (i.e., the default return)
529+ // have the output_type `Tuple([])`.
530+ ty:: Tuple ( t_list) if ( * t_list) . deref ( ) . is_empty ( ) => FfiType :: void ( ) ,
531+ _ => {
532+ throw_unsup_format ! ( "unsupported argument type for native call: {}" , layout. ty)
533+ }
443534 } )
444535 }
445536}
@@ -473,7 +564,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
473564 // Get the function arguments, copy them, and prepare the type descriptions.
474565 let mut libffi_args = Vec :: < OwnedArg > :: with_capacity ( args. len ( ) ) ;
475566 for arg in args. iter ( ) {
476- libffi_args. push ( this. op_to_ffi_arg ( arg, tracing) ?) ;
567+ libffi_args. push ( this. op_to_ffi_arg ( arg, tracing, & link_name ) ?) ;
477568 }
478569
479570 // Prepare all exposed memory (both previously exposed, and just newly exposed since a
@@ -536,3 +627,92 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
536627 interp_ok ( true )
537628 }
538629}
630+
631+ struct CallbackData < ' a , ' tcx > {
632+ args : Vec < Ty < ' tcx > > ,
633+ result : Ty < ' tcx > ,
634+ this : & ' a MiriInterpCx < ' tcx > ,
635+ link_name : Symbol ,
636+ ty : Ty < ' tcx > ,
637+ }
638+
639+ unsafe extern "C" fn callback_callback (
640+ cif : & libffi:: low:: ffi_cif ,
641+ result : & mut c_void ,
642+ args : * const * const c_void ,
643+ infos : & CallbackData < ' _ , ' _ > ,
644+ ) {
645+ debug_assert_eq ! ( cif. nargs as usize , infos. args. len( ) ) ;
646+ let mut rust_args = Vec :: with_capacity ( infos. args . len ( ) ) ;
647+ // cast away the pointer to pointer
648+ let mut args = args as * const c_void ;
649+ for arg in & infos. args {
650+ let scalar = match arg. kind ( ) {
651+ ty:: RawPtr ( ..) => {
652+ let ptr = StrictPointer :: new ( Provenance :: Wildcard , Size :: from_bytes ( args. addr ( ) ) ) ;
653+ args = unsafe { args. offset ( 1 ) } ;
654+ Scalar :: from_pointer ( ptr, infos. this )
655+ }
656+ // the other types
657+ _ => todo ! ( ) ,
658+ } ;
659+ rust_args. push ( scalar) ;
660+ }
661+
662+ CALLBACK_MESSAGES . with_borrow_mut ( |msgs| {
663+ msgs. push ( CallbackError {
664+ message : format ! ( "Tried to call a function pointer via FFI boundary. \
665+ That's not supported yet by miri\n This function pointer was registered by a call to `{}` \
666+ using an argument of the type `{}`", infos. link_name, infos. ty)
667+ . into ( ) ,
668+ } ) ;
669+ } ) ;
670+
671+ // write here the output
672+ // For now we just try to write some dummy output
673+ // by using some "reasonable" default values
674+ // to prevent crashing
675+ match infos. result . kind ( ) {
676+ ty:: RawPtr ( ..) => {
677+ write_helper :: < * mut c_void > ( result, std:: ptr:: null_mut ( ) ) ;
678+ }
679+ ty:: Int ( IntTy :: I8 ) => {
680+ write_helper :: < i8 > ( result, 0 ) ;
681+ }
682+ ty:: Int ( IntTy :: I16 ) => {
683+ write_helper :: < i32 > ( result, 0 ) ;
684+ }
685+ ty:: Int ( IntTy :: I32 ) => {
686+ write_helper :: < i32 > ( result, 0 ) ;
687+ }
688+ ty:: Int ( IntTy :: I64 ) => {
689+ write_helper :: < i64 > ( result, 0 ) ;
690+ }
691+ ty:: Int ( IntTy :: Isize ) => {
692+ write_helper :: < isize > ( result, 0 ) ;
693+ }
694+ ty:: Uint ( UintTy :: U8 ) => {
695+ write_helper :: < u8 > ( result, 0 ) ;
696+ }
697+ ty:: Uint ( UintTy :: U16 ) => {
698+ write_helper :: < u16 > ( result, 0 ) ;
699+ }
700+ ty:: Uint ( UintTy :: U32 ) => {
701+ write_helper :: < u32 > ( result, 0 ) ;
702+ }
703+ ty:: Uint ( UintTy :: U64 ) => {
704+ write_helper :: < u64 > ( result, 0 ) ;
705+ }
706+ ty:: Uint ( UintTy :: Usize ) => {
707+ write_helper :: < usize > ( result, 0 ) ;
708+ }
709+ // unsure how to handle that at allow
710+ // Just do nothing for now?
711+ _ => { }
712+ } ;
713+ }
714+
715+ fn write_helper < T > ( ptr : & mut c_void , value : T ) {
716+ let ptr = ( ptr as * mut c_void ) as * mut T ;
717+ unsafe { std:: ptr:: write ( ptr, value) } ;
718+ }
0 commit comments