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,12 @@ 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 (
307+ & self ,
308+ v : & OpTy < ' tcx > ,
309+ tracing : bool ,
310+ link_name : & Symbol ,
311+ ) -> InterpResult < ' tcx , OwnedArg > {
289312 let this = self . eval_context_ref ( ) ;
290313
291314 // This should go first so that we emit unsupported before doing a bunch
@@ -310,6 +333,44 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
310333 // casting the integer in `byte` to a pointer and using that.
311334 let bytes = match v. as_mplace_or_imm ( ) {
312335 either:: Either :: Left ( mplace) => {
336+ let ptr_overwrite = match v. layout . ty . kind ( ) {
337+ ty:: Adt ( _adt_def, args) =>
338+ if let ty:: FnPtr ( fn_ptr, _header) = args. type_at ( 0 ) . kind ( ) {
339+ let args = fn_ptr
340+ . skip_binder ( )
341+ . inputs ( )
342+ . into_iter ( )
343+ . map ( |i| {
344+ let layout = this. layout_of ( i. clone ( ) ) ?;
345+ this. ty_to_ffitype ( layout)
346+ } )
347+ . collect :: < InterpResult < ' _ , Vec < _ > > > ( ) ?;
348+ let res_type = fn_ptr. skip_binder ( ) . output ( ) ;
349+ let res_type = {
350+ let layout = this. layout_of ( res_type) ?;
351+ this. ty_to_ffitype ( layout) ?
352+ } ;
353+ let closure_builder = libffi:: middle:: Builder :: new ( )
354+ . args ( args)
355+ . res ( res_type)
356+ . abi ( libffi:: raw:: ffi_abi_FFI_UNIX64) ;
357+ let data = CallbackData {
358+ args : fn_ptr. skip_binder ( ) . inputs ( ) . to_vec ( ) ,
359+ result : fn_ptr. skip_binder ( ) . output ( ) ,
360+ this,
361+ link_name : link_name. clone ( ) ,
362+ ty : v. layout . ty ,
363+ } ;
364+ // todo: leaking is likely not optimal here
365+ let data = Box :: leak ( Box :: new ( data) ) ;
366+
367+ let closure = closure_builder. into_closure ( callback_callback, data) ;
368+ Some ( closure)
369+ } else {
370+ None
371+ } ,
372+ _ => None ,
373+ } ;
313374 // Get the alloc id corresponding to this mplace, alongside
314375 // a pointer that's offset to point to this particular
315376 // mplace (not one at the base addr of the allocation).
@@ -330,7 +391,35 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
330391 // Read the bytes that make up this argument. We cannot use the normal getter as
331392 // those would fail if any part of the argument is uninitialized. Native code
332393 // is kind of outside the interpreter, after all...
333- Box :: from ( alloc. inspect_with_uninit_and_ptr_outside_interpreter ( range) )
394+ let ret: Box < [ u8 ] > =
395+ Box :: from ( alloc. inspect_with_uninit_and_ptr_outside_interpreter ( range) ) ;
396+ if ret. iter ( ) . any ( |b| * b != 0 )
397+ && let Some ( ptr_overwrite) = ptr_overwrite
398+ {
399+ // we need to leak the closure here as we don't know when it's actually called
400+ // I'm not sure if it's possible to have a better solution for that
401+ let ptr_overwrite = Box :: leak ( Box :: new ( ptr_overwrite) ) ;
402+
403+ // we get a **reference** to a function ptr here
404+ // (The actual argument type doesn't matter)
405+ let ptr = unsafe {
406+ ptr_overwrite. instantiate_code_ptr :: < unsafe extern "C" fn ( * const c_void ) > ( )
407+ } ;
408+ // so deref away the reference
409+ let ptr = * ptr;
410+ // cast it to void as the actual function type doesn't matter
411+ let ptr = ptr as * const c_void ;
412+ // get a the address as usize to write it into the
413+ // right memory location
414+ let bytes = ptr. addr ( ) ;
415+ // bytes are in native endian, as that's literally
416+ // the definition of native endian
417+ let bytes = usize:: to_ne_bytes ( bytes) ;
418+ // return the bytes of the ptr
419+ Box :: from ( bytes)
420+ } else {
421+ ret
422+ }
334423 }
335424 either:: Either :: Right ( imm) => {
336425 let mut bytes: Box < [ u8 ] > = vec ! [ 0 ; imm. layout. size. bytes_usize( ) ] . into ( ) ;
@@ -439,7 +528,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
439528 interp_ok ( match layout. ty . kind ( ) {
440529 // Scalar types have already been handled above.
441530 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) ,
531+ // Functions with no declared return type (i.e., the default return)
532+ // have the output_type `Tuple([])`.
533+ ty:: Tuple ( t_list) if ( * t_list) . deref ( ) . is_empty ( ) => FfiType :: void ( ) ,
534+ _ => {
535+ throw_unsup_format ! ( "unsupported argument type for native call: {}" , layout. ty)
536+ }
443537 } )
444538 }
445539}
@@ -473,7 +567,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
473567 // Get the function arguments, copy them, and prepare the type descriptions.
474568 let mut libffi_args = Vec :: < OwnedArg > :: with_capacity ( args. len ( ) ) ;
475569 for arg in args. iter ( ) {
476- libffi_args. push ( this. op_to_ffi_arg ( arg, tracing) ?) ;
570+ libffi_args. push ( this. op_to_ffi_arg ( arg, tracing, & link_name ) ?) ;
477571 }
478572
479573 // Prepare all exposed memory (both previously exposed, and just newly exposed since a
@@ -536,3 +630,92 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
536630 interp_ok ( true )
537631 }
538632}
633+
634+ struct CallbackData < ' a , ' tcx > {
635+ args : Vec < Ty < ' tcx > > ,
636+ result : Ty < ' tcx > ,
637+ this : & ' a MiriInterpCx < ' tcx > ,
638+ link_name : Symbol ,
639+ ty : Ty < ' tcx > ,
640+ }
641+
642+ unsafe extern "C" fn callback_callback (
643+ cif : & libffi:: low:: ffi_cif ,
644+ result : & mut c_void ,
645+ args : * const * const c_void ,
646+ infos : & CallbackData < ' _ , ' _ > ,
647+ ) {
648+ debug_assert_eq ! ( cif. nargs as usize , infos. args. len( ) ) ;
649+ let mut rust_args = Vec :: with_capacity ( infos. args . len ( ) ) ;
650+ // cast away the pointer to pointer
651+ let mut args = args as * const c_void ;
652+ for arg in & infos. args {
653+ let scalar = match arg. kind ( ) {
654+ ty:: RawPtr ( ..) => {
655+ let ptr = StrictPointer :: new ( Provenance :: Wildcard , Size :: from_bytes ( args. addr ( ) ) ) ;
656+ args = unsafe { args. offset ( 1 ) } ;
657+ Scalar :: from_pointer ( ptr, infos. this )
658+ }
659+ // the other types
660+ _ => todo ! ( ) ,
661+ } ;
662+ rust_args. push ( scalar) ;
663+ }
664+
665+ CALLBACK_MESSAGES . with_borrow_mut ( |msgs| {
666+ msgs. push ( CallbackError {
667+ message : format ! ( "Tried to call a function pointer via FFI boundary. \
668+ That's not supported yet by miri\n This function pointer was registered by a call to `{}` \
669+ using an argument of the type `{}`", infos. link_name, infos. ty)
670+ . into ( ) ,
671+ } ) ;
672+ } ) ;
673+
674+ // write here the output
675+ // For now we just try to write some dummy output
676+ // by using some "reasonable" default values
677+ // to prevent crashing
678+ match infos. result . kind ( ) {
679+ ty:: RawPtr ( ..) => {
680+ write_helper :: < * mut c_void > ( result, std:: ptr:: null_mut ( ) ) ;
681+ }
682+ ty:: Int ( IntTy :: I8 ) => {
683+ write_helper :: < i8 > ( result, 0 ) ;
684+ }
685+ ty:: Int ( IntTy :: I16 ) => {
686+ write_helper :: < i32 > ( result, 0 ) ;
687+ }
688+ ty:: Int ( IntTy :: I32 ) => {
689+ write_helper :: < i32 > ( result, 0 ) ;
690+ }
691+ ty:: Int ( IntTy :: I64 ) => {
692+ write_helper :: < i64 > ( result, 0 ) ;
693+ }
694+ ty:: Int ( IntTy :: Isize ) => {
695+ write_helper :: < isize > ( result, 0 ) ;
696+ }
697+ ty:: Uint ( UintTy :: U8 ) => {
698+ write_helper :: < u8 > ( result, 0 ) ;
699+ }
700+ ty:: Uint ( UintTy :: U16 ) => {
701+ write_helper :: < u16 > ( result, 0 ) ;
702+ }
703+ ty:: Uint ( UintTy :: U32 ) => {
704+ write_helper :: < u32 > ( result, 0 ) ;
705+ }
706+ ty:: Uint ( UintTy :: U64 ) => {
707+ write_helper :: < u64 > ( result, 0 ) ;
708+ }
709+ ty:: Uint ( UintTy :: Usize ) => {
710+ write_helper :: < usize > ( result, 0 ) ;
711+ }
712+ // unsure how to handle that at allow
713+ // Just do nothing for now?
714+ _ => { }
715+ } ;
716+ }
717+
718+ fn write_helper < T > ( ptr : & mut c_void , value : T ) {
719+ let ptr = ( ptr as * mut c_void ) as * mut T ;
720+ unsafe { std:: ptr:: write ( ptr, value) } ;
721+ }
0 commit comments